Unfucked the frontend
ci/woodpecker/push/ociImagePush Pipeline was successful Details

This commit is contained in:
Natty 2024-04-08 03:10:23 +02:00
parent 156ad5c499
commit aefef079a7
Signed by: natty
GPG Key ID: BF6CB659ADEE60EC
397 changed files with 6197 additions and 6604 deletions

View File

@ -1,15 +1,15 @@
{
"tabWidth": 4,
"useTabs": false,
"singleQuote": false,
"vueIndentScriptAndStyle": false,
"plugins": ["vue"],
"overrides": [
{
"files": "*.vue",
"options": {
"parser": "vue"
}
}
]
"tabWidth": 4,
"useTabs": false,
"singleQuote": false,
"vueIndentScriptAndStyle": false,
"plugins": ["vue"],
"overrides": [
{
"files": "*.vue",
"options": {
"parser": "vue"
}
}
]
}

View File

@ -1,7 +1,7 @@
declare module "@/themes/*.json5" {
import { Theme } from "@/scripts/theme";
import { Theme } from "@/scripts/theme";
const theme: Theme;
const theme: Theme;
export default theme;
export default theme;
}

View File

@ -1,7 +1,7 @@
/// <reference types="vue/macros-global" />
declare module "*.vue" {
import type { DefineComponent } from "vue";
const component: DefineComponent<{}, {}, any>;
export default component;
import type { DefineComponent } from "vue";
const component: DefineComponent<{}, {}, any>;
export default component;
}

View File

@ -1,6 +1,7 @@
{
"name": "client",
"private": true,
"type": "module",
"scripts": {
"watch": "pnpm vite build --watch --mode development",
"watchRebuild": "pnpm vite build --watch",
@ -9,10 +10,11 @@
},
"devDependencies": {
"@discordapp/twemoji": "14.1.2",
"@phosphor-icons/web": "^2.0.3",
"@rollup/plugin-alias": "3.1.9",
"@rollup/plugin-json": "4.1.0",
"@rollup/pluginutils": "^4.2.1",
"@misskey-dev/browser-image-resizer": "^2024.1.0",
"@phosphor-icons/web": "^2.1.1",
"@rollup/plugin-alias": "^5.1.0",
"@rollup/plugin-json": "^6.1.0",
"@rollup/pluginutils": "^5.1.0",
"@types/escape-regexp": "0.0.1",
"@types/glob": "8.1.0",
"@types/gulp": "4.0.11",
@ -24,14 +26,12 @@
"@types/throttle-debounce": "5.0.0",
"@types/tinycolor2": "1.4.3",
"@types/uuid": "8.3.4",
"@vitejs/plugin-vue": "4.2.3",
"@vue/compiler-sfc": "3.3.4",
"@vue/runtime-core": "3.3.4",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/runtime-core": "^3.4",
"autobind-decorator": "2.4.0",
"autosize": "5.0.2",
"blurhash": "1.1.5",
"broadcast-channel": "4.19.1",
"browser-image-resizer": "github:misskey-dev/browser-image-resizer",
"calckey-js": "workspace:*",
"chart.js": "4.3.0",
"chartjs-adapter-date-fns": "3.0.0",
@ -44,12 +44,12 @@
"cross-env": "7.0.3",
"cypress": "10.11.0",
"date-fns": "2.30.0",
"emojilib": "github:thatonecalculator/emojilib",
"emojilib": "^3.0.12",
"escape-regexp": "0.0.1",
"eventemitter3": "4.0.7",
"focus-trap": "^7.4.3",
"focus-trap-vue": "^4.0.2",
"gsap": "^3.11.5",
"focus-trap": "^7.5.4",
"focus-trap-vue": "^4.0.3",
"gsap": "^3.12.5",
"idb-keyval": "6.2.1",
"insert-text-at-cursor": "0.3.0",
"json5": "2.2.3",
@ -62,9 +62,8 @@
"prettier-plugin-vue": "1.1.6",
"prismjs": "1.29.0",
"punycode": "2.1.1",
"querystring": "0.2.1",
"rndstr": "1.0.0",
"rollup": "3.23.1",
"rollup": "^4.14.1",
"s-age": "1.1.2",
"sass": "1.62.1",
"seedrandom": "3.0.5",
@ -80,17 +79,17 @@
"tsc-alias": "1.8.6",
"tsconfig-paths": "4.2.0",
"twemoji-parser": "14.0.0",
"typescript": "5.1.3",
"typescript": "^5.4.4",
"unicode-emoji-json": "^0.4.0",
"uuid": "9.0.0",
"vanilla-tilt": "1.8.0",
"vite": "4.3.9",
"vidstack": "^1.11",
"vite": "^5.2.8",
"vite-plugin-compression": "^0.5.1",
"vue": "3.3.4",
"vue-component-type-helpers": "1.8.27",
"vue": "^3.4.21",
"vue-component-type-helpers": "^2.0.11",
"vue-isyourpasswordsafe": "^2.0.0",
"vue-plyr": "^7.0.0",
"vue3-otp-input": "^0.4.1",
"vue3-otp-input": "^0.4.4",
"vuedraggable": "4.1.0"
}
}

View File

@ -49,8 +49,8 @@ export async function signout() {
.then((registrations) => {
return Promise.all(
registrations.map((registration) =>
registration.unregister()
)
registration.unregister(),
),
);
});
}
@ -80,7 +80,7 @@ export async function removeAccount(id: Account["id"]) {
const accounts = await getAccounts();
accounts.splice(
accounts.findIndex((x) => x.id === id),
1
1,
);
if (accounts.length > 0) await set("accounts", accounts);
@ -158,12 +158,12 @@ export async function openAccountMenu(
active?: misskey.entities.UserDetailed["id"];
onChoose?: (account: misskey.entities.UserDetailed) => void;
},
ev: MouseEvent
ev: MouseEvent,
) {
function showSigninDialog() {
popup(
defineAsyncComponent(
() => import("@/components/MkSigninDialog.vue")
() => import("@/components/MkSigninDialog.vue"),
),
{},
{
@ -172,14 +172,14 @@ export async function openAccountMenu(
success();
},
},
"closed"
"closed",
);
}
function createAccount() {
popup(
defineAsyncComponent(
() => import("@/components/MkSignupDialog.vue")
() => import("@/components/MkSignupDialog.vue"),
),
{},
{
@ -188,7 +188,7 @@ export async function openAccountMenu(
switchAccountWithToken(res.i);
},
},
"closed"
"closed",
);
}
@ -203,7 +203,7 @@ export async function openAccountMenu(
}
const storedAccounts = await getAccounts().then((accounts) =>
accounts.filter((x) => x.id !== $i.id)
accounts.filter((x) => x.id !== $i.id),
);
const accountsPromise = api("users/show", {
userIds: storedAccounts.map((x) => x.id),
@ -232,7 +232,7 @@ export async function openAccountMenu(
if (account == null) return res(null);
res(createItem(account));
});
})
}),
);
if (opts.withExtraOperation) {
@ -286,7 +286,7 @@ export async function openAccountMenu(
ev.currentTarget ?? ev.target,
{
align: "left",
}
},
);
} else {
popupMenu(
@ -297,7 +297,7 @@ export async function openAccountMenu(
ev.currentTarget ?? ev.target,
{
align: "left",
}
},
);
}
}

View File

@ -26,11 +26,11 @@ watch(
{
id: userIds.map((u) => (typeof u === "string" ? u : u.id)),
},
{}
{},
)
.then((p) => p.filter((u) => u !== null).map((u) => u!));
},
{ immediate: true, deep: true }
{ immediate: true, deep: true },
);
</script>

View File

@ -43,7 +43,7 @@ const props = withDefaults(
direction: "down",
reversed: false,
noGap: false,
}
},
);
type ItemType =
@ -82,12 +82,12 @@ watch(
effectiveItems.value = listItems;
},
{ deep: true, immediate: true }
{ deep: true, immediate: true },
);
function getDateText(item: { createdAt: string } | { created_at: string }) {
const dateTime = new Date(
magTransProperty(item, "createdAt", "created_at")
magTransProperty(item, "createdAt", "created_at"),
);
const date = dateTime.getDate();
const month = dateTime.getMonth() + 1;
@ -100,11 +100,11 @@ function getDateText(item: { createdAt: string } | { created_at: string }) {
const animationEnabled = ref(defaultStore.state.animation);
watch(
() => defaultStore.state.animation,
(val) => (animationEnabled.value = val)
(val) => (animationEnabled.value = val),
);
const parentType = computed(() =>
animationEnabled ? TransitionGroup : HTMLDivElement
animationEnabled ? TransitionGroup : HTMLDivElement,
);
const tag = computed(() => (animationEnabled ? "div" : null));
const name = computed(() => (animationEnabled ? "list" : null));
@ -129,7 +129,8 @@ defineSlots<{
}
> .list-enter-active {
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1),
transition:
transform 0.7s cubic-bezier(0.23, 1, 0.32, 1),
opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1);
}

View File

@ -19,7 +19,7 @@ const props = defineProps<{
}>();
const url = computed(
() => `https://matrix.to/#/@${props.username}:${props.host}`
() => `https://matrix.to/#/@${props.username}:${props.host}`,
);
</script>

View File

@ -263,7 +263,7 @@ const props = defineProps<{
collapsedReply?: boolean;
}>();
let note = $ref<packed.PackNoteMaybeFull>(structuredClone(toRaw(props.note)));
let note = ref<packed.PackNoteMaybeFull>(structuredClone(toRaw(props.note)));
const softMuteReasonI18nSrc = (what?: string) => {
if (what === "note") return i18n.ts.userSaysSomethingReason;
@ -282,13 +282,15 @@ const starButton = ref<InstanceType<typeof XStarButton>>();
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
const renoteTime = ref<HTMLElement | null>(null);
const reactButton = ref<HTMLElement | null>(null);
let appearNote = $computed(
() => magEffectiveNote(note) as packed.PackNoteMaybeFull
let appearNote = computed(
() => magEffectiveNote(note.value) as packed.PackNoteMaybeFull
);
const isMyRenote = $i && $i.id === appearNote.user.id;
const isMyRenote = computed(() => $i && $i.id === appearNote.value.user.id);
const showContent = ref(false);
const isDeleted = ref(false);
const muted = ref(getWordSoftMute(note, $i, defaultStore.state.mutedWords));
const muted = computed(() =>
getWordSoftMute(note.value, $i, defaultStore.state.mutedWords)
);
const translation = ref(null);
const translating = ref(false);
const enableEmojiReactions = defaultStore.state.enableEmojiReactions;
@ -307,7 +309,7 @@ const keymap = {
useNoteCapture({
rootEl: el,
note: $$(appearNote),
note: appearNote,
isDeletedRef: isDeleted,
});
@ -331,7 +333,7 @@ function react(viaKeyboard = false): void {
reactButton.value,
(reaction) => {
os.api("notes/reactions/create", {
noteId: appearNote.id,
noteId: appearNote.value.id,
reaction: magReactionToLegacy(reaction),
});
},
@ -374,21 +376,24 @@ function onContextmenu(ev: MouseEvent): void {
[
{
type: "label",
text: notePage(appearNote),
text: notePage(appearNote.value),
},
{
icon: "ph-browser ph-bold ph-lg",
text: i18n.ts.openInWindow,
action: () => {
os.pageWindow(notePage(appearNote));
os.pageWindow(notePage(appearNote.value));
},
},
notePage(appearNote) != location.pathname
notePage(appearNote.value) != location.pathname
? {
icon: "ph-arrows-out-simple ph-bold ph-lg",
text: i18n.ts.showInPage,
action: () => {
router.push(notePage(appearNote), "forcePage");
router.push(
notePage(appearNote.value),
"forcePage"
);
},
}
: undefined,
@ -397,22 +402,25 @@ function onContextmenu(ev: MouseEvent): void {
type: "a",
icon: "ph-arrow-square-out ph-bold ph-lg",
text: i18n.ts.openInNewTab,
href: notePage(appearNote),
href: notePage(appearNote.value),
target: "_blank",
},
{
icon: "ph-link-simple ph-bold ph-lg",
text: i18n.ts.copyLink,
action: () => {
copyToClipboard(`${url}${notePage(appearNote)}`);
copyToClipboard(`${url}${notePage(appearNote.value)}`);
},
},
appearNote.user.host != null
appearNote.value.user.host != null
? {
type: "a",
icon: "ph-arrow-square-up-right ph-bold ph-lg",
text: i18n.ts.showOnRemote,
href: appearNote.url ?? appearNote.uri ?? "",
href:
appearNote.value.url ??
appearNote.value.uri ??
"",
target: "_blank",
}
: undefined,
@ -425,7 +433,7 @@ function onContextmenu(ev: MouseEvent): void {
function menu(viaKeyboard = false): void {
os.popupMenu(
getNoteMenu({
note: note,
note: note.value,
translating,
translation,
menuButton: menuButton.value,
@ -449,7 +457,7 @@ function showRenoteMenu(viaKeyboard = false): void {
danger: true,
action: () => {
os.api("notes/delete", {
noteId: note.id,
noteId: note.value.id,
});
isDeleted.value = true;
},
@ -490,7 +498,7 @@ function noteClick(e) {
) {
e.stopPropagation();
} else {
router.push(notePage(appearNote));
router.push(notePage(appearNote.value));
}
}
@ -501,30 +509,30 @@ function setPostExpanded(val: boolean) {
}
const accessibleLabel = computed(() => {
let label = `${appearNote.user.username}; `;
if (appearNote.renoted_note) {
let label = `${appearNote.value.user.username}; `;
if (appearNote.value.renoted_note) {
label += `${i18n.t("renoted")} ${
appearNote.renoted_note.user.username
appearNote.value.renoted_note.user.username
}; `;
if (appearNote.renoted_note.cw) {
label += `${i18n.t("cw")}: ${appearNote.renoted_note.cw}; `;
if (appearNote.value.renoted_note.cw) {
label += `${i18n.t("cw")}: ${appearNote.value.renoted_note.cw}; `;
if (postIsExpanded.value) {
label += `${appearNote.renoted_note.text}; `;
label += `${appearNote.value.renoted_note.text}; `;
}
} else {
label += `${appearNote.renoted_note.text}; `;
label += `${appearNote.value.renoted_note.text}; `;
}
} else {
if (appearNote.cw) {
label += `${i18n.t("cw")}: ${appearNote.cw}; `;
if (appearNote.value.cw) {
label += `${i18n.t("cw")}: ${appearNote.value.cw}; `;
if (postIsExpanded.value) {
label += `${appearNote.text}; `;
label += `${appearNote.value.text}; `;
}
} else {
label += `${appearNote.text}; `;
label += `${appearNote.value.text}; `;
}
}
const date = new Date(appearNote.created_at);
const date = new Date(appearNote.value.created_at);
label += `${date.toLocaleTimeString()}`;
return label;
});

View File

@ -152,7 +152,16 @@
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, onUpdated, Ref, ref, toRaw, watch } from "vue";
import {
computed,
onMounted,
onUnmounted,
onUpdated,
Ref,
ref,
toRaw,
watch,
} from "vue";
import * as misskey from "calckey-js";
import MkTab from "@/components/MkTab.vue";
import MagNoteSub from "@/components/MagNoteSub.vue";
@ -185,9 +194,9 @@ const props = defineProps<{
pinned?: boolean;
}>();
let tab = $ref("replies");
let tab = ref("replies");
let note = $ref<packed.PackNoteMaybeFull>(structuredClone(toRaw(props.note)));
let note = ref<packed.PackNoteMaybeFull>(structuredClone(toRaw(props.note)));
const softMuteReasonI18nSrc = (what?: string) => {
if (what === "note") return i18n.ts.userSaysSomethingReason;
@ -204,14 +213,14 @@ const noteEl = ref<HTMLElement | null>(null);
const menuButton = ref<HTMLElement | null>(null);
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
const reactButton = ref<HTMLElement | null>(null);
let appearNote = $computed(
() => magEffectiveNote(note) as packed.PackNoteMaybeFull
let appearNote = computed(
() => magEffectiveNote(note.value) as packed.PackNoteMaybeFull,
);
let parentNote = $ref(appearNote.parent_note);
let parentNote = ref(appearNote.value.parent_note);
const showContent = ref(false);
const isDeleted = ref(false);
const muted = ref(
getWordSoftMute(appearNote, $i, defaultStore.state.mutedWords)
getWordSoftMute(appearNote.value, $i, defaultStore.state.mutedWords),
);
const translation = ref(null);
const translating = ref(false);
@ -234,7 +243,7 @@ const keymap = {
useNoteCapture({
rootEl: el as Ref<HTMLElement>,
note: $$(appearNote),
note: appearNote,
isDeletedRef: isDeleted,
});
@ -255,13 +264,13 @@ function react(viaKeyboard = false): void {
reactButton.value,
(reaction) => {
os.api("notes/reactions/create", {
noteId: appearNote.id,
noteId: appearNote.value.id,
reaction: magReactionToLegacy(reaction),
});
},
() => {
focus();
}
},
);
}
@ -281,13 +290,13 @@ function onContextmenu(ev: MouseEvent): void {
} else {
os.contextMenu(
getNoteMenu({
note: note,
note: note.value,
translating,
translation,
menuButton: menuButton.value,
isDeleted,
}),
ev
ev,
).then(focus);
}
}
@ -295,7 +304,7 @@ function onContextmenu(ev: MouseEvent): void {
function menu(viaKeyboard = false): void {
os.popupMenu(
getNoteMenu({
note: note,
note: note.value,
translating,
translation,
menuButton: menuButton.value,
@ -304,7 +313,7 @@ function menu(viaKeyboard = false): void {
menuButton.value ?? undefined,
{
viaKeyboard,
}
},
).then(focus);
}
@ -318,64 +327,63 @@ function blur() {
async function updateParent(note: packed.PackNoteMaybeFull) {
if (note.parent_note) {
parentNote = note.parent_note;
parentNote.value = note.parent_note;
return;
} else if (note.parent_note_id) {
parentNote = null;
parentNote = await os.magApi(
parentNote.value = null;
parentNote.value = await os.magApi(
endpoints.GetNoteById,
{ attachments: true, context: true },
{ id: note.parent_note_id }
{ id: note.parent_note_id },
);
return;
}
parentNote = null;
parentNote.value = null;
}
watch(appearNote, updateParent);
updateParent(note);
watch(appearNote, updateParent, { immediate: true });
os.api("notes/children", {
noteId: appearNote.id,
noteId: appearNote.value.id,
limit: 30,
depth: 12,
}).then((res) => {
res = res.reduce(
(acc: misskey.entities.Note[], resNote: misskey.entities.Note) => {
if (resNote.userId == appearNote.user.id) {
if (resNote.userId == appearNote.value.user.id) {
return [...acc, resNote];
}
return [resNote, ...acc];
},
[]
[],
);
Promise.all(res.map(resolveNote)).then((a) => {
replies.value = a;
directReplies.value = a
.filter((resNote) => resNote.parent_note_id === appearNote.id)
.filter((resNote) => resNote.parent_note_id === appearNote.value.id)
.reverse();
directQuotes.value = a.filter(
(resNote) => resNote.renoted_note_id === appearNote.id
(resNote) => resNote.renoted_note_id === appearNote.value.id,
);
});
});
if (appearNote.parent_note_id) {
if (appearNote.value.parent_note_id) {
os.api("notes/conversation", {
noteId: appearNote.parent_note_id,
noteId: appearNote.value.parent_note_id,
limit: 30,
}).then(async (res) => {
conversation.value = await Promise.all<packed.PackNoteMaybeFull>(
res?.reverse().map(resolveNote)
res?.reverse().map(resolveNote),
);
focus();
});
}
os.api("notes/clips", {
noteId: appearNote.id,
noteId: appearNote.value.id,
}).then((res) => {
clips.value = res as misskey.entities.Clip[];
});
@ -389,9 +397,9 @@ os.api("notes/clips", {
// const pagingComponent = $ref<InstanceType<typeof MkPagination>>();
function loadTab() {
if (tab === "renotes" && !renotes.value) {
if (tab.value === "renotes" && !renotes.value) {
os.api("notes/renotes", {
noteId: appearNote.id,
noteId: appearNote.value.id,
limit: 100,
}).then((res) => {
renotes.value = res;
@ -403,7 +411,7 @@ async function onNoteUpdated(noteData: NoteUpdatedEvent): Promise<void> {
const { type, id, body } = noteData;
let found = -1;
if (id === appearNote.id) {
if (id === appearNote.value.id) {
found = 0;
} else {
for (let i = 0; i < replies.value.length; i++) {
@ -430,7 +438,7 @@ async function onNoteUpdated(noteData: NoteUpdatedEvent): Promise<void> {
},
{
id: createdId,
}
},
);
replies.value.splice(found, 0, replyNote);
@ -566,7 +574,9 @@ onUnmounted(() => {
background: var(--panelHighlight);
border-radius: var(--radius);
opacity: 0;
transition: opacity 0.2s, background 0.2s;
transition:
opacity 0.2s,
background 0.2s;
z-index: -1;
}
&.reply-to {

View File

@ -23,10 +23,10 @@
v-tooltip.noDelay="
i18n.t('edited', {
date: new Date(
note.updated_at
note.updated_at,
).toLocaleDateString(),
time: new Date(
note.updated_at
note.updated_at,
).toLocaleTimeString(),
})
"

View File

@ -39,6 +39,6 @@ watch(
noteData.value = n;
});
},
{ immediate: true }
{ immediate: true },
);
</script>

View File

@ -15,7 +15,7 @@ import XNoteHeader from "@/components/MagNoteHeader.vue";
import MagSubNoteContent from "@/components/MagSubNoteContent.vue";
import { packed } from "magnetar-common";
const props = defineProps<{
defineProps<{
note: packed.PackNoteMaybeFull;
pinned?: boolean;
}>();

View File

@ -4,7 +4,7 @@
ref="el"
v-size="{ max: [450, 500] }"
class="wrpstxzv"
:id="detailedView ? appearNote.id : null"
:id="detailedView ? appearNote.id : undefined"
tabindex="-1"
:class="{
children: depth > 1,
@ -170,7 +170,7 @@
<script lang="ts" setup>
import type { Ref } from "vue";
import { inject, ref, toRaw, watch } from "vue";
import { computed, inject, ref, toRaw } from "vue";
import * as misskey from "calckey-js";
import XNoteHeader from "@/components/MagNoteHeader.vue";
import XSubNoteContent from "@/components/MagSubNoteContent.vue";
@ -219,18 +219,11 @@ const props = withDefaults(
{
depth: 1,
replyLevel: 1,
}
},
);
let noteCpy = $ref<packed.PackNoteMaybeFull>(
structuredClone(toRaw(props.note))
);
watch(
() => props.note,
(val) => {
noteCpy = structuredClone(toRaw(val));
}
let noteCpy = computed<packed.PackNoteMaybeFull>(() =>
structuredClone(toRaw(props.note)),
);
const softMuteReasonI18nSrc = (what?: string) => {
@ -249,19 +242,21 @@ const menuButton = ref<HTMLElement | null>(null);
const starButton = ref<InstanceType<typeof XStarButton>>();
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
const reactButton = ref<HTMLElement | null>(null);
let appearNote = $computed(
() => magEffectiveNote(noteCpy) as packed.PackNoteMaybeFull
let appearNote = computed(
() => magEffectiveNote(noteCpy.value) as packed.PackNoteMaybeFull,
);
const isDeleted = ref(false);
const muted = ref(getWordSoftMute(noteCpy, $i, defaultStore.state.mutedWords));
const muted = computed(() =>
getWordSoftMute(noteCpy.value, $i, defaultStore.state.mutedWords),
);
const translation = ref(null);
const translating = ref(false);
const replies: packed.PackNoteMaybeFull[] =
props.conversation
?.filter(
(item) =>
item.parent_note_id === appearNote.id ||
item.renoted_note_id === appearNote.id
item.parent_note_id === appearNote.value.id ||
item.renoted_note_id === appearNote.value.id,
)
.reverse() ?? [];
const enableEmojiReactions = defaultStore.state.enableEmojiReactions;
@ -269,7 +264,7 @@ const expandOnNoteClick = defaultStore.state.expandOnNoteClick;
useNoteCapture({
rootEl: el as Ref<HTMLElement>,
note: $$(appearNote),
note: appearNote,
isDeletedRef: isDeleted,
});
@ -290,13 +285,13 @@ function react(viaKeyboard = false): void {
reactButton.value,
(reaction) => {
os.api("notes/reactions/create", {
noteId: appearNote.id,
noteId: appearNote.value.id,
reaction: magReactionToLegacy(reaction),
});
},
() => {
focus();
}
},
);
}
@ -310,13 +305,13 @@ function undoReact(note: packed.PackNoteBase): void {
const currentClipPage = inject<Ref<misskey.entities.Clip> | null>(
"currentClipPage",
null
null,
);
function menu(viaKeyboard = false): void {
os.popupMenu(
getNoteMenu({
note: noteCpy,
note: noteCpy.value,
translating,
translation,
menuButton: menuButton.value,
@ -326,7 +321,7 @@ function menu(viaKeyboard = false): void {
menuButton.value ?? undefined,
{
viaKeyboard,
}
},
).then(focus);
}
@ -350,21 +345,24 @@ function onContextmenu(ev: MouseEvent): void {
[
{
type: "label",
text: notePage(appearNote),
text: notePage(appearNote.value),
},
{
icon: "ph-browser ph-bold ph-lg",
text: i18n.ts.openInWindow,
action: () => {
os.pageWindow(notePage(appearNote));
os.pageWindow(notePage(appearNote.value));
},
},
notePage(appearNote) != location.pathname
notePage(appearNote.value) != location.pathname
? {
icon: "ph-arrows-out-simple ph-bold ph-lg",
text: i18n.ts.showInPage,
action: () => {
router.push(notePage(appearNote), "forcePage");
router.push(
notePage(appearNote.value),
"forcePage",
);
},
}
: undefined,
@ -373,27 +371,27 @@ function onContextmenu(ev: MouseEvent): void {
type: "a",
icon: "ph-arrow-square-out ph-bold ph-lg",
text: i18n.ts.openInNewTab,
href: notePage(appearNote),
href: notePage(appearNote.value),
target: "_blank",
},
{
icon: "ph-link-simple ph-bold ph-lg",
text: i18n.ts.copyLink,
action: () => {
copyToClipboard(`${url}${notePage(appearNote)}`);
copyToClipboard(`${url}${notePage(appearNote.value)}`);
},
},
noteCpy.user.host != null
noteCpy.value.user.host != null
? {
type: "a",
icon: "ph-arrow-square-up-right ph-bold ph-lg",
text: i18n.ts.showOnRemote,
href: noteCpy.url ?? noteCpy.uri ?? "",
href: noteCpy.value.url ?? noteCpy.value.uri ?? "",
target: "_blank",
}
: undefined,
],
ev
ev,
);
}
}

View File

@ -274,7 +274,7 @@ const props = withDefaults(
{
withTime: false,
full: false,
}
},
);
const emit = defineEmits(["refresh"]);
@ -310,7 +310,7 @@ onMounted(() => {
() => props.notification.is_read,
() => {
readObserver!.disconnect();
}
},
);
}
});
@ -333,7 +333,7 @@ useTooltip(reactionRef, (showing) => {
targetElement: reactionRef.value.$el,
},
{},
"closed"
"closed",
);
});
</script>

View File

@ -143,7 +143,7 @@ const props = withDefaults(
{
withTime: false,
full: false,
}
},
);
const firstNotification = ref(props.notificationGroup.value[0]);
@ -152,7 +152,7 @@ watch(
(n) => {
firstNotification.value = n;
},
{ deep: true }
{ deep: true },
);
const emit = defineEmits(["refresh"]);
@ -175,7 +175,7 @@ onMounted(() => {
if (!entries.some((entry) => entry.isIntersecting)) return;
stream.send(
"readNotifications",
props.notificationGroup.value.map((n) => n.id)
props.notificationGroup.value.map((n) => n.id),
);
observer.disconnect();
});
@ -189,7 +189,7 @@ onMounted(() => {
() => firstNotification.value.is_read,
() => {
readObserver!.disconnect();
}
},
);
}
});
@ -201,7 +201,7 @@ onUnmounted(() => {
const hookTooltip = (
el: HTMLElement | ComponentPublicInstance | null,
notif: packed.PackNotification
notif: packed.PackNotification,
) => {
if (el == null) return;
@ -219,7 +219,7 @@ const hookTooltip = (
"$el" in elRef.value ? elRef.value.$el : elRef.value,
},
{},
"closed"
"closed",
);
});

View File

@ -39,7 +39,7 @@
</template>
<script lang="ts" setup>
import {} from "vue";
import { computed, ref } from "vue";
import MkSwitch from "./form/switch.vue";
import MkInfo from "./MkInfo.vue";
import MkButton from "./MkButton.vue";
@ -60,20 +60,20 @@ const props = withDefaults(
{
includingTypes: () => notificationTypes,
showGlobalToggle: true,
}
},
);
let includingTypes = $computed(() => props.includingTypes || []);
let includingTypes = computed(() => props.includingTypes || []);
const dialog = $ref<InstanceType<typeof XModalWindow>>();
const dialog = ref<InstanceType<typeof XModalWindow>>();
let typesMap = $ref(
let typesMap = ref(
Object.fromEntries(
notificationTypes.map((n) => [n, includingTypes.includes(n)])
) as Record<types.NotificationType, boolean>
notificationTypes.map((n) => [n, includingTypes.value.includes(n)]),
) as Record<types.NotificationType, boolean>,
);
let useGlobalSetting = $ref(includingTypes === null || props.showGlobalToggle);
let useGlobalSetting = ref(includingTypes === null || props.showGlobalToggle);
function ok() {
if (useGlobalSetting) {
@ -84,7 +84,7 @@ function ok() {
});
}
dialog?.close();
dialog.value?.close();
}
function disableAll() {

View File

@ -15,7 +15,7 @@
</template>
<script lang="ts" setup>
import { onMounted } from "vue";
import { onMounted, ref } from "vue";
import XNotification from "@/components/MagNotification.vue";
import * as os from "@/os";
@ -28,11 +28,11 @@ const emit = defineEmits<{
}>();
const zIndex = os.claimZIndex("high");
let showing = $ref(true);
let showing = ref(true);
onMounted(() => {
window.setTimeout(() => {
showing = false;
showing.value = false;
}, 6000);
});
</script>
@ -40,7 +40,9 @@ onMounted(() => {
<style lang="scss" scoped>
.notification-toast-enter-active,
.notification-toast-leave-active {
transition: opacity 0.3s, transform 0.3s !important;
transition:
opacity 0.3s,
transform 0.3s !important;
}
.notification-toast-enter-from,
.notification-toast-leave-to {

View File

@ -26,7 +26,7 @@
v-slot="{
item: notificationGroup,
}: {
item: NotificationGroup,
item: NotificationGroup;
}"
class="elsfgstc"
:items="items"
@ -140,7 +140,7 @@ const allowedGroups: types.NotificationType[] = ["Reaction", "Renote"];
watch(
() => props.includeTypes,
() => reload()
() => reload(),
);
const onNotification = (notification: packed.PackNotification) => {
@ -225,7 +225,7 @@ const emit = defineEmits<{
// 5. [MAYBE] Items that would be pushed out by another item CAN break boundaries, but only with adjacent items
const groupIntra = (
notifications: packed.PackNotification[]
notifications: packed.PackNotification[],
): NotificationGroup[] => {
const x = notifications
.map(
@ -235,13 +235,13 @@ const groupIntra = (
id: n.id,
created_at: n.created_at,
value: n,
} as NotificationGroup)
}) as NotificationGroup,
)
.map((g) => [g])
.reduce(
(
prev: NotificationGroup[],
[curr]: NotificationGroup[]
[curr]: NotificationGroup[],
): NotificationGroup[] => {
const currGroup = curr as NotificationGroup & {
type: "single";
@ -295,7 +295,7 @@ const groupIntra = (
magEffectiveNote(n.note).id ===
magEffectiveNote(currNoteGroup.value.note)
.id &&
n.type === currNoteGroup.value.type
n.type === currNoteGroup.value.type,
)
) {
p.value = [...p.value, currNoteGroup.value];
@ -317,14 +317,14 @@ const groupIntra = (
return [...prev, currGroup];
},
[] as NotificationGroup[]
[] as NotificationGroup[],
);
return x;
};
const fetch = async (
pagination: SpanFilter
pagination: SpanFilter,
): Promise<PaginatedResult<packed.PackNotification[]>> => {
return os.magApi(
endpoints.GetNotifications,
@ -338,7 +338,7 @@ const fetch = async (
limit: displayLimit,
},
{},
undefined
undefined,
);
};
@ -356,7 +356,7 @@ const init = async (): Promise<void> => {
(err) => {
error.value = true;
initialFetching.value = false;
}
},
);
};
@ -390,7 +390,7 @@ const fetchMore = async (): Promise<void> => {
},
(err) => {
moreFetching.value = false;
}
},
);
};
@ -437,7 +437,7 @@ const prepend = (item: packed.PackNotification): void => {
"note" in n &&
magEffectiveNote(n.note).id ===
magEffectiveNote(currNotification.note).id &&
n.type === currNotification.type
n.type === currNotification.type,
)
) {
return true;
@ -522,7 +522,7 @@ watch(
if (a.length === 0 && b.length === 0) return;
emit("queue", queue.value.length);
},
{ deep: true }
{ deep: true },
);
init();

View File

@ -65,13 +65,15 @@
<script
lang="ts"
setup
generic="T extends BackendApiEndpoint<
T['method'] & Method,
T['pathParams'] & string[],
T['request'],
T['response'],
T['paginated'] & true
>"
generic="
T extends BackendApiEndpoint<
T['method'] & Method,
T['pathParams'] & string[],
T['request'],
T['response'],
T['paginated'] & true
>
"
>
import { computed, isRef, onActivated, onDeactivated, ref, watch } from "vue";
import * as os from "@/os";
@ -98,7 +100,7 @@ export type PathParams<
P["request"],
P["response"],
P["paginated"] & true
>
>,
> = {
[K in keyof P["pathParams"] as P["pathParams"][K] & string]:
| string
@ -112,7 +114,7 @@ export type Paging<
P["request"],
P["response"],
P["paginated"] & true
>
>,
> = {
endpoint: P;
pathParams: PathParams<P>;
@ -131,7 +133,7 @@ const props = withDefaults(
}>(),
{
displayLimit: 30,
}
},
);
const emit = defineEmits<{
@ -152,7 +154,7 @@ const empty = computed(() => items.value.length === 0);
const error = ref(false);
const fetch = async (
pagination: SpanFilter
pagination: SpanFilter,
): Promise<PaginatedResult<T["response"]> & { data: { id: string } }[]> => {
const pathParams = props.pagination.pathParams;
const params = props.pagination.params;
@ -166,12 +168,12 @@ const fetch = async (
limit: props.pagination.limit,
},
pathParams,
undefined
undefined,
)
.then(
(res) =>
res as PaginatedResult<T["response"]> &
{ data: { id: string } }[]
{ data: { id: string } }[],
);
};
@ -191,7 +193,7 @@ const init = async (): Promise<void> => {
(err) => {
error.value = true;
initialFetching.value = false;
}
},
);
};
@ -222,7 +224,7 @@ const refresh = async (): Promise<void> => {
(err) => {
error.value = true;
initialFetching.value = false;
}
},
);
};
@ -254,7 +256,7 @@ const fetchMore = async (): Promise<void> => {
},
(err) => {
moreFetching.value = false;
}
},
);
};
@ -301,7 +303,7 @@ const prepend = (item: PageItem): void => {
queue.value = [];
items.value = [...queueRemoved, ...items.value].slice(
0,
props.displayLimit
props.displayLimit,
);
});
}
@ -327,7 +329,7 @@ const removeItem = (finder: (item: PageItem) => boolean): boolean => {
const updateItem = (
id: PageItem["id"],
replacer: (old: PageItem) => PageItem
replacer: (old: PageItem) => PageItem,
): boolean => {
const i = items.value.findIndex((item) => item.id === id);
if (i === -1) {
@ -348,7 +350,7 @@ watch(
if (a.length === 0 && b.length === 0) return;
emit("queue", queue.value.length);
},
{ deep: true }
{ deep: true },
);
init();

View File

@ -77,31 +77,31 @@ const pollRefreshing = ref(false);
const remaining = ref(-1);
const total = computed(() =>
sum(props.note.poll.options.map((x) => x.votes_count))
sum(props.note.poll.options.map((x) => x.votes_count)),
);
const closed = computed(() => remaining.value === 0);
const isLocal = computed(() => !props.note.uri);
const isVoted = computed(
() =>
!props.note.poll.multiple_choice &&
props.note.poll.options.some((c) => c.voted ?? false)
props.note.poll.options.some((c) => c.voted ?? false),
);
const timer = computed(() =>
i18n.t(
remaining.value >= 86400
? "_poll.remainingDays"
: remaining.value >= 3600
? "_poll.remainingHours"
: remaining.value >= 60
? "_poll.remainingMinutes"
: "_poll.remainingSeconds",
? "_poll.remainingHours"
: remaining.value >= 60
? "_poll.remainingMinutes"
: "_poll.remainingSeconds",
{
s: Math.floor(remaining.value % 60),
m: Math.floor(remaining.value / 60) % 60,
h: Math.floor(remaining.value / 3600) % 24,
d: Math.floor(remaining.value / 86400),
}
)
},
),
);
const showResult = ref(props.readOnly || isVoted.value);
@ -112,8 +112,8 @@ if (props.note.poll.expires_at) {
remaining.value = Math.floor(
Math.max(
new Date(props.note.poll.expires_at!).getTime() - Date.now(),
0
) / 1000
0,
) / 1000,
);
if (remaining.value === 0) {
showResult.value = true;
@ -137,7 +137,7 @@ async function refresh() {
os.magApi(
endpoints.GetNoteById,
{ attachments: true },
{ id: obj.object.id }
{ id: obj.object.id },
).then((n) => {
props.note.poll = { ...toRaw(props.note.poll), ...n.poll };
});

View File

@ -24,7 +24,7 @@ const props = defineProps<{
const canRenote = computed(
() =>
["public", "home", "Public", "Home"].includes(props.note.visibility) ||
props.note.user.id === $i?.id
props.note.user.id === $i?.id,
);
function quote(): void {

View File

@ -48,7 +48,7 @@ const hasRenotedBefore = ref<boolean>((props.note.self_renote_count ?? 0) > 0);
const canRenote = computed(
() =>
["Public", "Home"].includes(props.note.visibility) ||
($i && props.note.user.id === $i.id)
($i && props.note.user.id === $i.id),
);
useTooltip(buttonRef, async (showing) => {
@ -70,7 +70,7 @@ useTooltip(buttonRef, async (showing) => {
targetElement: buttonRef.value,
},
{},
"closed"
"closed",
);
});
@ -198,7 +198,7 @@ const renote = async (viaKeyboard = false, ev?: MouseEvent) => {
? {
renoteId: props.note.id,
visibility: magLegacyVisibility(
props.note.visibility
props.note.visibility,
),
visibleUserIds: props.note.visible_user_ids ?? [],
localOnly: true,
@ -206,10 +206,10 @@ const renote = async (viaKeyboard = false, ev?: MouseEvent) => {
: {
renoteId: props.note.id,
visibility: magLegacyVisibility(
props.note.visibility
props.note.visibility,
),
localOnly: true,
}
},
);
hasRenotedBefore.value = true;
const el =

View File

@ -23,7 +23,7 @@ import { MagChannelState } from "magnetar-common";
import { onMounted, onUnmounted, ref } from "vue";
defineProps<{
showConnected?: boolean
showConnected?: boolean;
}>();
const state = ref<MagChannelState>(magStream.state);
@ -65,7 +65,8 @@ const emit = defineEmits<{
z-index: 5;
top: 0;
&.closed, &.failed {
&.closed,
&.failed {
background-color: var(--infoWarnBg);
color: var(--error);
}

View File

@ -218,23 +218,23 @@ const isLong =
(props.note.text.split("\n").length > 10 ||
props.note.text.length > 800)) ||
(props.note.attachments ?? []).length > 4);
const collapsed = $ref(props.note.cw == null && isLong);
const collapsed = ref(props.note.cw == null && isLong);
const urls = props.note.text
? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5)
: null;
let showContent = $ref(false);
let showContent = ref(false);
const mfms = props.note.text
? extractMfmWithAnimation(mfm.parse(props.note.text))
: null;
const hasMfm = $ref(mfms && mfms.length > 0);
const hasMfm = ref(mfms && mfms.length > 0);
let disableMfm = $ref(defaultStore.state.animatedMfm);
let disableMfm = ref(defaultStore.state.animatedMfm);
async function toggleMfm() {
if (disableMfm) {
if (disableMfm.value) {
if (!defaultStore.state.animatedMfmWarnShown) {
const { canceled } = await os.confirm({
type: "warning",
@ -245,9 +245,9 @@ async function toggleMfm() {
defaultStore.set("animatedMfmWarnShown", true);
}
disableMfm = false;
disableMfm.value = false;
} else {
disableMfm = true;
disableMfm.value = true;
}
}

View File

@ -127,7 +127,7 @@
</template>
<script lang="ts" setup>
import { computed, onMounted } from "vue";
import { computed, onMounted, ref } from "vue";
import MkFollowButton from "@/components/MkFollowButton.vue";
import { userPage } from "@/filters/user";
import XShowMoreButton from "@/components/MkShowMoreButton.vue";
@ -151,18 +151,19 @@ const emit = defineEmits<{
}>();
const zIndex = os.claimZIndex("middle");
let user = $ref<packed.PackUserMaybeAll | null>(null);
let top = $ref(0);
let left = $ref(0);
let user = ref<packed.PackUserMaybeAll | null>(null);
let top = ref(0);
let left = ref(0);
let isLong = computed(() => {
if (!user?.description) return;
if (!user.value?.description) return;
return (
user.description.split("\n").length > 9 || user.description.length > 400
user.value.description.split("\n").length > 9 ||
user.value.description.length > 400
);
});
let collapsed = $ref(!isLong);
let collapsed = ref(!isLong);
onMounted(() => {
const options = { detail: true, profile: true, relation: true };
@ -176,7 +177,7 @@ onMounted(() => {
os.magApi(endpoints.GetUserByAcct, options, {
user_acct: canonical,
}).then((u) => {
user = u;
user.value = u;
});
} else {
const acctQuery = props.userTag.startsWith("@");
@ -189,19 +190,22 @@ onMounted(() => {
user_id: props.userTag,
});
apiCall.then((u) => (user = u));
apiCall.then((u) => (user.value = u));
}
const rect = props.source.getBoundingClientRect();
left = rect.left + props.source.offsetWidth / 2 - 300 / 2 + window.scrollX;
top = rect.top + props.source.offsetHeight + window.scrollY;
left.value =
rect.left + props.source.offsetWidth / 2 - 300 / 2 + window.scrollX;
top.value = rect.top + props.source.offsetHeight + window.scrollY;
});
</script>
<style lang="scss" scoped>
.popup-enter-active,
.popup-leave-active {
transition: opacity 0.3s, transform 0.3s !important;
transition:
opacity 0.3s,
transform 0.3s !important;
}
.popup-enter-from,
.popup-leave-to {

View File

@ -0,0 +1,86 @@
<template>
<media-player
class="player"
:title="video.comment"
:src="{
src: video.url,
type: magTransProperty(video, 'mime_type', 'type'),
}"
:volume="volume"
:controlsDelay="500"
:hideOnMouseLeave="true"
:load="'play'"
crossOrigin
playsInline
@volume-change="volumeChange"
>
<media-provider>
<media-poster
:src="magTransProperty(video, 'thumbnail_url', 'thumbnailUrl')"
>
</media-poster>
</media-provider>
<media-plyr-layout
:download="{ filename: video.name, url: video.url }"
:controls="[
'play',
'current-time',
'progress',
'duration',
'mute+volume',
'settings',
'download',
'pip',
'fullscreen',
]"
/>
</media-player>
</template>
<script lang="ts" setup>
import "vidstack/player/styles/plyr/theme.css";
import "vidstack/player";
import "vidstack/player/layouts";
import "vidstack/player/ui";
import type * as misskey from "calckey-js";
import { packed } from "magnetar-common";
import { magTransProperty } from "@/scripts-mag/mag-util";
import { ColdDeviceStorage } from "@/store";
import { ref } from "vue";
defineProps<{
video: packed.PackDriveFileBase | misskey.entities.DriveFile;
}>();
const volume = ref(ColdDeviceStorage.get("mediaVolume"));
function volumeChange(e: Event): void {
if (e?.target?.volume)
ColdDeviceStorage.set("mediaVolume", e.target.volume);
}
</script>
<style lang="scss">
.player {
--plyr-color-main: var(--accent);
&[data-view-type="video"] {
aspect-ratio: 16 / 9;
}
video {
width: 100%;
height: 100%;
}
& .plyr--video {
& .plyr__controls {
transition: opacity 0.2s ease-in-out;
}
&.plyr--hide-controls .plyr__controls {
transform: unset;
}
}
}
</style>

View File

@ -70,6 +70,7 @@ import MkSwitch from "@/components/form/switch.vue";
import MkKeyValue from "@/components/MkKeyValue.vue";
import * as os from "@/os";
import { i18n } from "@/i18n";
import { ref } from "vue";
const props = defineProps<{
report: any;
@ -79,11 +80,11 @@ const emit = defineEmits<{
(ev: "resolved", reportId: string): void;
}>();
let forward = $ref(props.report.forwarded);
let forward = ref(props.report.forwarded);
function resolve() {
os.apiWithDialog("admin/resolve-abuse-user-report", {
forward: forward,
forward: forward.value,
reportId: props.report.id,
}).then(() => {
emit("resolved", props.report.id);

View File

@ -67,7 +67,7 @@ function send() {
userId: props.user.id,
comment: comment.value,
},
undefined
undefined,
).then((res) => {
os.alert({
type: "success",

View File

@ -19,7 +19,7 @@
0,
1 -
angleDiff(hAngle, angle) / Math.PI -
numbersOpacityFactor
numbersOpacityFactor,
)
"
/>
@ -48,7 +48,7 @@
0,
1 -
angleDiff(hAngle, angle) / Math.PI -
numbersOpacityFactor
numbersOpacityFactor,
)
"
>
@ -108,14 +108,7 @@
</template>
<script lang="ts" setup>
import {
ref,
computed,
onMounted,
onBeforeUnmount,
shallowRef,
nextTick,
} from "vue";
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
import tinycolor from "tinycolor2";
import { globalEvents } from "@/events.js";
@ -151,7 +144,7 @@ const props = withDefaults(
graduations: "dots",
fadeGraduations: true,
sAnimation: "elastic",
}
},
);
const graduationsMajor = computed(() => {
@ -174,49 +167,51 @@ const texts = computed(() => {
});
let enabled = true;
let majorGraduationColor = $ref<string>();
let majorGraduationColor = ref<string>();
//let minorGraduationColor = $ref<string>();
let sHandColor = $ref<string>();
let mHandColor = $ref<string>();
let hHandColor = $ref<string>();
let nowColor = $ref<string>();
let h = $ref<number>(0);
let m = $ref<number>(0);
let s = $ref<number>(0);
let hAngle = $ref<number>(0);
let mAngle = $ref<number>(0);
let sAngle = $ref<number>(0);
let disableSAnimate = $ref(false);
let sHandColor = ref<string>();
let mHandColor = ref<string>();
let hHandColor = ref<string>();
let nowColor = ref<string>();
let h = ref<number>(0);
let m = ref<number>(0);
let s = ref<number>(0);
let hAngle = ref<number>(0);
let mAngle = ref<number>(0);
let sAngle = ref<number>(0);
let disableSAnimate = ref(false);
let sOneRound = false;
function tick() {
const now = new Date();
now.setMinutes(
now.getMinutes() + (new Date().getTimezoneOffset() + props.offset)
now.getMinutes() + (new Date().getTimezoneOffset() + props.offset),
);
s = now.getSeconds();
m = now.getMinutes();
h = now.getHours();
hAngle =
(Math.PI * ((h % (props.twentyfour ? 24 : 12)) + (m + s / 60) / 60)) /
s.value = now.getSeconds();
m.value = now.getMinutes();
h.value = now.getHours();
hAngle.value =
(Math.PI *
((h.value % (props.twentyfour ? 24 : 12)) +
(m.value + s.value / 60) / 60)) /
(props.twentyfour ? 12 : 6);
mAngle = (Math.PI * (m + s / 60)) / 30;
mAngle.value = (Math.PI * (m.value + s.value / 60)) / 30;
if (sOneRound) {
// (59->0)
sAngle = (Math.PI * 60) / 30;
sAngle.value = (Math.PI * 60) / 30;
window.setTimeout(() => {
disableSAnimate = true;
disableSAnimate.value = true;
window.setTimeout(() => {
sAngle = 0;
sAngle.value = 0;
window.setTimeout(() => {
disableSAnimate = false;
disableSAnimate.value = false;
}, 100);
}, 100);
}, 700);
} else {
sAngle = (Math.PI * s) / 30;
sAngle.value = (Math.PI * s.value) / 30;
}
sOneRound = s === 59;
sOneRound = s.value === 59;
}
tick();
@ -225,18 +220,18 @@ function calcColors() {
const computedStyle = getComputedStyle(document.documentElement);
const dark = tinycolor(computedStyle.getPropertyValue("--bg")).isDark();
const accent = tinycolor(
computedStyle.getPropertyValue("--accent")
computedStyle.getPropertyValue("--accent"),
).toHexString();
majorGraduationColor = dark
majorGraduationColor.value = dark
? "rgba(255, 255, 255, 0.3)"
: "rgba(0, 0, 0, 0.3)";
//minorGraduationColor = dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
sHandColor = dark ? "rgba(255, 255, 255, 0.5)" : "rgba(0, 0, 0, 0.3)";
mHandColor = tinycolor(
computedStyle.getPropertyValue("--fg")
sHandColor.value = dark ? "rgba(255, 255, 255, 0.5)" : "rgba(0, 0, 0, 0.3)";
mHandColor.value = tinycolor(
computedStyle.getPropertyValue("--fg"),
).toHexString();
hHandColor = accent;
nowColor = accent;
hHandColor.value = accent;
nowColor.value = accent;
}
calcColors();

View File

@ -268,7 +268,7 @@ function exec() {
} else if (props.type === "hashtag") {
if (!props.q || props.q === "") {
hashtags.value = JSON.parse(
localStorage.getItem("hashtags") || "[]"
localStorage.getItem("hashtags") || "[]",
);
fetching.value = false;
} else {
@ -288,7 +288,7 @@ function exec() {
//
sessionStorage.setItem(
cacheKey,
JSON.stringify(searchedHashtags)
JSON.stringify(searchedHashtags),
);
});
}
@ -298,7 +298,7 @@ function exec() {
// 使
emojis.value = defaultStore.state.recentlyUsedEmojis
.map((emoji) =>
emojiDb.find((dbEmoji) => dbEmoji.emoji === emoji)
emojiDb.find((dbEmoji) => dbEmoji.emoji === emoji),
)
.filter((x) => x) as EmojiDef[];
return;
@ -450,7 +450,7 @@ onMounted(() => {
nextTick(() => {
exec();
});
}
},
);
});
});
@ -470,7 +470,9 @@ onBeforeUnmount(() => {
max-width: 100%;
margin-top: calc(1em + 8px);
overflow: hidden;
transition: top 0.1s ease, left 0.1s ease;
transition:
top 0.1s ease,
left 0.1s ease;
> ol {
display: block;

View File

@ -27,7 +27,7 @@
</template>
<script lang="ts" setup>
import { nextTick, onMounted } from "vue";
import { nextTick, onMounted, ref } from "vue";
const props = defineProps<{
type?: "button" | "submit" | "reset";
@ -49,22 +49,30 @@ const emit = defineEmits<{
(ev: "click", payload: MouseEvent): void;
}>();
let el = $ref<HTMLElement | null>(null);
let ripples = $ref<HTMLElement | null>(null);
let el = ref<HTMLElement | null>(null);
let ripples = ref<HTMLElement | null>(null);
onMounted(() => {
if (props.autofocus) {
nextTick(() => {
el!.focus();
el.value!.focus();
});
}
});
function distance(p, q): number {
function distance(
p: { x: number; y: number },
q: { x: number; y: number },
): number {
return Math.hypot(p.x - q.x, p.y - q.y);
}
function calcCircleScale(boxW, boxH, circleCenterX, circleCenterY): number {
function calcCircleScale(
boxW: number,
boxH: number,
circleCenterX: number,
circleCenterY: number,
): number {
const origin = { x: circleCenterX, y: circleCenterY };
const dist1 = distance({ x: 0, y: 0 }, origin);
const dist2 = distance({ x: boxW, y: 0 }, origin);
@ -81,7 +89,7 @@ function onMousedown(evt: MouseEvent): void {
ripple.style.top = (evt.clientY - rect.top - 1).toString() + "px";
ripple.style.left = (evt.clientX - rect.left - 1).toString() + "px";
ripples!.appendChild(ripple);
ripples.value!.appendChild(ripple);
const circleCenterX = evt.clientX - rect.left;
const circleCenterY = evt.clientY - rect.top;
@ -90,7 +98,7 @@ function onMousedown(evt: MouseEvent): void {
target.clientWidth,
target.clientHeight,
circleCenterX,
circleCenterY
circleCenterY,
);
window.setTimeout(() => {
@ -101,7 +109,7 @@ function onMousedown(evt: MouseEvent): void {
ripple.style.opacity = "0";
}, 1000);
window.setTimeout(() => {
if (ripples) ripples.removeChild(ripple);
if (ripples) ripples.value!.removeChild(ripple);
}, 2000);
}
</script>

View File

@ -25,7 +25,7 @@ type Captcha = {
| "expired-callback"
| "error-callback"
| "endpoint"]?: unknown;
}
},
): string;
remove(id: string): void;
execute(id: string): void;
@ -78,7 +78,7 @@ const src = computed(() => {
});
const captcha = computed<Captcha>(
() => window[variable.value] || ({} as unknown as Captcha)
() => window[variable.value] || ({} as unknown as Captcha),
);
if (loaded) {
@ -91,7 +91,7 @@ if (loaded) {
async: true,
id: props.provider,
src: src.value,
})
}),
)
).addEventListener("load", () => (available.value = true));
}

View File

@ -8,7 +8,7 @@
</template>
<script lang="ts" setup>
import { onMounted, ref, watch, PropType, onUnmounted } from "vue";
import { onMounted, ref, watch, PropType } from "vue";
import {
Chart,
ArcElement,
@ -91,7 +91,7 @@ Chart.register(
Tooltip,
SubTitle,
Filler,
zoomPlugin
zoomPlugin,
//gradient,
);
@ -179,11 +179,11 @@ const render = () => {
//
Chart.defaults.color = getComputedStyle(
document.documentElement
document.documentElement,
).getPropertyValue("--fg");
const maxes = chartData.series.map((x, i) =>
Math.max(...x.data.map((d) => d.y))
Math.max(...x.data.map((d) => d.y)),
);
chartInstance = new Chart(chartEl.value, {
@ -471,9 +471,9 @@ const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
raw.local.inc,
negate(raw.local.dec),
raw.remote.inc,
negate(raw.remote.dec)
negate(raw.remote.dec),
)
: sum(raw[type].inc, negate(raw[type].dec))
: sum(raw[type].inc, negate(raw[type].dec)),
),
color: "#888888",
},
@ -483,7 +483,7 @@ const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
data: format(
type === "combined"
? sum(raw.local.diffs.renote, raw.remote.diffs.renote)
: raw[type].diffs.renote
: raw[type].diffs.renote,
),
color: colors.green,
},
@ -493,7 +493,7 @@ const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
data: format(
type === "combined"
? sum(raw.local.diffs.reply, raw.remote.diffs.reply)
: raw[type].diffs.reply
: raw[type].diffs.reply,
),
color: colors.yellow,
},
@ -503,7 +503,7 @@ const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
data: format(
type === "combined"
? sum(raw.local.diffs.normal, raw.remote.diffs.normal)
: raw[type].diffs.normal
: raw[type].diffs.normal,
),
color: colors.blue,
},
@ -514,9 +514,9 @@ const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
type === "combined"
? sum(
raw.local.diffs.withFile,
raw.remote.diffs.withFile
raw.remote.diffs.withFile,
)
: raw[type].diffs.withFile
: raw[type].diffs.withFile,
),
color: colors.purple,
},
@ -567,8 +567,8 @@ const fetchUsersChart = async (total: boolean): Promise<typeof chartData> => {
raw.local.inc,
negate(raw.local.dec),
raw.remote.inc,
negate(raw.remote.dec)
)
negate(raw.remote.dec),
),
),
},
{
@ -577,7 +577,7 @@ const fetchUsersChart = async (total: boolean): Promise<typeof chartData> => {
data: format(
total
? raw.local.total
: sum(raw.local.inc, negate(raw.local.dec))
: sum(raw.local.inc, negate(raw.local.dec)),
),
},
{
@ -586,7 +586,7 @@ const fetchUsersChart = async (total: boolean): Promise<typeof chartData> => {
data: format(
total
? raw.remote.total
: sum(raw.remote.inc, negate(raw.remote.dec))
: sum(raw.remote.inc, negate(raw.remote.dec)),
),
},
],
@ -675,8 +675,8 @@ const fetchDriveChart = async (): Promise<typeof chartData> => {
raw.local.incSize,
negate(raw.local.decSize),
raw.remote.incSize,
negate(raw.remote.decSize)
)
negate(raw.remote.decSize),
),
),
},
{
@ -719,8 +719,8 @@ const fetchDriveFilesChart = async (): Promise<typeof chartData> => {
raw.local.incCount,
negate(raw.local.decCount),
raw.remote.incCount,
negate(raw.remote.decCount)
)
negate(raw.remote.decCount),
),
),
},
{
@ -778,7 +778,7 @@ const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
};
const fetchInstanceUsersChart = async (
total: boolean
total: boolean,
): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/instance", {
host: props.args.host,
@ -794,7 +794,7 @@ const fetchInstanceUsersChart = async (
data: format(
total
? raw.users.total
: sum(raw.users.inc, negate(raw.users.dec))
: sum(raw.users.inc, negate(raw.users.dec)),
),
},
],
@ -802,7 +802,7 @@ const fetchInstanceUsersChart = async (
};
const fetchInstanceNotesChart = async (
total: boolean
total: boolean,
): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/instance", {
host: props.args.host,
@ -818,7 +818,7 @@ const fetchInstanceNotesChart = async (
data: format(
total
? raw.notes.total
: sum(raw.notes.inc, negate(raw.notes.dec))
: sum(raw.notes.inc, negate(raw.notes.dec)),
),
},
],
@ -826,7 +826,7 @@ const fetchInstanceNotesChart = async (
};
const fetchInstanceFfChart = async (
total: boolean
total: boolean,
): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/instance", {
host: props.args.host,
@ -842,7 +842,7 @@ const fetchInstanceFfChart = async (
data: format(
total
? raw.following.total
: sum(raw.following.inc, negate(raw.following.dec))
: sum(raw.following.inc, negate(raw.following.dec)),
),
},
{
@ -852,7 +852,7 @@ const fetchInstanceFfChart = async (
data: format(
total
? raw.followers.total
: sum(raw.followers.inc, negate(raw.followers.dec))
: sum(raw.followers.inc, negate(raw.followers.dec)),
),
},
],
@ -860,7 +860,7 @@ const fetchInstanceFfChart = async (
};
const fetchInstanceDriveUsageChart = async (
total: boolean
total: boolean,
): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/instance", {
host: props.args.host,
@ -877,7 +877,7 @@ const fetchInstanceDriveUsageChart = async (
data: format(
total
? raw.drive.totalUsage
: sum(raw.drive.incUsage, negate(raw.drive.decUsage))
: sum(raw.drive.incUsage, negate(raw.drive.decUsage)),
),
},
],
@ -885,7 +885,7 @@ const fetchInstanceDriveUsageChart = async (
};
const fetchInstanceDriveFilesChart = async (
total: boolean
total: boolean,
): Promise<typeof chartData> => {
const raw = await os.apiGet("charts/instance", {
host: props.args.host,
@ -901,7 +901,7 @@ const fetchInstanceDriveFilesChart = async (
data: format(
total
? raw.drive.totalFiles
: sum(raw.drive.incFiles, negate(raw.drive.decFiles))
: sum(raw.drive.incFiles, negate(raw.drive.decFiles)),
),
},
],

View File

@ -11,6 +11,8 @@
</template>
<script lang="ts" setup>
import { ref } from "vue";
import XModalWindow from "@/components/MkModalWindow.vue";
import XCheatSheet from "@/pages/mfm-cheat-sheet.vue";
import { i18n } from "@/i18n";
@ -20,10 +22,10 @@ const emit = defineEmits<{
(ev: "closed"): void;
}>();
const dialog = $ref<InstanceType<typeof XModalWindow>>();
const dialog = ref<InstanceType<typeof XModalWindow>>();
function close(res) {
dialog.close();
dialog.value.close();
}
</script>

View File

@ -18,13 +18,13 @@ const props = defineProps<{
}>();
const prismLang = computed(() =>
Prism.languages[props.lang] ? props.lang : "js"
Prism.languages[props.lang] ? props.lang : "js",
);
const html = computed(() =>
Prism.highlight(
props.code,
Prism.languages[prismLang.value],
prismLang.value
)
prismLang.value,
),
);
</script>

View File

@ -12,6 +12,6 @@ defineProps<{
}>();
const XCode = defineAsyncComponent(
() => import("@/components/MkCode.core.vue")
() => import("@/components/MkCode.core.vue"),
);
</script>

View File

@ -125,7 +125,7 @@ export default defineComponent({
},
{
immediate: true,
}
},
);
this.$el.style.setProperty("--maxHeight", this.maxHeight + "px");
@ -174,7 +174,9 @@ export default defineComponent({
.container-toggle-enter-active,
.container-toggle-leave-active {
overflow-y: hidden;
transition: opacity 0.5s, height 0.5s !important;
transition:
opacity 0.5s,
height 0.5s !important;
}
.container-toggle-enter-from {
opacity: 0;

View File

@ -12,7 +12,7 @@
</template>
<script lang="ts" setup>
import { onMounted, onBeforeUnmount } from "vue";
import { onMounted, onBeforeUnmount, ref } from "vue";
import MkMenu from "./MkMenu.vue";
import { MenuItem } from "./types/menu.vue";
import contains from "@/scripts/contains";
@ -27,16 +27,16 @@ const emit = defineEmits<{
(ev: "closed"): void;
}>();
let rootEl = $ref<HTMLDivElement>();
let rootEl = ref<HTMLDivElement>();
let zIndex = $ref<number>(os.claimZIndex("high"));
let zIndex = ref<number>(os.claimZIndex("high"));
onMounted(() => {
let left = props.ev.pageX + 1; // + 1
let top = props.ev.pageY + 1; // + 1
const width = rootEl.offsetWidth;
const height = rootEl.offsetHeight;
const width = rootEl.value.offsetWidth;
const height = rootEl.value.offsetHeight;
if (left + width - window.pageXOffset > window.innerWidth) {
left = window.innerWidth - width + window.pageXOffset;
@ -54,8 +54,8 @@ onMounted(() => {
left = 0;
}
rootEl.style.top = `${top}px`;
rootEl.style.left = `${left}px`;
rootEl.value.style.top = `${top}px`;
rootEl.value.style.left = `${left}px`;
for (const el of Array.from(document.querySelectorAll("body *"))) {
el.addEventListener("mousedown", onMousedown);
@ -69,7 +69,8 @@ onBeforeUnmount(() => {
});
function onMousedown(evt: Event) {
if (!contains(rootEl, evt.target) && rootEl !== evt.target) emit("closed");
if (!contains(rootEl.value, evt.target) && rootEl.value !== evt.target)
emit("closed");
}
</script>
@ -80,7 +81,8 @@ function onMousedown(evt: Event) {
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1),
transition:
opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1),
transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
transform-origin: left top;
}

View File

@ -34,7 +34,7 @@
</template>
<script lang="ts" setup>
import { nextTick, onMounted } from "vue";
import { nextTick, onMounted, ref } from "vue";
import * as misskey from "calckey-js";
import Cropper from "cropperjs";
import tinycolor from "tinycolor2";
@ -60,10 +60,10 @@ const props = defineProps<{
const imgUrl = `${url}/proxy/image.webp?${query({
url: props.file.url,
})}`;
let dialogEl = $ref<InstanceType<typeof XModalWindow>>();
let imgEl = $ref<HTMLImageElement>();
let dialogEl = ref<InstanceType<typeof XModalWindow>>();
let imgEl = ref<HTMLImageElement>();
let cropper: Cropper | null = null;
let loading = $ref(true);
let loading = ref(true);
const ok = async () => {
const promise = new Promise<misskey.entities.DriveFile>(async (res) => {
@ -94,16 +94,16 @@ const ok = async () => {
const f = await promise;
emit("ok", f);
dialogEl.close();
dialogEl.value.close();
};
const cancel = () => {
emit("cancel");
dialogEl.close();
dialogEl.value.close();
};
const onImageLoad = () => {
loading = false;
loading.value = false;
if (cropper) {
cropper.getCropperImage()!.$center("contain");
@ -112,13 +112,13 @@ const onImageLoad = () => {
};
onMounted(() => {
cropper = new Cropper(imgEl, {});
cropper = new Cropper(imgEl.value, {});
const computedStyle = getComputedStyle(document.documentElement);
const selection = cropper.getCropperSelection()!;
selection.themeColor = tinycolor(
computedStyle.getPropertyValue("--accent")
computedStyle.getPropertyValue("--accent"),
).toHexString();
selection.aspectRatio = props.aspectRatio;
selection.initialAspectRatio = props.aspectRatio;

View File

@ -68,7 +68,9 @@ defineExpose({
> span {
background: var(--cwBg) !important;
color: var(--cwFg);
transition: background 0.2s, color 0.2s;
transition:
background 0.2s,
color 0.2s;
> span {
font-weight: 500;
&::before {

View File

@ -78,14 +78,24 @@
okButtonDisabled &&
disabledReason === 'charactersExceeded'
"
v-text="i18n.t('_dialog.charactersExceeded', { current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })"
v-text="
i18n.t('_dialog.charactersExceeded', {
current: (inputValue as string).length,
max: input.maxLength ?? 'NaN',
})
"
/>
<span
v-else-if="
okButtonDisabled &&
disabledReason === 'charactersBelow'
"
v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })"
v-text="
i18n.t('_dialog.charactersBelow', {
current: (inputValue as string).length,
min: input.minLength ?? 'NaN',
})
"
/>
</template>
<template v-if="input.type === 'search'" #suffix>
@ -189,7 +199,7 @@
</template>
<script lang="ts" setup>
import { onBeforeUnmount, onMounted, ref, shallowRef } from "vue";
import { onBeforeUnmount, onMounted, ref, shallowRef, computed } from "vue";
import MkModal from "@/components/MkModal.vue";
import MkButton from "@/components/MkButton.vue";
import MkInput from "@/components/form/input.vue";
@ -258,7 +268,7 @@ const props = withDefaults(
isYesNo: false,
cancelableByBgClick: true,
}
},
);
const emit = defineEmits<{
@ -271,17 +281,15 @@ const modal = shallowRef<InstanceType<typeof MkModal>>();
const inputValue = ref<string | number | null>(props.input?.default ?? null);
const selectedValue = ref(props.select?.default ?? null);
let disabledReason = $ref<null | "charactersExceeded" | "charactersBelow">(
null
);
const okButtonDisabled = $computed<boolean>(() => {
let disabledReason = ref<null | "charactersExceeded" | "charactersBelow">(null);
const okButtonDisabled = computed<boolean>(() => {
if (props.input) {
if (props.input.minLength) {
if (
(inputValue.value || inputValue.value === "") &&
(inputValue.value as string).length < props.input.minLength
) {
disabledReason = "charactersBelow";
disabledReason.value = "charactersBelow";
return true;
}
}
@ -290,7 +298,7 @@ const okButtonDisabled = $computed<boolean>(() => {
inputValue.value &&
(inputValue.value as string).length > props.input.maxLength
) {
disabledReason = "charactersExceeded";
disabledReason.value = "charactersExceeded";
return true;
}
}
@ -311,8 +319,8 @@ async function ok() {
const result = props.input
? inputValue.value
: props.select
? selectedValue.value
: true;
? selectedValue.value
: true;
done(false, result);
}
@ -439,7 +447,7 @@ async function openSearchFilters(ev) {
},
],
ev.target,
{ noReturnFocus: true }
{ noReturnFocus: true },
);
inputEl.value.focus();
inputEl.value.selectRange(inputValue.value.length, inputValue.value.length); // cursor at end

View File

@ -23,7 +23,7 @@ const props = withDefaults(
showS: true,
showMs: false,
offset: 0 - new Date().getTimezoneOffset(),
}
},
);
let intervalId;
@ -45,7 +45,7 @@ watch(showColon, (v) => {
const tick = () => {
const now = new Date();
now.setMinutes(
now.getMinutes() + (new Date().getTimezoneOffset() + props.offset)
now.getMinutes() + (new Date().getTimezoneOffset() + props.offset),
);
hh.value = now.getHours().toString().padStart(2, "0");
mm.value = now.getMinutes().toString().padStart(2, "0");
@ -65,7 +65,7 @@ watch(
if (intervalId) window.clearInterval(intervalId);
intervalId = window.setInterval(tick, props.showMs ? 10 : 1000);
},
{ immediate: true }
{ immediate: true },
);
onUnmounted(() => {

View File

@ -56,7 +56,7 @@ const props = withDefaults(
{
isSelected: false,
selectMode: false,
}
},
);
const emit = defineEmits<{
@ -68,7 +68,7 @@ const emit = defineEmits<{
const isDragging = ref(false);
const title = computed(
() => `${props.file.name}\n${props.file.type} ${bytes(props.file.size)}`
() => `${props.file.name}\n${props.file.type} ${bytes(props.file.size)}`,
);
function getMenu() {
@ -124,7 +124,7 @@ function onClick(ev: MouseEvent) {
getMenu(),
(ev.currentTarget ?? ev.target ?? undefined) as
| HTMLElement
| undefined
| undefined,
);
}
}
@ -138,7 +138,7 @@ function onDragstart(ev: DragEvent) {
ev.dataTransfer.effectAllowed = "move";
ev.dataTransfer.setData(
_DATA_TRANSFER_DRIVE_FILE_,
JSON.stringify(props.file)
JSON.stringify(props.file),
);
}
isDragging.value = true;
@ -186,7 +186,7 @@ function describe() {
});
},
},
"closed"
"closed",
);
}

View File

@ -52,7 +52,7 @@ const props = withDefaults(
{
isSelected: false,
selectMode: false,
}
},
);
const emit = defineEmits<{
@ -184,7 +184,7 @@ function onDragstart(ev: DragEvent) {
ev.dataTransfer.effectAllowed = "move";
ev.dataTransfer.setData(
_DATA_TRANSFER_DRIVE_FOLDER_,
JSON.stringify(props.folder)
JSON.stringify(props.folder),
);
isDragging.value = true;
@ -256,13 +256,13 @@ function onContextmenu(ev: MouseEvent) {
action: () => {
os.popup(
defineAsyncComponent(
() => import("@/components/MkDriveWindow.vue")
() => import("@/components/MkDriveWindow.vue"),
),
{
initialFolder: props.folder,
},
{},
"closed"
"closed",
);
},
},
@ -280,7 +280,7 @@ function onContextmenu(ev: MouseEvent) {
action: deleteFolder,
},
],
ev
ev,
);
}
</script>

View File

@ -29,7 +29,7 @@ const emit = defineEmits<{
(
ev: "upload",
file: File,
folder?: Misskey.entities.DriveFolder | null
folder?: Misskey.entities.DriveFolder | null,
): void;
(ev: "removeFile", v: Misskey.entities.DriveFile["id"]): void;
(ev: "removeFolder", v: Misskey.entities.DriveFolder["id"]): void;

View File

@ -161,17 +161,17 @@ const props = withDefaults(
{
multiple: false,
select: null,
}
},
);
const emit = defineEmits<{
(
ev: "selected",
v: Misskey.entities.DriveFile | Misskey.entities.DriveFolder
v: Misskey.entities.DriveFile | Misskey.entities.DriveFolder,
): void;
(
ev: "change-selection",
v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]
v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[],
): void;
(ev: "move-root"): void;
(ev: "cd", v: Misskey.entities.DriveFolder | null): void;
@ -207,7 +207,7 @@ const ilFilesObserver = new IntersectionObserver(
entries.some((entry) => entry.isIntersecting) &&
!fetching.value &&
moreFiles.value &&
fetchMoreFiles()
fetchMoreFiles(),
);
watch(folder, () => emit("cd", folder.value));
@ -230,13 +230,13 @@ function onStreamDriveFileDeleted(fileId: string) {
}
function onStreamDriveFolderCreated(
createdFolder: Misskey.entities.DriveFolder
createdFolder: Misskey.entities.DriveFolder,
) {
addFolder(createdFolder, true);
}
function onStreamDriveFolderUpdated(
updatedFolder: Misskey.entities.DriveFolder
updatedFolder: Misskey.entities.DriveFolder,
) {
const current = folder.value ? folder.value.id : null;
if (current !== updatedFolder.parentId) {
@ -433,7 +433,7 @@ function onChangeFileInput() {
function upload(
file: File,
folderToUpload?: Misskey.entities.DriveFolder | null
folderToUpload?: Misskey.entities.DriveFolder | null,
) {
uploadFile(
file,
@ -441,7 +441,7 @@ function upload(
? folderToUpload.id
: null,
undefined,
keepOriginal.value
keepOriginal.value,
).then((res) => {
addFile(res, true);
});
@ -452,7 +452,7 @@ function chooseFile(file: Misskey.entities.DriveFile) {
if (props.multiple) {
if (isAlreadySelected) {
selectedFiles.value = selectedFiles.value.filter(
(f) => f.id !== file.id
(f) => f.id !== file.id,
);
} else {
selectedFiles.value.push(file);
@ -470,12 +470,12 @@ function chooseFile(file: Misskey.entities.DriveFile) {
function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) {
const isAlreadySelected = selectedFolders.value.some(
(f) => f.id === folderToChoose.id
(f) => f.id === folderToChoose.id,
);
if (props.multiple) {
if (isAlreadySelected) {
selectedFolders.value = selectedFolders.value.filter(
(f) => f.id !== folderToChoose.id
(f) => f.id !== folderToChoose.id,
);
} else {
selectedFolders.value.push(folderToChoose);
@ -707,7 +707,7 @@ function getMenu() {
icon: "ph-trash ph-bold ph-lg",
action: () => {
deleteFolder(
folder.value as Misskey.entities.DriveFolder
folder.value as Misskey.entities.DriveFolder,
);
},
}
@ -725,7 +725,7 @@ function getMenu() {
function showMenu(ev: MouseEvent) {
os.popupMenu(
getMenu(),
(ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined
(ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined,
);
}

View File

@ -17,8 +17,8 @@
? i18n.ts.selectFiles
: i18n.ts.selectFolders
: type === "file"
? i18n.ts.selectFile
: i18n.ts.selectFolder
? i18n.ts.selectFile
: i18n.ts.selectFolder
}}
<span
v-if="selected.length > 0"
@ -50,7 +50,7 @@ withDefaults(
}>(),
{
type: "file",
}
},
);
const emit = defineEmits<{

View File

@ -16,7 +16,7 @@
class="_button"
@click.prevent="
applyUnicodeSkinTone(
props.skinTones.indexOf(skinTone) + 1
props.skinTones.indexOf(skinTone) + 1,
)
"
>
@ -99,7 +99,7 @@ watch(
() => props.emojis,
(newVal) => {
localEmojis.value = newVal.map(resolveEmojis);
}
},
);
</script>

View File

@ -198,7 +198,7 @@ const props = withDefaults(
}>(),
{
showPinned: true,
}
},
);
const emit = defineEmits<{
@ -218,13 +218,13 @@ const {
} = defaultStore.reactiveState;
const size = computed(() =>
props.asReactionPicker ? reactionPickerSize.value : 1
props.asReactionPicker ? reactionPickerSize.value : 1,
);
const width = computed(() =>
props.asReactionPicker ? reactionPickerWidth.value : 3
props.asReactionPicker ? reactionPickerWidth.value : 3,
);
const height = computed(() =>
props.asReactionPicker ? reactionPickerHeight.value : 2
props.asReactionPicker ? reactionPickerHeight.value : 2,
);
const customEmojiCategories = emojiCategories;
const customEmojis = instance.emojis;
@ -240,7 +240,7 @@ const resolveEmojis = (e: string) =>
const resolvedPinned = computed(() => pinned.value.map(resolveEmojis));
const resolvedRecent = computed(() =>
recentlyUsedEmojis.value.map(resolveEmojis)
recentlyUsedEmojis.value.map(resolveEmojis),
);
watch(q, () => {
@ -282,8 +282,8 @@ watch(q, () => {
(keyword) =>
emoji.name.includes(keyword) ||
emoji.aliases.some((alias) =>
alias.includes(keyword)
)
alias.includes(keyword),
),
)
) {
matches.add(emoji);
@ -354,8 +354,8 @@ watch(q, () => {
(keyword) =>
emoji.slug.includes(keyword) ||
emoji.keywords?.some((alias) =>
alias.includes(keyword)
)
alias.includes(keyword),
),
)
) {
matches.add(emoji);
@ -459,7 +459,7 @@ function done(query?: any): boolean | void {
return true;
}
const exactMatchUnicode = emojilist.find(
(emoji) => emoji.emoji === q2 || emoji.slug === q2
(emoji) => emoji.emoji === q2 || emoji.slug === q2,
);
if (exactMatchUnicode) {
chosen(magUnicodeEmoji(exactMatchUnicode));

View File

@ -48,7 +48,7 @@ withDefaults(
manualShowing: null,
showPinned: true,
asReactionPicker: false,
}
},
);
const emit = defineEmits<{

View File

@ -11,7 +11,7 @@
:key="file.id"
v-tooltip.mfm="
`${file.type}\n${bytes(file.size)}\n${new Date(
file.createdAt
file.createdAt,
).toLocaleString()}\nby ${
file.user ? '@' + Acct.toString(file.user) : 'system'
}`

View File

@ -56,7 +56,7 @@ export default defineComponent({
this.persistKey &&
localStorage.getItem(localStoragePrefix + this.persistKey)
? localStorage.getItem(
localStoragePrefix + this.persistKey
localStoragePrefix + this.persistKey,
) === "t"
: this.expanded,
};
@ -66,7 +66,7 @@ export default defineComponent({
if (this.persistKey) {
localStorage.setItem(
localStoragePrefix + this.persistKey,
this.showBody ? "t" : "f"
this.showBody ? "t" : "f",
);
}
},
@ -85,9 +85,9 @@ export default defineComponent({
const bg = tinycolor(
rawBg.startsWith("var(")
? getComputedStyle(document.documentElement).getPropertyValue(
rawBg.slice(4, -1)
rawBg.slice(4, -1),
)
: rawBg
: rawBg,
);
bg.setAlpha(0.85);
this.bg = bg.toRgbString();
@ -123,7 +123,9 @@ export default defineComponent({
.folder-toggle-enter-active,
.folder-toggle-leave-active {
overflow-y: hidden;
transition: opacity 0.5s, height 0.5s !important;
transition:
opacity 0.5s,
height 0.5s !important;
}
.folder-toggle-enter-from {
opacity: 0;

View File

@ -28,7 +28,7 @@
</template>
<script lang="ts" setup>
import { onBeforeUnmount, onMounted } from "vue";
import { onBeforeUnmount, onMounted, ref } from "vue";
import type * as Misskey from "calckey-js";
import * as os from "@/os";
import { i18n } from "@/i18n";
@ -44,20 +44,20 @@ const props = defineProps<{
user: packed.PackUserBase | Misskey.entities.User;
}>();
let state = $ref(i18n.ts.processing);
let state = ref(i18n.ts.processing);
const connection = stream.useChannel("main");
let waitIncoming = $ref(false);
let waitIncoming = ref(false);
function accept(user: packed.PackUserBase | Misskey.entities.User) {
waitIncoming = true;
waitIncoming.value = true;
os.api("following/requests/accept", { userId: user.id }).then(() => {
globalEvents.emit("followeeProcessed", user);
});
}
function reject(user: packed.PackUserBase | Misskey.entities.User) {
waitIncoming = true;
waitIncoming.value = true;
os.api("following/requests/reject", { userId: user.id }).then(() => {
globalEvents.emit("followeeProcessed", user);
});

View File

@ -77,7 +77,7 @@
</template>
<script lang="ts" setup>
import { computed, onBeforeUnmount, onMounted } from "vue";
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
import type * as Misskey from "calckey-js";
import * as os from "@/os";
import { stream } from "@/stream";
@ -101,26 +101,26 @@ const props = withDefaults(
{
full: false,
large: false,
}
},
);
const isBlocking = computed(() =>
magTransProperty(props.user, "you_block", "isBlocking")
magTransProperty(props.user, "you_block", "isBlocking"),
);
let state = $ref(i18n.ts.processing);
let state = ref(i18n.ts.processing);
let isFollowing = $ref(
magTransProperty(props.user, "you_follow", "isFollowing")
let isFollowing = ref(
magTransProperty(props.user, "you_follow", "isFollowing"),
);
let hasPendingFollowRequestFromYou = $ref(
let hasPendingFollowRequestFromYou = ref(
magTransProperty(
props.user,
"you_request_follow",
"hasPendingFollowRequestFromYou"
)
"hasPendingFollowRequestFromYou",
),
);
let wait = $ref(false);
let wait = ref(false);
const connection = stream.useChannel("main");
if (
@ -137,13 +137,14 @@ function onFollowChange(user: Misskey.entities.User) {
return;
if (user.id === props.user.id) {
isFollowing = user.isFollowing;
hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
isFollowing.value = user.isFollowing;
hasPendingFollowRequestFromYou.value =
user.hasPendingFollowRequestFromYou;
}
}
async function onClick() {
wait = true;
wait.value = true;
try {
if (isBlocking.value) {
@ -162,7 +163,7 @@ async function onClick() {
});
}
emit("refresh");
} else if (isFollowing) {
} else if (isFollowing.value) {
const { canceled } = await os.confirm({
type: "warning",
text: i18n.t("unfollowConfirm", {
@ -176,29 +177,29 @@ async function onClick() {
userId: props.user.id,
});
} else {
if (hasPendingFollowRequestFromYou) {
if (hasPendingFollowRequestFromYou.value) {
await os.api("following/requests/cancel", {
userId: props.user.id,
});
hasPendingFollowRequestFromYou = false;
hasPendingFollowRequestFromYou.value = false;
} else {
await os.api("following/create", {
userId: props.user.id,
});
hasPendingFollowRequestFromYou = true;
hasPendingFollowRequestFromYou.value = true;
}
}
} catch (err) {
console.error(err);
} finally {
wait = false;
wait.value = false;
}
}
function menu(ev) {
os.popupMenu(
getUserMenu(props.user, router),
ev.currentTarget ?? ev.target
ev.currentTarget ?? ev.target,
);
}

View File

@ -62,6 +62,8 @@
</template>
<script lang="ts" setup>
import { ref } from "vue";
import {} from "vue";
import XModalWindow from "@/components/MkModalWindow.vue";
import MkButton from "@/components/MkButton.vue";
@ -75,20 +77,20 @@ const emit = defineEmits<{
(ev: "closed"): void;
}>();
let dialog: InstanceType<typeof XModalWindow> = $ref();
let dialog: InstanceType<typeof XModalWindow> = ref();
let username = $ref("");
let email = $ref("");
let processing = $ref(false);
let username = ref("");
let email = ref("");
let processing = ref(false);
async function onSubmit() {
processing = true;
processing.value = true;
await os.apiWithDialog("request-reset-password", {
username,
email,
username: username.value,
email: email.value,
});
emit("done");
dialog.close();
dialog.value.close();
}
</script>

View File

@ -18,7 +18,7 @@
<div class="xkpnjxcv _formRoot">
<template
v-for="item in Object.keys(form).filter(
(item) => !form[item].hidden
(item) => !form[item].hidden,
)"
>
<FormInput

View File

@ -9,7 +9,7 @@ import * as os from "@/os";
export default defineComponent({
components: {
XFormula: defineAsyncComponent(
() => import("@/components/MkFormulaCore.vue")
() => import("@/components/MkFormulaCore.vue"),
),
},
props: {

View File

@ -15,6 +15,8 @@ import {
onBeforeUnmount,
nextTick,
watch,
shallowRef,
ref,
} from "vue";
import { Chart } from "chart.js";
import tinycolor from "tinycolor2";
@ -33,11 +35,11 @@ const props = defineProps<{
src: string;
}>();
const rootEl = $shallowRef<HTMLDivElement>(null);
const chartEl = $shallowRef<HTMLCanvasElement>(null);
const rootEl = shallowRef<HTMLDivElement>(null);
const chartEl = shallowRef<HTMLCanvasElement>(null);
const now = new Date();
let chartInstance: Chart = null;
let fetching = $ref(true);
let fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip({
position: "middle",
@ -53,8 +55,8 @@ async function renderChart() {
chartInstance.destroy();
}
const wide = rootEl.offsetWidth > 700;
const narrow = rootEl.offsetWidth < 400;
const wide = rootEl.value.offsetWidth > 700;
const narrow = rootEl.value.offsetWidth < 400;
const weeks = wide ? 50 : narrow ? 10 : 25;
const chartLimit = 7 * weeks;
@ -123,7 +125,7 @@ async function renderChart() {
values = addArrays(raw.diffs.normal, raw.diffs.reply, raw.diffs.renote);
}
fetching = false;
fetching.value = false;
await nextTick();
@ -141,7 +143,7 @@ async function renderChart() {
const marginEachCell = 4;
chartInstance = new Chart(chartEl, {
chartInstance = new Chart(chartEl.value, {
type: "matrix",
data: {
datasets: [
@ -257,9 +259,9 @@ async function renderChart() {
watch(
() => props.src,
() => {
fetching = true;
fetching.value = true;
renderChart();
}
},
);
onMounted(async () => {

View File

@ -26,6 +26,8 @@
</template>
<script lang="ts" setup>
import { ref } from "vue";
import {} from "vue";
import type * as misskey from "calckey-js";
import bytes from "@/filters/bytes";
@ -36,14 +38,14 @@ const props = withDefaults(
defineProps<{
image: misskey.entities.DriveFile;
}>(),
{}
{},
);
const emit = defineEmits<{
(ev: "closed"): void;
}>();
const modal = $ref<InstanceType<typeof MkModal>>();
const modal = ref<InstanceType<typeof MkModal>>();
</script>
<style lang="scss" scoped>

View File

@ -20,7 +20,7 @@
</template>
<script lang="ts" setup>
import { onMounted } from "vue";
import { onMounted, ref } from "vue";
import { decode } from "blurhash";
const props = withDefaults(
@ -40,23 +40,23 @@ const props = withDefaults(
title: null,
size: 64,
cover: true,
}
},
);
const canvas = $ref<HTMLCanvasElement>();
let loaded = $ref(false);
const canvas = ref<HTMLCanvasElement>();
let loaded = ref(false);
function draw() {
if (props.hash == null) return;
const pixels = decode(props.hash, props.size, props.size);
const ctx = canvas.getContext("2d");
const ctx = canvas.value.getContext("2d");
const imageData = ctx!.createImageData(props.size, props.size);
imageData.data.set(pixels);
ctx!.putImageData(imageData, 0, 0);
}
function onLoad() {
loaded = true;
loaded.value = true;
}
onMounted(() => {

View File

@ -24,6 +24,8 @@
</template>
<script lang="ts" setup>
import { ref } from "vue";
import * as calckey from "calckey-js";
import MkMiniChart from "@/components/MkMiniChart.vue";
import * as os from "@/os";
@ -33,7 +35,7 @@ const props = defineProps<{
instance: calckey.entities.Instance;
}>();
let chartValues = $ref<number[] | null>(null);
let chartValues = ref<number[] | null>(null);
os.apiGet("charts/instance", {
host: props.instance.host,
@ -42,7 +44,7 @@ os.apiGet("charts/instance", {
}).then((res) => {
//
res.requests.received.splice(0, 1);
chartValues = res.requests.received;
chartValues.value = res.requests.received;
});
function getInstanceIcon(instance): string {

View File

@ -56,6 +56,8 @@
</template>
<script lang="ts" setup>
import { ref } from "vue";
import MkInput from "@/components/form/input.vue";
import XModalWindow from "@/components/MkModalWindow.vue";
import * as os from "@/os";
@ -68,47 +70,47 @@ const emit = defineEmits<{
(ev: "closed"): void;
}>();
let hostname = $ref("");
let instances: Instance[] = $ref([]);
let selected: Instance | null = $ref(null);
let dialogEl = $ref<InstanceType<typeof XModalWindow>>();
let hostname = ref("");
let instances: Instance[] = ref([]);
let selected: Instance | null = ref(null);
let dialogEl = ref<InstanceType<typeof XModalWindow>>();
let searchOrderLatch = 0;
const search = () => {
if (hostname === "") {
instances = [];
if (hostname.value === "") {
instances.value = [];
return;
}
const searchId = ++searchOrderLatch;
os.api("federation/instances", {
host: hostname,
host: hostname.value,
limit: 10,
blocked: false,
suspended: false,
sort: "+pubSub",
}).then((_instances) => {
if (searchId !== searchOrderLatch) return;
instances = _instances.map(
instances.value = _instances.map(
(x) =>
({
id: x.id,
host: x.host,
iconUrl: x.iconUrl,
} as Instance)
}) as Instance,
);
});
};
const ok = () => {
if (selected == null) return;
emit("ok", selected);
dialogEl?.close();
if (selected.value == null) return;
emit("ok", selected.value);
dialogEl.value?.close();
};
const cancel = () => {
emit("cancel");
dialogEl?.close();
dialogEl.value?.close();
};
</script>

View File

@ -102,7 +102,7 @@
</template>
<script lang="ts" setup>
import { onMounted } from "vue";
import { onMounted, ref, shallowRef } from "vue";
import { Chart } from "chart.js";
import MkSelect from "@/components/form/select.vue";
import MkChart from "@/components/MkChart.vue";
@ -116,11 +116,11 @@ import { initChart } from "@/scripts/init-chart";
initChart();
const chartLimit = 500;
let chartSpan = $ref<"hour" | "day">("hour");
let chartSrc = $ref("active-users");
let heatmapSrc = $ref("active-users");
let subDoughnutEl = $shallowRef<HTMLCanvasElement>();
let pubDoughnutEl = $shallowRef<HTMLCanvasElement>();
let chartSpan = ref<"hour" | "day">("hour");
let chartSrc = ref("active-users");
let heatmapSrc = ref("active-users");
let subDoughnutEl = shallowRef<HTMLCanvasElement>();
let pubDoughnutEl = shallowRef<HTMLCanvasElement>();
const { handler: externalTooltipHandler1 } = useChartTooltip({
position: "middle",
@ -138,7 +138,7 @@ function createDoughnut(chartEl, tooltip, data) {
{
backgroundColor: data.map((x) => x.color),
borderColor: getComputedStyle(
document.documentElement
document.documentElement,
).getPropertyValue("--panel"),
borderWidth: 2,
hoverOffset: 0,
@ -161,7 +161,7 @@ function createDoughnut(chartEl, tooltip, data) {
ev,
"nearest",
{ intersect: true },
false
false,
)[0];
if (hit && data[hit.index].onClick) {
data[hit.index].onClick();
@ -189,7 +189,7 @@ function createDoughnut(chartEl, tooltip, data) {
onMounted(() => {
os.apiGet("federation/stats", { limit: 30 }).then((fedStats) => {
createDoughnut(
subDoughnutEl,
subDoughnutEl.value,
externalTooltipHandler1,
fedStats.topSubInstances
.map((x) => ({
@ -206,11 +206,11 @@ onMounted(() => {
color: "#80808080",
value: fedStats.otherFollowersCount,
},
])
]),
);
createDoughnut(
pubDoughnutEl,
pubDoughnutEl.value,
externalTooltipHandler2,
fedStats.topPubInstances
.map((x) => ({
@ -227,7 +227,7 @@ onMounted(() => {
color: "#80808080",
value: fedStats.otherFollowingCount,
},
])
]),
);
});
});

View File

@ -4,7 +4,7 @@
v-tooltip="
capitalize(
magTransProperty(instance, 'software_name', 'softwareName') ??
'?'
'?',
)
"
ref="ticker"
@ -16,6 +16,8 @@
</template>
<script lang="ts" setup>
import { ref } from "vue";
import { instanceName } from "@/config";
import { instance as Instance } from "@/instance";
import { getProxiedImageUrlNullable } from "@/scripts/media-proxy";
@ -27,7 +29,7 @@ const props = defineProps<{
instance?: Misskey.entities.User["instance"] | types.InstanceTicker | null;
}>();
let ticker = $ref<HTMLElement | null>(null);
let ticker = ref<HTMLElement | null>(null);
// if no instance data is given, this is for the local instance
const instance = props.instance ?? {
@ -38,7 +40,7 @@ const instance = props.instance ?? {
name: instanceName,
themeColor: (
document.querySelector(
'meta[name="theme-color-orig"]'
'meta[name="theme-color-orig"]',
) as HTMLMetaElement
)?.content,
softwareName: (Instance.softwareName || "Magnetar") as string | null,
@ -59,18 +61,18 @@ const bg = {
};
function getInstanceIcon(
instance?: Misskey.entities.User["instance"] | types.InstanceTicker | null
instance?: Misskey.entities.User["instance"] | types.InstanceTicker | null,
): string {
if (!instance) return "/client-assets/dummy.png";
return (
getProxiedImageUrlNullable(
magTransProperty(instance, "icon_url", "iconUrl"),
"preview"
"preview",
) ??
getProxiedImageUrlNullable(
magTransProperty(instance, "favicon_url", "faviconUrl"),
"preview"
"preview",
) ??
"/client-assets/dummy.png"
);
@ -106,8 +108,11 @@ function getInstanceIcon(
font-weight: bold;
text-overflow: ellipsis;
white-space: nowrap;
text-shadow: -1px -1px 0 var(--bg), 1px -1px 0 var(--bg),
-1px 1px 0 var(--bg), 1px 1px 0 var(--bg);
text-shadow:
-1px -1px 0 var(--bg),
1px -1px 0 var(--bg),
-1px 1px 0 var(--bg),
1px 1px 0 var(--bg);
.article > .main &,
.header > .body & {
display: unset;

View File

@ -32,7 +32,7 @@ const props = withDefaults(
{
copy: null,
oneline: false,
}
},
);
const copy_ = () => {

View File

@ -46,6 +46,8 @@
</template>
<script lang="ts" setup>
import { ref } from "vue";
import MkModal from "@/components/MkModal.vue";
import { navbarItemDef } from "@/navbar";
import { defaultStore } from "@/store";
@ -59,7 +61,7 @@ const props = withDefaults(
}>(),
{
anchor: () => ({ x: "right", y: "center" }),
}
},
);
const emit = defineEmits<{
@ -70,10 +72,10 @@ const preferedModalType =
deviceKind === "desktop" && props.src != null
? "popup"
: deviceKind === "smartphone"
? "drawer"
: "dialog";
? "drawer"
: "dialog";
const modal = $ref<InstanceType<typeof MkModal>>();
const modal = ref<InstanceType<typeof MkModal>>();
const menu = defaultStore.state.menu;
@ -91,7 +93,7 @@ const items = Object.keys(navbarItemDef)
}));
function close() {
modal.close();
modal.value.close();
}
</script>

View File

@ -18,7 +18,7 @@
</template>
<script lang="ts" setup>
import { defineAsyncComponent } from "vue";
import { defineAsyncComponent, ref } from "vue";
import { url as local } from "@/config";
import { useTooltip } from "@/scripts/use-tooltip";
import * as os from "@/os";
@ -28,27 +28,27 @@ const props = withDefaults(
url: string;
rel?: null | string;
}>(),
{}
{},
);
const self = props.url.startsWith(local);
const attr = self ? "to" : "href";
const target = self ? null : "_blank";
const el = $ref();
const el = ref();
useTooltip($$(el), (showing) => {
useTooltip(el, (showing) => {
os.popup(
defineAsyncComponent(
() => import("@/components/MkUrlPreviewPopup.vue")
() => import("@/components/MkUrlPreviewPopup.vue"),
),
{
showing,
url: props.url,
source: el,
source: el.value,
},
{},
"closed"
"closed",
);
});
</script>

View File

@ -63,9 +63,9 @@ export default {
: undefined,
},
},
$slots.default()
)
)
$slots.default(),
),
),
),
]);
},

View File

@ -17,30 +17,7 @@
"
class="audio"
>
<VuePlyr
:options="{
controls: [
'play-large',
'play',
'progress',
'current-time',
'mute',
'volume',
'download',
],
disableContextMenu: false,
}"
>
<audio
ref="audioEl"
class="audio"
:src="media.url"
:title="media.name"
controls
preload="metadata"
@volumechange="volumechange"
/>
</VuePlyr>
<MagVideoPlayer :video="media"></MagVideoPlayer>
</div>
<a
v-else
@ -58,11 +35,9 @@
</template>
<script lang="ts" setup>
import { onMounted } from "vue";
import VuePlyr from "vue-plyr";
import { onMounted, ref } from "vue";
import type * as misskey from "calckey-js";
import { ColdDeviceStorage } from "@/store";
import "vue-plyr/dist/vue-plyr.css";
import { i18n } from "@/i18n";
import { packed } from "magnetar-common";
import { magTransProperty } from "@/scripts-mag/mag-util";
@ -74,16 +49,8 @@ const props = withDefaults(
{}
);
const audioEl = $ref<HTMLAudioElement | null>();
let hide = $ref(true);
function volumechange() {
if (audioEl) ColdDeviceStorage.set("mediaVolume", audioEl.volume);
}
onMounted(() => {
if (audioEl) audioEl.volume = ColdDeviceStorage.get("mediaVolume");
});
const audioEl = ref<HTMLAudioElement | null>();
let hide = ref(true);
</script>
<style lang="scss" scoped>

View File

@ -191,10 +191,10 @@ export default defineComponent({
caption() {
const img = document.getElementById(
"imgtocaption"
"imgtocaption",
) as HTMLImageElement;
const ta = document.getElementById(
"captioninput"
"captioninput",
) as HTMLTextAreaElement;
os.api("drive/files/caption-image", {
url: img.src,

View File

@ -63,7 +63,7 @@
</template>
<script lang="ts" setup>
import { watch } from "vue";
import { watch, ref } from "vue";
import type * as misskey from "calckey-js";
import * as os from "@/os";
import { getStaticImageUrl } from "@/scripts/get-static-image-url";
@ -78,7 +78,7 @@ const props = defineProps<{
raw?: boolean;
}>();
let hide = $ref(true);
let hide = ref(true);
function noAltTextPopup(event: MouseEvent) {
os.alert({
@ -104,16 +104,16 @@ const url =
props.raw || defaultStore.state.loadRawImages
? props.image.url
: defaultStore.state.disableShowingAnimatedImages
? getStaticImageUrl(
magTransProperty(props.image, "thumbnail_url", "thumbnailUrl")!
)
: magTransProperty(props.image, "thumbnail_url", "thumbnailUrl")!;
? getStaticImageUrl(
magTransProperty(props.image, "thumbnail_url", "thumbnailUrl")!,
)
: magTransProperty(props.image, "thumbnail_url", "thumbnailUrl")!;
// Plugin:register_note_view_interruptor 使watch
watch(
() => props.image,
() => {
hide =
hide.value =
defaultStore.state.nsfw === "force"
? true
: magTransProperty(props.image, "sensitive", "isSensitive") &&
@ -122,7 +122,7 @@ watch(
{
deep: true,
immediate: true,
}
},
);
</script>

View File

@ -14,7 +14,7 @@
<div ref="gallery" @click.stop>
<template
v-for="media in mediaList.filter((media) =>
previewable(media)
previewable(media),
)"
>
<XVideo
@ -22,7 +22,7 @@
magTransProperty(
media,
'mime_type',
'type'
'type',
).startsWith('video')
"
:key="media.id"
@ -33,7 +33,7 @@
magTransProperty(
media,
'mime_type',
'type'
'type',
).startsWith('image')
"
:key="media.id"
@ -87,7 +87,7 @@ onMounted(() => {
const properties = magTransProperty(
media,
"media_metadata",
"properties"
"properties",
);
const item = {
@ -142,7 +142,7 @@ onMounted(() => {
const properties = magTransProperty(
file,
"media_metadata",
"properties"
"properties",
);
itemData.src = file.url;
@ -205,7 +205,7 @@ onMounted(() => {
});
const previewable = (
file: packed.PackDriveFileBase | misskey.entities.DriveFile
file: packed.PackDriveFileBase | misskey.entities.DriveFile,
): boolean => {
const type = magTransProperty(file, "mime_type", "type");
@ -217,7 +217,7 @@ const previewable = (
);
};
const previewableCount = props.mediaList.filter((media) =>
previewable(media)
previewable(media),
).length;
</script>

View File

@ -12,40 +12,8 @@
<span>{{ i18n.ts.clickToShow }}</span>
</div>
</div>
<div v-else class="video" :class="{ mini }">
<VuePlyr
ref="plyr"
:options="{
controls: [
'play-large',
'play',
'progress',
'current-time',
'mute',
'volume',
'pip',
'download',
'fullscreen',
],
disableContextMenu: false,
}"
>
<video
:poster="
magTransProperty(video, 'thumbnail_url', 'thumbnailUrl')
"
:title="video.comment"
:aria-label="video.comment"
preload="none"
controls
@contextmenu.stop
>
<source
:src="video.url"
:type="magTransProperty(video, 'mime_type', 'type')"
/>
</video>
</VuePlyr>
<div v-else class="video">
<MagVideoPlayer :video="video"> </MagVideoPlayer>
<button
v-tooltip="i18n.ts.hide"
class="_button hide"
@ -57,43 +25,29 @@
</template>
<script lang="ts" setup>
import { onMounted, ref } from "vue";
import VuePlyr from "vue-plyr";
import { ref } from "vue";
import type * as misskey from "calckey-js";
import { defaultStore } from "@/store";
import "vue-plyr/dist/vue-plyr.css";
import { i18n } from "@/i18n";
import { packed } from "magnetar-common";
import { magTransProperty } from "@/scripts-mag/mag-util";
import MagVideoPlayer from "./MagVideoPlayer.vue";
const props = defineProps<{
video: packed.PackDriveFileBase | misskey.entities.DriveFile;
}>();
const plyr = ref();
const mini = ref(false);
const hide = ref(
defaultStore.state.nsfw === "force"
? true
: magTransProperty(props.video, "sensitive", "isSensitive") &&
defaultStore.state.nsfw !== "ignore"
);
onMounted(() => {
mini.value = plyr.value.player.media.scrollWidth < 300;
if (mini.value) {
plyr.value.player.on("play", () => {
plyr.value.player.fullscreen.enter();
});
}
});
</script>
<style lang="scss" scoped>
.video {
position: relative;
--plyr-color-main: var(--accent);
> .hide {
display: block;
@ -113,31 +67,6 @@ onMounted(() => {
display: block;
}
}
> video {
display: flex;
justify-content: center;
align-items: center;
font-size: 3.5em;
overflow: hidden;
background-position: center;
background-size: cover;
width: 100%;
height: 100%;
}
&.mini {
:deep(.plyr:not(:fullscreen)) {
min-width: unset !important;
.plyr__control--overlaid,
.plyr__progress__container,
.plyr__volume,
[data-plyr="fullscreen"] {
display: none;
}
}
}
}
.icozogqfvdetwohsdglrbswgrejoxbdj {

View File

@ -213,19 +213,19 @@ const emit = defineEmits<{
(ev: "close", actioned?: boolean): void;
}>();
let itemsEl = $ref<HTMLDivElement>();
let itemsEl = ref<HTMLDivElement>();
let items2: InnerMenuItem[] = $ref([]);
let items2: InnerMenuItem[] = ref([]);
let child = $ref<InstanceType<typeof XChild>>();
let child = ref<InstanceType<typeof XChild>>();
let childShowingItem = $ref<MenuItem | null>();
let childShowingItem = ref<MenuItem | null>();
watch(
() => props.items,
() => {
const items: (MenuItem | MenuPending)[] = [...props.items].filter(
(item) => item !== undefined
(item) => item !== undefined,
);
for (let i = 0; i < items.length; i++) {
@ -235,24 +235,24 @@ watch(
// if item is Promise
items[i] = { type: "pending" };
item.then((actualItem) => {
items2[i] = actualItem;
items2.value[i] = actualItem;
});
}
}
items2 = items as InnerMenuItem[];
items2.value = items as InnerMenuItem[];
},
{
immediate: true,
}
},
);
let childMenu = $ref<MenuItem[] | null>();
let childTarget = $ref<HTMLElement | null>();
let childMenu = ref<MenuItem[] | null>();
let childTarget = ref<HTMLElement | null>();
function closeChild() {
childMenu = null;
childShowingItem = null;
childMenu.value = null;
childShowingItem.value = null;
}
function childActioned() {
@ -262,11 +262,12 @@ function childActioned() {
function onGlobalMousedown(event: MouseEvent) {
if (
childTarget &&
(event.target === childTarget || childTarget.contains(event.target))
childTarget.value &&
(event.target === childTarget.value ||
childTarget.value.contains(event.target))
)
return;
if (child && child.checkHit(event)) return;
if (child.value && child.value.checkHit(event)) return;
closeChild();
}
@ -285,9 +286,9 @@ async function showChildren(item: MenuItem, ev: MouseEvent) {
os.popupMenu(item.children, ev.currentTarget ?? ev.target);
close();
} else {
childTarget = ev.currentTarget ?? ev.target;
childMenu = item.children;
childShowingItem = item;
childTarget.value = ev.currentTarget ?? ev.target;
childMenu.value = item.children;
childShowingItem.value = item;
}
}

View File

@ -25,7 +25,7 @@
</template>
<script lang="ts" setup>
import { onUnmounted, watch } from "vue";
import { onUnmounted, watch, ref } from "vue";
import { v4 as uuid } from "uuid";
import tinycolor from "tinycolor2";
import { useInterval } from "@/scripts/use-interval";
@ -37,13 +37,13 @@ const props = defineProps<{
const viewBoxX = 50;
const viewBoxY = 50;
const gradientId = uuid();
let polylinePoints = $ref("");
let polygonPoints = $ref("");
let headX = $ref<number | null>(null);
let headY = $ref<number | null>(null);
let clock = $ref<number | null>(null);
let polylinePoints = ref("");
let polygonPoints = ref("");
let headX = ref<number | null>(null);
let headY = ref<number | null>(null);
let clock = ref<number | null>(null);
const accent = tinycolor(
getComputedStyle(document.documentElement).getPropertyValue("--accent")
getComputedStyle(document.documentElement).getPropertyValue("--accent"),
);
const color = accent.toRgbString();
@ -56,12 +56,14 @@ function draw(): void {
(1 - n / peak) * viewBoxY,
]);
polylinePoints = _polylinePoints.map((xy) => `${xy[0]},${xy[1]}`).join(" ");
polylinePoints.value = _polylinePoints
.map((xy) => `${xy[0]},${xy[1]}`)
.join(" ");
polygonPoints = `0,${viewBoxY} ${polylinePoints} ${viewBoxX},${viewBoxY}`;
polygonPoints.value = `0,${viewBoxY} ${polylinePoints.value} ${viewBoxX},${viewBoxY}`;
headX = _polylinePoints[_polylinePoints.length - 1][0];
headY = _polylinePoints[_polylinePoints.length - 1][1];
headX.value = _polylinePoints[_polylinePoints.length - 1][0];
headY.value = _polylinePoints[_polylinePoints.length - 1][1];
}
watch(() => props.src, draw, { immediate: true });

View File

@ -77,7 +77,16 @@
</template>
<script lang="ts" setup>
import { nextTick, onMounted, onUnmounted, provide, watch } from "vue";
import {
nextTick,
onMounted,
onUnmounted,
provide,
watch,
ref,
shallowRef,
computed,
} from "vue";
import * as os from "@/os";
import { isTouchUsing } from "@/scripts/touch";
import { defaultStore } from "@/store";
@ -116,7 +125,7 @@ const props = withDefaults(
noOverlap: true,
transparentBg: false,
noReturnFocus: false,
}
},
);
const emit = defineEmits<{
@ -130,14 +139,14 @@ const emit = defineEmits<{
provide("modal", true);
let maxHeight = $ref<number>();
let fixed = $ref(false);
let transformOrigin = $ref("center");
let showing = $ref(true);
let content = $shallowRef<HTMLElement>();
let maxHeight = ref<number>();
let fixed = ref(false);
let transformOrigin = ref("center");
let showing = ref(true);
let content = shallowRef<HTMLElement>();
const zIndex = os.claimZIndex(props.zPriority);
let useSendAnime = $ref(false);
const type = $computed<ModalTypes>(() => {
let useSendAnime = ref(false);
const type = computed<ModalTypes>(() => {
if (props.preferType === "auto") {
if (
!defaultStore.state.disableDrawer &&
@ -152,30 +161,30 @@ const type = $computed<ModalTypes>(() => {
return props.preferType!;
}
});
const isEnableBgTransparent = $computed(
() => props.transparentBg && type === "popup"
const isEnableBgTransparent = computed(
() => props.transparentBg && type.value === "popup",
);
let transitionName = $computed(() =>
let transitionName = computed(() =>
defaultStore.state.animation
? useSendAnime
? useSendAnime.value
? "send"
: type === "drawer"
? "modal-drawer"
: type === "popup"
? "modal-popup"
: "modal"
: ""
: type.value === "drawer"
? "modal-drawer"
: type.value === "popup"
? "modal-popup"
: "modal"
: "",
);
let transitionDuration = $computed(() =>
transitionName === "send"
let transitionDuration = computed(() =>
transitionName.value === "send"
? 400
: transitionName === "modal-popup"
? 100
: transitionName === "modal"
? 200
: transitionName === "modal-drawer"
? 200
: 0
: transitionName.value === "modal-popup"
? 100
: transitionName.value === "modal"
? 200
: transitionName.value === "modal-drawer"
? 200
: 0,
);
let contentClicking = false;
@ -187,12 +196,12 @@ function close(ev, opts: { useSendAnimation?: boolean } = {}) {
// history.forward();
// }
if (opts.useSendAnimation) {
useSendAnime = true;
useSendAnime.value = true;
}
// eslint-disable-next-line vue/no-mutating-props
if (props.src) props.src.style.pointerEvents = "auto";
showing = false;
showing.value = false;
emit("close");
if (!props.noReturnFocus) {
focusedElement.focus();
@ -204,8 +213,8 @@ function onBgClick() {
emit("click");
}
if (type === "drawer") {
maxHeight = window.innerHeight / 1.5;
if (type.value === "drawer") {
maxHeight.value = window.innerHeight / 1.5;
}
const keymap = {
@ -216,21 +225,21 @@ const MARGIN = 16;
const align = () => {
if (props.src == null) return;
if (type === "drawer") return;
if (type === "dialog") return;
if (type.value === "drawer") return;
if (type.value === "dialog") return;
if (content == null) return;
if (content.value == null) return;
const srcRect = props.src.getBoundingClientRect();
const width = content!.offsetWidth;
const height = content!.offsetHeight;
const width = content.value!.offsetWidth;
const height = content.value!.offsetHeight;
let left;
let top;
const x = srcRect.left + (fixed ? 0 : window.pageXOffset);
const y = srcRect.top + (fixed ? 0 : window.pageYOffset);
const x = srcRect.left + (fixed.value ? 0 : window.pageXOffset);
const y = srcRect.top + (fixed.value ? 0 : window.pageYOffset);
if (props.anchor.x === "center") {
left = x + props.src.offsetWidth / 2 - width / 2;
@ -248,7 +257,7 @@ const align = () => {
top = y + props.src.offsetHeight;
}
if (fixed) {
if (fixed.value) {
//
if (left + width > window.innerWidth) {
left = window.innerWidth - width;
@ -261,16 +270,16 @@ const align = () => {
if (top + height > window.innerHeight - MARGIN) {
if (props.noOverlap && props.anchor.x === "center") {
if (underSpace >= upperSpace / 3) {
maxHeight = underSpace;
maxHeight.value = underSpace;
} else {
maxHeight = upperSpace;
maxHeight.value = upperSpace;
top = upperSpace + MARGIN - height;
}
} else {
top = window.innerHeight - MARGIN - height;
}
} else {
maxHeight = underSpace;
maxHeight.value = underSpace;
}
} else {
//
@ -286,9 +295,9 @@ const align = () => {
if (top + height - window.scrollY > window.innerHeight - MARGIN) {
if (props.noOverlap && props.anchor.x === "center") {
if (underSpace >= upperSpace / 3) {
maxHeight = underSpace;
maxHeight.value = underSpace;
} else {
maxHeight = upperSpace;
maxHeight.value = upperSpace;
top = window.scrollY + (upperSpace + MARGIN - height);
}
} else {
@ -300,7 +309,7 @@ const align = () => {
1;
}
} else {
maxHeight = underSpace;
maxHeight.value = underSpace;
}
}
@ -317,36 +326,43 @@ const align = () => {
if (
top >=
srcRect.top + props.src.offsetHeight + (fixed ? 0 : window.pageYOffset)
srcRect.top +
props.src.offsetHeight +
(fixed.value ? 0 : window.pageYOffset)
) {
transformOriginY = "top";
} else if (top + height <= srcRect.top + (fixed ? 0 : window.pageYOffset)) {
} else if (
top + height <=
srcRect.top + (fixed.value ? 0 : window.pageYOffset)
) {
transformOriginY = "bottom";
}
if (
left >=
srcRect.left + props.src.offsetWidth + (fixed ? 0 : window.pageXOffset)
srcRect.left +
props.src.offsetWidth +
(fixed.value ? 0 : window.pageXOffset)
) {
transformOriginX = "left";
} else if (
left + width <=
srcRect.left + (fixed ? 0 : window.pageXOffset)
srcRect.left + (fixed.value ? 0 : window.pageXOffset)
) {
transformOriginX = "right";
}
transformOrigin = `${transformOriginX} ${transformOriginY}`;
transformOrigin.value = `${transformOriginX} ${transformOriginY}`;
content.style.left = left + "px";
content.style.top = top + "px";
content.value.style.left = left + "px";
content.value.style.top = top + "px";
};
const onOpened = () => {
emit("opened");
//
const el = content!.children[0];
const el = content.value!.children[0];
el.addEventListener(
"mousedown",
(ev) => {
@ -359,10 +375,10 @@ const onOpened = () => {
contentClicking = false;
}, 100);
},
{ passive: true, once: true }
{ passive: true, once: true },
);
},
{ passive: true }
{ passive: true },
);
// if (props.preferType == "dialog") {
// history.pushState(null, "", location.href);
@ -378,19 +394,20 @@ onMounted(() => {
// eslint-disable-next-line vue/no-mutating-props
props.src.style.pointerEvents = "none";
}
fixed = type === "drawer" || getFixedContainer(props.src) != null;
fixed.value =
type.value === "drawer" || getFixedContainer(props.src) != null;
await nextTick();
align();
},
{ immediate: true }
{ immediate: true },
);
nextTick(() => {
new ResizeObserver((entries, observer) => {
align();
}).observe(content!);
}).observe(content.value!);
});
});
onUnmounted(() => {
@ -414,7 +431,8 @@ defineExpose({
> .content {
transform: translateY(0px);
transition: opacity 0.3s ease-in,
transition:
opacity 0.3s ease-in,
transform 0.3s cubic-bezier(0.5, -0.5, 1, 0.5) !important;
}
}
@ -439,7 +457,9 @@ defineExpose({
> .content {
transform-origin: var(--transformOrigin);
transition: opacity 0.2s, transform 0.2s !important;
transition:
opacity 0.2s,
transform 0.2s !important;
}
}
.transition_modal_enterFrom,
@ -464,7 +484,8 @@ defineExpose({
> .content {
transform-origin: var(--transformOrigin);
transition: opacity 0.2s cubic-bezier(0, 0, 0.2, 1),
transition:
opacity 0.2s cubic-bezier(0, 0, 0.2, 1),
transform 0.2s cubic-bezier(0, 0, 0.2, 1) !important;
}
}

View File

@ -48,7 +48,7 @@
</template>
<script lang="ts" setup>
import { ComputedRef, provide } from "vue";
import { ComputedRef, provide, ref, computed } from "vue";
import MkModal from "@/components/MkModal.vue";
import { popout as _popout } from "@/scripts/popout";
import copyToClipboard from "@/scripts/copy-to-clipboard";
@ -76,27 +76,27 @@ const router = new Router(routes, props.initialPath);
router.addListener("push", (ctx) => {});
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
let rootEl = $ref();
let modal = $ref<InstanceType<typeof MkModal>>();
let path = $ref(props.initialPath);
let width = $ref(860);
let height = $ref(660);
let pageMetadata = ref<null | ComputedRef<PageMetadata>>();
let rootEl = ref();
let modal = ref<InstanceType<typeof MkModal>>();
let path = ref(props.initialPath);
let width = ref(860);
let height = ref(660);
const history = [];
provide("router", router);
provideMetadataReceiver((info) => {
pageMetadata = info;
pageMetadata.value = info;
});
provide("shouldOmitHeaderTitle", true);
provide("shouldHeaderThin", true);
const pageUrl = $computed(() => url + path);
const contextmenu = $computed(() => {
const pageUrl = computed(() => url + path.value);
const contextmenu = computed(() => {
return [
{
type: "label",
text: path,
text: path.value,
},
{
icon: "ph-arrows-out-simple ph-bold ph-lg",
@ -113,15 +113,15 @@ const contextmenu = $computed(() => {
icon: "ph-arrow-square-out ph-bold ph-lg",
text: i18n.ts.openInNewTab,
action: () => {
window.open(pageUrl, "_blank");
modal.close();
window.open(pageUrl.value, "_blank");
modal.value.close();
},
},
{
icon: "ph-link-simple ph-bold ph-lg",
text: i18n.ts.copyLink,
action: () => {
copyToClipboard(pageUrl);
copyToClipboard(pageUrl.value);
},
},
];
@ -137,17 +137,17 @@ function back() {
}
function expand() {
mainRouter.push(path);
modal.close();
mainRouter.push(path.value);
modal.value.close();
}
function popout() {
_popout(path, rootEl);
modal.close();
_popout(path.value, rootEl.value);
modal.value.close();
}
function onContextmenu(ev: MouseEvent) {
os.contextMenu(contextmenu, ev);
os.contextMenu(contextmenu.value, ev);
}
</script>

View File

@ -17,8 +17,8 @@
? `${props.height}px`
: null
: height
? `min(${props.height}px, 100%)`
: '100%',
? `min(${props.height}px, 100%)`
: '100%',
}"
tabindex="-1"
>
@ -62,6 +62,8 @@
</template>
<script lang="ts" setup>
import { shallowRef } from "vue";
import { FocusTrap } from "focus-trap-vue";
import MkModal from "./MkModal.vue";
import { i18n } from "@/i18n";
@ -80,7 +82,7 @@ const props = withDefaults(
width: 400,
height: null,
scroll: true,
}
},
);
const emit = defineEmits<{
@ -90,12 +92,12 @@ const emit = defineEmits<{
(event: "ok"): void;
}>();
let modal = $shallowRef<InstanceType<typeof MkModal>>();
let rootEl = $shallowRef<HTMLElement>();
let headerEl = $shallowRef<HTMLElement>();
let modal = shallowRef<InstanceType<typeof MkModal>>();
let rootEl = shallowRef<HTMLElement>();
let headerEl = shallowRef<HTMLElement>();
const close = (ev) => {
modal?.close(ev);
modal.value?.close(ev);
};
const onBgClick = () => {

View File

@ -22,6 +22,6 @@ watch(
},
{
immediate: true,
}
},
);
</script>

View File

@ -30,7 +30,7 @@
</template>
<script lang="ts" setup>
import { ComputedRef, inject, provide } from "vue";
import { ComputedRef, inject, provide, ref, computed } from "vue";
import RouterView from "@/components/global/RouterView.vue";
import XWindow from "@/components/MkWindow.vue";
import { popout as _popout } from "@/scripts/popout";
@ -56,18 +56,18 @@ defineEmits<{
const router = new Router(routes, props.initialPath);
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
let windowEl = $ref<InstanceType<typeof XWindow>>();
const history = $ref<{ path: string; key: any }[]>([
let pageMetadata = ref<null | ComputedRef<PageMetadata>>();
let windowEl = ref<InstanceType<typeof XWindow>>();
const history = ref<{ path: string; key: any }[]>([
{
path: router.getCurrentPath(),
key: router.getCurrentKey(),
},
]);
const buttonsLeft = $computed(() => {
const buttonsLeft = computed(() => {
const buttons = [];
if (history.length > 1) {
if (history.value.length > 1) {
buttons.push({
icon: "ph-caret-left ph-bold ph-lg",
onClick: back,
@ -76,7 +76,7 @@ const buttonsLeft = $computed(() => {
return buttons;
});
const buttonsRight = $computed(() => {
const buttonsRight = computed(() => {
const buttons = [
{
icon: "ph-arrows-out-simple ph-bold ph-lg",
@ -89,18 +89,18 @@ const buttonsRight = $computed(() => {
});
router.addListener("push", (ctx) => {
history.push({ path: ctx.path, key: ctx.key });
history.value.push({ path: ctx.path, key: ctx.key });
});
provide("router", router);
provideMetadataReceiver((info) => {
pageMetadata = info;
pageMetadata.value = info;
});
provide("shouldOmitHeaderTitle", true);
provide("shouldBackButton", false);
provide("shouldHeaderThin", true);
const contextmenu = $computed(() => [
const contextmenu = computed(() => [
{
icon: "ph-arrows-out-simple ph-bold ph-lg",
text: i18n.ts.showInPage,
@ -116,7 +116,7 @@ const contextmenu = $computed(() => [
text: i18n.ts.openInNewTab,
action: () => {
window.open(url + router.getCurrentPath(), "_blank");
windowEl.close();
windowEl.value.close();
},
},
{
@ -129,29 +129,29 @@ const contextmenu = $computed(() => [
]);
function menu(ev) {
os.popupMenu(contextmenu, ev.currentTarget ?? ev.target);
os.popupMenu(contextmenu.value, ev.currentTarget ?? ev.target);
}
function back() {
history.pop();
history.value.pop();
router.replace(
history[history.length - 1].path,
history[history.length - 1].key
history.value[history.value.length - 1].path,
history.value[history.value.length - 1].key,
);
}
function close() {
windowEl.close();
windowEl.value.close();
}
function expand() {
mainRouter.push(router.getCurrentPath(), "forcePage");
windowEl.close();
windowEl.value.close();
}
function popout() {
_popout(router.getCurrentPath(), windowEl.$el);
windowEl.close();
_popout(router.getCurrentPath(), windowEl.value.$el);
windowEl.value.close();
}
defineExpose({

View File

@ -85,7 +85,7 @@ import { magTransProperty } from "@/scripts-mag/mag-util";
import { i18n } from "@/i18n";
export type Paging<
E extends keyof misskey.Endpoints = keyof misskey.Endpoints
E extends keyof misskey.Endpoints = keyof misskey.Endpoints,
> = {
endpoint: E;
limit: number;
@ -119,7 +119,7 @@ const props = withDefaults(
}>(),
{
displayLimit: 30,
}
},
);
const emit = defineEmits<{
@ -166,7 +166,7 @@ const init = async (): Promise<void> => {
(err) => {
error.value = true;
fetching.value = false;
}
},
);
};
@ -206,7 +206,7 @@ const refresh = async (): Promise<void> => {
(err) => {
error.value = true;
fetching.value = false;
}
},
);
};
@ -241,8 +241,8 @@ const fetchMore = async (): Promise<void> => {
magTransProperty(
lastItem,
"createdAt",
"created_at"
)
"created_at",
),
).getTime()
: undefined,
untilId: lastItem?.id ?? undefined,
@ -259,7 +259,7 @@ const fetchMore = async (): Promise<void> => {
},
(err) => {
moreFetching.value = false;
}
},
);
};
@ -311,7 +311,7 @@ const prepend = (item: Item): void => {
queue.value = [];
items.value = [...queueRemoved, ...items.value].slice(
0,
props.displayLimit
props.displayLimit,
);
});
}
@ -355,7 +355,7 @@ watch(
if (a.length === 0 && b.length === 0) return;
emit("queue", queue.value.length);
},
{ deep: true }
{ deep: true },
);
init();

View File

@ -96,7 +96,7 @@ const emit = defineEmits<{
expiredAfter: number;
choices: string[];
multiple: boolean;
}
},
): void;
}>();
@ -104,7 +104,7 @@ const choices = ref(props.modelValue.choices);
const multiple = ref(props.modelValue.multiple);
const expiration = ref("infinite");
const atDate = ref(
formatDateTimeString(addTime(new Date(), 1, "day"), "yyyy-MM-dd")
formatDateTimeString(addTime(new Date(), 1, "day"), "yyyy-MM-dd"),
);
const atTime = ref("00:00");
const after = ref(0);
@ -166,8 +166,8 @@ function get() {
...(expiration.value === "at"
? { expiresAt: calcAt() }
: expiration.value === "after"
? { expiredAfter: calcAfter() }
: {}),
? { expiredAfter: calcAfter() }
: {}),
};
}
@ -176,7 +176,7 @@ watch(
() => emit("update:modelValue", get()),
{
deep: true,
}
},
);
</script>

View File

@ -24,6 +24,8 @@
</template>
<script lang="ts" setup>
import { ref } from "vue";
import {} from "vue";
import MkModal from "./MkModal.vue";
import MkMenu from "./MkMenu.vue";
@ -42,7 +44,7 @@ const emit = defineEmits<{
(ev: "closed"): void;
}>();
let modal = $ref<InstanceType<typeof MkModal>>();
let modal = ref<InstanceType<typeof MkModal>>();
</script>
<style lang="scss" scoped>

View File

@ -89,8 +89,8 @@
reply
? 'ph-arrow-u-up-left ph-bold ph-lg'
: renote
? 'ph-quotes ph-bold ph-lg'
: 'ph-paper-plane-tilt ph-bold ph-lg'
? 'ph-quotes ph-bold ph-lg'
: 'ph-paper-plane-tilt ph-bold ph-lg'
"
></i>
</button>
@ -235,7 +235,15 @@
</template>
<script lang="ts" setup>
import { defineAsyncComponent, inject, nextTick, onMounted, watch } from "vue";
import {
defineAsyncComponent,
inject,
nextTick,
onMounted,
watch,
ref,
computed,
} from "vue";
import * as mfm from "mfm-js";
import * as misskey from "calckey-js";
import insertTextAtCursor from "insert-text-at-cursor";
@ -295,7 +303,7 @@ const props = withDefaults(
initialVisibleUsers: () => [],
autofocus: true,
showMfmCheatSheet: true,
}
},
);
const emit = defineEmits<{
@ -304,47 +312,47 @@ const emit = defineEmits<{
(ev: "esc"): void;
}>();
const textareaEl = $ref<HTMLTextAreaElement | null>(null);
const cwInputEl = $ref<HTMLInputElement | null>(null);
const hashtagsInputEl = $ref<HTMLInputElement | null>(null);
const visibilityButton = $ref<HTMLElement | null>(null);
const textareaEl = ref<HTMLTextAreaElement | null>(null);
const cwInputEl = ref<HTMLInputElement | null>(null);
const hashtagsInputEl = ref<HTMLInputElement | null>(null);
const visibilityButton = ref<HTMLElement | null>(null);
let posting = $ref(false);
let text = $ref(props.initialText ?? "");
let files = $ref(props.initialFiles ?? []);
let poll = $ref<{
let posting = ref(false);
let text = ref(props.initialText ?? "");
let files = ref(props.initialFiles ?? []);
let poll = ref<{
choices: string[];
multiple: boolean;
expiresAt: string | null;
expiredAfter: string | null;
} | null>(null);
let useCw = $ref(false);
let showPreview = $ref(false);
let cw = $ref<string | null>(null);
let localOnly = $ref<boolean>(
let useCw = ref(false);
let showPreview = ref(false);
let cw = ref<string | null>(null);
let localOnly = ref<boolean>(
props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility
? defaultStore.state.localOnly
: defaultStore.state.defaultNoteLocalOnly
: defaultStore.state.defaultNoteLocalOnly,
);
let visibility = $ref(
let visibility = ref(
props.initialVisibility ??
((defaultStore.state.rememberNoteVisibility
? defaultStore.state.visibility
: defaultStore.state
.defaultNoteVisibility) as (typeof misskey.noteVisibilities)[number])
.defaultNoteVisibility) as (typeof misskey.noteVisibilities)[number]),
);
let visibleUsers = $ref([]);
let visibleUsers = ref([]);
if (props.initialVisibleUsers) {
props.initialVisibleUsers.forEach(pushVisibleUser);
}
let autocomplete = $ref(null);
let draghover = $ref(false);
let quoteId = $ref<string | null>(null);
let hasNotSpecifiedMentions = $ref(false);
let recentHashtags = $ref(JSON.parse(localStorage.getItem("hashtags") || "[]"));
let imeText = $ref("");
let autocomplete = ref(null);
let draghover = ref(false);
let quoteId = ref<string | null>(null);
let hasNotSpecifiedMentions = ref(false);
let recentHashtags = ref(JSON.parse(localStorage.getItem("hashtags") || "[]"));
let imeText = ref("");
const draftKey = $computed((): string => {
const draftKey = computed((): string => {
if (props.editId) {
return `edit:${props.editId}`;
}
@ -362,7 +370,7 @@ const draftKey = $computed((): string => {
return key;
});
const placeholder = $computed((): string => {
const placeholder = computed((): string => {
if (props.renote) {
return i18n.ts._postForm.quotePlaceholder;
} else if (props.reply) {
@ -380,57 +388,60 @@ const placeholder = $computed((): string => {
}
});
const submitText = $computed((): string => {
const submitText = computed((): string => {
return props.editId
? i18n.ts.edit
: props.renote
? i18n.ts.quote
: props.reply
? i18n.ts.reply
: i18n.ts.note;
? i18n.ts.quote
: props.reply
? i18n.ts.reply
: i18n.ts.note;
});
const textLength = $computed((): number => {
return length((preprocess(text) + imeText).trim());
const textLength = computed((): number => {
return length((preprocess(text.value) + imeText.value).trim());
});
const maxTextLength = $computed((): number => {
const maxTextLength = computed((): number => {
return instance ? instance.maxNoteTextLength : 1000;
});
const canPost = $computed((): boolean => {
const canPost = computed((): boolean => {
return (
!posting &&
(1 <= textLength || 1 <= files.length || !!poll || !!props.renote) &&
textLength <= maxTextLength &&
(!poll || poll.choices.length >= 2)
!posting.value &&
(1 <= textLength.value ||
1 <= files.value.length ||
!!poll.value ||
!!props.renote) &&
textLength.value <= maxTextLength.value &&
(!poll.value || poll.value.choices.length >= 2)
);
});
const withHashtags = $computed(
defaultStore.makeGetterSetter("postFormWithHashtags")
const withHashtags = computed(
defaultStore.makeGetterSetter("postFormWithHashtags"),
);
const hashtags = $computed(defaultStore.makeGetterSetter("postFormHashtags"));
const hashtags = computed(defaultStore.makeGetterSetter("postFormHashtags"));
watch($$(text), () => {
watch(text, () => {
checkMissingMention();
});
watch(
$$(visibleUsers),
visibleUsers,
() => {
checkMissingMention();
},
{
deep: true,
}
},
);
if (props.mention) {
text = props.mention.host
text.value = props.mention.host
? `@${props.mention.username}@${toASCII(props.mention.host)}`
: `@${props.mention.username}`;
text += " ";
text.value += " ";
}
if (
@ -438,7 +449,7 @@ if (
(props.reply.user.username !== $i.username ||
(props.reply.user.host != null && props.reply.user.host !== host))
) {
text = `@${props.reply.user.username}${
text.value = `@${props.reply.user.username}${
props.reply.user.host != null
? "@" + toASCII(props.reply.user.host)
: ""
@ -453,17 +464,17 @@ if (props.reply && props.reply.text != null) {
const mention = x.host
? `@${x.username}@${toASCII(x.host)}`
: otherHost == null || otherHost === host
? `@${x.username}`
: `@${x.username}@${toASCII(otherHost)}`;
? `@${x.username}`
: `@${x.username}@${toASCII(otherHost)}`;
//
if ($i.username === x.username && (x.host == null || x.host === host))
continue;
//
if (text.includes(`${mention} `)) continue;
if (text.value.includes(`${mention} `)) continue;
text += `${mention} `;
text.value += `${mention} `;
}
}
@ -471,30 +482,30 @@ if (props.reply && props.reply.text != null) {
if (
props.reply &&
["home", "followers", "specified"].includes(
magLegacyVisibility(props.reply.visibility)
magLegacyVisibility(props.reply.visibility),
)
) {
if (
magLegacyVisibility(props.reply.visibility) === "home" &&
visibility === "followers"
visibility.value === "followers"
) {
visibility = "followers";
visibility.value = "followers";
} else if (
["home", "followers"].includes(
magLegacyVisibility(props.reply.visibility)
magLegacyVisibility(props.reply.visibility),
) &&
visibility === "specified"
visibility.value === "specified"
) {
visibility = "specified";
visibility.value = "specified";
} else {
visibility = magLegacyVisibility(props.reply.visibility);
visibility.value = magLegacyVisibility(props.reply.visibility);
}
if (visibility === "specified") {
if (visibility.value === "specified") {
const ids = props.reply.visible_user_ids;
if (ids) {
os.api("users/show", {
userIds: ids.filter(
(uid) => uid !== $i.id && uid !== props.reply!.user.id
(uid) => uid !== $i.id && uid !== props.reply!.user.id,
),
}).then((users) => {
users.forEach(pushVisibleUser);
@ -505,74 +516,74 @@ if (
os.api("users/show", { userId: props.reply.user.id }).then(
(user) => {
pushVisibleUser(user);
}
},
);
}
}
}
if (props.specified) {
visibility = "specified";
visibility.value = "specified";
pushVisibleUser(props.specified);
}
// keep cw when reply
if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
useCw = true;
cw = props.reply.cw;
useCw.value = true;
cw.value = props.reply.cw;
}
function watchForDraft() {
watch($$(text), () => saveDraft());
watch($$(useCw), () => saveDraft());
watch($$(cw), () => saveDraft());
watch($$(poll), () => saveDraft());
watch($$(files), () => saveDraft(), { deep: true });
watch($$(visibility), () => saveDraft());
watch($$(localOnly), () => saveDraft());
watch(text, () => saveDraft());
watch(useCw, () => saveDraft());
watch(cw, () => saveDraft());
watch(poll, () => saveDraft());
watch(files, () => saveDraft(), { deep: true });
watch(visibility, () => saveDraft());
watch(localOnly, () => saveDraft());
}
function checkMissingMention() {
if (visibility === "specified") {
const ast = mfm.parse(text);
if (visibility.value === "specified") {
const ast = mfm.parse(text.value);
for (const x of extractMentions(ast)) {
if (
!visibleUsers.some(
(u) => u.username === x.username && u.host === x.host
!visibleUsers.value.some(
(u) => u.username === x.username && u.host === x.host,
)
) {
hasNotSpecifiedMentions = true;
hasNotSpecifiedMentions.value = true;
return;
}
}
hasNotSpecifiedMentions = false;
hasNotSpecifiedMentions.value = false;
}
}
function addMissingMention() {
const ast = mfm.parse(text);
const ast = mfm.parse(text.value);
for (const x of extractMentions(ast)) {
if (
!visibleUsers.some(
(u) => u.username === x.username && u.host === x.host
!visibleUsers.value.some(
(u) => u.username === x.username && u.host === x.host,
)
) {
os.api("users/show", { username: x.username, host: x.host }).then(
(user) => {
visibleUsers.push(user);
}
visibleUsers.value.push(user);
},
);
}
}
}
function togglePoll() {
if (poll) {
poll = null;
if (poll.value) {
poll.value = null;
} else {
poll = {
poll.value = {
choices: ["", ""],
multiple: false,
expiresAt: null,
@ -582,15 +593,15 @@ function togglePoll() {
}
function addTag(tag: string) {
insertTextAtCursor(textareaEl, ` #${tag} `);
insertTextAtCursor(textareaEl.value, ` #${tag} `);
}
function focus() {
if (textareaEl) {
textareaEl.focus();
textareaEl.setSelectionRange(
textareaEl.value.length,
textareaEl.value.length
if (textareaEl.value) {
textareaEl.value.focus();
textareaEl.value.setSelectionRange(
textareaEl.value.value.length,
textareaEl.value.value.length,
);
}
}
@ -599,72 +610,72 @@ function chooseFileFrom(ev) {
selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(
(files_) => {
for (const file of files_) {
files.push(file);
files.value.push(file);
}
}
},
);
}
function detachFile(id) {
files = files.filter((x) => x.id !== id);
files.value = files.value.filter((x) => x.id !== id);
}
function updateFiles(_files) {
files = _files;
files.value = _files;
}
function updateFileSensitive(file, sensitive) {
const idx = files.findIndex((x) => x.id === file.id);
const f = files[idx];
const idx = files.value.findIndex((x) => x.id === file.id);
const f = files.value[idx];
if ("isSensitive" in f) f.isSensitive = sensitive;
else f.sensitive = sensitive;
}
function updateFileName(file, name) {
files[files.findIndex((x) => x.id === file.id)].name = name;
files.value[files.value.findIndex((x) => x.id === file.id)].name = name;
}
function upload(file: File, name?: string) {
uploadFile(file, defaultStore.state.uploadFolder, name).then((res) => {
files.push(res);
files.value.push(res);
});
}
function setVisibility() {
os.popup(
defineAsyncComponent(
() => import("@/components/MkVisibilityPicker.vue")
() => import("@/components/MkVisibilityPicker.vue"),
),
{
currentVisibility: visibility,
currentLocalOnly: localOnly,
src: visibilityButton,
currentVisibility: visibility.value,
currentLocalOnly: localOnly.value,
src: visibilityButton.value,
},
{
changeVisibility: (v) => {
visibility = v;
visibility.value = v;
if (defaultStore.state.rememberNoteVisibility) {
defaultStore.set("visibility", visibility);
defaultStore.set("visibility", visibility.value);
}
},
changeLocalOnly: (v) => {
localOnly = v;
localOnly.value = v;
if (defaultStore.state.rememberNoteVisibility) {
defaultStore.set("localOnly", localOnly);
defaultStore.set("localOnly", localOnly.value);
}
},
},
"closed"
"closed",
);
}
function pushVisibleUser(user) {
if (
!visibleUsers.some(
(u) => u.username === user.username && u.host === user.host
!visibleUsers.value.some(
(u) => u.username === user.username && u.host === user.host,
)
) {
visibleUsers.push(user);
visibleUsers.value.push(user);
}
}
@ -675,37 +686,37 @@ function addVisibleUser() {
}
function removeVisibleUser(user) {
visibleUsers = erase(user, visibleUsers);
visibleUsers.value = erase(user, visibleUsers.value);
}
function clear() {
text = "";
files = [];
poll = null;
quoteId = null;
text.value = "";
files.value = [];
poll.value = null;
quoteId.value = null;
}
function onKeydown(ev: KeyboardEvent) {
if (
(ev.which === 10 || ev.which === 13) &&
(ev.ctrlKey || ev.metaKey) &&
canPost
canPost.value
)
post();
if (ev.which === 27) emit("esc");
}
function onCompositionUpdate(ev: CompositionEvent) {
imeText = ev.data;
imeText.value = ev.data;
}
function onCompositionEnd(ev: CompositionEvent) {
imeText = "";
imeText.value = "";
}
async function onPaste(ev: ClipboardEvent) {
for (const { item, i } of Array.from(ev.clipboardData.items).map(
(item, i) => ({ item, i })
(item, i) => ({ item, i }),
)) {
if (item.kind === "file") {
const file = item.getAsFile();
@ -713,7 +724,7 @@ async function onPaste(ev: ClipboardEvent) {
const ext = lio >= 0 ? file.name.slice(lio) : "";
const formatted = `${formatTimeString(
new Date(file.lastModified),
defaultStore.state.pastedFileName
defaultStore.state.pastedFileName,
).replace(/{{number}}/g, `${i + 1}`)}${ext}`;
upload(file, formatted);
}
@ -721,7 +732,7 @@ async function onPaste(ev: ClipboardEvent) {
const paste = ev.clipboardData.getData("text");
if (!props.renote && !quoteId && paste.startsWith(url + "/notes/")) {
if (!props.renote && !quoteId.value && paste.startsWith(url + "/notes/")) {
ev.preventDefault();
os.yesno({
@ -729,11 +740,11 @@ async function onPaste(ev: ClipboardEvent) {
text: i18n.ts.quoteQuestion,
}).then(({ canceled }) => {
if (canceled) {
insertTextAtCursor(textareaEl, paste);
insertTextAtCursor(textareaEl.value, paste);
return;
}
quoteId = paste
quoteId.value = paste
.substring(url.length)
.match(/^\/notes\/(.+?)\/?$/)[1];
});
@ -746,7 +757,7 @@ function onDragover(ev) {
const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
if (isFile || isDriveFile) {
ev.preventDefault();
draghover = true;
draghover.value = true;
switch (ev.dataTransfer.effectAllowed) {
case "all":
case "uninitialized":
@ -767,15 +778,15 @@ function onDragover(ev) {
}
function onDragenter(ev) {
draghover = true;
draghover.value = true;
}
function onDragleave(ev) {
draghover = false;
draghover.value = false;
}
function onDrop(ev): void {
draghover = false;
draghover.value = false;
//
if (ev.dataTransfer.files.length > 0) {
@ -788,7 +799,7 @@ function onDrop(ev): void {
const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
if (driveFile != null && driveFile !== "") {
const file = JSON.parse(driveFile);
files.push(file);
files.value.push(file);
ev.preventDefault();
}
//#endregion
@ -797,16 +808,16 @@ function onDrop(ev): void {
function saveDraft() {
const draftData = JSON.parse(localStorage.getItem("drafts") || "{}");
draftData[draftKey] = {
draftData[draftKey.value] = {
updatedAt: new Date(),
data: {
text: text,
useCw: useCw,
cw: cw,
visibility: visibility,
localOnly: localOnly,
files: files,
poll: poll,
text: text.value,
useCw: useCw.value,
cw: cw.value,
visibility: visibility.value,
localOnly: localOnly.value,
files: files.value,
poll: poll.value,
},
};
@ -816,36 +827,37 @@ function saveDraft() {
function deleteDraft() {
const draftData = JSON.parse(localStorage.getItem("drafts") || "{}");
delete draftData[draftKey];
delete draftData[draftKey.value];
localStorage.setItem("drafts", JSON.stringify(draftData));
}
async function post() {
const processedText = preprocess(text);
const processedText = preprocess(text.value);
let postData = {
editId: props.editId ? props.editId : undefined,
text: processedText === "" ? undefined : processedText,
fileIds: files.length > 0 ? files.map((f) => f.id) : undefined,
fileIds:
files.value.length > 0 ? files.value.map((f) => f.id) : undefined,
replyId: props.reply ? props.reply.id : undefined,
renoteId: props.renote
? props.renote.id
: quoteId
? quoteId
: undefined,
poll: poll,
cw: useCw ? cw || "" : undefined,
localOnly: localOnly,
visibility: visibility,
: quoteId.value
? quoteId.value
: undefined,
poll: poll.value,
cw: useCw.value ? cw.value || "" : undefined,
localOnly: localOnly.value,
visibility: visibility.value,
visibleUserIds:
visibility === "specified"
? visibleUsers.map((u) => u.id)
visibility.value === "specified"
? visibleUsers.value.map((u) => u.id)
: undefined,
};
if (withHashtags && hashtags && hashtags.trim() !== "") {
const hashtags_ = hashtags
if (withHashtags.value && hashtags.value && hashtags.value.trim() !== "") {
const hashtags_ = hashtags.value
.trim()
.split(" ")
.map((x) => (x.startsWith("#") ? x : "#" + x))
@ -857,12 +869,14 @@ async function post() {
let token = undefined;
if (postAccount) {
if (postAccount.value) {
const storedAccounts = await getAccounts();
token = storedAccounts.find((x) => x.id === postAccount.id)?.token;
token = storedAccounts.find(
(x) => x.id === postAccount.value.id,
)?.token;
}
posting = true;
posting.value = true;
os.api(postData.editId ? "notes/edit" : "notes/create", postData, token)
.then(() => {
clear();
@ -875,19 +889,19 @@ async function post() {
.filter((x) => x.type === "hashtag")
.map((x) => x.props.hashtag);
const history = JSON.parse(
localStorage.getItem("hashtags") || "[]"
localStorage.getItem("hashtags") || "[]",
) as string[];
localStorage.setItem(
"hashtags",
JSON.stringify(unique(hashtags_.concat(history)))
JSON.stringify(unique(hashtags_.concat(history))),
);
}
posting = false;
postAccount = null;
posting.value = false;
postAccount.value = null;
});
})
.catch((err) => {
posting = false;
posting.value = false;
os.alert({
type: "error",
text: err.message + "\n" + (err as any).id,
@ -901,35 +915,35 @@ function cancel() {
function insertMention() {
os.selectUser().then((user) => {
insertTextAtCursor(textareaEl, "@" + Acct.toString(user) + " ");
insertTextAtCursor(textareaEl.value, "@" + Acct.toString(user) + " ");
});
}
async function insertEmoji(ev: MouseEvent) {
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl);
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl.value);
}
async function openCheatSheet(ev: MouseEvent) {
os.popup(XCheatSheet, {}, {}, "closed");
}
let postAccount = $ref<misskey.entities.UserDetailed | null>(null);
let postAccount = ref<misskey.entities.UserDetailed | null>(null);
function openAccountMenu(ev: MouseEvent) {
openAccountMenu_(
{
withExtraOperation: false,
includeCurrentAccount: true,
active: postAccount != null ? postAccount.id : $i.id,
active: postAccount.value != null ? postAccount.value.id : $i.id,
onChoose: (account) => {
if (account.id === $i.id) {
postAccount = null;
postAccount.value = null;
} else {
postAccount = account;
postAccount.value = account;
}
},
},
ev
ev,
);
}
@ -943,27 +957,27 @@ onMounted(() => {
}
// TODO: detach when unmount
new Autocomplete(textareaEl, $$(text));
new Autocomplete(cwInputEl, $$(cw));
new Autocomplete(hashtagsInputEl, $$(hashtags));
new Autocomplete(textareaEl.value, text);
new Autocomplete(cwInputEl.value, cw);
new Autocomplete(hashtagsInputEl.value, hashtags);
nextTick(() => {
// 稿
if (!props.instant && !props.mention && !props.specified) {
const draft = JSON.parse(localStorage.getItem("drafts") || "{}")[
draftKey
draftKey.value
];
if (draft) {
text = draft.data.text;
useCw = draft.data.useCw;
cw = draft.data.cw;
visibility = draft.data.visibility;
localOnly = draft.data.localOnly;
files = (draft.data.files || []).filter(
(draftFile) => draftFile
text.value = draft.data.text;
useCw.value = draft.data.useCw;
cw.value = draft.data.cw;
visibility.value = draft.data.visibility;
localOnly.value = draft.data.localOnly;
files.value = (draft.data.files || []).filter(
(draftFile) => draftFile,
);
if (draft.data.poll) {
poll = draft.data.poll;
poll.value = draft.data.poll;
}
}
}
@ -971,13 +985,13 @@ onMounted(() => {
//
if (props.initialNote) {
const init = props.initialNote;
text = init.text ? init.text : "";
text.value = init.text ? init.text : "";
files = init.attachments ?? [];
cw = init.cw;
useCw = init.cw != null;
files.value = init.attachments ?? [];
cw.value = init.cw;
useCw.value = init.cw != null;
if (init.poll) {
poll = {
poll.value = {
choices: init.poll.options.map((x) => x.title),
multiple: init.poll.multiple_choice,
expiresAt: init.poll.expires_at,
@ -985,9 +999,9 @@ onMounted(() => {
expiredAfter: null,
};
}
visibility = magLegacyVisibility(init.visibility);
localOnly = init.local_only ?? false;
quoteId = init.renoted_note ? init.renoted_note.id : null;
visibility.value = magLegacyVisibility(init.visibility);
localOnly.value = init.local_only ?? false;
quoteId.value = init.renoted_note ? init.renoted_note.id : null;
}
nextTick(() => watchForDraft());

View File

@ -42,7 +42,7 @@ import { i18n } from "@/i18n";
export default defineComponent({
components: {
XDraggable: defineAsyncComponent(() =>
import("vuedraggable").then((x) => x.default)
import("vuedraggable").then((x) => x.default),
),
MkDriveFileThumbnail,
},
@ -94,7 +94,7 @@ export default defineComponent({
this.$emit(
"changeSensitive",
file,
!(file.sensitive ?? file.isSensitive)
!(file.sensitive ?? file.isSensitive),
);
});
},
@ -117,7 +117,7 @@ export default defineComponent({
async describe(file) {
os.popup(
defineAsyncComponent(
() => import("@/components/MkMediaCaption.vue")
() => import("@/components/MkMediaCaption.vue"),
),
{
title: i18n.ts.describeFile,
@ -140,7 +140,7 @@ export default defineComponent({
});
},
},
"closed"
"closed",
);
},
@ -183,7 +183,7 @@ export default defineComponent({
},
},
],
ev.currentTarget ?? ev.target
ev.currentTarget ?? ev.target,
)
.then(() => (this.menu = null));
},

View File

@ -19,6 +19,8 @@
</template>
<script lang="ts" setup>
import { shallowRef } from "vue";
import * as misskey from "calckey-js";
import MkModal from "@/components/MkModal.vue";
import MkPostForm from "@/components/MkPostForm.vue";
@ -45,11 +47,11 @@ const emit = defineEmits<{
(ev: "closed"): void;
}>();
let modal = $shallowRef<InstanceType<typeof MkModal>>();
let form = $shallowRef<InstanceType<typeof MkPostForm>>();
let modal = shallowRef<InstanceType<typeof MkModal>>();
let form = shallowRef<InstanceType<typeof MkPostForm>>();
function onPosted() {
modal.close({
modal.value.close({
useSendAnimation: true,
});
}

View File

@ -53,6 +53,8 @@
</template>
<script setup lang="ts">
import { ref } from "vue";
import { $i, getAccounts } from "@/account";
import MkButton from "@/components/MkButton.vue";
import { instance } from "@/instance";
@ -74,12 +76,12 @@ defineProps<{
}>();
// ServiceWorker registration
let registration = $ref<ServiceWorkerRegistration | undefined>();
let registration = ref<ServiceWorkerRegistration | undefined>();
// If this browser supports push notification
let supported = $ref(false);
let supported = ref(false);
// If this browser has already subscribed to push notification
let pushSubscription = $ref<PushSubscription | null>(null);
let pushRegistrationInServer = $ref<
let pushSubscription = ref<PushSubscription | null>(null);
let pushRegistrationInServer = ref<
| {
state?: string;
key?: string;
@ -91,23 +93,24 @@ let pushRegistrationInServer = $ref<
>();
function subscribe() {
if (!registration || !supported || !instance.swPublickey) return;
if (!registration.value || !supported.value || !instance.swPublickey)
return;
// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
return promiseDialog(
registration.pushManager
registration.value.pushManager
.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(
instance.swPublickey
instance.swPublickey,
),
})
.then(
async (subscription) => {
pushSubscription = subscription;
pushSubscription.value = subscription;
// Register
pushRegistrationInServer = await api("sw/register", {
pushRegistrationInServer.value = await api("sw/register", {
endpoint: subscription.endpoint,
auth: encode(subscription.getKey("auth")),
publickey: encode(subscription.getKey("p256dh")),
@ -118,7 +121,7 @@ function subscribe() {
//
if (err?.name === "NotAllowedError") {
console.info(
"User denied the notification permission request."
"User denied the notification permission request.",
);
return;
}
@ -128,20 +131,20 @@ function subscribe() {
//
//
await unsubscribe();
}
},
),
null,
null
null,
);
}
async function unsubscribe() {
if (!pushSubscription) return;
if (!pushSubscription.value) return;
const endpoint = pushSubscription.endpoint;
const endpoint = pushSubscription.value.endpoint;
const accounts = await getAccounts();
pushRegistrationInServer = undefined;
pushRegistrationInServer.value = undefined;
if ($i && accounts.length >= 2) {
apiWithDialog("sw/unregister", {
@ -149,11 +152,11 @@ async function unsubscribe() {
endpoint,
});
} else {
pushSubscription.unsubscribe();
pushSubscription.value.unsubscribe();
apiWithDialog("sw/unregister", {
endpoint,
});
pushSubscription = null;
pushSubscription.value = null;
}
}
@ -184,20 +187,21 @@ if (navigator.serviceWorker == null) {
// TODO:
} else {
navigator.serviceWorker.ready.then(async (swr) => {
registration = swr;
registration.value = swr;
pushSubscription = await registration.pushManager.getSubscription();
pushSubscription.value =
await registration.value.pushManager.getSubscription();
if (instance.swPublickey && "PushManager" in window && $i && $i.token) {
supported = true;
supported.value = true;
if (pushSubscription) {
if (pushSubscription.value) {
const res = await api("sw/show-registration", {
endpoint: pushSubscription.endpoint,
endpoint: pushSubscription.value.endpoint,
});
if (res) {
pushRegistrationInServer = res;
pushRegistrationInServer.value = res;
}
}
}
@ -205,6 +209,6 @@ if (navigator.serviceWorker == null) {
}
defineExpose({
pushRegistrationInServer: $$(pushRegistrationInServer),
pushRegistrationInServer: pushRegistrationInServer,
});
</script>

View File

@ -37,7 +37,7 @@
</template>
<script lang="ts" setup>
import { onMounted, watch } from "vue";
import { onMounted, watch, ref } from "vue";
import * as misskey from "calckey-js";
import MkUserCardMini from "@/components/MkUserCardMini.vue";
import * as os from "@/os";
@ -48,26 +48,26 @@ const props = defineProps<{
noteId: string;
}>();
let tab = $ref<string>();
let note = $ref<packed.PackNoteBase>();
let reactions = $ref<types.ReactionPair[]>();
let users = $ref<misskey.entities.UserLite[]>();
let tab = ref<string>();
let note = ref<packed.PackNoteBase>();
let reactions = ref<types.ReactionPair[]>();
let users = ref<misskey.entities.UserLite[]>();
watch($$(tab), async () => {
watch(tab, async () => {
const res = await os.api("notes/reactions", {
noteId: props.noteId,
type: tab,
type: tab.value,
limit: 30,
});
users = res.map((x) => x.user);
users.value = res.map((x) => x.user);
});
onMounted(() => {
os.magApi(endpoints.GetNoteById, {}, { id: props.noteId }).then((res) => {
reactions = res.reactions;
tab = magReactionToLegacy(reactions.map((r) => r[0])[0]);
note = res;
reactions.value = res.reactions;
tab.value = magReactionToLegacy(reactions.value.map((r) => r[0])[0]);
note.value = res;
});
});
</script>

View File

@ -41,7 +41,7 @@ const props = defineProps<{
const buttonRef = ref<HTMLElement>();
const canToggle = computed(
() => !magReactionToLegacy(props.reaction).match(/@\w/) && $i
() => !magReactionToLegacy(props.reaction).match(/@\w/) && $i,
);
const toggleReaction = () => {
@ -90,10 +90,10 @@ useTooltip(
targetElement: buttonRef.value,
},
{},
"closed"
"closed",
);
},
100
100,
);
</script>

View File

@ -30,7 +30,7 @@ const initialReactions = new Set(
? props.note.reactions.map((v) => magReactionToLegacy(v[0]))
: Object.keys(props.note.reactions)
.map((v) => magConvertReaction(v))
.map(magReactionToLegacy)
.map(magReactionToLegacy),
);
const isMe = computed(() => $i && $i.id === props.note.user.id);

View File

@ -85,7 +85,7 @@ const props = withDefaults(
}>(),
{
particle: true,
}
},
);
const emit = defineEmits<{

View File

@ -122,7 +122,7 @@ export default defineComponent({
action: () => {},
},
],
ev.currentTarget ?? ev.target
ev.currentTarget ?? ev.target,
);
},
},

View File

@ -127,7 +127,7 @@
<script lang="ts" setup>
import Vue3OtpInput from "vue3-otp-input";
import { defineAsyncComponent } from "vue";
import { defineAsyncComponent, ref, computed } from "vue";
import { toUnicode } from "punycode/";
import MkButton from "@/components/MkButton.vue";
import MkInput from "@/components/form/input.vue";
@ -139,24 +139,24 @@ import { login } from "@/account";
import { instance } from "@/instance";
import { i18n } from "@/i18n";
let signing = $ref(false);
let user = $ref(null);
let username = $ref("");
let password = $ref("");
let token = $ref("");
let host = $ref(toUnicode(configHost));
let totpLogin = $ref(false);
let credential = $ref(null);
let challengeData = $ref(null);
let queryingKey = $ref(false);
let hCaptchaResponse = $ref(null);
let reCaptchaResponse = $ref(null);
let signing = ref(false);
let user = ref(null);
let username = ref("");
let password = ref("");
let token = ref("");
let host = ref(toUnicode(configHost));
let totpLogin = ref(false);
let credential = ref(null);
let challengeData = ref(null);
let queryingKey = ref(false);
let hCaptchaResponse = ref(null);
let reCaptchaResponse = ref(null);
const updateToken = (value: string) => {
token = value.toString();
token.value = value.toString();
};
const meta = $computed(() => instance);
const meta = computed(() => instance);
const emit = defineEmits<{
(ev: "login", v: any): void;
@ -182,14 +182,14 @@ const props = defineProps({
function onUsernameChange() {
os.api("users/show", {
username: username,
username: username.value,
}).then(
(userResponse) => {
user = userResponse;
user.value = userResponse;
},
() => {
user = null;
}
user.value = null;
},
);
}
@ -200,38 +200,40 @@ function onLogin(res) {
}
function queryKey() {
queryingKey = true;
queryingKey.value = true;
return navigator.credentials
.get({
publicKey: {
challenge: byteify(challengeData.challenge, "base64"),
allowCredentials: challengeData.securityKeys.map((key) => ({
id: byteify(key.id, "hex"),
type: "public-key",
transports: ["usb", "nfc", "ble", "internal"],
})),
challenge: byteify(challengeData.value.challenge, "base64"),
allowCredentials: challengeData.value.securityKeys.map(
(key) => ({
id: byteify(key.id, "hex"),
type: "public-key",
transports: ["usb", "nfc", "ble", "internal"],
}),
),
timeout: 60 * 1000,
},
})
.catch(() => {
queryingKey = false;
queryingKey.value = false;
return Promise.reject(null);
})
.then((credential) => {
queryingKey = false;
signing = true;
queryingKey.value = false;
signing.value = true;
return os.api("signin", {
username,
password,
username: username.value,
password: password.value,
signature: hexify(credential.response.signature),
authenticatorData: hexify(
credential.response.authenticatorData
credential.response.authenticatorData,
),
clientDataJSON: hexify(credential.response.clientDataJSON),
credentialId: credential.id,
challengeId: challengeData.challengeId,
"hcaptcha-response": hCaptchaResponse,
"g-recaptcha-response": reCaptchaResponse,
challengeId: challengeData.value.challengeId,
"hcaptcha-response": hCaptchaResponse.value,
"g-recaptcha-response": reCaptchaResponse.value,
});
})
.then((res) => {
@ -244,39 +246,42 @@ function queryKey() {
type: "error",
text: i18n.ts.signinFailed,
});
signing = false;
signing.value = false;
});
}
function onSubmit() {
signing = true;
signing.value = true;
console.log("submit");
if (!totpLogin && user && user.twoFactorEnabled) {
if (window.PublicKeyCredential && user.securityKeys) {
if (!totpLogin.value && user.value && user.value.twoFactorEnabled) {
if (window.PublicKeyCredential && user.value.securityKeys) {
os.api("signin", {
username,
password,
"hcaptcha-response": hCaptchaResponse,
"g-recaptcha-response": reCaptchaResponse,
username: username.value,
password: password.value,
"hcaptcha-response": hCaptchaResponse.value,
"g-recaptcha-response": reCaptchaResponse.value,
})
.then((res) => {
totpLogin = true;
signing = false;
challengeData = res;
totpLogin.value = true;
signing.value = false;
challengeData.value = res;
return queryKey();
})
.catch(loginFailed);
} else {
totpLogin = true;
signing = false;
totpLogin.value = true;
signing.value = false;
}
} else {
os.api("signin", {
username,
password,
"hcaptcha-response": hCaptchaResponse,
"g-recaptcha-response": reCaptchaResponse,
token: user && user.twoFactorEnabled ? token : undefined,
username: username.value,
password: password.value,
"hcaptcha-response": hCaptchaResponse.value,
"g-recaptcha-response": reCaptchaResponse.value,
token:
user.value && user.value.twoFactorEnabled
? token.value
: undefined,
})
.then((res) => {
emit("login", res);
@ -326,9 +331,9 @@ function loginFailed(err) {
}
}
challengeData = null;
totpLogin = false;
signing = false;
challengeData.value = null;
totpLogin.value = false;
signing.value = false;
}
function resetPassword() {
@ -336,7 +341,7 @@ function resetPassword() {
defineAsyncComponent(() => import("@/components/MkForgotPassword.vue")),
{},
{},
"closed"
"closed",
);
}

View File

@ -12,6 +12,8 @@
</template>
<script lang="ts" setup>
import { ref } from "vue";
import {} from "vue";
import MkSignin from "@/components/MkSignin.vue";
import XModalWindow from "@/components/MkModalWindow.vue";
@ -25,7 +27,7 @@ const props = withDefaults(
{
autoSet: false,
message: "",
}
},
);
const emit = defineEmits<{
@ -34,15 +36,15 @@ const emit = defineEmits<{
(ev: "cancelled"): void;
}>();
const dialog = $ref<InstanceType<typeof XModalWindow>>();
const dialog = ref<InstanceType<typeof XModalWindow>>();
function onClose() {
emit("cancelled");
dialog.close();
dialog.value.close();
}
function onLogin(res) {
emit("done", res);
dialog.close();
dialog.value.close();
}
</script>

View File

@ -284,6 +284,8 @@
</template>
<script lang="ts" setup>
import { ref, computed } from "vue";
import {} from "vue";
import getPasswordStrength from "syuilo-password-strength";
import { toUnicode } from "punycode/";
@ -303,7 +305,7 @@ const props = withDefaults(
}>(),
{
autoSet: false,
}
},
);
const emit = defineEmits<{
@ -313,14 +315,14 @@ const emit = defineEmits<{
const host = toUnicode(config.host);
let hcaptcha = $ref();
let recaptcha = $ref();
let hcaptcha = ref();
let recaptcha = ref();
let username: string = $ref("");
let password: string = $ref("");
let retypedPassword: string = $ref("");
let invitationCode: string = $ref("");
let email = $ref("");
let username: string = ref("");
let password: string = ref("");
let retypedPassword: string = ref("");
let invitationCode: string = ref("");
let email = ref("");
let usernameState:
| null
| "wait"
@ -329,8 +331,8 @@ let usernameState:
| "error"
| "invalid-format"
| "min-range"
| "max-range" = $ref(null);
let invitationState: null | "entered" = $ref(null);
| "max-range" = ref(null);
let invitationState: null | "entered" = ref(null);
let emailState:
| null
| "wait"
@ -341,141 +343,142 @@ let emailState:
| "unavailable:mx"
| "unavailable:smtp"
| "unavailable"
| "error" = $ref(null);
let passwordStrength: "" | "low" | "medium" | "high" = $ref("");
let passwordRetypeState: null | "match" | "not-match" = $ref(null);
let submitting: boolean = $ref(false);
let ToSAgreement: boolean = $ref(false);
let hCaptchaResponse = $ref(null);
let reCaptchaResponse = $ref(null);
| "error" = ref(null);
let passwordStrength: "" | "low" | "medium" | "high" = ref("");
let passwordRetypeState: null | "match" | "not-match" = ref(null);
let submitting: boolean = ref(false);
let ToSAgreement: boolean = ref(false);
let hCaptchaResponse = ref(null);
let reCaptchaResponse = ref(null);
const shouldDisableSubmitting = $computed((): boolean => {
const shouldDisableSubmitting = computed((): boolean => {
return (
submitting ||
(instance.tosUrl && !ToSAgreement) ||
(instance.enableHcaptcha && !hCaptchaResponse) ||
(instance.enableRecaptcha && !reCaptchaResponse) ||
passwordRetypeState === "not-match"
submitting.value ||
(instance.tosUrl && !ToSAgreement.value) ||
(instance.enableHcaptcha && !hCaptchaResponse.value) ||
(instance.enableRecaptcha && !reCaptchaResponse.value) ||
passwordRetypeState.value === "not-match"
);
});
function onChangeInvitationCode(): void {
if (invitationCode === "") {
invitationState = null;
if (invitationCode.value === "") {
invitationState.value = null;
return;
}
invitationState = "entered";
invitationState.value = "entered";
}
function onChangeUsername(): void {
if (username === "") {
usernameState = null;
if (username.value === "") {
usernameState.value = null;
return;
}
{
const err = !username.match(/^[a-zA-Z0-9_]+$/)
const err = !username.value.match(/^[a-zA-Z0-9_]+$/)
? "invalid-format"
: username.length < 1
? "min-range"
: username.length > 20
? "max-range"
: null;
: username.value.length < 1
? "min-range"
: username.value.length > 20
? "max-range"
: null;
if (err) {
usernameState = err;
usernameState.value = err;
return;
}
}
usernameState = "wait";
usernameState.value = "wait";
os.api("username/available", {
username,
username: username.value,
})
.then((result) => {
usernameState = result.available ? "ok" : "unavailable";
usernameState.value = result.available ? "ok" : "unavailable";
})
.catch(() => {
usernameState = "error";
usernameState.value = "error";
});
}
function onChangeEmail(): void {
if (email === "") {
emailState = null;
if (email.value === "") {
emailState.value = null;
return;
}
emailState = "wait";
emailState.value = "wait";
os.api("email-address/available", {
emailAddress: email,
emailAddress: email.value,
})
.then((result) => {
emailState = result.available
emailState.value = result.available
? "ok"
: result.reason === "used"
? "unavailable:used"
: result.reason === "format"
? "unavailable:format"
: result.reason === "disposable"
? "unavailable:disposable"
: result.reason === "mx"
? "unavailable:mx"
: result.reason === "smtp"
? "unavailable:smtp"
: "unavailable";
? "unavailable:used"
: result.reason === "format"
? "unavailable:format"
: result.reason === "disposable"
? "unavailable:disposable"
: result.reason === "mx"
? "unavailable:mx"
: result.reason === "smtp"
? "unavailable:smtp"
: "unavailable";
})
.catch(() => {
emailState = "error";
emailState.value = "error";
});
}
function onChangePassword(): void {
if (password === "") {
passwordStrength = "";
if (password.value === "") {
passwordStrength.value = "";
return;
}
const strength = getPasswordStrength(password);
passwordStrength =
const strength = getPasswordStrength(password.value);
passwordStrength.value =
strength > 0.7 ? "high" : strength > 0.3 ? "medium" : "low";
}
function onChangePasswordRetype(): void {
if (retypedPassword === "") {
passwordRetypeState = null;
if (retypedPassword.value === "") {
passwordRetypeState.value = null;
return;
}
passwordRetypeState = password === retypedPassword ? "match" : "not-match";
passwordRetypeState.value =
password.value === retypedPassword.value ? "match" : "not-match";
}
function onSubmit(): void {
if (submitting) return;
submitting = true;
if (submitting.value) return;
submitting.value = true;
os.api("signup", {
username,
password,
emailAddress: email,
invitationCode,
"hcaptcha-response": hCaptchaResponse,
"g-recaptcha-response": reCaptchaResponse,
username: username.value,
password: password.value,
emailAddress: email.value,
invitationCode: invitationCode.value,
"hcaptcha-response": hCaptchaResponse.value,
"g-recaptcha-response": reCaptchaResponse.value,
})
.then(() => {
if (instance.emailRequiredForSignup) {
os.alert({
type: "success",
title: i18n.ts._signup.almostThere,
text: i18n.t("_signup.emailSent", { email }),
text: i18n.t("_signup.emailSent", { email: email.value }),
});
emit("signupEmailPending");
} else {
os.api("signin", {
username,
password,
username: username.value,
password: password.value,
}).then((res) => {
emit("signup", res);
@ -486,9 +489,9 @@ function onSubmit(): void {
}
})
.catch(() => {
submitting = false;
hcaptcha.reset?.();
recaptcha.reset?.();
submitting.value = false;
hcaptcha.value.reset?.();
recaptcha.value.reset?.();
os.alert({
type: "error",

View File

@ -20,6 +20,8 @@
</template>
<script lang="ts" setup>
import { ref } from "vue";
import {} from "vue";
import XSignup from "@/components/MkSignup.vue";
import XModalWindow from "@/components/MkModalWindow.vue";
@ -31,7 +33,7 @@ const props = withDefaults(
}>(),
{
autoSet: false,
}
},
);
const emit = defineEmits<{
@ -39,14 +41,14 @@ const emit = defineEmits<{
(ev: "closed"): void;
}>();
const dialog = $ref<InstanceType<typeof XModalWindow>>();
const dialog = ref<InstanceType<typeof XModalWindow>>();
function onSignup(res) {
emit("done", res);
dialog?.close();
dialog.value?.close();
}
function onSignupEmailPending() {
dialog?.close();
dialog.value?.close();
}
</script>

View File

@ -105,13 +105,16 @@ onMounted(() => {
particles.value.push(particle);
window.setTimeout(() => {
particles.value = particles.value.filter(
(x) => x.id !== particle.id
(x) => x.id !== particle.id,
);
}, particle.dur - 100);
window.setTimeout(() => {
add();
}, 500 + Math.random() * 500);
window.setTimeout(
() => {
add();
},
500 + Math.random() * 500,
);
};
add();
}

View File

@ -104,7 +104,7 @@ useTooltip(buttonRef, async (showing) => {
targetElement: buttonRef.value,
},
{},
"closed"
"closed",
);
});
</script>

View File

@ -37,13 +37,13 @@ export default defineComponent({
onClick: () => {
this.$emit(
"update:modelValue",
option.props?.value
option.props?.value,
);
},
},
option.children
)
)
option.children,
),
),
);
},
});

Some files were not shown because too many files have changed in this diff Show More