fix types
This commit is contained in:
parent
3e43819ba1
commit
16880b1231
|
@ -1,3 +1,4 @@
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny:
|
||||||
type FIXME = any;
|
type FIXME = any;
|
||||||
|
|
||||||
declare const _LANGS_: string[][];
|
declare const _LANGS_: string[][];
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<article
|
<article
|
||||||
v-if="!muted.muted || muted.what === 'reply'"
|
v-if="!muted.muted || muted.what === 'reply'"
|
||||||
:id="detailedView ? appearNote.id : null"
|
:id="detailedView ? appearNote.id : undefined"
|
||||||
ref="el"
|
ref="el"
|
||||||
v-size="{ max: [450, 500] }"
|
v-size="{ max: [450, 500] }"
|
||||||
class="wrpstxzv"
|
class="wrpstxzv"
|
||||||
|
@ -35,10 +35,10 @@
|
||||||
:parent-id="parentId"
|
:parent-id="parentId"
|
||||||
:conversation="conversation"
|
:conversation="conversation"
|
||||||
:detailed-view="detailedView"
|
:detailed-view="detailedView"
|
||||||
@focusfooter="footerEl.focus()"
|
@focusfooter="footerEl!.focus()"
|
||||||
/>
|
/>
|
||||||
<div v-if="translating || translation" class="translation">
|
<div v-if="translating || translation" class="translation">
|
||||||
<MkLoading v-if="translating" mini />
|
<MkLoading v-if="translating || translation == null" mini />
|
||||||
<div v-else class="translated">
|
<div v-else class="translated">
|
||||||
<b
|
<b
|
||||||
>{{
|
>{{
|
||||||
|
@ -217,6 +217,7 @@ import { useNoteCapture } from "@/scripts/use-note-capture";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
import { deepClone } from "@/scripts/clone";
|
import { deepClone } from "@/scripts/clone";
|
||||||
import icon from "@/scripts/icon";
|
import icon from "@/scripts/icon";
|
||||||
|
import type { NoteTranslation } from "@/types/note";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
@ -256,12 +257,12 @@ const isRenote =
|
||||||
note.value.fileIds.length === 0 &&
|
note.value.fileIds.length === 0 &&
|
||||||
note.value.poll == null;
|
note.value.poll == null;
|
||||||
|
|
||||||
const el = ref<HTMLElement>();
|
const el = ref<HTMLElement | null>(null);
|
||||||
const footerEl = ref<HTMLElement>();
|
const footerEl = ref<HTMLElement | null>(null);
|
||||||
const menuButton = ref<HTMLElement>();
|
const menuButton = ref<HTMLElement>();
|
||||||
const starButton = ref<InstanceType<typeof XStarButton>>();
|
const starButton = ref<InstanceType<typeof XStarButton> | null>(null);
|
||||||
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
const renoteButton = ref<InstanceType<typeof XRenoteButton> | null>(null);
|
||||||
const reactButton = ref<HTMLElement>();
|
const reactButton = ref<HTMLElement | null>(null);
|
||||||
const appearNote = computed(() =>
|
const appearNote = computed(() =>
|
||||||
isRenote ? (note.value.renote as entities.Note) : note.value,
|
isRenote ? (note.value.renote as entities.Note) : note.value,
|
||||||
);
|
);
|
||||||
|
@ -274,7 +275,7 @@ const muted = ref(
|
||||||
defaultStore.state.mutedLangs,
|
defaultStore.state.mutedLangs,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
const translation = ref(null);
|
const translation = ref<NoteTranslation | null>(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
const replies: entities.Note[] =
|
const replies: entities.Note[] =
|
||||||
props.conversation
|
props.conversation
|
||||||
|
@ -330,21 +331,21 @@ useNoteCapture({
|
||||||
isDeletedRef: isDeleted,
|
isDeletedRef: isDeleted,
|
||||||
});
|
});
|
||||||
|
|
||||||
function reply(viaKeyboard = false): void {
|
function reply(_viaKeyboard = false): void {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
os.post({
|
os.post({
|
||||||
reply: appearNote.value,
|
reply: appearNote.value,
|
||||||
animation: !viaKeyboard,
|
// animation: !viaKeyboard,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
focus();
|
focus();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function react(viaKeyboard = false): void {
|
function react(_viaKeyboard = false): void {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
blur();
|
blur();
|
||||||
reactionPicker.show(
|
reactionPicker.show(
|
||||||
reactButton.value,
|
reactButton.value!,
|
||||||
(reaction) => {
|
(reaction) => {
|
||||||
os.api("notes/reactions/create", {
|
os.api("notes/reactions/create", {
|
||||||
noteId: appearNote.value.id,
|
noteId: appearNote.value.id,
|
||||||
|
@ -388,14 +389,15 @@ function menu(viaKeyboard = false): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onContextmenu(ev: MouseEvent): void {
|
function onContextmenu(ev: MouseEvent): void {
|
||||||
const isLink = (el: HTMLElement) => {
|
const isLink = (el: HTMLElement | null) => {
|
||||||
|
if (el == null) return;
|
||||||
if (el.tagName === "A") return true;
|
if (el.tagName === "A") return true;
|
||||||
if (el.parentElement) {
|
if (el.parentElement) {
|
||||||
return isLink(el.parentElement);
|
return isLink(el.parentElement);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (isLink(ev.target)) return;
|
if (isLink(ev.target as HTMLElement | null)) return;
|
||||||
if (window.getSelection().toString() !== "") return;
|
if (window.getSelection()?.toString() !== "") return;
|
||||||
|
|
||||||
if (defaultStore.state.useReactionPickerForContextMenu) {
|
if (defaultStore.state.useReactionPickerForContextMenu) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
@ -454,15 +456,15 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
function focus() {
|
function focus() {
|
||||||
el.value.focus();
|
el.value!.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function blur() {
|
function blur() {
|
||||||
el.value.blur();
|
el.value!.blur();
|
||||||
}
|
}
|
||||||
|
|
||||||
function noteClick(e) {
|
function noteClick(e: MouseEvent) {
|
||||||
if (document.getSelection().type === "Range" || !expandOnNoteClick) {
|
if (document.getSelection()?.type === "Range" || !expandOnNoteClick) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
} else {
|
} else {
|
||||||
router.push(notePage(props.note));
|
router.push(notePage(props.note));
|
||||||
|
|
|
@ -84,14 +84,10 @@ import { formatDateTimeString } from "@/scripts/format-time-string";
|
||||||
import { addTime } from "@/scripts/time";
|
import { addTime } from "@/scripts/time";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import icon from "@/scripts/icon";
|
import icon from "@/scripts/icon";
|
||||||
|
import type { PollType } from "@/types/post-form";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: {
|
modelValue: PollType;
|
||||||
expiresAt: string;
|
|
||||||
expiredAfter: number;
|
|
||||||
choices: string[];
|
|
||||||
multiple: boolean;
|
|
||||||
};
|
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
"update:modelValue": [
|
"update:modelValue": [
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
class="account _button"
|
class="account _button"
|
||||||
@click="openAccountMenu"
|
@click="openAccountMenu"
|
||||||
>
|
>
|
||||||
<MkAvatar :user="postAccount ?? me" class="avatar" />
|
<MkAvatar :user="postAccount ?? me!" class="avatar" />
|
||||||
</button>
|
</button>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<span
|
<span
|
||||||
|
@ -297,14 +297,22 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, inject, nextTick, onMounted, ref, watch } from "vue";
|
import {
|
||||||
|
type Ref,
|
||||||
|
computed,
|
||||||
|
inject,
|
||||||
|
nextTick,
|
||||||
|
onMounted,
|
||||||
|
ref,
|
||||||
|
watch,
|
||||||
|
} from "vue";
|
||||||
import * as mfm from "mfm-js";
|
import * as mfm from "mfm-js";
|
||||||
import autosize from "autosize";
|
import autosize from "autosize";
|
||||||
import insertTextAtCursor from "insert-text-at-cursor";
|
import insertTextAtCursor from "insert-text-at-cursor";
|
||||||
import { length } from "stringz";
|
import { length } from "stringz";
|
||||||
import { toASCII } from "punycode/";
|
import { toASCII } from "punycode/";
|
||||||
import { acct } from "firefish-js";
|
import { acct } from "firefish-js";
|
||||||
import type { entities, languages } from "firefish-js";
|
import type { ApiTypes, entities, languages } from "firefish-js";
|
||||||
import { throttle } from "throttle-debounce";
|
import { throttle } from "throttle-debounce";
|
||||||
import XNoteSimple from "@/components/MkNoteSimple.vue";
|
import XNoteSimple from "@/components/MkNoteSimple.vue";
|
||||||
import XNotePreview from "@/components/MkNotePreview.vue";
|
import XNotePreview from "@/components/MkNotePreview.vue";
|
||||||
|
@ -341,6 +349,7 @@ import type { MenuItem } from "@/types/menu";
|
||||||
import icon from "@/scripts/icon";
|
import icon from "@/scripts/icon";
|
||||||
import MkVisibilityPicker from "@/components/MkVisibilityPicker.vue";
|
import MkVisibilityPicker from "@/components/MkVisibilityPicker.vue";
|
||||||
import type { NoteVisibility } from "@/types/note";
|
import type { NoteVisibility } from "@/types/note";
|
||||||
|
import type { NoteDraft, PollType } from "@/types/post-form";
|
||||||
|
|
||||||
const modal = inject("modal");
|
const modal = inject("modal");
|
||||||
|
|
||||||
|
@ -353,11 +362,11 @@ const props = withDefaults(
|
||||||
specified?: entities.User;
|
specified?: entities.User;
|
||||||
initialText?: string;
|
initialText?: string;
|
||||||
initialVisibility?: NoteVisibility;
|
initialVisibility?: NoteVisibility;
|
||||||
initialLanguage?: typeof languages;
|
initialLanguage?: (typeof languages)[number];
|
||||||
initialFiles?: entities.DriveFile[];
|
initialFiles?: entities.DriveFile[];
|
||||||
initialLocalOnly?: boolean;
|
initialLocalOnly?: boolean;
|
||||||
initialVisibleUsers?: entities.User[];
|
initialVisibleUsers?: entities.User[];
|
||||||
initialNote?: entities.Note;
|
initialNote?: NoteDraft;
|
||||||
instant?: boolean;
|
instant?: boolean;
|
||||||
fixed?: boolean;
|
fixed?: boolean;
|
||||||
autofocus?: boolean;
|
autofocus?: boolean;
|
||||||
|
@ -390,12 +399,7 @@ const showBigPostButton = defaultStore.state.showBigPostButton;
|
||||||
const posting = ref(false);
|
const posting = ref(false);
|
||||||
const text = ref(props.initialText ?? "");
|
const text = ref(props.initialText ?? "");
|
||||||
const files = ref(props.initialFiles ?? ([] as entities.DriveFile[]));
|
const files = ref(props.initialFiles ?? ([] as entities.DriveFile[]));
|
||||||
const poll = ref<{
|
const poll = ref<PollType | null>(null);
|
||||||
choices: string[];
|
|
||||||
multiple: boolean;
|
|
||||||
expiresAt: string | null;
|
|
||||||
expiredAfter: string | null;
|
|
||||||
} | null>(null);
|
|
||||||
const useCw = ref(false);
|
const useCw = ref(false);
|
||||||
const showPreview = ref(defaultStore.state.showPreviewByDefault);
|
const showPreview = ref(defaultStore.state.showPreviewByDefault);
|
||||||
const cw = ref<string | null>(null);
|
const cw = ref<string | null>(null);
|
||||||
|
@ -411,12 +415,12 @@ const visibility = ref(
|
||||||
: defaultStore.state.defaultNoteVisibility),
|
: defaultStore.state.defaultNoteVisibility),
|
||||||
);
|
);
|
||||||
|
|
||||||
const visibleUsers = ref([]);
|
const visibleUsers = ref<entities.User[]>([]);
|
||||||
if (props.initialVisibleUsers) {
|
if (props.initialVisibleUsers) {
|
||||||
props.initialVisibleUsers.forEach(pushVisibleUser);
|
props.initialVisibleUsers.forEach(pushVisibleUser);
|
||||||
}
|
}
|
||||||
const draghover = ref(false);
|
const draghover = ref(false);
|
||||||
const quoteId = ref(null);
|
const quoteId = ref<string | null>(null);
|
||||||
const hasNotSpecifiedMentions = ref(false);
|
const hasNotSpecifiedMentions = ref(false);
|
||||||
const recentHashtags = ref(
|
const recentHashtags = ref(
|
||||||
JSON.parse(localStorage.getItem("hashtags") || "[]"),
|
JSON.parse(localStorage.getItem("hashtags") || "[]"),
|
||||||
|
@ -500,7 +504,9 @@ const canPost = computed((): boolean => {
|
||||||
const withHashtags = computed(
|
const withHashtags = computed(
|
||||||
defaultStore.makeGetterSetter("postFormWithHashtags"),
|
defaultStore.makeGetterSetter("postFormWithHashtags"),
|
||||||
);
|
);
|
||||||
const hashtags = computed(defaultStore.makeGetterSetter("postFormHashtags"));
|
const hashtags = computed(
|
||||||
|
defaultStore.makeGetterSetter("postFormHashtags"),
|
||||||
|
) as Ref<string | null>;
|
||||||
|
|
||||||
watch(text, () => {
|
watch(text, () => {
|
||||||
checkMissingMention();
|
checkMissingMention();
|
||||||
|
@ -525,7 +531,7 @@ if (props.mention) {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
props.reply &&
|
props.reply &&
|
||||||
(props.reply.user.username !== me.username ||
|
(props.reply.user.username !== me!.username ||
|
||||||
(props.reply.user.host != null && props.reply.user.host !== host))
|
(props.reply.user.host != null && props.reply.user.host !== host))
|
||||||
) {
|
) {
|
||||||
text.value = `@${props.reply.user.username}${
|
text.value = `@${props.reply.user.username}${
|
||||||
|
@ -545,7 +551,7 @@ if (props.reply && props.reply.text != null) {
|
||||||
: `@${x.username}@${toASCII(otherHost)}`;
|
: `@${x.username}@${toASCII(otherHost)}`;
|
||||||
|
|
||||||
// exclude me
|
// exclude me
|
||||||
if (me.username === x.username && (x.host == null || x.host === host))
|
if (me!.username === x.username && (x.host == null || x.host === host))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// remove duplicates
|
// remove duplicates
|
||||||
|
@ -579,7 +585,7 @@ if (
|
||||||
if (props.reply.visibleUserIds) {
|
if (props.reply.visibleUserIds) {
|
||||||
os.api("users/show", {
|
os.api("users/show", {
|
||||||
userIds: props.reply.visibleUserIds.filter(
|
userIds: props.reply.visibleUserIds.filter(
|
||||||
(uid) => uid !== me.id && uid !== props.reply.userId,
|
(uid) => uid !== me!.id && uid !== props.reply!.userId,
|
||||||
),
|
),
|
||||||
}).then((users) => {
|
}).then((users) => {
|
||||||
users.forEach(pushVisibleUser);
|
users.forEach(pushVisibleUser);
|
||||||
|
@ -588,7 +594,7 @@ if (
|
||||||
visibility.value = "private";
|
visibility.value = "private";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.reply.userId !== me.id) {
|
if (props.reply.userId !== me!.id) {
|
||||||
os.api("users/show", { userId: props.reply.userId }).then((user) => {
|
os.api("users/show", { userId: props.reply.userId }).then((user) => {
|
||||||
pushVisibleUser(user);
|
pushVisibleUser(user);
|
||||||
});
|
});
|
||||||
|
@ -615,7 +621,7 @@ const addRe = (s: string) => {
|
||||||
if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
|
if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
|
||||||
useCw.value = true;
|
useCw.value = true;
|
||||||
cw.value =
|
cw.value =
|
||||||
props.reply.user.username === me.username
|
props.reply.user.username === me!.username
|
||||||
? props.reply.cw
|
? props.reply.cw
|
||||||
: addRe(props.reply.cw);
|
: addRe(props.reply.cw);
|
||||||
}
|
}
|
||||||
|
@ -894,11 +900,14 @@ function onCompositionEnd(ev: CompositionEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onPaste(ev: ClipboardEvent) {
|
async function onPaste(ev: ClipboardEvent) {
|
||||||
|
if (ev.clipboardData == null) return;
|
||||||
|
|
||||||
for (const { item, i } of Array.from(ev.clipboardData.items).map(
|
for (const { item, i } of Array.from(ev.clipboardData.items).map(
|
||||||
(item, i) => ({ item, i }),
|
(item, i) => ({ item, i }),
|
||||||
)) {
|
)) {
|
||||||
if (item.kind === "file") {
|
if (item.kind === "file") {
|
||||||
const file = item.getAsFile();
|
const file = item.getAsFile();
|
||||||
|
if (file == null) continue;
|
||||||
const lio = file.name.lastIndexOf(".");
|
const lio = file.name.lastIndexOf(".");
|
||||||
const ext = lio >= 0 ? file.name.slice(lio) : "";
|
const ext = lio >= 0 ? file.name.slice(lio) : "";
|
||||||
const formatted = `${formatTimeString(
|
const formatted = `${formatTimeString(
|
||||||
|
@ -911,7 +920,7 @@ async function onPaste(ev: ClipboardEvent) {
|
||||||
|
|
||||||
const paste = ev.clipboardData?.getData("text") ?? "";
|
const paste = ev.clipboardData?.getData("text") ?? "";
|
||||||
|
|
||||||
if (!props.renote && !quoteId.value && paste.startsWith(url + "/notes/")) {
|
if (!props.renote && !quoteId.value && paste.startsWith(`${url}/notes/`)) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
os.yesno({
|
os.yesno({
|
||||||
|
@ -919,13 +928,13 @@ async function onPaste(ev: ClipboardEvent) {
|
||||||
text: i18n.ts.quoteQuestion,
|
text: i18n.ts.quoteQuestion,
|
||||||
}).then(({ canceled }) => {
|
}).then(({ canceled }) => {
|
||||||
if (canceled) {
|
if (canceled) {
|
||||||
insertTextAtCursor(textareaEl.value, paste);
|
insertTextAtCursor(textareaEl.value!, paste);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
quoteId.value = paste
|
quoteId.value = paste
|
||||||
.substring(url.length)
|
.substring(url.length)
|
||||||
.match(/^\/notes\/(.+?)\/?$/)[1];
|
.match(/^\/notes\/(.+?)\/?$/)![1];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -956,16 +965,17 @@ function onDragover(ev) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDragenter(ev) {
|
function onDragenter(_ev) {
|
||||||
draghover.value = true;
|
draghover.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDragleave(ev) {
|
function onDragleave(_ev) {
|
||||||
draghover.value = false;
|
draghover.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDrop(ev): void {
|
function onDrop(ev: DragEvent): void {
|
||||||
draghover.value = false;
|
draghover.value = false;
|
||||||
|
if (ev.dataTransfer == null) return;
|
||||||
|
|
||||||
// ファイルだったら
|
// ファイルだったら
|
||||||
if (ev.dataTransfer.files.length > 0) {
|
if (ev.dataTransfer.files.length > 0) {
|
||||||
|
@ -1064,7 +1074,7 @@ async function post() {
|
||||||
|
|
||||||
const processedText = preprocess(text.value);
|
const processedText = preprocess(text.value);
|
||||||
|
|
||||||
let postData = {
|
let postData: ApiTypes.NoteSubmitReq = {
|
||||||
editId: props.editId ? props.editId : undefined,
|
editId: props.editId ? props.editId : undefined,
|
||||||
text: processedText === "" ? undefined : processedText,
|
text: processedText === "" ? undefined : processedText,
|
||||||
fileIds: files.value.length > 0 ? files.value.map((f) => f.id) : undefined,
|
fileIds: files.value.length > 0 ? files.value.map((f) => f.id) : undefined,
|
||||||
|
@ -1092,7 +1102,7 @@ async function post() {
|
||||||
const hashtags_ = hashtags.value
|
const hashtags_ = hashtags.value
|
||||||
.trim()
|
.trim()
|
||||||
.split(" ")
|
.split(" ")
|
||||||
.map((x) => (x.startsWith("#") ? x : "#" + x))
|
.map((x) => (x.startsWith("#") ? x : `#${x}`))
|
||||||
.join(" ");
|
.join(" ");
|
||||||
postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_;
|
postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_;
|
||||||
}
|
}
|
||||||
|
@ -1104,11 +1114,11 @@ async function post() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let token;
|
let token: string | undefined;
|
||||||
|
|
||||||
if (postAccount.value) {
|
if (postAccount.value) {
|
||||||
const storedAccounts = await getAccounts();
|
const storedAccounts = await getAccounts();
|
||||||
token = storedAccounts.find((x) => x.id === postAccount.value.id)?.token;
|
token = storedAccounts.find((x) => x.id === postAccount.value!.id)?.token;
|
||||||
}
|
}
|
||||||
|
|
||||||
posting.value = true;
|
posting.value = true;
|
||||||
|
@ -1119,10 +1129,11 @@ async function post() {
|
||||||
deleteDraft();
|
deleteDraft();
|
||||||
emit("posted");
|
emit("posted");
|
||||||
if (postData.text && postData.text !== "") {
|
if (postData.text && postData.text !== "") {
|
||||||
const hashtags_ = mfm
|
const hashtags_ = (
|
||||||
.parse(postData.text)
|
mfm
|
||||||
.filter((x) => x.type === "hashtag")
|
.parse(postData.text)
|
||||||
.map((x) => x.props.hashtag);
|
.filter((x) => x.type === "hashtag") as mfm.MfmHashtag[]
|
||||||
|
).map((x) => x.props.hashtag);
|
||||||
const history = JSON.parse(
|
const history = JSON.parse(
|
||||||
localStorage.getItem("hashtags") || "[]",
|
localStorage.getItem("hashtags") || "[]",
|
||||||
) as string[];
|
) as string[];
|
||||||
|
@ -1133,7 +1144,7 @@ async function post() {
|
||||||
}
|
}
|
||||||
posting.value = false;
|
posting.value = false;
|
||||||
postAccount.value = null;
|
postAccount.value = null;
|
||||||
nextTick(() => autosize.update(textareaEl.value));
|
nextTick(() => autosize.update(textareaEl.value!));
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((err: { message: string; id: string }) => {
|
.catch((err: { message: string; id: string }) => {
|
||||||
|
@ -1169,19 +1180,23 @@ function cancel() {
|
||||||
|
|
||||||
function insertMention() {
|
function insertMention() {
|
||||||
os.selectUser().then((user) => {
|
os.selectUser().then((user) => {
|
||||||
insertTextAtCursor(textareaEl.value, "@" + acct.toString(user) + " ");
|
insertTextAtCursor(textareaEl.value!, `@${acct.toString(user)} `);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function insertEmoji(ev: MouseEvent) {
|
async function insertEmoji(ev: MouseEvent) {
|
||||||
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl.value);
|
os.openEmojiPicker(
|
||||||
|
(ev.currentTarget ?? ev.target) as HTMLElement,
|
||||||
|
{},
|
||||||
|
textareaEl.value,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openCheatSheet(ev: MouseEvent) {
|
async function openCheatSheet(ev: MouseEvent) {
|
||||||
os.popup(XCheatSheet, {}, {}, "closed");
|
os.popup(XCheatSheet, {}, {}, "closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
function showActions(ev) {
|
function showActions(ev: MouseEvent) {
|
||||||
os.popupMenu(
|
os.popupMenu(
|
||||||
postFormActions.map((action) => ({
|
postFormActions.map((action) => ({
|
||||||
text: action.title,
|
text: action.title,
|
||||||
|
@ -1198,7 +1213,7 @@ function showActions(ev) {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
ev.currentTarget ?? ev.target,
|
(ev.currentTarget ?? ev.target) as HTMLElement,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1209,9 +1224,9 @@ function openAccountMenu(ev: MouseEvent) {
|
||||||
{
|
{
|
||||||
withExtraOperation: false,
|
withExtraOperation: false,
|
||||||
includeCurrentAccount: true,
|
includeCurrentAccount: true,
|
||||||
active: postAccount.value != null ? postAccount.value.id : me.id,
|
active: postAccount.value != null ? postAccount.value.id : me!.id,
|
||||||
onChoose: (account) => {
|
onChoose: (account) => {
|
||||||
if (account.id === me.id) {
|
if (account.id === me!.id) {
|
||||||
postAccount.value = null;
|
postAccount.value = null;
|
||||||
} else {
|
} else {
|
||||||
postAccount.value = account;
|
postAccount.value = account;
|
||||||
|
@ -1232,14 +1247,14 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: detach when unmount
|
// TODO: detach when unmount
|
||||||
new Autocomplete(textareaEl.value, text);
|
new Autocomplete(textareaEl.value!, text);
|
||||||
new Autocomplete(cwInputEl.value, cw);
|
new Autocomplete(cwInputEl.value!, cw as Ref<string>);
|
||||||
new Autocomplete(hashtagsInputEl.value, hashtags);
|
new Autocomplete(hashtagsInputEl.value!, hashtags as Ref<string>);
|
||||||
|
|
||||||
autosize(textareaEl.value);
|
autosize(textareaEl.value!);
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
autosize(textareaEl.value);
|
autosize(textareaEl.value!);
|
||||||
// 書きかけの投稿を復元
|
// 書きかけの投稿を復元
|
||||||
if (!props.instant && !props.mention && !props.specified) {
|
if (!props.instant && !props.mention && !props.specified) {
|
||||||
const draft = JSON.parse(localStorage.getItem("drafts") || "{}")[
|
const draft = JSON.parse(localStorage.getItem("drafts") || "{}")[
|
||||||
|
@ -1275,8 +1290,8 @@ onMounted(() => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
visibility.value = init.visibility;
|
visibility.value = init.visibility;
|
||||||
localOnly.value = init.localOnly;
|
localOnly.value = init.localOnly ?? false;
|
||||||
language.value = init.lang;
|
language.value = init.lang ?? null;
|
||||||
quoteId.value = init.renote ? init.renote.id : null;
|
quoteId.value = init.renote ? init.renote.id : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1289,7 +1304,7 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
nextTick(() => watchForDraft());
|
nextTick(() => watchForDraft());
|
||||||
nextTick(() => autosize.update(textareaEl.value));
|
nextTick(() => autosize.update(textareaEl.value!));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -25,6 +25,7 @@ import type { entities, languages } from "firefish-js";
|
||||||
import MkModal from "@/components/MkModal.vue";
|
import MkModal from "@/components/MkModal.vue";
|
||||||
import MkPostForm from "@/components/MkPostForm.vue";
|
import MkPostForm from "@/components/MkPostForm.vue";
|
||||||
import type { NoteVisibility } from "@/types/note";
|
import type { NoteVisibility } from "@/types/note";
|
||||||
|
import type { NoteDraft } from "@/types/post-form";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
reply?: entities.Note;
|
reply?: entities.Note;
|
||||||
|
@ -34,11 +35,11 @@ const props = defineProps<{
|
||||||
specified?: entities.User;
|
specified?: entities.User;
|
||||||
initialText?: string;
|
initialText?: string;
|
||||||
initialVisibility?: NoteVisibility;
|
initialVisibility?: NoteVisibility;
|
||||||
initialLanguage?: typeof languages;
|
initialLanguage?: (typeof languages)[number];
|
||||||
initialFiles?: entities.DriveFile[];
|
initialFiles?: entities.DriveFile[];
|
||||||
initialLocalOnly?: boolean;
|
initialLocalOnly?: boolean;
|
||||||
initialVisibleUsers?: entities.User[];
|
initialVisibleUsers?: entities.User[];
|
||||||
initialNote?: entities.Note;
|
initialNote?: NoteDraft;
|
||||||
instant?: boolean;
|
instant?: boolean;
|
||||||
fixed?: boolean;
|
fixed?: boolean;
|
||||||
autofocus?: boolean;
|
autofocus?: boolean;
|
||||||
|
@ -53,7 +54,7 @@ const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
const form = shallowRef<InstanceType<typeof MkPostForm>>();
|
const form = shallowRef<InstanceType<typeof MkPostForm>>();
|
||||||
|
|
||||||
function onPosted() {
|
function onPosted() {
|
||||||
modal.value.close({
|
modal.value!.close({
|
||||||
useSendAnimation: true,
|
useSendAnimation: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -319,7 +319,7 @@ const usernameState = ref<
|
||||||
| "invalid-format"
|
| "invalid-format"
|
||||||
| "min-range"
|
| "min-range"
|
||||||
| "max-range"
|
| "max-range"
|
||||||
>(null);
|
>(null);
|
||||||
const invitationState = ref<null | "entered">(null);
|
const invitationState = ref<null | "entered">(null);
|
||||||
const emailState = ref<
|
const emailState = ref<
|
||||||
| null
|
| null
|
||||||
|
@ -331,9 +331,10 @@ const emailState = ref<
|
||||||
| "unavailable:mx"
|
| "unavailable:mx"
|
||||||
| "unavailable:smtp"
|
| "unavailable:smtp"
|
||||||
| "unavailable"
|
| "unavailable"
|
||||||
| "error">(null);
|
| "error"
|
||||||
|
>(null);
|
||||||
const passwordStrength = ref<"" | "low" | "medium" | "high">("");
|
const passwordStrength = ref<"" | "low" | "medium" | "high">("");
|
||||||
const passwordRetypeState = ref<null | "match" | "not-match" >(null);
|
const passwordRetypeState = ref<null | "match" | "not-match">(null);
|
||||||
const submitting = ref(false);
|
const submitting = ref(false);
|
||||||
const ToSAgreement = ref(false);
|
const ToSAgreement = ref(false);
|
||||||
const hCaptchaResponse = ref(null);
|
const hCaptchaResponse = ref(null);
|
||||||
|
|
|
@ -76,6 +76,7 @@ onMounted(() => {
|
||||||
src: "/client-assets/tagcanvas.min.js",
|
src: "/client-assets/tagcanvas.min.js",
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
// biome-ignore lint/suspicious/noAssignInExpressions: assign it intentially
|
||||||
.addEventListener("load", () => (available.value = true));
|
.addEventListener("load", () => (available.value = true));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -153,7 +153,7 @@ const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
currentVisibility: NoteVisibility;
|
currentVisibility: NoteVisibility;
|
||||||
currentLocalOnly: boolean;
|
currentLocalOnly: boolean;
|
||||||
src?: HTMLElement;
|
src?: HTMLElement | null;
|
||||||
}>(),
|
}>(),
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
|
|
|
@ -48,7 +48,7 @@ const id = os.getUniqueId();
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
modelValue: number;
|
modelValue: number | null;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
min: number;
|
min: number;
|
||||||
max: number;
|
max: number;
|
||||||
|
|
|
@ -75,29 +75,29 @@ const headerActions = computed(() =>
|
||||||
icon: `${icon("ph-pencil")}`,
|
icon: `${icon("ph-pencil")}`,
|
||||||
text: i18n.ts.toEdit,
|
text: i18n.ts.toEdit,
|
||||||
handler: async (): Promise<void> => {
|
handler: async (): Promise<void> => {
|
||||||
const { canceled, result } = await os.form(clip.value.name, {
|
const { canceled, result } = await os.form(clip.value!.name, {
|
||||||
name: {
|
name: {
|
||||||
type: "string",
|
type: "string",
|
||||||
label: i18n.ts.name,
|
label: i18n.ts.name,
|
||||||
default: clip.value.name,
|
default: clip.value!.name,
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: "string",
|
type: "string",
|
||||||
required: false,
|
required: false,
|
||||||
multiline: true,
|
multiline: true,
|
||||||
label: i18n.ts.description,
|
label: i18n.ts.description,
|
||||||
default: clip.value.description,
|
default: clip.value!.description,
|
||||||
},
|
},
|
||||||
isPublic: {
|
isPublic: {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
label: i18n.ts.public,
|
label: i18n.ts.public,
|
||||||
default: clip.value.isPublic,
|
default: clip.value!.isPublic,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
|
|
||||||
os.apiWithDialog("clips/update", {
|
os.apiWithDialog("clips/update", {
|
||||||
clipId: clip.value.id,
|
clipId: clip.value!.id,
|
||||||
...result,
|
...result,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,14 +2,15 @@ import { markRaw, ref } from "vue";
|
||||||
import { isSignedIn } from "./me";
|
import { isSignedIn } from "./me";
|
||||||
import { Storage } from "./pizzax";
|
import { Storage } from "./pizzax";
|
||||||
import type { NoteVisibility } from "@/types/note";
|
import type { NoteVisibility } from "@/types/note";
|
||||||
|
import type { entities, ApiTypes } from "firefish-js";
|
||||||
|
|
||||||
export const postFormActions: {
|
export const postFormActions: {
|
||||||
title: string;
|
title: string;
|
||||||
handler: (note: entities.Note) => void | Promise<void>;
|
handler: (from, update) => void | Promise<void>;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
export const userActions: {
|
export const userActions: {
|
||||||
title: string;
|
title: string;
|
||||||
handler: (note: entities.Note) => void | Promise<void>;
|
handler: (user: entities.User) => void | Promise<void>;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
export const noteActions: {
|
export const noteActions: {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -19,7 +20,7 @@ export const noteViewInterruptors: {
|
||||||
handler: (note: entities.Note) => Promise<entities.Note>;
|
handler: (note: entities.Note) => Promise<entities.Note>;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
export const notePostInterruptors: {
|
export const notePostInterruptors: {
|
||||||
handler: (note: entities.Note) => Promise<entities.Note>;
|
handler: (note: ApiTypes.NoteSubmitReq) => Promise<ApiTypes.NoteSubmitReq>;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
|
|
||||||
const menuOptions = [
|
const menuOptions = [
|
||||||
|
@ -466,7 +467,6 @@ import darkTheme from "@/themes/d-rosepine.json5";
|
||||||
* Storage for configuration information that does not need to be constantly loaded into memory (non-reactive)
|
* Storage for configuration information that does not need to be constantly loaded into memory (non-reactive)
|
||||||
*/
|
*/
|
||||||
import lightTheme from "@/themes/l-rosepinedawn.json5";
|
import lightTheme from "@/themes/l-rosepinedawn.json5";
|
||||||
import { entities } from "firefish-js";
|
|
||||||
|
|
||||||
export class ColdDeviceStorage {
|
export class ColdDeviceStorage {
|
||||||
public static default = {
|
public static default = {
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import type { entities } from "firefish-js";
|
||||||
|
|
||||||
|
export type PollType = {
|
||||||
|
choices: string[];
|
||||||
|
multiple: boolean;
|
||||||
|
expiresAt: string | null;
|
||||||
|
expiredAfter: number | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type NoteDraft = entities.Note & {
|
||||||
|
poll?: PollType;
|
||||||
|
};
|
|
@ -30,5 +30,5 @@
|
||||||
"jsx": "preserve"
|
"jsx": "preserve"
|
||||||
},
|
},
|
||||||
"compileOnSave": false,
|
"compileOnSave": false,
|
||||||
"include": ["./src/**/*.ts", "./src/**/*.vue"]
|
"include": ["./src/**/*.ts", "./src/**/*.vue", "./@types"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@ import type {
|
||||||
UserDetailed,
|
UserDetailed,
|
||||||
UserGroup,
|
UserGroup,
|
||||||
UserList,
|
UserList,
|
||||||
UserLite,
|
|
||||||
UserSorting,
|
UserSorting,
|
||||||
} from "./entities";
|
} from "./entities";
|
||||||
|
|
||||||
|
@ -46,11 +45,13 @@ type TODO = Record<string, any> | null;
|
||||||
|
|
||||||
type NoParams = Record<string, never>;
|
type NoParams = Record<string, never>;
|
||||||
|
|
||||||
type ShowUserReq = { username: string; host?: string } | { userId: User["id"] };
|
type ShowUserReq =
|
||||||
|
| { username: string; host?: string | null }
|
||||||
|
| { userId: User["id"] };
|
||||||
|
|
||||||
type NoteSubmitReq = {
|
export type NoteSubmitReq = {
|
||||||
editId?: null | Note["id"];
|
editId?: null | Note["id"];
|
||||||
visibility?: "public" | "home" | "followers" | "specified";
|
visibility?: (typeof consts.noteVisibilities)[number];
|
||||||
visibleUserIds?: User["id"][];
|
visibleUserIds?: User["id"][];
|
||||||
text?: null | string;
|
text?: null | string;
|
||||||
cw?: null | string;
|
cw?: null | string;
|
||||||
|
@ -62,10 +63,11 @@ type NoteSubmitReq = {
|
||||||
channelId?: null | Channel["id"];
|
channelId?: null | Channel["id"];
|
||||||
poll?: null | {
|
poll?: null | {
|
||||||
choices: string[];
|
choices: string[];
|
||||||
multiple?: boolean;
|
multiple: boolean;
|
||||||
expiresAt?: null | number;
|
expiresAt: string | null;
|
||||||
expiredAfter?: null | number;
|
expiredAfter: number | null;
|
||||||
};
|
};
|
||||||
|
lang?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Endpoints = {
|
export type Endpoints = {
|
||||||
|
|
|
@ -29,7 +29,7 @@ export type UserLite = {
|
||||||
isIndexable: boolean;
|
isIndexable: boolean;
|
||||||
isCat?: boolean;
|
isCat?: boolean;
|
||||||
speakAsCat?: boolean;
|
speakAsCat?: boolean;
|
||||||
driveCapacityOverrideMb: number | null,
|
driveCapacityOverrideMb: number | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UserDetailed = UserLite & {
|
export type UserDetailed = UserLite & {
|
||||||
|
@ -238,8 +238,8 @@ export interface RenoteNotification extends BaseNotification {
|
||||||
user: User;
|
user: User;
|
||||||
userId: User["id"];
|
userId: User["id"];
|
||||||
note: Note & {
|
note: Note & {
|
||||||
renote: Note,
|
renote: Note;
|
||||||
renoteId: string,
|
renoteId: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export interface QuoteNotification extends BaseNotification {
|
export interface QuoteNotification extends BaseNotification {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import * as acct from "./acct";
|
import * as acct from "./acct";
|
||||||
import type { Acct } from "./acct";
|
import type { Acct } from "./acct";
|
||||||
import { Endpoints } from "./api.types";
|
import { Endpoints } from "./api.types";
|
||||||
|
import type * as ApiTypes from "./api.types";
|
||||||
import * as consts from "./consts";
|
import * as consts from "./consts";
|
||||||
import Stream, { Connection } from "./streaming";
|
import Stream, { Connection } from "./streaming";
|
||||||
import * as StreamTypes from "./streaming.types";
|
import * as StreamTypes from "./streaming.types";
|
||||||
|
@ -8,6 +9,7 @@ import type * as TypeUtils from "./type-utils";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Endpoints,
|
Endpoints,
|
||||||
|
type ApiTypes,
|
||||||
Stream,
|
Stream,
|
||||||
Connection as ChannelConnection,
|
Connection as ChannelConnection,
|
||||||
StreamTypes,
|
StreamTypes,
|
||||||
|
|
Loading…
Reference in New Issue