rudeshark.net/packages/client/src/pages/share.vue
2023-04-07 17:01:42 -07:00

201 lines
4.7 KiB
Vue

<template>
<MkStickyContainer>
<template #header
><MkPageHeader :actions="headerActions" :tabs="headerTabs"
/></template>
<MkSpacer :content-max="800">
<XPostForm
v-if="state === 'writing'"
fixed
:instant="true"
:initial-text="initialText"
:initial-visibility="visibility"
:initial-files="files"
:initial-local-only="localOnly"
:reply="reply"
:renote="renote"
:initial-visible-users="visibleUsers"
class="_panel"
@posted="state = 'posted'"
/>
<MkButton
v-else-if="state === 'posted'"
primary
class="close"
@click="close()"
>{{ i18n.ts.close }}</MkButton
>
</MkSpacer>
</MkStickyContainer>
</template>
<script lang="ts" setup>
// SPECIFICATION: https://misskey-hub.net/docs/features/share-form.html
import {} from "vue";
import { noteVisibilities } from "calckey-js";
import * as Acct from "calckey-js/built/acct";
import * as Misskey from "calckey-js";
import MkButton from "@/components/MkButton.vue";
import XPostForm from "@/components/MkPostForm.vue";
import * as os from "@/os";
import { mainRouter } from "@/router";
import { definePageMetadata } from "@/scripts/page-metadata";
import { i18n } from "@/i18n";
const urlParams = new URLSearchParams(window.location.search);
const localOnlyQuery = urlParams.get("localOnly");
const visibilityQuery = urlParams.get("visibility");
let state = $ref("fetching" as "fetching" | "writing" | "posted");
let title = $ref(urlParams.get("title"));
const text = urlParams.get("text");
const url = urlParams.get("url");
let initialText = $ref(null as string | null);
let reply = $ref(null as Misskey.entities.Note | null);
let renote = $ref(null as Misskey.entities.Note | null);
let visibility = $ref(
noteVisibilities.includes(visibilityQuery) ? visibilityQuery : null
);
let localOnly = $ref(
localOnlyQuery === "0" ? false : localOnlyQuery === "1" ? true : null
);
let files = $ref([] as Misskey.entities.DriveFile[]);
let visibleUsers = $ref([] as Misskey.entities.User[]);
async function init() {
let noteText = "";
if (title) noteText += `[ ${title} ]\n`;
// Googleニュース対策
if (text?.startsWith(`${title}.\n`))
noteText += text.replace(`${title}.\n`, "");
else if (text && title !== text) noteText += `${text}\n`;
if (url) noteText += `${url}`;
initialText = noteText.trim();
if (visibility === "specified") {
const visibleUserIds = urlParams.get("visibleUserIds");
const visibleAccts = urlParams.get("visibleAccts");
await Promise.all(
[
...(visibleUserIds
? visibleUserIds.split(",").map((userId) => ({ userId }))
: []),
...(visibleAccts
? visibleAccts.split(",").map(Acct.parse)
: []),
]
// TypeScriptの指示通りに変換する
.map((q) =>
"username" in q
? {
username: q.username,
host: q.host === null ? undefined : q.host,
}
: q
)
.map((q) =>
os.api("users/show", q).then(
(user) => {
visibleUsers.push(user);
},
() => {
console.error(
`Invalid user query: ${JSON.stringify(q)}`
);
}
)
)
);
}
try {
//#region Reply
const replyId = urlParams.get("replyId");
const replyUri = urlParams.get("replyUri");
if (replyId) {
reply = await os.api("notes/show", {
noteId: replyId,
});
} else if (replyUri) {
const obj = await os.api("ap/show", {
uri: replyUri,
});
if (obj.type === "Note") {
reply = obj.object;
}
}
//#endregion
//#region Renote
const renoteId = urlParams.get("renoteId");
const renoteUri = urlParams.get("renoteUri");
if (renoteId) {
renote = await os.api("notes/show", {
noteId: renoteId,
});
} else if (renoteUri) {
const obj = await os.api("ap/show", {
uri: renoteUri,
});
if (obj.type === "Note") {
renote = obj.object;
}
}
//#endregion
//#region Drive files
const fileIds = urlParams.get("fileIds");
if (fileIds) {
await Promise.all(
fileIds.split(",").map((fileId) =>
os.api("drive/files/show", { fileId }).then(
(file) => {
files.push(file);
},
() => {
console.error(`Failed to fetch a file ${fileId}`);
}
)
)
);
}
//#endregion
} catch (err) {
os.alert({
type: "error",
title: err.message,
text: err.name,
});
}
state = "writing";
}
init();
function close(): void {
window.close();
// 閉じなければ100ms後タイムラインに
window.setTimeout(() => {
mainRouter.push("/");
}, 100);
}
const headerActions = $computed(() => []);
const headerTabs = $computed(() => []);
definePageMetadata({
title: i18n.ts.share,
icon: "ph-share-network ph-bold ph-lg",
});
</script>
<style lang="scss" scoped>
.close {
margin: 16px auto;
}
</style>