Skip to content

Commit

Permalink
[Feature Request]: 外部サービスからSkyshareへ入力を送りたい (#109)
Browse files Browse the repository at this point in the history
✨ リクエストパラメータからポスト内容の初期値を設定する実装を追加

♻️ コンポーネントに依存しない変数を分離 & jsDoc追記

💄 文字列の表示を「タイトル」「本文」「リンク」の順で並び替え & 関数のjsDocを追記

🎨 変数名をキャメルケースで統一

✨ リクエストパラメータからポスト内容の初期値を設定する実装を追加

♻️ コンポーネントに依存しない変数を分離 & jsDoc追記

💄 文字列の表示を「タイトル」「本文」「リンク」の順で並び替え & 関数のjsDocを追記

🎨 変数名をキャメルケースで統一

本文が初期化されている場合に、Linkcardを自動的に取得するコードを追加

---------

Co-authored-by: nekono <91360587+nkte8@users.noreply.github.com>
  • Loading branch information
ZEKE320 and nkte8 authored Apr 23, 2024
1 parent 01558fc commit 70fb8a4
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 72 deletions.
17 changes: 14 additions & 3 deletions astro/public/materials/manifest.webmanifest
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"$schema": "https://json.schemastore.org/web-manifest-combined.json",
"name": "Skyshare",
"short_name": "Skyshare",
"start_url": "/",
Expand All @@ -11,11 +12,21 @@
"sizes": "any",
"type": "image/svg+xml",
"purpose": "any"
},{
},
{
"src": "/materials/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
],
"share_target": {
"action": "/app",
"method": "GET",
"enctype": "application/x-www-form-urlencoded",
"params": {
"title": "sharedTitle",
"text": "sharedText",
"url": "sharedUrl"
}
}
}

50 changes: 48 additions & 2 deletions astro/src/components/Client/bsky/PostForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import LanguageSelectList from "./selectLists/LanguageSelectList"
import Details from "../common/Details"
import TagInputList from "./unique/TagInputList"
import SelfLabelsSelectList from "./selectLists/SelfLabelsSelectList"
import LinkcardAttachButton from "./buttons/LinkcardAttachButton"
import LinkcardAttachButton, {
handleGetOGP,
} from "./buttons/LinkcardAttachButton"
import PostButton from "./buttons/PostButton"
import AddImageButton from "./buttons/AddImageButton"
import MediaPreview from "./MediaPreview"
Expand All @@ -37,6 +39,34 @@ export type callbackPostOptions = {
previewData: Blob | null
}

/**
* リクエストパラメータから投稿テキストの初期値を生成します
* @param searchParams リクエストパラメータ
* @returns 投稿テキストの初期値
*/
const createInitialPostText = (searchParams: URLSearchParams) => {
const sharedTitle: string | null = searchParams.get("sharedTitle")
const sharedText: string | null = searchParams.get("sharedText")
const sharedUrl: string | null = searchParams.get("sharedUrl")

let sharedContent: string = ""
if (sharedTitle !== null) {
sharedContent += `${sharedTitle}\n`
}
if (sharedText !== null) {
sharedContent += `${sharedText}\n`
}
if (sharedUrl !== null) {
sharedContent += `${sharedUrl}\n`
}
return sharedContent
}

/** リクエストパラメータ */
const searchParams = new URLSearchParams(window.location.search)
/** 投稿テキストの初期値 */
const initialPostText: string = createInitialPostText(searchParams)

const Component = ({
setMsgInfo,
isProcessing,
Expand All @@ -54,8 +84,10 @@ const Component = ({
setMediaData: Dispatch<SetStateAction<MediaData>>
setPopupPreviewOptions: Dispatch<SetStateAction<popupPreviewOptions>>
}) => {
// 配置されたページのURL
const siteurl = location.origin
// Post内容を格納する変数とディスパッチャー
const [postText, setPostText] = useState<string>("")
const [postText, setPostText] = useState<string>(initialPostText)
// Postの実行状態を管理する変数とディスパッチャー
const [language, setLanguage] = useState<string>("ja")
// 下書きのstate情報
Expand All @@ -81,6 +113,19 @@ const Component = ({
e.preventDefault()
}
window.addEventListener("dragover", handleDragOver)
const getOGP = async () => {
await handleGetOGP({
postText,
setProcessing,
setMsgInfo,
siteurl,
setMediaData,
})
}
if (postText !== "") {
getOGP().catch((_: unknown) => {})
}

return () => {
window.removeEventListener("dragover", handleDragOver)
}
Expand Down Expand Up @@ -185,6 +230,7 @@ const Component = ({
/>
</TextInputBox>
<LinkcardAttachButton
siteurl={siteurl}
postText={postText}
setMediaData={setMediaData}
isProcessing={isProcessing}
Expand Down
166 changes: 99 additions & 67 deletions astro/src/components/Client/bsky/buttons/LinkcardAttachButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,95 +11,126 @@ import { getOgpMeta, getOgpBlob } from "@/lib/getOgp"
import { richTextFacetParser } from "@/utils/richTextParser"
import { msgInfo, MediaData } from "../../common/types"

export const Component = ({
const handleGetOGP = async ({
postText,
setMediaData,
isProcessing,
setProcessing,
setMsgInfo,
siteurl,
setMediaData,
}: {
postText: string
setMediaData: Dispatch<SetStateAction<MediaData | null>>
isProcessing: boolean
setProcessing: Dispatch<SetStateAction<boolean>>
setMsgInfo: Dispatch<SetStateAction<msgInfo>>
siteurl: string
setMediaData: Dispatch<SetStateAction<MediaData | null>>
}) => {
const siteurl = location.origin
const linkMaxLength = 50
const [linkUrl, setLinkUrl] = useState<string | null>(null)
const handleGetOGP = async () => {
if (linkUrl === null) return
setProcessing(true)
setMsgInfo({
isError: false,
msg: "リンクカードを取得中...",
const linkUrl = getLinkFromPostText({ postText })
if (linkUrl === null) return
setProcessing(true)
setMsgInfo({
isError: false,
msg: "リンクカードを取得中...",
})
try {
let blob: Blob | null = null
const ogpMeta = await getOgpMeta({
siteurl,
externalUrl: linkUrl,
languageCode: "ja",
})
try {
let blob: Blob | null = null
const ogpMeta = await getOgpMeta({
if (ogpMeta.type === "error") {
const e: Error = new Error(ogpMeta.message)
e.name = ogpMeta.error
throw e
}
// titleが存在しない場合は、暫定的にTitleをURLにする
if (ogpMeta.title === "") {
ogpMeta.title = linkUrl
}
if (ogpMeta.image !== "") {
blob = await getOgpBlob({
siteurl,
externalUrl: linkUrl,
externalUrl: ogpMeta.image,
languageCode: "ja",
})
if (ogpMeta.type === "error") {
const e: Error = new Error(ogpMeta.message)
e.name = ogpMeta.error
throw e
}
// titleが存在しない場合は、暫定的にTitleをURLにする
if (ogpMeta.title === "") {
ogpMeta.title = linkUrl
}
if (ogpMeta.image !== "") {
blob = await getOgpBlob({
siteurl,
externalUrl: ogpMeta.image,
languageCode: "ja",
})
}
setMediaData({
type: "external",
meta: {
...ogpMeta,
url: linkUrl,
}
setMediaData({
type: "external",
meta: {
...ogpMeta,
url: linkUrl,
},
images: [
{
blob,
},
images: [
{
blob,
},
],
})
],
})
setMsgInfo({
isError: false,
msg: "リンクカードを取得しました!",
})
} catch (e: unknown) {
if (e instanceof Error) {
setMsgInfo({
isError: false,
msg: "リンクカードを取得しました!",
isError: true,
msg: `${e.name}: ${e.message}`,
})
} catch (e: unknown) {
if (e instanceof Error) {
setMsgInfo({
isError: true,
msg: `${e.name}: ${e.message}`,
})
}
//リンクカード設定を解除
setMediaData(null)
}
setProcessing(false)
//リンクカード設定を解除
setMediaData(null)
}
setProcessing(false)
}

const getLinkFromPostText = ({
postText,
}: {
postText: string
}): string | null => {
const richTextLinkParser = new richTextFacetParser("link")
const parseResult = richTextLinkParser.getFacet(postText)
if (parseResult.length < 1) {
return null
} else {
// 貼られた最後のlinkからOGPを取得する
// いずれは任意のURLを選べるようにしたい
return parseResult[parseResult.length - 1]
}
}

export const Component = ({
postText,
setMediaData,
isProcessing,
setProcessing,
setMsgInfo,
siteurl,
}: {
postText: string
setMediaData: Dispatch<SetStateAction<MediaData | null>>
isProcessing: boolean
setProcessing: Dispatch<SetStateAction<boolean>>
setMsgInfo: Dispatch<SetStateAction<msgInfo>>
siteurl: string
}) => {
const linkMaxLength = 50
const [linkUrl, setLinkUrl] = useState<string | null>(null)
useEffect(() => {
const richTextLinkParser = new richTextFacetParser("link")
const parseResult = richTextLinkParser.getFacet(postText)
if (parseResult.length < 1) {
setLinkUrl(null)
} else {
// 貼られた最後のlinkからOGPを取得する
// いずれは任意のURLを選べるようにしたい
setLinkUrl(parseResult[parseResult.length - 1])
}
setLinkUrl(getLinkFromPostText({ postText }))
}, [postText])
return (
<ProcButton
buttonID="linkcardattach"
handler={handleGetOGP}
handler={() =>
handleGetOGP({
postText,
setProcessing,
setMsgInfo,
siteurl,
setMediaData,
})
}
isProcessing={isProcessing}
context={
<>
Expand All @@ -119,3 +150,4 @@ export const Component = ({
)
}
export default Component
export { handleGetOGP }

0 comments on commit 70fb8a4

Please sign in to comment.