diff --git a/astro/public/materials/manifest.webmanifest b/astro/public/materials/manifest.webmanifest index fc15571..6d15802 100644 --- a/astro/public/materials/manifest.webmanifest +++ b/astro/public/materials/manifest.webmanifest @@ -1,4 +1,5 @@ { + "$schema": "https://json.schemastore.org/web-manifest-combined.json", "name": "Skyshare", "short_name": "Skyshare", "start_url": "/", @@ -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" + } + } } - \ No newline at end of file diff --git a/astro/src/components/Client/bsky/PostForm.tsx b/astro/src/components/Client/bsky/PostForm.tsx index 4a57a3e..7048a85 100644 --- a/astro/src/components/Client/bsky/PostForm.tsx +++ b/astro/src/components/Client/bsky/PostForm.tsx @@ -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" @@ -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, @@ -54,8 +84,10 @@ const Component = ({ setMediaData: Dispatch> setPopupPreviewOptions: Dispatch> }) => { + // 配置されたページのURL + const siteurl = location.origin // Post内容を格納する変数とディスパッチャー - const [postText, setPostText] = useState("") + const [postText, setPostText] = useState(initialPostText) // Postの実行状態を管理する変数とディスパッチャー const [language, setLanguage] = useState("ja") // 下書きのstate情報 @@ -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) } @@ -185,6 +230,7 @@ const Component = ({ /> > - isProcessing: boolean setProcessing: Dispatch> setMsgInfo: Dispatch> + siteurl: string + setMediaData: Dispatch> }) => { - const siteurl = location.origin - const linkMaxLength = 50 - const [linkUrl, setLinkUrl] = useState(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> + isProcessing: boolean + setProcessing: Dispatch> + setMsgInfo: Dispatch> + siteurl: string +}) => { + const linkMaxLength = 50 + const [linkUrl, setLinkUrl] = useState(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 ( + handleGetOGP({ + postText, + setProcessing, + setMsgInfo, + siteurl, + setMediaData, + }) + } isProcessing={isProcessing} context={ <> @@ -119,3 +150,4 @@ export const Component = ({ ) } export default Component +export { handleGetOGP }