Unfucked the frontend
ci/woodpecker/push/ociImagePush Pipeline was successful
Details
ci/woodpecker/push/ociImagePush Pipeline was successful
Details
This commit is contained in:
parent
156ad5c499
commit
aefef079a7
|
@ -1,15 +1,15 @@
|
||||||
{
|
{
|
||||||
"tabWidth": 4,
|
"tabWidth": 4,
|
||||||
"useTabs": false,
|
"useTabs": false,
|
||||||
"singleQuote": false,
|
"singleQuote": false,
|
||||||
"vueIndentScriptAndStyle": false,
|
"vueIndentScriptAndStyle": false,
|
||||||
"plugins": ["vue"],
|
"plugins": ["vue"],
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": "*.vue",
|
"files": "*.vue",
|
||||||
"options": {
|
"options": {
|
||||||
"parser": "vue"
|
"parser": "vue"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
declare module "@/themes/*.json5" {
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/// <reference types="vue/macros-global" />
|
/// <reference types="vue/macros-global" />
|
||||||
|
|
||||||
declare module "*.vue" {
|
declare module "*.vue" {
|
||||||
import type { DefineComponent } from "vue";
|
import type { DefineComponent } from "vue";
|
||||||
const component: DefineComponent<{}, {}, any>;
|
const component: DefineComponent<{}, {}, any>;
|
||||||
export default component;
|
export default component;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "client",
|
"name": "client",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch": "pnpm vite build --watch --mode development",
|
"watch": "pnpm vite build --watch --mode development",
|
||||||
"watchRebuild": "pnpm vite build --watch",
|
"watchRebuild": "pnpm vite build --watch",
|
||||||
|
@ -9,10 +10,11 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@discordapp/twemoji": "14.1.2",
|
"@discordapp/twemoji": "14.1.2",
|
||||||
"@phosphor-icons/web": "^2.0.3",
|
"@misskey-dev/browser-image-resizer": "^2024.1.0",
|
||||||
"@rollup/plugin-alias": "3.1.9",
|
"@phosphor-icons/web": "^2.1.1",
|
||||||
"@rollup/plugin-json": "4.1.0",
|
"@rollup/plugin-alias": "^5.1.0",
|
||||||
"@rollup/pluginutils": "^4.2.1",
|
"@rollup/plugin-json": "^6.1.0",
|
||||||
|
"@rollup/pluginutils": "^5.1.0",
|
||||||
"@types/escape-regexp": "0.0.1",
|
"@types/escape-regexp": "0.0.1",
|
||||||
"@types/glob": "8.1.0",
|
"@types/glob": "8.1.0",
|
||||||
"@types/gulp": "4.0.11",
|
"@types/gulp": "4.0.11",
|
||||||
|
@ -24,14 +26,12 @@
|
||||||
"@types/throttle-debounce": "5.0.0",
|
"@types/throttle-debounce": "5.0.0",
|
||||||
"@types/tinycolor2": "1.4.3",
|
"@types/tinycolor2": "1.4.3",
|
||||||
"@types/uuid": "8.3.4",
|
"@types/uuid": "8.3.4",
|
||||||
"@vitejs/plugin-vue": "4.2.3",
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
"@vue/compiler-sfc": "3.3.4",
|
"@vue/runtime-core": "^3.4",
|
||||||
"@vue/runtime-core": "3.3.4",
|
|
||||||
"autobind-decorator": "2.4.0",
|
"autobind-decorator": "2.4.0",
|
||||||
"autosize": "5.0.2",
|
"autosize": "5.0.2",
|
||||||
"blurhash": "1.1.5",
|
"blurhash": "1.1.5",
|
||||||
"broadcast-channel": "4.19.1",
|
"broadcast-channel": "4.19.1",
|
||||||
"browser-image-resizer": "github:misskey-dev/browser-image-resizer",
|
|
||||||
"calckey-js": "workspace:*",
|
"calckey-js": "workspace:*",
|
||||||
"chart.js": "4.3.0",
|
"chart.js": "4.3.0",
|
||||||
"chartjs-adapter-date-fns": "3.0.0",
|
"chartjs-adapter-date-fns": "3.0.0",
|
||||||
|
@ -44,12 +44,12 @@
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "10.11.0",
|
"cypress": "10.11.0",
|
||||||
"date-fns": "2.30.0",
|
"date-fns": "2.30.0",
|
||||||
"emojilib": "github:thatonecalculator/emojilib",
|
"emojilib": "^3.0.12",
|
||||||
"escape-regexp": "0.0.1",
|
"escape-regexp": "0.0.1",
|
||||||
"eventemitter3": "4.0.7",
|
"eventemitter3": "4.0.7",
|
||||||
"focus-trap": "^7.4.3",
|
"focus-trap": "^7.5.4",
|
||||||
"focus-trap-vue": "^4.0.2",
|
"focus-trap-vue": "^4.0.3",
|
||||||
"gsap": "^3.11.5",
|
"gsap": "^3.12.5",
|
||||||
"idb-keyval": "6.2.1",
|
"idb-keyval": "6.2.1",
|
||||||
"insert-text-at-cursor": "0.3.0",
|
"insert-text-at-cursor": "0.3.0",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
|
@ -62,9 +62,8 @@
|
||||||
"prettier-plugin-vue": "1.1.6",
|
"prettier-plugin-vue": "1.1.6",
|
||||||
"prismjs": "1.29.0",
|
"prismjs": "1.29.0",
|
||||||
"punycode": "2.1.1",
|
"punycode": "2.1.1",
|
||||||
"querystring": "0.2.1",
|
|
||||||
"rndstr": "1.0.0",
|
"rndstr": "1.0.0",
|
||||||
"rollup": "3.23.1",
|
"rollup": "^4.14.1",
|
||||||
"s-age": "1.1.2",
|
"s-age": "1.1.2",
|
||||||
"sass": "1.62.1",
|
"sass": "1.62.1",
|
||||||
"seedrandom": "3.0.5",
|
"seedrandom": "3.0.5",
|
||||||
|
@ -80,17 +79,17 @@
|
||||||
"tsc-alias": "1.8.6",
|
"tsc-alias": "1.8.6",
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"twemoji-parser": "14.0.0",
|
"twemoji-parser": "14.0.0",
|
||||||
"typescript": "5.1.3",
|
"typescript": "^5.4.4",
|
||||||
"unicode-emoji-json": "^0.4.0",
|
"unicode-emoji-json": "^0.4.0",
|
||||||
"uuid": "9.0.0",
|
"uuid": "9.0.0",
|
||||||
"vanilla-tilt": "1.8.0",
|
"vanilla-tilt": "1.8.0",
|
||||||
"vite": "4.3.9",
|
"vidstack": "^1.11",
|
||||||
|
"vite": "^5.2.8",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vue": "3.3.4",
|
"vue": "^3.4.21",
|
||||||
"vue-component-type-helpers": "1.8.27",
|
"vue-component-type-helpers": "^2.0.11",
|
||||||
"vue-isyourpasswordsafe": "^2.0.0",
|
"vue-isyourpasswordsafe": "^2.0.0",
|
||||||
"vue-plyr": "^7.0.0",
|
"vue3-otp-input": "^0.4.4",
|
||||||
"vue3-otp-input": "^0.4.1",
|
|
||||||
"vuedraggable": "4.1.0"
|
"vuedraggable": "4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,8 +49,8 @@ export async function signout() {
|
||||||
.then((registrations) => {
|
.then((registrations) => {
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
registrations.map((registration) =>
|
registrations.map((registration) =>
|
||||||
registration.unregister()
|
registration.unregister(),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ export async function removeAccount(id: Account["id"]) {
|
||||||
const accounts = await getAccounts();
|
const accounts = await getAccounts();
|
||||||
accounts.splice(
|
accounts.splice(
|
||||||
accounts.findIndex((x) => x.id === id),
|
accounts.findIndex((x) => x.id === id),
|
||||||
1
|
1,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (accounts.length > 0) await set("accounts", accounts);
|
if (accounts.length > 0) await set("accounts", accounts);
|
||||||
|
@ -158,12 +158,12 @@ export async function openAccountMenu(
|
||||||
active?: misskey.entities.UserDetailed["id"];
|
active?: misskey.entities.UserDetailed["id"];
|
||||||
onChoose?: (account: misskey.entities.UserDetailed) => void;
|
onChoose?: (account: misskey.entities.UserDetailed) => void;
|
||||||
},
|
},
|
||||||
ev: MouseEvent
|
ev: MouseEvent,
|
||||||
) {
|
) {
|
||||||
function showSigninDialog() {
|
function showSigninDialog() {
|
||||||
popup(
|
popup(
|
||||||
defineAsyncComponent(
|
defineAsyncComponent(
|
||||||
() => import("@/components/MkSigninDialog.vue")
|
() => import("@/components/MkSigninDialog.vue"),
|
||||||
),
|
),
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
|
@ -172,14 +172,14 @@ export async function openAccountMenu(
|
||||||
success();
|
success();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"closed"
|
"closed",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAccount() {
|
function createAccount() {
|
||||||
popup(
|
popup(
|
||||||
defineAsyncComponent(
|
defineAsyncComponent(
|
||||||
() => import("@/components/MkSignupDialog.vue")
|
() => import("@/components/MkSignupDialog.vue"),
|
||||||
),
|
),
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
|
@ -188,7 +188,7 @@ export async function openAccountMenu(
|
||||||
switchAccountWithToken(res.i);
|
switchAccountWithToken(res.i);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"closed"
|
"closed",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +203,7 @@ export async function openAccountMenu(
|
||||||
}
|
}
|
||||||
|
|
||||||
const storedAccounts = await getAccounts().then((accounts) =>
|
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", {
|
const accountsPromise = api("users/show", {
|
||||||
userIds: storedAccounts.map((x) => x.id),
|
userIds: storedAccounts.map((x) => x.id),
|
||||||
|
@ -232,7 +232,7 @@ export async function openAccountMenu(
|
||||||
if (account == null) return res(null);
|
if (account == null) return res(null);
|
||||||
res(createItem(account));
|
res(createItem(account));
|
||||||
});
|
});
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (opts.withExtraOperation) {
|
if (opts.withExtraOperation) {
|
||||||
|
@ -286,7 +286,7 @@ export async function openAccountMenu(
|
||||||
ev.currentTarget ?? ev.target,
|
ev.currentTarget ?? ev.target,
|
||||||
{
|
{
|
||||||
align: "left",
|
align: "left",
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
popupMenu(
|
popupMenu(
|
||||||
|
@ -297,7 +297,7 @@ export async function openAccountMenu(
|
||||||
ev.currentTarget ?? ev.target,
|
ev.currentTarget ?? ev.target,
|
||||||
{
|
{
|
||||||
align: "left",
|
align: "left",
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,11 +26,11 @@ watch(
|
||||||
{
|
{
|
||||||
id: userIds.map((u) => (typeof u === "string" ? u : u.id)),
|
id: userIds.map((u) => (typeof u === "string" ? u : u.id)),
|
||||||
},
|
},
|
||||||
{}
|
{},
|
||||||
)
|
)
|
||||||
.then((p) => p.filter((u) => u !== null).map((u) => u!));
|
.then((p) => p.filter((u) => u !== null).map((u) => u!));
|
||||||
},
|
},
|
||||||
{ immediate: true, deep: true }
|
{ immediate: true, deep: true },
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ const props = withDefaults(
|
||||||
direction: "down",
|
direction: "down",
|
||||||
reversed: false,
|
reversed: false,
|
||||||
noGap: false,
|
noGap: false,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
type ItemType =
|
type ItemType =
|
||||||
|
@ -82,12 +82,12 @@ watch(
|
||||||
|
|
||||||
effectiveItems.value = listItems;
|
effectiveItems.value = listItems;
|
||||||
},
|
},
|
||||||
{ deep: true, immediate: true }
|
{ deep: true, immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
function getDateText(item: { createdAt: string } | { created_at: string }) {
|
function getDateText(item: { createdAt: string } | { created_at: string }) {
|
||||||
const dateTime = new Date(
|
const dateTime = new Date(
|
||||||
magTransProperty(item, "createdAt", "created_at")
|
magTransProperty(item, "createdAt", "created_at"),
|
||||||
);
|
);
|
||||||
const date = dateTime.getDate();
|
const date = dateTime.getDate();
|
||||||
const month = dateTime.getMonth() + 1;
|
const month = dateTime.getMonth() + 1;
|
||||||
|
@ -100,11 +100,11 @@ function getDateText(item: { createdAt: string } | { created_at: string }) {
|
||||||
const animationEnabled = ref(defaultStore.state.animation);
|
const animationEnabled = ref(defaultStore.state.animation);
|
||||||
watch(
|
watch(
|
||||||
() => defaultStore.state.animation,
|
() => defaultStore.state.animation,
|
||||||
(val) => (animationEnabled.value = val)
|
(val) => (animationEnabled.value = val),
|
||||||
);
|
);
|
||||||
|
|
||||||
const parentType = computed(() =>
|
const parentType = computed(() =>
|
||||||
animationEnabled ? TransitionGroup : HTMLDivElement
|
animationEnabled ? TransitionGroup : HTMLDivElement,
|
||||||
);
|
);
|
||||||
const tag = computed(() => (animationEnabled ? "div" : null));
|
const tag = computed(() => (animationEnabled ? "div" : null));
|
||||||
const name = computed(() => (animationEnabled ? "list" : null));
|
const name = computed(() => (animationEnabled ? "list" : null));
|
||||||
|
@ -129,7 +129,8 @@ defineSlots<{
|
||||||
}
|
}
|
||||||
|
|
||||||
> .list-enter-active {
|
> .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);
|
opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ const props = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const url = computed(
|
const url = computed(
|
||||||
() => `https://matrix.to/#/@${props.username}:${props.host}`
|
() => `https://matrix.to/#/@${props.username}:${props.host}`,
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -263,7 +263,7 @@ const props = defineProps<{
|
||||||
collapsedReply?: boolean;
|
collapsedReply?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let note = $ref<packed.PackNoteMaybeFull>(structuredClone(toRaw(props.note)));
|
let note = ref<packed.PackNoteMaybeFull>(structuredClone(toRaw(props.note)));
|
||||||
|
|
||||||
const softMuteReasonI18nSrc = (what?: string) => {
|
const softMuteReasonI18nSrc = (what?: string) => {
|
||||||
if (what === "note") return i18n.ts.userSaysSomethingReason;
|
if (what === "note") return i18n.ts.userSaysSomethingReason;
|
||||||
|
@ -282,13 +282,15 @@ const starButton = ref<InstanceType<typeof XStarButton>>();
|
||||||
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
||||||
const renoteTime = ref<HTMLElement | null>(null);
|
const renoteTime = ref<HTMLElement | null>(null);
|
||||||
const reactButton = ref<HTMLElement | null>(null);
|
const reactButton = ref<HTMLElement | null>(null);
|
||||||
let appearNote = $computed(
|
let appearNote = computed(
|
||||||
() => magEffectiveNote(note) as packed.PackNoteMaybeFull
|
() => 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 showContent = ref(false);
|
||||||
const isDeleted = 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 translation = ref(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
const enableEmojiReactions = defaultStore.state.enableEmojiReactions;
|
const enableEmojiReactions = defaultStore.state.enableEmojiReactions;
|
||||||
|
@ -307,7 +309,7 @@ const keymap = {
|
||||||
|
|
||||||
useNoteCapture({
|
useNoteCapture({
|
||||||
rootEl: el,
|
rootEl: el,
|
||||||
note: $$(appearNote),
|
note: appearNote,
|
||||||
isDeletedRef: isDeleted,
|
isDeletedRef: isDeleted,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -331,7 +333,7 @@ function react(viaKeyboard = false): void {
|
||||||
reactButton.value,
|
reactButton.value,
|
||||||
(reaction) => {
|
(reaction) => {
|
||||||
os.api("notes/reactions/create", {
|
os.api("notes/reactions/create", {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
reaction: magReactionToLegacy(reaction),
|
reaction: magReactionToLegacy(reaction),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -374,21 +376,24 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
type: "label",
|
type: "label",
|
||||||
text: notePage(appearNote),
|
text: notePage(appearNote.value),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-browser ph-bold ph-lg",
|
icon: "ph-browser ph-bold ph-lg",
|
||||||
text: i18n.ts.openInWindow,
|
text: i18n.ts.openInWindow,
|
||||||
action: () => {
|
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",
|
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
||||||
text: i18n.ts.showInPage,
|
text: i18n.ts.showInPage,
|
||||||
action: () => {
|
action: () => {
|
||||||
router.push(notePage(appearNote), "forcePage");
|
router.push(
|
||||||
|
notePage(appearNote.value),
|
||||||
|
"forcePage"
|
||||||
|
);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
@ -397,22 +402,25 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
type: "a",
|
type: "a",
|
||||||
icon: "ph-arrow-square-out ph-bold ph-lg",
|
icon: "ph-arrow-square-out ph-bold ph-lg",
|
||||||
text: i18n.ts.openInNewTab,
|
text: i18n.ts.openInNewTab,
|
||||||
href: notePage(appearNote),
|
href: notePage(appearNote.value),
|
||||||
target: "_blank",
|
target: "_blank",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-link-simple ph-bold ph-lg",
|
icon: "ph-link-simple ph-bold ph-lg",
|
||||||
text: i18n.ts.copyLink,
|
text: i18n.ts.copyLink,
|
||||||
action: () => {
|
action: () => {
|
||||||
copyToClipboard(`${url}${notePage(appearNote)}`);
|
copyToClipboard(`${url}${notePage(appearNote.value)}`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
appearNote.user.host != null
|
appearNote.value.user.host != null
|
||||||
? {
|
? {
|
||||||
type: "a",
|
type: "a",
|
||||||
icon: "ph-arrow-square-up-right ph-bold ph-lg",
|
icon: "ph-arrow-square-up-right ph-bold ph-lg",
|
||||||
text: i18n.ts.showOnRemote,
|
text: i18n.ts.showOnRemote,
|
||||||
href: appearNote.url ?? appearNote.uri ?? "",
|
href:
|
||||||
|
appearNote.value.url ??
|
||||||
|
appearNote.value.uri ??
|
||||||
|
"",
|
||||||
target: "_blank",
|
target: "_blank",
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
@ -425,7 +433,7 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
function menu(viaKeyboard = false): void {
|
function menu(viaKeyboard = false): void {
|
||||||
os.popupMenu(
|
os.popupMenu(
|
||||||
getNoteMenu({
|
getNoteMenu({
|
||||||
note: note,
|
note: note.value,
|
||||||
translating,
|
translating,
|
||||||
translation,
|
translation,
|
||||||
menuButton: menuButton.value,
|
menuButton: menuButton.value,
|
||||||
|
@ -449,7 +457,7 @@ function showRenoteMenu(viaKeyboard = false): void {
|
||||||
danger: true,
|
danger: true,
|
||||||
action: () => {
|
action: () => {
|
||||||
os.api("notes/delete", {
|
os.api("notes/delete", {
|
||||||
noteId: note.id,
|
noteId: note.value.id,
|
||||||
});
|
});
|
||||||
isDeleted.value = true;
|
isDeleted.value = true;
|
||||||
},
|
},
|
||||||
|
@ -490,7 +498,7 @@ function noteClick(e) {
|
||||||
) {
|
) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
} else {
|
} else {
|
||||||
router.push(notePage(appearNote));
|
router.push(notePage(appearNote.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,30 +509,30 @@ function setPostExpanded(val: boolean) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const accessibleLabel = computed(() => {
|
const accessibleLabel = computed(() => {
|
||||||
let label = `${appearNote.user.username}; `;
|
let label = `${appearNote.value.user.username}; `;
|
||||||
if (appearNote.renoted_note) {
|
if (appearNote.value.renoted_note) {
|
||||||
label += `${i18n.t("renoted")} ${
|
label += `${i18n.t("renoted")} ${
|
||||||
appearNote.renoted_note.user.username
|
appearNote.value.renoted_note.user.username
|
||||||
}; `;
|
}; `;
|
||||||
if (appearNote.renoted_note.cw) {
|
if (appearNote.value.renoted_note.cw) {
|
||||||
label += `${i18n.t("cw")}: ${appearNote.renoted_note.cw}; `;
|
label += `${i18n.t("cw")}: ${appearNote.value.renoted_note.cw}; `;
|
||||||
if (postIsExpanded.value) {
|
if (postIsExpanded.value) {
|
||||||
label += `${appearNote.renoted_note.text}; `;
|
label += `${appearNote.value.renoted_note.text}; `;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
label += `${appearNote.renoted_note.text}; `;
|
label += `${appearNote.value.renoted_note.text}; `;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (appearNote.cw) {
|
if (appearNote.value.cw) {
|
||||||
label += `${i18n.t("cw")}: ${appearNote.cw}; `;
|
label += `${i18n.t("cw")}: ${appearNote.value.cw}; `;
|
||||||
if (postIsExpanded.value) {
|
if (postIsExpanded.value) {
|
||||||
label += `${appearNote.text}; `;
|
label += `${appearNote.value.text}; `;
|
||||||
}
|
}
|
||||||
} else {
|
} 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()}`;
|
label += `${date.toLocaleTimeString()}`;
|
||||||
return label;
|
return label;
|
||||||
});
|
});
|
||||||
|
|
|
@ -152,7 +152,16 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 * as misskey from "calckey-js";
|
||||||
import MkTab from "@/components/MkTab.vue";
|
import MkTab from "@/components/MkTab.vue";
|
||||||
import MagNoteSub from "@/components/MagNoteSub.vue";
|
import MagNoteSub from "@/components/MagNoteSub.vue";
|
||||||
|
@ -185,9 +194,9 @@ const props = defineProps<{
|
||||||
pinned?: boolean;
|
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) => {
|
const softMuteReasonI18nSrc = (what?: string) => {
|
||||||
if (what === "note") return i18n.ts.userSaysSomethingReason;
|
if (what === "note") return i18n.ts.userSaysSomethingReason;
|
||||||
|
@ -204,14 +213,14 @@ const noteEl = ref<HTMLElement | null>(null);
|
||||||
const menuButton = ref<HTMLElement | null>(null);
|
const menuButton = ref<HTMLElement | null>(null);
|
||||||
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
||||||
const reactButton = ref<HTMLElement | null>(null);
|
const reactButton = ref<HTMLElement | null>(null);
|
||||||
let appearNote = $computed(
|
let appearNote = computed(
|
||||||
() => magEffectiveNote(note) as packed.PackNoteMaybeFull
|
() => magEffectiveNote(note.value) as packed.PackNoteMaybeFull,
|
||||||
);
|
);
|
||||||
let parentNote = $ref(appearNote.parent_note);
|
let parentNote = ref(appearNote.value.parent_note);
|
||||||
const showContent = ref(false);
|
const showContent = ref(false);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const muted = ref(
|
const muted = ref(
|
||||||
getWordSoftMute(appearNote, $i, defaultStore.state.mutedWords)
|
getWordSoftMute(appearNote.value, $i, defaultStore.state.mutedWords),
|
||||||
);
|
);
|
||||||
const translation = ref(null);
|
const translation = ref(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
|
@ -234,7 +243,7 @@ const keymap = {
|
||||||
|
|
||||||
useNoteCapture({
|
useNoteCapture({
|
||||||
rootEl: el as Ref<HTMLElement>,
|
rootEl: el as Ref<HTMLElement>,
|
||||||
note: $$(appearNote),
|
note: appearNote,
|
||||||
isDeletedRef: isDeleted,
|
isDeletedRef: isDeleted,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -255,13 +264,13 @@ function react(viaKeyboard = false): void {
|
||||||
reactButton.value,
|
reactButton.value,
|
||||||
(reaction) => {
|
(reaction) => {
|
||||||
os.api("notes/reactions/create", {
|
os.api("notes/reactions/create", {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
reaction: magReactionToLegacy(reaction),
|
reaction: magReactionToLegacy(reaction),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
focus();
|
focus();
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,13 +290,13 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
} else {
|
} else {
|
||||||
os.contextMenu(
|
os.contextMenu(
|
||||||
getNoteMenu({
|
getNoteMenu({
|
||||||
note: note,
|
note: note.value,
|
||||||
translating,
|
translating,
|
||||||
translation,
|
translation,
|
||||||
menuButton: menuButton.value,
|
menuButton: menuButton.value,
|
||||||
isDeleted,
|
isDeleted,
|
||||||
}),
|
}),
|
||||||
ev
|
ev,
|
||||||
).then(focus);
|
).then(focus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,7 +304,7 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
function menu(viaKeyboard = false): void {
|
function menu(viaKeyboard = false): void {
|
||||||
os.popupMenu(
|
os.popupMenu(
|
||||||
getNoteMenu({
|
getNoteMenu({
|
||||||
note: note,
|
note: note.value,
|
||||||
translating,
|
translating,
|
||||||
translation,
|
translation,
|
||||||
menuButton: menuButton.value,
|
menuButton: menuButton.value,
|
||||||
|
@ -304,7 +313,7 @@ function menu(viaKeyboard = false): void {
|
||||||
menuButton.value ?? undefined,
|
menuButton.value ?? undefined,
|
||||||
{
|
{
|
||||||
viaKeyboard,
|
viaKeyboard,
|
||||||
}
|
},
|
||||||
).then(focus);
|
).then(focus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,64 +327,63 @@ function blur() {
|
||||||
|
|
||||||
async function updateParent(note: packed.PackNoteMaybeFull) {
|
async function updateParent(note: packed.PackNoteMaybeFull) {
|
||||||
if (note.parent_note) {
|
if (note.parent_note) {
|
||||||
parentNote = note.parent_note;
|
parentNote.value = note.parent_note;
|
||||||
return;
|
return;
|
||||||
} else if (note.parent_note_id) {
|
} else if (note.parent_note_id) {
|
||||||
parentNote = null;
|
parentNote.value = null;
|
||||||
parentNote = await os.magApi(
|
parentNote.value = await os.magApi(
|
||||||
endpoints.GetNoteById,
|
endpoints.GetNoteById,
|
||||||
{ attachments: true, context: true },
|
{ attachments: true, context: true },
|
||||||
{ id: note.parent_note_id }
|
{ id: note.parent_note_id },
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
parentNote = null;
|
parentNote.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(appearNote, updateParent);
|
watch(appearNote, updateParent, { immediate: true });
|
||||||
updateParent(note);
|
|
||||||
|
|
||||||
os.api("notes/children", {
|
os.api("notes/children", {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
limit: 30,
|
limit: 30,
|
||||||
depth: 12,
|
depth: 12,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
res = res.reduce(
|
res = res.reduce(
|
||||||
(acc: misskey.entities.Note[], resNote: misskey.entities.Note) => {
|
(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 [...acc, resNote];
|
||||||
}
|
}
|
||||||
return [resNote, ...acc];
|
return [resNote, ...acc];
|
||||||
},
|
},
|
||||||
[]
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
Promise.all(res.map(resolveNote)).then((a) => {
|
Promise.all(res.map(resolveNote)).then((a) => {
|
||||||
replies.value = a;
|
replies.value = a;
|
||||||
directReplies.value = a
|
directReplies.value = a
|
||||||
.filter((resNote) => resNote.parent_note_id === appearNote.id)
|
.filter((resNote) => resNote.parent_note_id === appearNote.value.id)
|
||||||
.reverse();
|
.reverse();
|
||||||
directQuotes.value = a.filter(
|
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", {
|
os.api("notes/conversation", {
|
||||||
noteId: appearNote.parent_note_id,
|
noteId: appearNote.value.parent_note_id,
|
||||||
limit: 30,
|
limit: 30,
|
||||||
}).then(async (res) => {
|
}).then(async (res) => {
|
||||||
conversation.value = await Promise.all<packed.PackNoteMaybeFull>(
|
conversation.value = await Promise.all<packed.PackNoteMaybeFull>(
|
||||||
res?.reverse().map(resolveNote)
|
res?.reverse().map(resolveNote),
|
||||||
);
|
);
|
||||||
focus();
|
focus();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
os.api("notes/clips", {
|
os.api("notes/clips", {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
clips.value = res as misskey.entities.Clip[];
|
clips.value = res as misskey.entities.Clip[];
|
||||||
});
|
});
|
||||||
|
@ -389,9 +397,9 @@ os.api("notes/clips", {
|
||||||
// const pagingComponent = $ref<InstanceType<typeof MkPagination>>();
|
// const pagingComponent = $ref<InstanceType<typeof MkPagination>>();
|
||||||
|
|
||||||
function loadTab() {
|
function loadTab() {
|
||||||
if (tab === "renotes" && !renotes.value) {
|
if (tab.value === "renotes" && !renotes.value) {
|
||||||
os.api("notes/renotes", {
|
os.api("notes/renotes", {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
limit: 100,
|
limit: 100,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
renotes.value = res;
|
renotes.value = res;
|
||||||
|
@ -403,7 +411,7 @@ async function onNoteUpdated(noteData: NoteUpdatedEvent): Promise<void> {
|
||||||
const { type, id, body } = noteData;
|
const { type, id, body } = noteData;
|
||||||
|
|
||||||
let found = -1;
|
let found = -1;
|
||||||
if (id === appearNote.id) {
|
if (id === appearNote.value.id) {
|
||||||
found = 0;
|
found = 0;
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < replies.value.length; i++) {
|
for (let i = 0; i < replies.value.length; i++) {
|
||||||
|
@ -430,7 +438,7 @@ async function onNoteUpdated(noteData: NoteUpdatedEvent): Promise<void> {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: createdId,
|
id: createdId,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
replies.value.splice(found, 0, replyNote);
|
replies.value.splice(found, 0, replyNote);
|
||||||
|
@ -566,7 +574,9 @@ onUnmounted(() => {
|
||||||
background: var(--panelHighlight);
|
background: var(--panelHighlight);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.2s, background 0.2s;
|
transition:
|
||||||
|
opacity 0.2s,
|
||||||
|
background 0.2s;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
}
|
}
|
||||||
&.reply-to {
|
&.reply-to {
|
||||||
|
|
|
@ -23,10 +23,10 @@
|
||||||
v-tooltip.noDelay="
|
v-tooltip.noDelay="
|
||||||
i18n.t('edited', {
|
i18n.t('edited', {
|
||||||
date: new Date(
|
date: new Date(
|
||||||
note.updated_at
|
note.updated_at,
|
||||||
).toLocaleDateString(),
|
).toLocaleDateString(),
|
||||||
time: new Date(
|
time: new Date(
|
||||||
note.updated_at
|
note.updated_at,
|
||||||
).toLocaleTimeString(),
|
).toLocaleTimeString(),
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
|
|
|
@ -39,6 +39,6 @@ watch(
|
||||||
noteData.value = n;
|
noteData.value = n;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -15,7 +15,7 @@ import XNoteHeader from "@/components/MagNoteHeader.vue";
|
||||||
import MagSubNoteContent from "@/components/MagSubNoteContent.vue";
|
import MagSubNoteContent from "@/components/MagSubNoteContent.vue";
|
||||||
import { packed } from "magnetar-common";
|
import { packed } from "magnetar-common";
|
||||||
|
|
||||||
const props = defineProps<{
|
defineProps<{
|
||||||
note: packed.PackNoteMaybeFull;
|
note: packed.PackNoteMaybeFull;
|
||||||
pinned?: boolean;
|
pinned?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
ref="el"
|
ref="el"
|
||||||
v-size="{ max: [450, 500] }"
|
v-size="{ max: [450, 500] }"
|
||||||
class="wrpstxzv"
|
class="wrpstxzv"
|
||||||
:id="detailedView ? appearNote.id : null"
|
:id="detailedView ? appearNote.id : undefined"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
:class="{
|
:class="{
|
||||||
children: depth > 1,
|
children: depth > 1,
|
||||||
|
@ -170,7 +170,7 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { Ref } from "vue";
|
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 * as misskey from "calckey-js";
|
||||||
import XNoteHeader from "@/components/MagNoteHeader.vue";
|
import XNoteHeader from "@/components/MagNoteHeader.vue";
|
||||||
import XSubNoteContent from "@/components/MagSubNoteContent.vue";
|
import XSubNoteContent from "@/components/MagSubNoteContent.vue";
|
||||||
|
@ -219,18 +219,11 @@ const props = withDefaults(
|
||||||
{
|
{
|
||||||
depth: 1,
|
depth: 1,
|
||||||
replyLevel: 1,
|
replyLevel: 1,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let noteCpy = $ref<packed.PackNoteMaybeFull>(
|
let noteCpy = computed<packed.PackNoteMaybeFull>(() =>
|
||||||
structuredClone(toRaw(props.note))
|
structuredClone(toRaw(props.note)),
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.note,
|
|
||||||
(val) => {
|
|
||||||
noteCpy = structuredClone(toRaw(val));
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const softMuteReasonI18nSrc = (what?: string) => {
|
const softMuteReasonI18nSrc = (what?: string) => {
|
||||||
|
@ -249,19 +242,21 @@ const menuButton = ref<HTMLElement | null>(null);
|
||||||
const starButton = ref<InstanceType<typeof XStarButton>>();
|
const starButton = ref<InstanceType<typeof XStarButton>>();
|
||||||
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
||||||
const reactButton = ref<HTMLElement | null>(null);
|
const reactButton = ref<HTMLElement | null>(null);
|
||||||
let appearNote = $computed(
|
let appearNote = computed(
|
||||||
() => magEffectiveNote(noteCpy) as packed.PackNoteMaybeFull
|
() => magEffectiveNote(noteCpy.value) as packed.PackNoteMaybeFull,
|
||||||
);
|
);
|
||||||
const isDeleted = ref(false);
|
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 translation = ref(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
const replies: packed.PackNoteMaybeFull[] =
|
const replies: packed.PackNoteMaybeFull[] =
|
||||||
props.conversation
|
props.conversation
|
||||||
?.filter(
|
?.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
item.parent_note_id === appearNote.id ||
|
item.parent_note_id === appearNote.value.id ||
|
||||||
item.renoted_note_id === appearNote.id
|
item.renoted_note_id === appearNote.value.id,
|
||||||
)
|
)
|
||||||
.reverse() ?? [];
|
.reverse() ?? [];
|
||||||
const enableEmojiReactions = defaultStore.state.enableEmojiReactions;
|
const enableEmojiReactions = defaultStore.state.enableEmojiReactions;
|
||||||
|
@ -269,7 +264,7 @@ const expandOnNoteClick = defaultStore.state.expandOnNoteClick;
|
||||||
|
|
||||||
useNoteCapture({
|
useNoteCapture({
|
||||||
rootEl: el as Ref<HTMLElement>,
|
rootEl: el as Ref<HTMLElement>,
|
||||||
note: $$(appearNote),
|
note: appearNote,
|
||||||
isDeletedRef: isDeleted,
|
isDeletedRef: isDeleted,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -290,13 +285,13 @@ function react(viaKeyboard = false): void {
|
||||||
reactButton.value,
|
reactButton.value,
|
||||||
(reaction) => {
|
(reaction) => {
|
||||||
os.api("notes/reactions/create", {
|
os.api("notes/reactions/create", {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.value.id,
|
||||||
reaction: magReactionToLegacy(reaction),
|
reaction: magReactionToLegacy(reaction),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
focus();
|
focus();
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,13 +305,13 @@ function undoReact(note: packed.PackNoteBase): void {
|
||||||
|
|
||||||
const currentClipPage = inject<Ref<misskey.entities.Clip> | null>(
|
const currentClipPage = inject<Ref<misskey.entities.Clip> | null>(
|
||||||
"currentClipPage",
|
"currentClipPage",
|
||||||
null
|
null,
|
||||||
);
|
);
|
||||||
|
|
||||||
function menu(viaKeyboard = false): void {
|
function menu(viaKeyboard = false): void {
|
||||||
os.popupMenu(
|
os.popupMenu(
|
||||||
getNoteMenu({
|
getNoteMenu({
|
||||||
note: noteCpy,
|
note: noteCpy.value,
|
||||||
translating,
|
translating,
|
||||||
translation,
|
translation,
|
||||||
menuButton: menuButton.value,
|
menuButton: menuButton.value,
|
||||||
|
@ -326,7 +321,7 @@ function menu(viaKeyboard = false): void {
|
||||||
menuButton.value ?? undefined,
|
menuButton.value ?? undefined,
|
||||||
{
|
{
|
||||||
viaKeyboard,
|
viaKeyboard,
|
||||||
}
|
},
|
||||||
).then(focus);
|
).then(focus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,21 +345,24 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
type: "label",
|
type: "label",
|
||||||
text: notePage(appearNote),
|
text: notePage(appearNote.value),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-browser ph-bold ph-lg",
|
icon: "ph-browser ph-bold ph-lg",
|
||||||
text: i18n.ts.openInWindow,
|
text: i18n.ts.openInWindow,
|
||||||
action: () => {
|
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",
|
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
||||||
text: i18n.ts.showInPage,
|
text: i18n.ts.showInPage,
|
||||||
action: () => {
|
action: () => {
|
||||||
router.push(notePage(appearNote), "forcePage");
|
router.push(
|
||||||
|
notePage(appearNote.value),
|
||||||
|
"forcePage",
|
||||||
|
);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
@ -373,27 +371,27 @@ function onContextmenu(ev: MouseEvent): void {
|
||||||
type: "a",
|
type: "a",
|
||||||
icon: "ph-arrow-square-out ph-bold ph-lg",
|
icon: "ph-arrow-square-out ph-bold ph-lg",
|
||||||
text: i18n.ts.openInNewTab,
|
text: i18n.ts.openInNewTab,
|
||||||
href: notePage(appearNote),
|
href: notePage(appearNote.value),
|
||||||
target: "_blank",
|
target: "_blank",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-link-simple ph-bold ph-lg",
|
icon: "ph-link-simple ph-bold ph-lg",
|
||||||
text: i18n.ts.copyLink,
|
text: i18n.ts.copyLink,
|
||||||
action: () => {
|
action: () => {
|
||||||
copyToClipboard(`${url}${notePage(appearNote)}`);
|
copyToClipboard(`${url}${notePage(appearNote.value)}`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
noteCpy.user.host != null
|
noteCpy.value.user.host != null
|
||||||
? {
|
? {
|
||||||
type: "a",
|
type: "a",
|
||||||
icon: "ph-arrow-square-up-right ph-bold ph-lg",
|
icon: "ph-arrow-square-up-right ph-bold ph-lg",
|
||||||
text: i18n.ts.showOnRemote,
|
text: i18n.ts.showOnRemote,
|
||||||
href: noteCpy.url ?? noteCpy.uri ?? "",
|
href: noteCpy.value.url ?? noteCpy.value.uri ?? "",
|
||||||
target: "_blank",
|
target: "_blank",
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
],
|
],
|
||||||
ev
|
ev,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,7 +274,7 @@ const props = withDefaults(
|
||||||
{
|
{
|
||||||
withTime: false,
|
withTime: false,
|
||||||
full: false,
|
full: false,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits(["refresh"]);
|
const emit = defineEmits(["refresh"]);
|
||||||
|
@ -310,7 +310,7 @@ onMounted(() => {
|
||||||
() => props.notification.is_read,
|
() => props.notification.is_read,
|
||||||
() => {
|
() => {
|
||||||
readObserver!.disconnect();
|
readObserver!.disconnect();
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -333,7 +333,7 @@ useTooltip(reactionRef, (showing) => {
|
||||||
targetElement: reactionRef.value.$el,
|
targetElement: reactionRef.value.$el,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
"closed"
|
"closed",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -143,7 +143,7 @@ const props = withDefaults(
|
||||||
{
|
{
|
||||||
withTime: false,
|
withTime: false,
|
||||||
full: false,
|
full: false,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const firstNotification = ref(props.notificationGroup.value[0]);
|
const firstNotification = ref(props.notificationGroup.value[0]);
|
||||||
|
@ -152,7 +152,7 @@ watch(
|
||||||
(n) => {
|
(n) => {
|
||||||
firstNotification.value = n;
|
firstNotification.value = n;
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits(["refresh"]);
|
const emit = defineEmits(["refresh"]);
|
||||||
|
@ -175,7 +175,7 @@ onMounted(() => {
|
||||||
if (!entries.some((entry) => entry.isIntersecting)) return;
|
if (!entries.some((entry) => entry.isIntersecting)) return;
|
||||||
stream.send(
|
stream.send(
|
||||||
"readNotifications",
|
"readNotifications",
|
||||||
props.notificationGroup.value.map((n) => n.id)
|
props.notificationGroup.value.map((n) => n.id),
|
||||||
);
|
);
|
||||||
observer.disconnect();
|
observer.disconnect();
|
||||||
});
|
});
|
||||||
|
@ -189,7 +189,7 @@ onMounted(() => {
|
||||||
() => firstNotification.value.is_read,
|
() => firstNotification.value.is_read,
|
||||||
() => {
|
() => {
|
||||||
readObserver!.disconnect();
|
readObserver!.disconnect();
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -201,7 +201,7 @@ onUnmounted(() => {
|
||||||
|
|
||||||
const hookTooltip = (
|
const hookTooltip = (
|
||||||
el: HTMLElement | ComponentPublicInstance | null,
|
el: HTMLElement | ComponentPublicInstance | null,
|
||||||
notif: packed.PackNotification
|
notif: packed.PackNotification,
|
||||||
) => {
|
) => {
|
||||||
if (el == null) return;
|
if (el == null) return;
|
||||||
|
|
||||||
|
@ -219,7 +219,7 @@ const hookTooltip = (
|
||||||
"$el" in elRef.value ? elRef.value.$el : elRef.value,
|
"$el" in elRef.value ? elRef.value.$el : elRef.value,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
"closed"
|
"closed",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {} from "vue";
|
import { computed, ref } from "vue";
|
||||||
import MkSwitch from "./form/switch.vue";
|
import MkSwitch from "./form/switch.vue";
|
||||||
import MkInfo from "./MkInfo.vue";
|
import MkInfo from "./MkInfo.vue";
|
||||||
import MkButton from "./MkButton.vue";
|
import MkButton from "./MkButton.vue";
|
||||||
|
@ -60,20 +60,20 @@ const props = withDefaults(
|
||||||
{
|
{
|
||||||
includingTypes: () => notificationTypes,
|
includingTypes: () => notificationTypes,
|
||||||
showGlobalToggle: true,
|
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(
|
Object.fromEntries(
|
||||||
notificationTypes.map((n) => [n, includingTypes.includes(n)])
|
notificationTypes.map((n) => [n, includingTypes.value.includes(n)]),
|
||||||
) as Record<types.NotificationType, boolean>
|
) as Record<types.NotificationType, boolean>,
|
||||||
);
|
);
|
||||||
|
|
||||||
let useGlobalSetting = $ref(includingTypes === null || props.showGlobalToggle);
|
let useGlobalSetting = ref(includingTypes === null || props.showGlobalToggle);
|
||||||
|
|
||||||
function ok() {
|
function ok() {
|
||||||
if (useGlobalSetting) {
|
if (useGlobalSetting) {
|
||||||
|
@ -84,7 +84,7 @@ function ok() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog?.close();
|
dialog.value?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function disableAll() {
|
function disableAll() {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import XNotification from "@/components/MagNotification.vue";
|
import XNotification from "@/components/MagNotification.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
|
||||||
|
@ -28,11 +28,11 @@ const emit = defineEmits<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const zIndex = os.claimZIndex("high");
|
const zIndex = os.claimZIndex("high");
|
||||||
let showing = $ref(true);
|
let showing = ref(true);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
showing = false;
|
showing.value = false;
|
||||||
}, 6000);
|
}, 6000);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -40,7 +40,9 @@ onMounted(() => {
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.notification-toast-enter-active,
|
.notification-toast-enter-active,
|
||||||
.notification-toast-leave-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-enter-from,
|
||||||
.notification-toast-leave-to {
|
.notification-toast-leave-to {
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
v-slot="{
|
v-slot="{
|
||||||
item: notificationGroup,
|
item: notificationGroup,
|
||||||
}: {
|
}: {
|
||||||
item: NotificationGroup,
|
item: NotificationGroup;
|
||||||
}"
|
}"
|
||||||
class="elsfgstc"
|
class="elsfgstc"
|
||||||
:items="items"
|
:items="items"
|
||||||
|
@ -140,7 +140,7 @@ const allowedGroups: types.NotificationType[] = ["Reaction", "Renote"];
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.includeTypes,
|
() => props.includeTypes,
|
||||||
() => reload()
|
() => reload(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onNotification = (notification: packed.PackNotification) => {
|
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
|
// 5. [MAYBE] Items that would be pushed out by another item CAN break boundaries, but only with adjacent items
|
||||||
|
|
||||||
const groupIntra = (
|
const groupIntra = (
|
||||||
notifications: packed.PackNotification[]
|
notifications: packed.PackNotification[],
|
||||||
): NotificationGroup[] => {
|
): NotificationGroup[] => {
|
||||||
const x = notifications
|
const x = notifications
|
||||||
.map(
|
.map(
|
||||||
|
@ -235,13 +235,13 @@ const groupIntra = (
|
||||||
id: n.id,
|
id: n.id,
|
||||||
created_at: n.created_at,
|
created_at: n.created_at,
|
||||||
value: n,
|
value: n,
|
||||||
} as NotificationGroup)
|
}) as NotificationGroup,
|
||||||
)
|
)
|
||||||
.map((g) => [g])
|
.map((g) => [g])
|
||||||
.reduce(
|
.reduce(
|
||||||
(
|
(
|
||||||
prev: NotificationGroup[],
|
prev: NotificationGroup[],
|
||||||
[curr]: NotificationGroup[]
|
[curr]: NotificationGroup[],
|
||||||
): NotificationGroup[] => {
|
): NotificationGroup[] => {
|
||||||
const currGroup = curr as NotificationGroup & {
|
const currGroup = curr as NotificationGroup & {
|
||||||
type: "single";
|
type: "single";
|
||||||
|
@ -295,7 +295,7 @@ const groupIntra = (
|
||||||
magEffectiveNote(n.note).id ===
|
magEffectiveNote(n.note).id ===
|
||||||
magEffectiveNote(currNoteGroup.value.note)
|
magEffectiveNote(currNoteGroup.value.note)
|
||||||
.id &&
|
.id &&
|
||||||
n.type === currNoteGroup.value.type
|
n.type === currNoteGroup.value.type,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
p.value = [...p.value, currNoteGroup.value];
|
p.value = [...p.value, currNoteGroup.value];
|
||||||
|
@ -317,14 +317,14 @@ const groupIntra = (
|
||||||
|
|
||||||
return [...prev, currGroup];
|
return [...prev, currGroup];
|
||||||
},
|
},
|
||||||
[] as NotificationGroup[]
|
[] as NotificationGroup[],
|
||||||
);
|
);
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetch = async (
|
const fetch = async (
|
||||||
pagination: SpanFilter
|
pagination: SpanFilter,
|
||||||
): Promise<PaginatedResult<packed.PackNotification[]>> => {
|
): Promise<PaginatedResult<packed.PackNotification[]>> => {
|
||||||
return os.magApi(
|
return os.magApi(
|
||||||
endpoints.GetNotifications,
|
endpoints.GetNotifications,
|
||||||
|
@ -338,7 +338,7 @@ const fetch = async (
|
||||||
limit: displayLimit,
|
limit: displayLimit,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
undefined
|
undefined,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -356,7 +356,7 @@ const init = async (): Promise<void> => {
|
||||||
(err) => {
|
(err) => {
|
||||||
error.value = true;
|
error.value = true;
|
||||||
initialFetching.value = false;
|
initialFetching.value = false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -390,7 +390,7 @@ const fetchMore = async (): Promise<void> => {
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
moreFetching.value = false;
|
moreFetching.value = false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -437,7 +437,7 @@ const prepend = (item: packed.PackNotification): void => {
|
||||||
"note" in n &&
|
"note" in n &&
|
||||||
magEffectiveNote(n.note).id ===
|
magEffectiveNote(n.note).id ===
|
||||||
magEffectiveNote(currNotification.note).id &&
|
magEffectiveNote(currNotification.note).id &&
|
||||||
n.type === currNotification.type
|
n.type === currNotification.type,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -522,7 +522,7 @@ watch(
|
||||||
if (a.length === 0 && b.length === 0) return;
|
if (a.length === 0 && b.length === 0) return;
|
||||||
emit("queue", queue.value.length);
|
emit("queue", queue.value.length);
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
|
|
@ -65,13 +65,15 @@
|
||||||
<script
|
<script
|
||||||
lang="ts"
|
lang="ts"
|
||||||
setup
|
setup
|
||||||
generic="T extends BackendApiEndpoint<
|
generic="
|
||||||
T['method'] & Method,
|
T extends BackendApiEndpoint<
|
||||||
T['pathParams'] & string[],
|
T['method'] & Method,
|
||||||
T['request'],
|
T['pathParams'] & string[],
|
||||||
T['response'],
|
T['request'],
|
||||||
T['paginated'] & true
|
T['response'],
|
||||||
>"
|
T['paginated'] & true
|
||||||
|
>
|
||||||
|
"
|
||||||
>
|
>
|
||||||
import { computed, isRef, onActivated, onDeactivated, ref, watch } from "vue";
|
import { computed, isRef, onActivated, onDeactivated, ref, watch } from "vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
@ -98,7 +100,7 @@ export type PathParams<
|
||||||
P["request"],
|
P["request"],
|
||||||
P["response"],
|
P["response"],
|
||||||
P["paginated"] & true
|
P["paginated"] & true
|
||||||
>
|
>,
|
||||||
> = {
|
> = {
|
||||||
[K in keyof P["pathParams"] as P["pathParams"][K] & string]:
|
[K in keyof P["pathParams"] as P["pathParams"][K] & string]:
|
||||||
| string
|
| string
|
||||||
|
@ -112,7 +114,7 @@ export type Paging<
|
||||||
P["request"],
|
P["request"],
|
||||||
P["response"],
|
P["response"],
|
||||||
P["paginated"] & true
|
P["paginated"] & true
|
||||||
>
|
>,
|
||||||
> = {
|
> = {
|
||||||
endpoint: P;
|
endpoint: P;
|
||||||
pathParams: PathParams<P>;
|
pathParams: PathParams<P>;
|
||||||
|
@ -131,7 +133,7 @@ const props = withDefaults(
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
displayLimit: 30,
|
displayLimit: 30,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -152,7 +154,7 @@ const empty = computed(() => items.value.length === 0);
|
||||||
const error = ref(false);
|
const error = ref(false);
|
||||||
|
|
||||||
const fetch = async (
|
const fetch = async (
|
||||||
pagination: SpanFilter
|
pagination: SpanFilter,
|
||||||
): Promise<PaginatedResult<T["response"]> & { data: { id: string } }[]> => {
|
): Promise<PaginatedResult<T["response"]> & { data: { id: string } }[]> => {
|
||||||
const pathParams = props.pagination.pathParams;
|
const pathParams = props.pagination.pathParams;
|
||||||
const params = props.pagination.params;
|
const params = props.pagination.params;
|
||||||
|
@ -166,12 +168,12 @@ const fetch = async (
|
||||||
limit: props.pagination.limit,
|
limit: props.pagination.limit,
|
||||||
},
|
},
|
||||||
pathParams,
|
pathParams,
|
||||||
undefined
|
undefined,
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
(res) =>
|
(res) =>
|
||||||
res as PaginatedResult<T["response"]> &
|
res as PaginatedResult<T["response"]> &
|
||||||
{ data: { id: string } }[]
|
{ data: { id: string } }[],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -191,7 +193,7 @@ const init = async (): Promise<void> => {
|
||||||
(err) => {
|
(err) => {
|
||||||
error.value = true;
|
error.value = true;
|
||||||
initialFetching.value = false;
|
initialFetching.value = false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -222,7 +224,7 @@ const refresh = async (): Promise<void> => {
|
||||||
(err) => {
|
(err) => {
|
||||||
error.value = true;
|
error.value = true;
|
||||||
initialFetching.value = false;
|
initialFetching.value = false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -254,7 +256,7 @@ const fetchMore = async (): Promise<void> => {
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
moreFetching.value = false;
|
moreFetching.value = false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -301,7 +303,7 @@ const prepend = (item: PageItem): void => {
|
||||||
queue.value = [];
|
queue.value = [];
|
||||||
items.value = [...queueRemoved, ...items.value].slice(
|
items.value = [...queueRemoved, ...items.value].slice(
|
||||||
0,
|
0,
|
||||||
props.displayLimit
|
props.displayLimit,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -327,7 +329,7 @@ const removeItem = (finder: (item: PageItem) => boolean): boolean => {
|
||||||
|
|
||||||
const updateItem = (
|
const updateItem = (
|
||||||
id: PageItem["id"],
|
id: PageItem["id"],
|
||||||
replacer: (old: PageItem) => PageItem
|
replacer: (old: PageItem) => PageItem,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
const i = items.value.findIndex((item) => item.id === id);
|
const i = items.value.findIndex((item) => item.id === id);
|
||||||
if (i === -1) {
|
if (i === -1) {
|
||||||
|
@ -348,7 +350,7 @@ watch(
|
||||||
if (a.length === 0 && b.length === 0) return;
|
if (a.length === 0 && b.length === 0) return;
|
||||||
emit("queue", queue.value.length);
|
emit("queue", queue.value.length);
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
|
|
@ -77,31 +77,31 @@ const pollRefreshing = ref(false);
|
||||||
const remaining = ref(-1);
|
const remaining = ref(-1);
|
||||||
|
|
||||||
const total = computed(() =>
|
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 closed = computed(() => remaining.value === 0);
|
||||||
const isLocal = computed(() => !props.note.uri);
|
const isLocal = computed(() => !props.note.uri);
|
||||||
const isVoted = computed(
|
const isVoted = computed(
|
||||||
() =>
|
() =>
|
||||||
!props.note.poll.multiple_choice &&
|
!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(() =>
|
const timer = computed(() =>
|
||||||
i18n.t(
|
i18n.t(
|
||||||
remaining.value >= 86400
|
remaining.value >= 86400
|
||||||
? "_poll.remainingDays"
|
? "_poll.remainingDays"
|
||||||
: remaining.value >= 3600
|
: remaining.value >= 3600
|
||||||
? "_poll.remainingHours"
|
? "_poll.remainingHours"
|
||||||
: remaining.value >= 60
|
: remaining.value >= 60
|
||||||
? "_poll.remainingMinutes"
|
? "_poll.remainingMinutes"
|
||||||
: "_poll.remainingSeconds",
|
: "_poll.remainingSeconds",
|
||||||
{
|
{
|
||||||
s: Math.floor(remaining.value % 60),
|
s: Math.floor(remaining.value % 60),
|
||||||
m: Math.floor(remaining.value / 60) % 60,
|
m: Math.floor(remaining.value / 60) % 60,
|
||||||
h: Math.floor(remaining.value / 3600) % 24,
|
h: Math.floor(remaining.value / 3600) % 24,
|
||||||
d: Math.floor(remaining.value / 86400),
|
d: Math.floor(remaining.value / 86400),
|
||||||
}
|
},
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const showResult = ref(props.readOnly || isVoted.value);
|
const showResult = ref(props.readOnly || isVoted.value);
|
||||||
|
@ -112,8 +112,8 @@ if (props.note.poll.expires_at) {
|
||||||
remaining.value = Math.floor(
|
remaining.value = Math.floor(
|
||||||
Math.max(
|
Math.max(
|
||||||
new Date(props.note.poll.expires_at!).getTime() - Date.now(),
|
new Date(props.note.poll.expires_at!).getTime() - Date.now(),
|
||||||
0
|
0,
|
||||||
) / 1000
|
) / 1000,
|
||||||
);
|
);
|
||||||
if (remaining.value === 0) {
|
if (remaining.value === 0) {
|
||||||
showResult.value = true;
|
showResult.value = true;
|
||||||
|
@ -137,7 +137,7 @@ async function refresh() {
|
||||||
os.magApi(
|
os.magApi(
|
||||||
endpoints.GetNoteById,
|
endpoints.GetNoteById,
|
||||||
{ attachments: true },
|
{ attachments: true },
|
||||||
{ id: obj.object.id }
|
{ id: obj.object.id },
|
||||||
).then((n) => {
|
).then((n) => {
|
||||||
props.note.poll = { ...toRaw(props.note.poll), ...n.poll };
|
props.note.poll = { ...toRaw(props.note.poll), ...n.poll };
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,7 +24,7 @@ const props = defineProps<{
|
||||||
const canRenote = computed(
|
const canRenote = computed(
|
||||||
() =>
|
() =>
|
||||||
["public", "home", "Public", "Home"].includes(props.note.visibility) ||
|
["public", "home", "Public", "Home"].includes(props.note.visibility) ||
|
||||||
props.note.user.id === $i?.id
|
props.note.user.id === $i?.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
function quote(): void {
|
function quote(): void {
|
||||||
|
|
|
@ -48,7 +48,7 @@ const hasRenotedBefore = ref<boolean>((props.note.self_renote_count ?? 0) > 0);
|
||||||
const canRenote = computed(
|
const canRenote = computed(
|
||||||
() =>
|
() =>
|
||||||
["Public", "Home"].includes(props.note.visibility) ||
|
["Public", "Home"].includes(props.note.visibility) ||
|
||||||
($i && props.note.user.id === $i.id)
|
($i && props.note.user.id === $i.id),
|
||||||
);
|
);
|
||||||
|
|
||||||
useTooltip(buttonRef, async (showing) => {
|
useTooltip(buttonRef, async (showing) => {
|
||||||
|
@ -70,7 +70,7 @@ useTooltip(buttonRef, async (showing) => {
|
||||||
targetElement: buttonRef.value,
|
targetElement: buttonRef.value,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
"closed"
|
"closed",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -198,7 +198,7 @@ const renote = async (viaKeyboard = false, ev?: MouseEvent) => {
|
||||||
? {
|
? {
|
||||||
renoteId: props.note.id,
|
renoteId: props.note.id,
|
||||||
visibility: magLegacyVisibility(
|
visibility: magLegacyVisibility(
|
||||||
props.note.visibility
|
props.note.visibility,
|
||||||
),
|
),
|
||||||
visibleUserIds: props.note.visible_user_ids ?? [],
|
visibleUserIds: props.note.visible_user_ids ?? [],
|
||||||
localOnly: true,
|
localOnly: true,
|
||||||
|
@ -206,10 +206,10 @@ const renote = async (viaKeyboard = false, ev?: MouseEvent) => {
|
||||||
: {
|
: {
|
||||||
renoteId: props.note.id,
|
renoteId: props.note.id,
|
||||||
visibility: magLegacyVisibility(
|
visibility: magLegacyVisibility(
|
||||||
props.note.visibility
|
props.note.visibility,
|
||||||
),
|
),
|
||||||
localOnly: true,
|
localOnly: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
hasRenotedBefore.value = true;
|
hasRenotedBefore.value = true;
|
||||||
const el =
|
const el =
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { MagChannelState } from "magnetar-common";
|
||||||
import { onMounted, onUnmounted, ref } from "vue";
|
import { onMounted, onUnmounted, ref } from "vue";
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
showConnected?: boolean
|
showConnected?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const state = ref<MagChannelState>(magStream.state);
|
const state = ref<MagChannelState>(magStream.state);
|
||||||
|
@ -65,7 +65,8 @@ const emit = defineEmits<{
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
||||||
&.closed, &.failed {
|
&.closed,
|
||||||
|
&.failed {
|
||||||
background-color: var(--infoWarnBg);
|
background-color: var(--infoWarnBg);
|
||||||
color: var(--error);
|
color: var(--error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,23 +218,23 @@ const isLong =
|
||||||
(props.note.text.split("\n").length > 10 ||
|
(props.note.text.split("\n").length > 10 ||
|
||||||
props.note.text.length > 800)) ||
|
props.note.text.length > 800)) ||
|
||||||
(props.note.attachments ?? []).length > 4);
|
(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
|
const urls = props.note.text
|
||||||
? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5)
|
? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
let showContent = $ref(false);
|
let showContent = ref(false);
|
||||||
|
|
||||||
const mfms = props.note.text
|
const mfms = props.note.text
|
||||||
? extractMfmWithAnimation(mfm.parse(props.note.text))
|
? extractMfmWithAnimation(mfm.parse(props.note.text))
|
||||||
: null;
|
: 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() {
|
async function toggleMfm() {
|
||||||
if (disableMfm) {
|
if (disableMfm.value) {
|
||||||
if (!defaultStore.state.animatedMfmWarnShown) {
|
if (!defaultStore.state.animatedMfmWarnShown) {
|
||||||
const { canceled } = await os.confirm({
|
const { canceled } = await os.confirm({
|
||||||
type: "warning",
|
type: "warning",
|
||||||
|
@ -245,9 +245,9 @@ async function toggleMfm() {
|
||||||
defaultStore.set("animatedMfmWarnShown", true);
|
defaultStore.set("animatedMfmWarnShown", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
disableMfm = false;
|
disableMfm.value = false;
|
||||||
} else {
|
} else {
|
||||||
disableMfm = true;
|
disableMfm.value = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,7 +127,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted } from "vue";
|
import { computed, onMounted, ref } from "vue";
|
||||||
import MkFollowButton from "@/components/MkFollowButton.vue";
|
import MkFollowButton from "@/components/MkFollowButton.vue";
|
||||||
import { userPage } from "@/filters/user";
|
import { userPage } from "@/filters/user";
|
||||||
import XShowMoreButton from "@/components/MkShowMoreButton.vue";
|
import XShowMoreButton from "@/components/MkShowMoreButton.vue";
|
||||||
|
@ -151,18 +151,19 @@ const emit = defineEmits<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const zIndex = os.claimZIndex("middle");
|
const zIndex = os.claimZIndex("middle");
|
||||||
let user = $ref<packed.PackUserMaybeAll | null>(null);
|
let user = ref<packed.PackUserMaybeAll | null>(null);
|
||||||
let top = $ref(0);
|
let top = ref(0);
|
||||||
let left = $ref(0);
|
let left = ref(0);
|
||||||
|
|
||||||
let isLong = computed(() => {
|
let isLong = computed(() => {
|
||||||
if (!user?.description) return;
|
if (!user.value?.description) return;
|
||||||
|
|
||||||
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(() => {
|
onMounted(() => {
|
||||||
const options = { detail: true, profile: true, relation: true };
|
const options = { detail: true, profile: true, relation: true };
|
||||||
|
@ -176,7 +177,7 @@ onMounted(() => {
|
||||||
os.magApi(endpoints.GetUserByAcct, options, {
|
os.magApi(endpoints.GetUserByAcct, options, {
|
||||||
user_acct: canonical,
|
user_acct: canonical,
|
||||||
}).then((u) => {
|
}).then((u) => {
|
||||||
user = u;
|
user.value = u;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const acctQuery = props.userTag.startsWith("@");
|
const acctQuery = props.userTag.startsWith("@");
|
||||||
|
@ -189,19 +190,22 @@ onMounted(() => {
|
||||||
user_id: props.userTag,
|
user_id: props.userTag,
|
||||||
});
|
});
|
||||||
|
|
||||||
apiCall.then((u) => (user = u));
|
apiCall.then((u) => (user.value = u));
|
||||||
}
|
}
|
||||||
|
|
||||||
const rect = props.source.getBoundingClientRect();
|
const rect = props.source.getBoundingClientRect();
|
||||||
left = rect.left + props.source.offsetWidth / 2 - 300 / 2 + window.scrollX;
|
left.value =
|
||||||
top = rect.top + props.source.offsetHeight + window.scrollY;
|
rect.left + props.source.offsetWidth / 2 - 300 / 2 + window.scrollX;
|
||||||
|
top.value = rect.top + props.source.offsetHeight + window.scrollY;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.popup-enter-active,
|
.popup-enter-active,
|
||||||
.popup-leave-active {
|
.popup-leave-active {
|
||||||
transition: opacity 0.3s, transform 0.3s !important;
|
transition:
|
||||||
|
opacity 0.3s,
|
||||||
|
transform 0.3s !important;
|
||||||
}
|
}
|
||||||
.popup-enter-from,
|
.popup-enter-from,
|
||||||
.popup-leave-to {
|
.popup-leave-to {
|
||||||
|
|
|
@ -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>
|
|
@ -70,6 +70,7 @@ import MkSwitch from "@/components/form/switch.vue";
|
||||||
import MkKeyValue from "@/components/MkKeyValue.vue";
|
import MkKeyValue from "@/components/MkKeyValue.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
report: any;
|
report: any;
|
||||||
|
@ -79,11 +80,11 @@ const emit = defineEmits<{
|
||||||
(ev: "resolved", reportId: string): void;
|
(ev: "resolved", reportId: string): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let forward = $ref(props.report.forwarded);
|
let forward = ref(props.report.forwarded);
|
||||||
|
|
||||||
function resolve() {
|
function resolve() {
|
||||||
os.apiWithDialog("admin/resolve-abuse-user-report", {
|
os.apiWithDialog("admin/resolve-abuse-user-report", {
|
||||||
forward: forward,
|
forward: forward.value,
|
||||||
reportId: props.report.id,
|
reportId: props.report.id,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
emit("resolved", props.report.id);
|
emit("resolved", props.report.id);
|
||||||
|
|
|
@ -67,7 +67,7 @@ function send() {
|
||||||
userId: props.user.id,
|
userId: props.user.id,
|
||||||
comment: comment.value,
|
comment: comment.value,
|
||||||
},
|
},
|
||||||
undefined
|
undefined,
|
||||||
).then((res) => {
|
).then((res) => {
|
||||||
os.alert({
|
os.alert({
|
||||||
type: "success",
|
type: "success",
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
0,
|
0,
|
||||||
1 -
|
1 -
|
||||||
angleDiff(hAngle, angle) / Math.PI -
|
angleDiff(hAngle, angle) / Math.PI -
|
||||||
numbersOpacityFactor
|
numbersOpacityFactor,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
0,
|
0,
|
||||||
1 -
|
1 -
|
||||||
angleDiff(hAngle, angle) / Math.PI -
|
angleDiff(hAngle, angle) / Math.PI -
|
||||||
numbersOpacityFactor
|
numbersOpacityFactor,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
|
@ -108,14 +108,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {
|
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
|
||||||
ref,
|
|
||||||
computed,
|
|
||||||
onMounted,
|
|
||||||
onBeforeUnmount,
|
|
||||||
shallowRef,
|
|
||||||
nextTick,
|
|
||||||
} from "vue";
|
|
||||||
import tinycolor from "tinycolor2";
|
import tinycolor from "tinycolor2";
|
||||||
import { globalEvents } from "@/events.js";
|
import { globalEvents } from "@/events.js";
|
||||||
|
|
||||||
|
@ -151,7 +144,7 @@ const props = withDefaults(
|
||||||
graduations: "dots",
|
graduations: "dots",
|
||||||
fadeGraduations: true,
|
fadeGraduations: true,
|
||||||
sAnimation: "elastic",
|
sAnimation: "elastic",
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const graduationsMajor = computed(() => {
|
const graduationsMajor = computed(() => {
|
||||||
|
@ -174,49 +167,51 @@ const texts = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
let enabled = true;
|
let enabled = true;
|
||||||
let majorGraduationColor = $ref<string>();
|
let majorGraduationColor = ref<string>();
|
||||||
//let minorGraduationColor = $ref<string>();
|
//let minorGraduationColor = $ref<string>();
|
||||||
let sHandColor = $ref<string>();
|
let sHandColor = ref<string>();
|
||||||
let mHandColor = $ref<string>();
|
let mHandColor = ref<string>();
|
||||||
let hHandColor = $ref<string>();
|
let hHandColor = ref<string>();
|
||||||
let nowColor = $ref<string>();
|
let nowColor = ref<string>();
|
||||||
let h = $ref<number>(0);
|
let h = ref<number>(0);
|
||||||
let m = $ref<number>(0);
|
let m = ref<number>(0);
|
||||||
let s = $ref<number>(0);
|
let s = ref<number>(0);
|
||||||
let hAngle = $ref<number>(0);
|
let hAngle = ref<number>(0);
|
||||||
let mAngle = $ref<number>(0);
|
let mAngle = ref<number>(0);
|
||||||
let sAngle = $ref<number>(0);
|
let sAngle = ref<number>(0);
|
||||||
let disableSAnimate = $ref(false);
|
let disableSAnimate = ref(false);
|
||||||
let sOneRound = false;
|
let sOneRound = false;
|
||||||
|
|
||||||
function tick() {
|
function tick() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
now.setMinutes(
|
now.setMinutes(
|
||||||
now.getMinutes() + (new Date().getTimezoneOffset() + props.offset)
|
now.getMinutes() + (new Date().getTimezoneOffset() + props.offset),
|
||||||
);
|
);
|
||||||
s = now.getSeconds();
|
s.value = now.getSeconds();
|
||||||
m = now.getMinutes();
|
m.value = now.getMinutes();
|
||||||
h = now.getHours();
|
h.value = now.getHours();
|
||||||
hAngle =
|
hAngle.value =
|
||||||
(Math.PI * ((h % (props.twentyfour ? 24 : 12)) + (m + s / 60) / 60)) /
|
(Math.PI *
|
||||||
|
((h.value % (props.twentyfour ? 24 : 12)) +
|
||||||
|
(m.value + s.value / 60) / 60)) /
|
||||||
(props.twentyfour ? 12 : 6);
|
(props.twentyfour ? 12 : 6);
|
||||||
mAngle = (Math.PI * (m + s / 60)) / 30;
|
mAngle.value = (Math.PI * (m.value + s.value / 60)) / 30;
|
||||||
if (sOneRound) {
|
if (sOneRound) {
|
||||||
// 秒針が一周した際のアニメーションをよしなに処理する(これが無いと秒が59->0になったときに期待したアニメーションにならない)
|
// 秒針が一周した際のアニメーションをよしなに処理する(これが無いと秒が59->0になったときに期待したアニメーションにならない)
|
||||||
sAngle = (Math.PI * 60) / 30;
|
sAngle.value = (Math.PI * 60) / 30;
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
disableSAnimate = true;
|
disableSAnimate.value = true;
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
sAngle = 0;
|
sAngle.value = 0;
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
disableSAnimate = false;
|
disableSAnimate.value = false;
|
||||||
}, 100);
|
}, 100);
|
||||||
}, 100);
|
}, 100);
|
||||||
}, 700);
|
}, 700);
|
||||||
} else {
|
} else {
|
||||||
sAngle = (Math.PI * s) / 30;
|
sAngle.value = (Math.PI * s.value) / 30;
|
||||||
}
|
}
|
||||||
sOneRound = s === 59;
|
sOneRound = s.value === 59;
|
||||||
}
|
}
|
||||||
|
|
||||||
tick();
|
tick();
|
||||||
|
@ -225,18 +220,18 @@ function calcColors() {
|
||||||
const computedStyle = getComputedStyle(document.documentElement);
|
const computedStyle = getComputedStyle(document.documentElement);
|
||||||
const dark = tinycolor(computedStyle.getPropertyValue("--bg")).isDark();
|
const dark = tinycolor(computedStyle.getPropertyValue("--bg")).isDark();
|
||||||
const accent = tinycolor(
|
const accent = tinycolor(
|
||||||
computedStyle.getPropertyValue("--accent")
|
computedStyle.getPropertyValue("--accent"),
|
||||||
).toHexString();
|
).toHexString();
|
||||||
majorGraduationColor = dark
|
majorGraduationColor.value = dark
|
||||||
? "rgba(255, 255, 255, 0.3)"
|
? "rgba(255, 255, 255, 0.3)"
|
||||||
: "rgba(0, 0, 0, 0.3)";
|
: "rgba(0, 0, 0, 0.3)";
|
||||||
//minorGraduationColor = dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
|
//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)";
|
sHandColor.value = dark ? "rgba(255, 255, 255, 0.5)" : "rgba(0, 0, 0, 0.3)";
|
||||||
mHandColor = tinycolor(
|
mHandColor.value = tinycolor(
|
||||||
computedStyle.getPropertyValue("--fg")
|
computedStyle.getPropertyValue("--fg"),
|
||||||
).toHexString();
|
).toHexString();
|
||||||
hHandColor = accent;
|
hHandColor.value = accent;
|
||||||
nowColor = accent;
|
nowColor.value = accent;
|
||||||
}
|
}
|
||||||
|
|
||||||
calcColors();
|
calcColors();
|
||||||
|
|
|
@ -268,7 +268,7 @@ function exec() {
|
||||||
} else if (props.type === "hashtag") {
|
} else if (props.type === "hashtag") {
|
||||||
if (!props.q || props.q === "") {
|
if (!props.q || props.q === "") {
|
||||||
hashtags.value = JSON.parse(
|
hashtags.value = JSON.parse(
|
||||||
localStorage.getItem("hashtags") || "[]"
|
localStorage.getItem("hashtags") || "[]",
|
||||||
);
|
);
|
||||||
fetching.value = false;
|
fetching.value = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -288,7 +288,7 @@ function exec() {
|
||||||
// キャッシュ
|
// キャッシュ
|
||||||
sessionStorage.setItem(
|
sessionStorage.setItem(
|
||||||
cacheKey,
|
cacheKey,
|
||||||
JSON.stringify(searchedHashtags)
|
JSON.stringify(searchedHashtags),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -298,7 +298,7 @@ function exec() {
|
||||||
// 最近使った絵文字をサジェスト
|
// 最近使った絵文字をサジェスト
|
||||||
emojis.value = defaultStore.state.recentlyUsedEmojis
|
emojis.value = defaultStore.state.recentlyUsedEmojis
|
||||||
.map((emoji) =>
|
.map((emoji) =>
|
||||||
emojiDb.find((dbEmoji) => dbEmoji.emoji === emoji)
|
emojiDb.find((dbEmoji) => dbEmoji.emoji === emoji),
|
||||||
)
|
)
|
||||||
.filter((x) => x) as EmojiDef[];
|
.filter((x) => x) as EmojiDef[];
|
||||||
return;
|
return;
|
||||||
|
@ -450,7 +450,7 @@ onMounted(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
exec();
|
exec();
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -470,7 +470,9 @@ onBeforeUnmount(() => {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
margin-top: calc(1em + 8px);
|
margin-top: calc(1em + 8px);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: top 0.1s ease, left 0.1s ease;
|
transition:
|
||||||
|
top 0.1s ease,
|
||||||
|
left 0.1s ease;
|
||||||
|
|
||||||
> ol {
|
> ol {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onMounted } from "vue";
|
import { nextTick, onMounted, ref } from "vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
type?: "button" | "submit" | "reset";
|
type?: "button" | "submit" | "reset";
|
||||||
|
@ -49,22 +49,30 @@ const emit = defineEmits<{
|
||||||
(ev: "click", payload: MouseEvent): void;
|
(ev: "click", payload: MouseEvent): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let el = $ref<HTMLElement | null>(null);
|
let el = ref<HTMLElement | null>(null);
|
||||||
let ripples = $ref<HTMLElement | null>(null);
|
let ripples = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.autofocus) {
|
if (props.autofocus) {
|
||||||
nextTick(() => {
|
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);
|
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 origin = { x: circleCenterX, y: circleCenterY };
|
||||||
const dist1 = distance({ x: 0, y: 0 }, origin);
|
const dist1 = distance({ x: 0, y: 0 }, origin);
|
||||||
const dist2 = distance({ x: boxW, 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.top = (evt.clientY - rect.top - 1).toString() + "px";
|
||||||
ripple.style.left = (evt.clientX - rect.left - 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 circleCenterX = evt.clientX - rect.left;
|
||||||
const circleCenterY = evt.clientY - rect.top;
|
const circleCenterY = evt.clientY - rect.top;
|
||||||
|
@ -90,7 +98,7 @@ function onMousedown(evt: MouseEvent): void {
|
||||||
target.clientWidth,
|
target.clientWidth,
|
||||||
target.clientHeight,
|
target.clientHeight,
|
||||||
circleCenterX,
|
circleCenterX,
|
||||||
circleCenterY
|
circleCenterY,
|
||||||
);
|
);
|
||||||
|
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
|
@ -101,7 +109,7 @@ function onMousedown(evt: MouseEvent): void {
|
||||||
ripple.style.opacity = "0";
|
ripple.style.opacity = "0";
|
||||||
}, 1000);
|
}, 1000);
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
if (ripples) ripples.removeChild(ripple);
|
if (ripples) ripples.value!.removeChild(ripple);
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -25,7 +25,7 @@ type Captcha = {
|
||||||
| "expired-callback"
|
| "expired-callback"
|
||||||
| "error-callback"
|
| "error-callback"
|
||||||
| "endpoint"]?: unknown;
|
| "endpoint"]?: unknown;
|
||||||
}
|
},
|
||||||
): string;
|
): string;
|
||||||
remove(id: string): void;
|
remove(id: string): void;
|
||||||
execute(id: string): void;
|
execute(id: string): void;
|
||||||
|
@ -78,7 +78,7 @@ const src = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const captcha = computed<Captcha>(
|
const captcha = computed<Captcha>(
|
||||||
() => window[variable.value] || ({} as unknown as Captcha)
|
() => window[variable.value] || ({} as unknown as Captcha),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (loaded) {
|
if (loaded) {
|
||||||
|
@ -91,7 +91,7 @@ if (loaded) {
|
||||||
async: true,
|
async: true,
|
||||||
id: props.provider,
|
id: props.provider,
|
||||||
src: src.value,
|
src: src.value,
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
).addEventListener("load", () => (available.value = true));
|
).addEventListener("load", () => (available.value = true));
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref, watch, PropType, onUnmounted } from "vue";
|
import { onMounted, ref, watch, PropType } from "vue";
|
||||||
import {
|
import {
|
||||||
Chart,
|
Chart,
|
||||||
ArcElement,
|
ArcElement,
|
||||||
|
@ -91,7 +91,7 @@ Chart.register(
|
||||||
Tooltip,
|
Tooltip,
|
||||||
SubTitle,
|
SubTitle,
|
||||||
Filler,
|
Filler,
|
||||||
zoomPlugin
|
zoomPlugin,
|
||||||
//gradient,
|
//gradient,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -179,11 +179,11 @@ const render = () => {
|
||||||
|
|
||||||
// フォントカラー
|
// フォントカラー
|
||||||
Chart.defaults.color = getComputedStyle(
|
Chart.defaults.color = getComputedStyle(
|
||||||
document.documentElement
|
document.documentElement,
|
||||||
).getPropertyValue("--fg");
|
).getPropertyValue("--fg");
|
||||||
|
|
||||||
const maxes = chartData.series.map((x, i) =>
|
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, {
|
chartInstance = new Chart(chartEl.value, {
|
||||||
|
@ -471,9 +471,9 @@ const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
|
||||||
raw.local.inc,
|
raw.local.inc,
|
||||||
negate(raw.local.dec),
|
negate(raw.local.dec),
|
||||||
raw.remote.inc,
|
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",
|
color: "#888888",
|
||||||
},
|
},
|
||||||
|
@ -483,7 +483,7 @@ const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
|
||||||
data: format(
|
data: format(
|
||||||
type === "combined"
|
type === "combined"
|
||||||
? sum(raw.local.diffs.renote, raw.remote.diffs.renote)
|
? sum(raw.local.diffs.renote, raw.remote.diffs.renote)
|
||||||
: raw[type].diffs.renote
|
: raw[type].diffs.renote,
|
||||||
),
|
),
|
||||||
color: colors.green,
|
color: colors.green,
|
||||||
},
|
},
|
||||||
|
@ -493,7 +493,7 @@ const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
|
||||||
data: format(
|
data: format(
|
||||||
type === "combined"
|
type === "combined"
|
||||||
? sum(raw.local.diffs.reply, raw.remote.diffs.reply)
|
? sum(raw.local.diffs.reply, raw.remote.diffs.reply)
|
||||||
: raw[type].diffs.reply
|
: raw[type].diffs.reply,
|
||||||
),
|
),
|
||||||
color: colors.yellow,
|
color: colors.yellow,
|
||||||
},
|
},
|
||||||
|
@ -503,7 +503,7 @@ const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
|
||||||
data: format(
|
data: format(
|
||||||
type === "combined"
|
type === "combined"
|
||||||
? sum(raw.local.diffs.normal, raw.remote.diffs.normal)
|
? sum(raw.local.diffs.normal, raw.remote.diffs.normal)
|
||||||
: raw[type].diffs.normal
|
: raw[type].diffs.normal,
|
||||||
),
|
),
|
||||||
color: colors.blue,
|
color: colors.blue,
|
||||||
},
|
},
|
||||||
|
@ -514,9 +514,9 @@ const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
|
||||||
type === "combined"
|
type === "combined"
|
||||||
? sum(
|
? sum(
|
||||||
raw.local.diffs.withFile,
|
raw.local.diffs.withFile,
|
||||||
raw.remote.diffs.withFile
|
raw.remote.diffs.withFile,
|
||||||
)
|
)
|
||||||
: raw[type].diffs.withFile
|
: raw[type].diffs.withFile,
|
||||||
),
|
),
|
||||||
color: colors.purple,
|
color: colors.purple,
|
||||||
},
|
},
|
||||||
|
@ -567,8 +567,8 @@ const fetchUsersChart = async (total: boolean): Promise<typeof chartData> => {
|
||||||
raw.local.inc,
|
raw.local.inc,
|
||||||
negate(raw.local.dec),
|
negate(raw.local.dec),
|
||||||
raw.remote.inc,
|
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(
|
data: format(
|
||||||
total
|
total
|
||||||
? raw.local.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(
|
data: format(
|
||||||
total
|
total
|
||||||
? raw.remote.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,
|
raw.local.incSize,
|
||||||
negate(raw.local.decSize),
|
negate(raw.local.decSize),
|
||||||
raw.remote.incSize,
|
raw.remote.incSize,
|
||||||
negate(raw.remote.decSize)
|
negate(raw.remote.decSize),
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -719,8 +719,8 @@ const fetchDriveFilesChart = async (): Promise<typeof chartData> => {
|
||||||
raw.local.incCount,
|
raw.local.incCount,
|
||||||
negate(raw.local.decCount),
|
negate(raw.local.decCount),
|
||||||
raw.remote.incCount,
|
raw.remote.incCount,
|
||||||
negate(raw.remote.decCount)
|
negate(raw.remote.decCount),
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -778,7 +778,7 @@ const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchInstanceUsersChart = async (
|
const fetchInstanceUsersChart = async (
|
||||||
total: boolean
|
total: boolean,
|
||||||
): Promise<typeof chartData> => {
|
): Promise<typeof chartData> => {
|
||||||
const raw = await os.apiGet("charts/instance", {
|
const raw = await os.apiGet("charts/instance", {
|
||||||
host: props.args.host,
|
host: props.args.host,
|
||||||
|
@ -794,7 +794,7 @@ const fetchInstanceUsersChart = async (
|
||||||
data: format(
|
data: format(
|
||||||
total
|
total
|
||||||
? raw.users.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 (
|
const fetchInstanceNotesChart = async (
|
||||||
total: boolean
|
total: boolean,
|
||||||
): Promise<typeof chartData> => {
|
): Promise<typeof chartData> => {
|
||||||
const raw = await os.apiGet("charts/instance", {
|
const raw = await os.apiGet("charts/instance", {
|
||||||
host: props.args.host,
|
host: props.args.host,
|
||||||
|
@ -818,7 +818,7 @@ const fetchInstanceNotesChart = async (
|
||||||
data: format(
|
data: format(
|
||||||
total
|
total
|
||||||
? raw.notes.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 (
|
const fetchInstanceFfChart = async (
|
||||||
total: boolean
|
total: boolean,
|
||||||
): Promise<typeof chartData> => {
|
): Promise<typeof chartData> => {
|
||||||
const raw = await os.apiGet("charts/instance", {
|
const raw = await os.apiGet("charts/instance", {
|
||||||
host: props.args.host,
|
host: props.args.host,
|
||||||
|
@ -842,7 +842,7 @@ const fetchInstanceFfChart = async (
|
||||||
data: format(
|
data: format(
|
||||||
total
|
total
|
||||||
? raw.following.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(
|
data: format(
|
||||||
total
|
total
|
||||||
? raw.followers.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 (
|
const fetchInstanceDriveUsageChart = async (
|
||||||
total: boolean
|
total: boolean,
|
||||||
): Promise<typeof chartData> => {
|
): Promise<typeof chartData> => {
|
||||||
const raw = await os.apiGet("charts/instance", {
|
const raw = await os.apiGet("charts/instance", {
|
||||||
host: props.args.host,
|
host: props.args.host,
|
||||||
|
@ -877,7 +877,7 @@ const fetchInstanceDriveUsageChart = async (
|
||||||
data: format(
|
data: format(
|
||||||
total
|
total
|
||||||
? raw.drive.totalUsage
|
? 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 (
|
const fetchInstanceDriveFilesChart = async (
|
||||||
total: boolean
|
total: boolean,
|
||||||
): Promise<typeof chartData> => {
|
): Promise<typeof chartData> => {
|
||||||
const raw = await os.apiGet("charts/instance", {
|
const raw = await os.apiGet("charts/instance", {
|
||||||
host: props.args.host,
|
host: props.args.host,
|
||||||
|
@ -901,7 +901,7 @@ const fetchInstanceDriveFilesChart = async (
|
||||||
data: format(
|
data: format(
|
||||||
total
|
total
|
||||||
? raw.drive.totalFiles
|
? raw.drive.totalFiles
|
||||||
: sum(raw.drive.incFiles, negate(raw.drive.decFiles))
|
: sum(raw.drive.incFiles, negate(raw.drive.decFiles)),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||||
import XCheatSheet from "@/pages/mfm-cheat-sheet.vue";
|
import XCheatSheet from "@/pages/mfm-cheat-sheet.vue";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
@ -20,10 +22,10 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
const dialog = ref<InstanceType<typeof XModalWindow>>();
|
||||||
|
|
||||||
function close(res) {
|
function close(res) {
|
||||||
dialog.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -18,13 +18,13 @@ const props = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const prismLang = computed(() =>
|
const prismLang = computed(() =>
|
||||||
Prism.languages[props.lang] ? props.lang : "js"
|
Prism.languages[props.lang] ? props.lang : "js",
|
||||||
);
|
);
|
||||||
const html = computed(() =>
|
const html = computed(() =>
|
||||||
Prism.highlight(
|
Prism.highlight(
|
||||||
props.code,
|
props.code,
|
||||||
Prism.languages[prismLang.value],
|
Prism.languages[prismLang.value],
|
||||||
prismLang.value
|
prismLang.value,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -12,6 +12,6 @@ defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const XCode = defineAsyncComponent(
|
const XCode = defineAsyncComponent(
|
||||||
() => import("@/components/MkCode.core.vue")
|
() => import("@/components/MkCode.core.vue"),
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -125,7 +125,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
this.$el.style.setProperty("--maxHeight", this.maxHeight + "px");
|
this.$el.style.setProperty("--maxHeight", this.maxHeight + "px");
|
||||||
|
@ -174,7 +174,9 @@ export default defineComponent({
|
||||||
.container-toggle-enter-active,
|
.container-toggle-enter-active,
|
||||||
.container-toggle-leave-active {
|
.container-toggle-leave-active {
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
transition: opacity 0.5s, height 0.5s !important;
|
transition:
|
||||||
|
opacity 0.5s,
|
||||||
|
height 0.5s !important;
|
||||||
}
|
}
|
||||||
.container-toggle-enter-from {
|
.container-toggle-enter-from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onBeforeUnmount } from "vue";
|
import { onMounted, onBeforeUnmount, ref } from "vue";
|
||||||
import MkMenu from "./MkMenu.vue";
|
import MkMenu from "./MkMenu.vue";
|
||||||
import { MenuItem } from "./types/menu.vue";
|
import { MenuItem } from "./types/menu.vue";
|
||||||
import contains from "@/scripts/contains";
|
import contains from "@/scripts/contains";
|
||||||
|
@ -27,16 +27,16 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(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(() => {
|
onMounted(() => {
|
||||||
let left = props.ev.pageX + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
let left = props.ev.pageX + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
||||||
let top = props.ev.pageY + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
let top = props.ev.pageY + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
||||||
|
|
||||||
const width = rootEl.offsetWidth;
|
const width = rootEl.value.offsetWidth;
|
||||||
const height = rootEl.offsetHeight;
|
const height = rootEl.value.offsetHeight;
|
||||||
|
|
||||||
if (left + width - window.pageXOffset > window.innerWidth) {
|
if (left + width - window.pageXOffset > window.innerWidth) {
|
||||||
left = window.innerWidth - width + window.pageXOffset;
|
left = window.innerWidth - width + window.pageXOffset;
|
||||||
|
@ -54,8 +54,8 @@ onMounted(() => {
|
||||||
left = 0;
|
left = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
rootEl.style.top = `${top}px`;
|
rootEl.value.style.top = `${top}px`;
|
||||||
rootEl.style.left = `${left}px`;
|
rootEl.value.style.left = `${left}px`;
|
||||||
|
|
||||||
for (const el of Array.from(document.querySelectorAll("body *"))) {
|
for (const el of Array.from(document.querySelectorAll("body *"))) {
|
||||||
el.addEventListener("mousedown", onMousedown);
|
el.addEventListener("mousedown", onMousedown);
|
||||||
|
@ -69,7 +69,8 @@ onBeforeUnmount(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
function onMousedown(evt: Event) {
|
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>
|
</script>
|
||||||
|
|
||||||
|
@ -80,7 +81,8 @@ function onMousedown(evt: Event) {
|
||||||
|
|
||||||
.fade-enter-active,
|
.fade-enter-active,
|
||||||
.fade-leave-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 0.5s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
transform-origin: left top;
|
transform-origin: left top;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onMounted } from "vue";
|
import { nextTick, onMounted, ref } from "vue";
|
||||||
import * as misskey from "calckey-js";
|
import * as misskey from "calckey-js";
|
||||||
import Cropper from "cropperjs";
|
import Cropper from "cropperjs";
|
||||||
import tinycolor from "tinycolor2";
|
import tinycolor from "tinycolor2";
|
||||||
|
@ -60,10 +60,10 @@ const props = defineProps<{
|
||||||
const imgUrl = `${url}/proxy/image.webp?${query({
|
const imgUrl = `${url}/proxy/image.webp?${query({
|
||||||
url: props.file.url,
|
url: props.file.url,
|
||||||
})}`;
|
})}`;
|
||||||
let dialogEl = $ref<InstanceType<typeof XModalWindow>>();
|
let dialogEl = ref<InstanceType<typeof XModalWindow>>();
|
||||||
let imgEl = $ref<HTMLImageElement>();
|
let imgEl = ref<HTMLImageElement>();
|
||||||
let cropper: Cropper | null = null;
|
let cropper: Cropper | null = null;
|
||||||
let loading = $ref(true);
|
let loading = ref(true);
|
||||||
|
|
||||||
const ok = async () => {
|
const ok = async () => {
|
||||||
const promise = new Promise<misskey.entities.DriveFile>(async (res) => {
|
const promise = new Promise<misskey.entities.DriveFile>(async (res) => {
|
||||||
|
@ -94,16 +94,16 @@ const ok = async () => {
|
||||||
const f = await promise;
|
const f = await promise;
|
||||||
|
|
||||||
emit("ok", f);
|
emit("ok", f);
|
||||||
dialogEl.close();
|
dialogEl.value.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
emit("cancel");
|
emit("cancel");
|
||||||
dialogEl.close();
|
dialogEl.value.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onImageLoad = () => {
|
const onImageLoad = () => {
|
||||||
loading = false;
|
loading.value = false;
|
||||||
|
|
||||||
if (cropper) {
|
if (cropper) {
|
||||||
cropper.getCropperImage()!.$center("contain");
|
cropper.getCropperImage()!.$center("contain");
|
||||||
|
@ -112,13 +112,13 @@ const onImageLoad = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
cropper = new Cropper(imgEl, {});
|
cropper = new Cropper(imgEl.value, {});
|
||||||
|
|
||||||
const computedStyle = getComputedStyle(document.documentElement);
|
const computedStyle = getComputedStyle(document.documentElement);
|
||||||
|
|
||||||
const selection = cropper.getCropperSelection()!;
|
const selection = cropper.getCropperSelection()!;
|
||||||
selection.themeColor = tinycolor(
|
selection.themeColor = tinycolor(
|
||||||
computedStyle.getPropertyValue("--accent")
|
computedStyle.getPropertyValue("--accent"),
|
||||||
).toHexString();
|
).toHexString();
|
||||||
selection.aspectRatio = props.aspectRatio;
|
selection.aspectRatio = props.aspectRatio;
|
||||||
selection.initialAspectRatio = props.aspectRatio;
|
selection.initialAspectRatio = props.aspectRatio;
|
||||||
|
|
|
@ -68,7 +68,9 @@ defineExpose({
|
||||||
> span {
|
> span {
|
||||||
background: var(--cwBg) !important;
|
background: var(--cwBg) !important;
|
||||||
color: var(--cwFg);
|
color: var(--cwFg);
|
||||||
transition: background 0.2s, color 0.2s;
|
transition:
|
||||||
|
background 0.2s,
|
||||||
|
color 0.2s;
|
||||||
> span {
|
> span {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
&::before {
|
&::before {
|
||||||
|
|
|
@ -78,14 +78,24 @@
|
||||||
okButtonDisabled &&
|
okButtonDisabled &&
|
||||||
disabledReason === 'charactersExceeded'
|
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
|
<span
|
||||||
v-else-if="
|
v-else-if="
|
||||||
okButtonDisabled &&
|
okButtonDisabled &&
|
||||||
disabledReason === 'charactersBelow'
|
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>
|
||||||
<template v-if="input.type === 'search'" #suffix>
|
<template v-if="input.type === 'search'" #suffix>
|
||||||
|
@ -189,7 +199,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 MkModal from "@/components/MkModal.vue";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
import MkInput from "@/components/form/input.vue";
|
import MkInput from "@/components/form/input.vue";
|
||||||
|
@ -258,7 +268,7 @@ const props = withDefaults(
|
||||||
isYesNo: false,
|
isYesNo: false,
|
||||||
|
|
||||||
cancelableByBgClick: true,
|
cancelableByBgClick: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -271,17 +281,15 @@ const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
const inputValue = ref<string | number | null>(props.input?.default ?? null);
|
const inputValue = ref<string | number | null>(props.input?.default ?? null);
|
||||||
const selectedValue = ref(props.select?.default ?? null);
|
const selectedValue = ref(props.select?.default ?? null);
|
||||||
|
|
||||||
let disabledReason = $ref<null | "charactersExceeded" | "charactersBelow">(
|
let disabledReason = ref<null | "charactersExceeded" | "charactersBelow">(null);
|
||||||
null
|
const okButtonDisabled = computed<boolean>(() => {
|
||||||
);
|
|
||||||
const okButtonDisabled = $computed<boolean>(() => {
|
|
||||||
if (props.input) {
|
if (props.input) {
|
||||||
if (props.input.minLength) {
|
if (props.input.minLength) {
|
||||||
if (
|
if (
|
||||||
(inputValue.value || inputValue.value === "") &&
|
(inputValue.value || inputValue.value === "") &&
|
||||||
(inputValue.value as string).length < props.input.minLength
|
(inputValue.value as string).length < props.input.minLength
|
||||||
) {
|
) {
|
||||||
disabledReason = "charactersBelow";
|
disabledReason.value = "charactersBelow";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,7 +298,7 @@ const okButtonDisabled = $computed<boolean>(() => {
|
||||||
inputValue.value &&
|
inputValue.value &&
|
||||||
(inputValue.value as string).length > props.input.maxLength
|
(inputValue.value as string).length > props.input.maxLength
|
||||||
) {
|
) {
|
||||||
disabledReason = "charactersExceeded";
|
disabledReason.value = "charactersExceeded";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,8 +319,8 @@ async function ok() {
|
||||||
const result = props.input
|
const result = props.input
|
||||||
? inputValue.value
|
? inputValue.value
|
||||||
: props.select
|
: props.select
|
||||||
? selectedValue.value
|
? selectedValue.value
|
||||||
: true;
|
: true;
|
||||||
done(false, result);
|
done(false, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,7 +447,7 @@ async function openSearchFilters(ev) {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
ev.target,
|
ev.target,
|
||||||
{ noReturnFocus: true }
|
{ noReturnFocus: true },
|
||||||
);
|
);
|
||||||
inputEl.value.focus();
|
inputEl.value.focus();
|
||||||
inputEl.value.selectRange(inputValue.value.length, inputValue.value.length); // cursor at end
|
inputEl.value.selectRange(inputValue.value.length, inputValue.value.length); // cursor at end
|
||||||
|
|
|
@ -23,7 +23,7 @@ const props = withDefaults(
|
||||||
showS: true,
|
showS: true,
|
||||||
showMs: false,
|
showMs: false,
|
||||||
offset: 0 - new Date().getTimezoneOffset(),
|
offset: 0 - new Date().getTimezoneOffset(),
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let intervalId;
|
let intervalId;
|
||||||
|
@ -45,7 +45,7 @@ watch(showColon, (v) => {
|
||||||
const tick = () => {
|
const tick = () => {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
now.setMinutes(
|
now.setMinutes(
|
||||||
now.getMinutes() + (new Date().getTimezoneOffset() + props.offset)
|
now.getMinutes() + (new Date().getTimezoneOffset() + props.offset),
|
||||||
);
|
);
|
||||||
hh.value = now.getHours().toString().padStart(2, "0");
|
hh.value = now.getHours().toString().padStart(2, "0");
|
||||||
mm.value = now.getMinutes().toString().padStart(2, "0");
|
mm.value = now.getMinutes().toString().padStart(2, "0");
|
||||||
|
@ -65,7 +65,7 @@ watch(
|
||||||
if (intervalId) window.clearInterval(intervalId);
|
if (intervalId) window.clearInterval(intervalId);
|
||||||
intervalId = window.setInterval(tick, props.showMs ? 10 : 1000);
|
intervalId = window.setInterval(tick, props.showMs ? 10 : 1000);
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
|
|
@ -56,7 +56,7 @@ const props = withDefaults(
|
||||||
{
|
{
|
||||||
isSelected: false,
|
isSelected: false,
|
||||||
selectMode: false,
|
selectMode: false,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -68,7 +68,7 @@ const emit = defineEmits<{
|
||||||
const isDragging = ref(false);
|
const isDragging = ref(false);
|
||||||
|
|
||||||
const title = computed(
|
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() {
|
function getMenu() {
|
||||||
|
@ -124,7 +124,7 @@ function onClick(ev: MouseEvent) {
|
||||||
getMenu(),
|
getMenu(),
|
||||||
(ev.currentTarget ?? ev.target ?? undefined) as
|
(ev.currentTarget ?? ev.target ?? undefined) as
|
||||||
| HTMLElement
|
| HTMLElement
|
||||||
| undefined
|
| undefined,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ function onDragstart(ev: DragEvent) {
|
||||||
ev.dataTransfer.effectAllowed = "move";
|
ev.dataTransfer.effectAllowed = "move";
|
||||||
ev.dataTransfer.setData(
|
ev.dataTransfer.setData(
|
||||||
_DATA_TRANSFER_DRIVE_FILE_,
|
_DATA_TRANSFER_DRIVE_FILE_,
|
||||||
JSON.stringify(props.file)
|
JSON.stringify(props.file),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
isDragging.value = true;
|
isDragging.value = true;
|
||||||
|
@ -186,7 +186,7 @@ function describe() {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"closed"
|
"closed",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ const props = withDefaults(
|
||||||
{
|
{
|
||||||
isSelected: false,
|
isSelected: false,
|
||||||
selectMode: false,
|
selectMode: false,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -184,7 +184,7 @@ function onDragstart(ev: DragEvent) {
|
||||||
ev.dataTransfer.effectAllowed = "move";
|
ev.dataTransfer.effectAllowed = "move";
|
||||||
ev.dataTransfer.setData(
|
ev.dataTransfer.setData(
|
||||||
_DATA_TRANSFER_DRIVE_FOLDER_,
|
_DATA_TRANSFER_DRIVE_FOLDER_,
|
||||||
JSON.stringify(props.folder)
|
JSON.stringify(props.folder),
|
||||||
);
|
);
|
||||||
isDragging.value = true;
|
isDragging.value = true;
|
||||||
|
|
||||||
|
@ -256,13 +256,13 @@ function onContextmenu(ev: MouseEvent) {
|
||||||
action: () => {
|
action: () => {
|
||||||
os.popup(
|
os.popup(
|
||||||
defineAsyncComponent(
|
defineAsyncComponent(
|
||||||
() => import("@/components/MkDriveWindow.vue")
|
() => import("@/components/MkDriveWindow.vue"),
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
initialFolder: props.folder,
|
initialFolder: props.folder,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
"closed"
|
"closed",
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -280,7 +280,7 @@ function onContextmenu(ev: MouseEvent) {
|
||||||
action: deleteFolder,
|
action: deleteFolder,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
ev
|
ev,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -29,7 +29,7 @@ const emit = defineEmits<{
|
||||||
(
|
(
|
||||||
ev: "upload",
|
ev: "upload",
|
||||||
file: File,
|
file: File,
|
||||||
folder?: Misskey.entities.DriveFolder | null
|
folder?: Misskey.entities.DriveFolder | null,
|
||||||
): void;
|
): void;
|
||||||
(ev: "removeFile", v: Misskey.entities.DriveFile["id"]): void;
|
(ev: "removeFile", v: Misskey.entities.DriveFile["id"]): void;
|
||||||
(ev: "removeFolder", v: Misskey.entities.DriveFolder["id"]): void;
|
(ev: "removeFolder", v: Misskey.entities.DriveFolder["id"]): void;
|
||||||
|
|
|
@ -161,17 +161,17 @@ const props = withDefaults(
|
||||||
{
|
{
|
||||||
multiple: false,
|
multiple: false,
|
||||||
select: null,
|
select: null,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(
|
(
|
||||||
ev: "selected",
|
ev: "selected",
|
||||||
v: Misskey.entities.DriveFile | Misskey.entities.DriveFolder
|
v: Misskey.entities.DriveFile | Misskey.entities.DriveFolder,
|
||||||
): void;
|
): void;
|
||||||
(
|
(
|
||||||
ev: "change-selection",
|
ev: "change-selection",
|
||||||
v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]
|
v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[],
|
||||||
): void;
|
): void;
|
||||||
(ev: "move-root"): void;
|
(ev: "move-root"): void;
|
||||||
(ev: "cd", v: Misskey.entities.DriveFolder | null): void;
|
(ev: "cd", v: Misskey.entities.DriveFolder | null): void;
|
||||||
|
@ -207,7 +207,7 @@ const ilFilesObserver = new IntersectionObserver(
|
||||||
entries.some((entry) => entry.isIntersecting) &&
|
entries.some((entry) => entry.isIntersecting) &&
|
||||||
!fetching.value &&
|
!fetching.value &&
|
||||||
moreFiles.value &&
|
moreFiles.value &&
|
||||||
fetchMoreFiles()
|
fetchMoreFiles(),
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(folder, () => emit("cd", folder.value));
|
watch(folder, () => emit("cd", folder.value));
|
||||||
|
@ -230,13 +230,13 @@ function onStreamDriveFileDeleted(fileId: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onStreamDriveFolderCreated(
|
function onStreamDriveFolderCreated(
|
||||||
createdFolder: Misskey.entities.DriveFolder
|
createdFolder: Misskey.entities.DriveFolder,
|
||||||
) {
|
) {
|
||||||
addFolder(createdFolder, true);
|
addFolder(createdFolder, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onStreamDriveFolderUpdated(
|
function onStreamDriveFolderUpdated(
|
||||||
updatedFolder: Misskey.entities.DriveFolder
|
updatedFolder: Misskey.entities.DriveFolder,
|
||||||
) {
|
) {
|
||||||
const current = folder.value ? folder.value.id : null;
|
const current = folder.value ? folder.value.id : null;
|
||||||
if (current !== updatedFolder.parentId) {
|
if (current !== updatedFolder.parentId) {
|
||||||
|
@ -433,7 +433,7 @@ function onChangeFileInput() {
|
||||||
|
|
||||||
function upload(
|
function upload(
|
||||||
file: File,
|
file: File,
|
||||||
folderToUpload?: Misskey.entities.DriveFolder | null
|
folderToUpload?: Misskey.entities.DriveFolder | null,
|
||||||
) {
|
) {
|
||||||
uploadFile(
|
uploadFile(
|
||||||
file,
|
file,
|
||||||
|
@ -441,7 +441,7 @@ function upload(
|
||||||
? folderToUpload.id
|
? folderToUpload.id
|
||||||
: null,
|
: null,
|
||||||
undefined,
|
undefined,
|
||||||
keepOriginal.value
|
keepOriginal.value,
|
||||||
).then((res) => {
|
).then((res) => {
|
||||||
addFile(res, true);
|
addFile(res, true);
|
||||||
});
|
});
|
||||||
|
@ -452,7 +452,7 @@ function chooseFile(file: Misskey.entities.DriveFile) {
|
||||||
if (props.multiple) {
|
if (props.multiple) {
|
||||||
if (isAlreadySelected) {
|
if (isAlreadySelected) {
|
||||||
selectedFiles.value = selectedFiles.value.filter(
|
selectedFiles.value = selectedFiles.value.filter(
|
||||||
(f) => f.id !== file.id
|
(f) => f.id !== file.id,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
selectedFiles.value.push(file);
|
selectedFiles.value.push(file);
|
||||||
|
@ -470,12 +470,12 @@ function chooseFile(file: Misskey.entities.DriveFile) {
|
||||||
|
|
||||||
function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) {
|
function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) {
|
||||||
const isAlreadySelected = selectedFolders.value.some(
|
const isAlreadySelected = selectedFolders.value.some(
|
||||||
(f) => f.id === folderToChoose.id
|
(f) => f.id === folderToChoose.id,
|
||||||
);
|
);
|
||||||
if (props.multiple) {
|
if (props.multiple) {
|
||||||
if (isAlreadySelected) {
|
if (isAlreadySelected) {
|
||||||
selectedFolders.value = selectedFolders.value.filter(
|
selectedFolders.value = selectedFolders.value.filter(
|
||||||
(f) => f.id !== folderToChoose.id
|
(f) => f.id !== folderToChoose.id,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
selectedFolders.value.push(folderToChoose);
|
selectedFolders.value.push(folderToChoose);
|
||||||
|
@ -707,7 +707,7 @@ function getMenu() {
|
||||||
icon: "ph-trash ph-bold ph-lg",
|
icon: "ph-trash ph-bold ph-lg",
|
||||||
action: () => {
|
action: () => {
|
||||||
deleteFolder(
|
deleteFolder(
|
||||||
folder.value as Misskey.entities.DriveFolder
|
folder.value as Misskey.entities.DriveFolder,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -725,7 +725,7 @@ function getMenu() {
|
||||||
function showMenu(ev: MouseEvent) {
|
function showMenu(ev: MouseEvent) {
|
||||||
os.popupMenu(
|
os.popupMenu(
|
||||||
getMenu(),
|
getMenu(),
|
||||||
(ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined
|
(ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
? i18n.ts.selectFiles
|
? i18n.ts.selectFiles
|
||||||
: i18n.ts.selectFolders
|
: i18n.ts.selectFolders
|
||||||
: type === "file"
|
: type === "file"
|
||||||
? i18n.ts.selectFile
|
? i18n.ts.selectFile
|
||||||
: i18n.ts.selectFolder
|
: i18n.ts.selectFolder
|
||||||
}}
|
}}
|
||||||
<span
|
<span
|
||||||
v-if="selected.length > 0"
|
v-if="selected.length > 0"
|
||||||
|
@ -50,7 +50,7 @@ withDefaults(
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
type: "file",
|
type: "file",
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
class="_button"
|
class="_button"
|
||||||
@click.prevent="
|
@click.prevent="
|
||||||
applyUnicodeSkinTone(
|
applyUnicodeSkinTone(
|
||||||
props.skinTones.indexOf(skinTone) + 1
|
props.skinTones.indexOf(skinTone) + 1,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
|
@ -99,7 +99,7 @@ watch(
|
||||||
() => props.emojis,
|
() => props.emojis,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
localEmojis.value = newVal.map(resolveEmojis);
|
localEmojis.value = newVal.map(resolveEmojis);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -198,7 +198,7 @@ const props = withDefaults(
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
showPinned: true,
|
showPinned: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -218,13 +218,13 @@ const {
|
||||||
} = defaultStore.reactiveState;
|
} = defaultStore.reactiveState;
|
||||||
|
|
||||||
const size = computed(() =>
|
const size = computed(() =>
|
||||||
props.asReactionPicker ? reactionPickerSize.value : 1
|
props.asReactionPicker ? reactionPickerSize.value : 1,
|
||||||
);
|
);
|
||||||
const width = computed(() =>
|
const width = computed(() =>
|
||||||
props.asReactionPicker ? reactionPickerWidth.value : 3
|
props.asReactionPicker ? reactionPickerWidth.value : 3,
|
||||||
);
|
);
|
||||||
const height = computed(() =>
|
const height = computed(() =>
|
||||||
props.asReactionPicker ? reactionPickerHeight.value : 2
|
props.asReactionPicker ? reactionPickerHeight.value : 2,
|
||||||
);
|
);
|
||||||
const customEmojiCategories = emojiCategories;
|
const customEmojiCategories = emojiCategories;
|
||||||
const customEmojis = instance.emojis;
|
const customEmojis = instance.emojis;
|
||||||
|
@ -240,7 +240,7 @@ const resolveEmojis = (e: string) =>
|
||||||
|
|
||||||
const resolvedPinned = computed(() => pinned.value.map(resolveEmojis));
|
const resolvedPinned = computed(() => pinned.value.map(resolveEmojis));
|
||||||
const resolvedRecent = computed(() =>
|
const resolvedRecent = computed(() =>
|
||||||
recentlyUsedEmojis.value.map(resolveEmojis)
|
recentlyUsedEmojis.value.map(resolveEmojis),
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(q, () => {
|
watch(q, () => {
|
||||||
|
@ -282,8 +282,8 @@ watch(q, () => {
|
||||||
(keyword) =>
|
(keyword) =>
|
||||||
emoji.name.includes(keyword) ||
|
emoji.name.includes(keyword) ||
|
||||||
emoji.aliases.some((alias) =>
|
emoji.aliases.some((alias) =>
|
||||||
alias.includes(keyword)
|
alias.includes(keyword),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
matches.add(emoji);
|
matches.add(emoji);
|
||||||
|
@ -354,8 +354,8 @@ watch(q, () => {
|
||||||
(keyword) =>
|
(keyword) =>
|
||||||
emoji.slug.includes(keyword) ||
|
emoji.slug.includes(keyword) ||
|
||||||
emoji.keywords?.some((alias) =>
|
emoji.keywords?.some((alias) =>
|
||||||
alias.includes(keyword)
|
alias.includes(keyword),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
matches.add(emoji);
|
matches.add(emoji);
|
||||||
|
@ -459,7 +459,7 @@ function done(query?: any): boolean | void {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const exactMatchUnicode = emojilist.find(
|
const exactMatchUnicode = emojilist.find(
|
||||||
(emoji) => emoji.emoji === q2 || emoji.slug === q2
|
(emoji) => emoji.emoji === q2 || emoji.slug === q2,
|
||||||
);
|
);
|
||||||
if (exactMatchUnicode) {
|
if (exactMatchUnicode) {
|
||||||
chosen(magUnicodeEmoji(exactMatchUnicode));
|
chosen(magUnicodeEmoji(exactMatchUnicode));
|
||||||
|
|
|
@ -48,7 +48,7 @@ withDefaults(
|
||||||
manualShowing: null,
|
manualShowing: null,
|
||||||
showPinned: true,
|
showPinned: true,
|
||||||
asReactionPicker: false,
|
asReactionPicker: false,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
:key="file.id"
|
:key="file.id"
|
||||||
v-tooltip.mfm="
|
v-tooltip.mfm="
|
||||||
`${file.type}\n${bytes(file.size)}\n${new Date(
|
`${file.type}\n${bytes(file.size)}\n${new Date(
|
||||||
file.createdAt
|
file.createdAt,
|
||||||
).toLocaleString()}\nby ${
|
).toLocaleString()}\nby ${
|
||||||
file.user ? '@' + Acct.toString(file.user) : 'system'
|
file.user ? '@' + Acct.toString(file.user) : 'system'
|
||||||
}`
|
}`
|
||||||
|
|
|
@ -56,7 +56,7 @@ export default defineComponent({
|
||||||
this.persistKey &&
|
this.persistKey &&
|
||||||
localStorage.getItem(localStoragePrefix + this.persistKey)
|
localStorage.getItem(localStoragePrefix + this.persistKey)
|
||||||
? localStorage.getItem(
|
? localStorage.getItem(
|
||||||
localStoragePrefix + this.persistKey
|
localStoragePrefix + this.persistKey,
|
||||||
) === "t"
|
) === "t"
|
||||||
: this.expanded,
|
: this.expanded,
|
||||||
};
|
};
|
||||||
|
@ -66,7 +66,7 @@ export default defineComponent({
|
||||||
if (this.persistKey) {
|
if (this.persistKey) {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
localStoragePrefix + this.persistKey,
|
localStoragePrefix + this.persistKey,
|
||||||
this.showBody ? "t" : "f"
|
this.showBody ? "t" : "f",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -85,9 +85,9 @@ export default defineComponent({
|
||||||
const bg = tinycolor(
|
const bg = tinycolor(
|
||||||
rawBg.startsWith("var(")
|
rawBg.startsWith("var(")
|
||||||
? getComputedStyle(document.documentElement).getPropertyValue(
|
? getComputedStyle(document.documentElement).getPropertyValue(
|
||||||
rawBg.slice(4, -1)
|
rawBg.slice(4, -1),
|
||||||
)
|
)
|
||||||
: rawBg
|
: rawBg,
|
||||||
);
|
);
|
||||||
bg.setAlpha(0.85);
|
bg.setAlpha(0.85);
|
||||||
this.bg = bg.toRgbString();
|
this.bg = bg.toRgbString();
|
||||||
|
@ -123,7 +123,9 @@ export default defineComponent({
|
||||||
.folder-toggle-enter-active,
|
.folder-toggle-enter-active,
|
||||||
.folder-toggle-leave-active {
|
.folder-toggle-leave-active {
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
transition: opacity 0.5s, height 0.5s !important;
|
transition:
|
||||||
|
opacity 0.5s,
|
||||||
|
height 0.5s !important;
|
||||||
}
|
}
|
||||||
.folder-toggle-enter-from {
|
.folder-toggle-enter-from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onBeforeUnmount, onMounted } from "vue";
|
import { onBeforeUnmount, onMounted, ref } from "vue";
|
||||||
import type * as Misskey from "calckey-js";
|
import type * as Misskey from "calckey-js";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
@ -44,20 +44,20 @@ const props = defineProps<{
|
||||||
user: packed.PackUserBase | Misskey.entities.User;
|
user: packed.PackUserBase | Misskey.entities.User;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let state = $ref(i18n.ts.processing);
|
let state = ref(i18n.ts.processing);
|
||||||
const connection = stream.useChannel("main");
|
const connection = stream.useChannel("main");
|
||||||
|
|
||||||
let waitIncoming = $ref(false);
|
let waitIncoming = ref(false);
|
||||||
|
|
||||||
function accept(user: packed.PackUserBase | Misskey.entities.User) {
|
function accept(user: packed.PackUserBase | Misskey.entities.User) {
|
||||||
waitIncoming = true;
|
waitIncoming.value = true;
|
||||||
os.api("following/requests/accept", { userId: user.id }).then(() => {
|
os.api("following/requests/accept", { userId: user.id }).then(() => {
|
||||||
globalEvents.emit("followeeProcessed", user);
|
globalEvents.emit("followeeProcessed", user);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function reject(user: packed.PackUserBase | Misskey.entities.User) {
|
function reject(user: packed.PackUserBase | Misskey.entities.User) {
|
||||||
waitIncoming = true;
|
waitIncoming.value = true;
|
||||||
os.api("following/requests/reject", { userId: user.id }).then(() => {
|
os.api("following/requests/reject", { userId: user.id }).then(() => {
|
||||||
globalEvents.emit("followeeProcessed", user);
|
globalEvents.emit("followeeProcessed", user);
|
||||||
});
|
});
|
||||||
|
|
|
@ -77,7 +77,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 type * as Misskey from "calckey-js";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { stream } from "@/stream";
|
import { stream } from "@/stream";
|
||||||
|
@ -101,26 +101,26 @@ const props = withDefaults(
|
||||||
{
|
{
|
||||||
full: false,
|
full: false,
|
||||||
large: false,
|
large: false,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const isBlocking = computed(() =>
|
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(
|
let isFollowing = ref(
|
||||||
magTransProperty(props.user, "you_follow", "isFollowing")
|
magTransProperty(props.user, "you_follow", "isFollowing"),
|
||||||
);
|
);
|
||||||
let hasPendingFollowRequestFromYou = $ref(
|
let hasPendingFollowRequestFromYou = ref(
|
||||||
magTransProperty(
|
magTransProperty(
|
||||||
props.user,
|
props.user,
|
||||||
"you_request_follow",
|
"you_request_follow",
|
||||||
"hasPendingFollowRequestFromYou"
|
"hasPendingFollowRequestFromYou",
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
let wait = $ref(false);
|
let wait = ref(false);
|
||||||
const connection = stream.useChannel("main");
|
const connection = stream.useChannel("main");
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -137,13 +137,14 @@ function onFollowChange(user: Misskey.entities.User) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (user.id === props.user.id) {
|
if (user.id === props.user.id) {
|
||||||
isFollowing = user.isFollowing;
|
isFollowing.value = user.isFollowing;
|
||||||
hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
hasPendingFollowRequestFromYou.value =
|
||||||
|
user.hasPendingFollowRequestFromYou;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onClick() {
|
async function onClick() {
|
||||||
wait = true;
|
wait.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isBlocking.value) {
|
if (isBlocking.value) {
|
||||||
|
@ -162,7 +163,7 @@ async function onClick() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
emit("refresh");
|
emit("refresh");
|
||||||
} else if (isFollowing) {
|
} else if (isFollowing.value) {
|
||||||
const { canceled } = await os.confirm({
|
const { canceled } = await os.confirm({
|
||||||
type: "warning",
|
type: "warning",
|
||||||
text: i18n.t("unfollowConfirm", {
|
text: i18n.t("unfollowConfirm", {
|
||||||
|
@ -176,29 +177,29 @@ async function onClick() {
|
||||||
userId: props.user.id,
|
userId: props.user.id,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (hasPendingFollowRequestFromYou) {
|
if (hasPendingFollowRequestFromYou.value) {
|
||||||
await os.api("following/requests/cancel", {
|
await os.api("following/requests/cancel", {
|
||||||
userId: props.user.id,
|
userId: props.user.id,
|
||||||
});
|
});
|
||||||
hasPendingFollowRequestFromYou = false;
|
hasPendingFollowRequestFromYou.value = false;
|
||||||
} else {
|
} else {
|
||||||
await os.api("following/create", {
|
await os.api("following/create", {
|
||||||
userId: props.user.id,
|
userId: props.user.id,
|
||||||
});
|
});
|
||||||
hasPendingFollowRequestFromYou = true;
|
hasPendingFollowRequestFromYou.value = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
} finally {
|
} finally {
|
||||||
wait = false;
|
wait.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function menu(ev) {
|
function menu(ev) {
|
||||||
os.popupMenu(
|
os.popupMenu(
|
||||||
getUserMenu(props.user, router),
|
getUserMenu(props.user, router),
|
||||||
ev.currentTarget ?? ev.target
|
ev.currentTarget ?? ev.target,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
|
@ -75,20 +77,20 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let dialog: InstanceType<typeof XModalWindow> = $ref();
|
let dialog: InstanceType<typeof XModalWindow> = ref();
|
||||||
|
|
||||||
let username = $ref("");
|
let username = ref("");
|
||||||
let email = $ref("");
|
let email = ref("");
|
||||||
let processing = $ref(false);
|
let processing = ref(false);
|
||||||
|
|
||||||
async function onSubmit() {
|
async function onSubmit() {
|
||||||
processing = true;
|
processing.value = true;
|
||||||
await os.apiWithDialog("request-reset-password", {
|
await os.apiWithDialog("request-reset-password", {
|
||||||
username,
|
username: username.value,
|
||||||
email,
|
email: email.value,
|
||||||
});
|
});
|
||||||
emit("done");
|
emit("done");
|
||||||
dialog.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<div class="xkpnjxcv _formRoot">
|
<div class="xkpnjxcv _formRoot">
|
||||||
<template
|
<template
|
||||||
v-for="item in Object.keys(form).filter(
|
v-for="item in Object.keys(form).filter(
|
||||||
(item) => !form[item].hidden
|
(item) => !form[item].hidden,
|
||||||
)"
|
)"
|
||||||
>
|
>
|
||||||
<FormInput
|
<FormInput
|
||||||
|
|
|
@ -9,7 +9,7 @@ import * as os from "@/os";
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
XFormula: defineAsyncComponent(
|
XFormula: defineAsyncComponent(
|
||||||
() => import("@/components/MkFormulaCore.vue")
|
() => import("@/components/MkFormulaCore.vue"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -15,6 +15,8 @@ import {
|
||||||
onBeforeUnmount,
|
onBeforeUnmount,
|
||||||
nextTick,
|
nextTick,
|
||||||
watch,
|
watch,
|
||||||
|
shallowRef,
|
||||||
|
ref,
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import { Chart } from "chart.js";
|
import { Chart } from "chart.js";
|
||||||
import tinycolor from "tinycolor2";
|
import tinycolor from "tinycolor2";
|
||||||
|
@ -33,11 +35,11 @@ const props = defineProps<{
|
||||||
src: string;
|
src: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const rootEl = $shallowRef<HTMLDivElement>(null);
|
const rootEl = shallowRef<HTMLDivElement>(null);
|
||||||
const chartEl = $shallowRef<HTMLCanvasElement>(null);
|
const chartEl = shallowRef<HTMLCanvasElement>(null);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
let chartInstance: Chart = null;
|
let chartInstance: Chart = null;
|
||||||
let fetching = $ref(true);
|
let fetching = ref(true);
|
||||||
|
|
||||||
const { handler: externalTooltipHandler } = useChartTooltip({
|
const { handler: externalTooltipHandler } = useChartTooltip({
|
||||||
position: "middle",
|
position: "middle",
|
||||||
|
@ -53,8 +55,8 @@ async function renderChart() {
|
||||||
chartInstance.destroy();
|
chartInstance.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
const wide = rootEl.offsetWidth > 700;
|
const wide = rootEl.value.offsetWidth > 700;
|
||||||
const narrow = rootEl.offsetWidth < 400;
|
const narrow = rootEl.value.offsetWidth < 400;
|
||||||
|
|
||||||
const weeks = wide ? 50 : narrow ? 10 : 25;
|
const weeks = wide ? 50 : narrow ? 10 : 25;
|
||||||
const chartLimit = 7 * weeks;
|
const chartLimit = 7 * weeks;
|
||||||
|
@ -123,7 +125,7 @@ async function renderChart() {
|
||||||
values = addArrays(raw.diffs.normal, raw.diffs.reply, raw.diffs.renote);
|
values = addArrays(raw.diffs.normal, raw.diffs.reply, raw.diffs.renote);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetching = false;
|
fetching.value = false;
|
||||||
|
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
|
@ -141,7 +143,7 @@ async function renderChart() {
|
||||||
|
|
||||||
const marginEachCell = 4;
|
const marginEachCell = 4;
|
||||||
|
|
||||||
chartInstance = new Chart(chartEl, {
|
chartInstance = new Chart(chartEl.value, {
|
||||||
type: "matrix",
|
type: "matrix",
|
||||||
data: {
|
data: {
|
||||||
datasets: [
|
datasets: [
|
||||||
|
@ -257,9 +259,9 @@ async function renderChart() {
|
||||||
watch(
|
watch(
|
||||||
() => props.src,
|
() => props.src,
|
||||||
() => {
|
() => {
|
||||||
fetching = true;
|
fetching.value = true;
|
||||||
renderChart();
|
renderChart();
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import type * as misskey from "calckey-js";
|
import type * as misskey from "calckey-js";
|
||||||
import bytes from "@/filters/bytes";
|
import bytes from "@/filters/bytes";
|
||||||
|
@ -36,14 +38,14 @@ const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
image: misskey.entities.DriveFile;
|
image: misskey.entities.DriveFile;
|
||||||
}>(),
|
}>(),
|
||||||
{}
|
{},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const modal = $ref<InstanceType<typeof MkModal>>();
|
const modal = ref<InstanceType<typeof MkModal>>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import { decode } from "blurhash";
|
import { decode } from "blurhash";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
|
@ -40,23 +40,23 @@ const props = withDefaults(
|
||||||
title: null,
|
title: null,
|
||||||
size: 64,
|
size: 64,
|
||||||
cover: true,
|
cover: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const canvas = $ref<HTMLCanvasElement>();
|
const canvas = ref<HTMLCanvasElement>();
|
||||||
let loaded = $ref(false);
|
let loaded = ref(false);
|
||||||
|
|
||||||
function draw() {
|
function draw() {
|
||||||
if (props.hash == null) return;
|
if (props.hash == null) return;
|
||||||
const pixels = decode(props.hash, props.size, props.size);
|
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);
|
const imageData = ctx!.createImageData(props.size, props.size);
|
||||||
imageData.data.set(pixels);
|
imageData.data.set(pixels);
|
||||||
ctx!.putImageData(imageData, 0, 0);
|
ctx!.putImageData(imageData, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLoad() {
|
function onLoad() {
|
||||||
loaded = true;
|
loaded.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import * as calckey from "calckey-js";
|
import * as calckey from "calckey-js";
|
||||||
import MkMiniChart from "@/components/MkMiniChart.vue";
|
import MkMiniChart from "@/components/MkMiniChart.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
@ -33,7 +35,7 @@ const props = defineProps<{
|
||||||
instance: calckey.entities.Instance;
|
instance: calckey.entities.Instance;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let chartValues = $ref<number[] | null>(null);
|
let chartValues = ref<number[] | null>(null);
|
||||||
|
|
||||||
os.apiGet("charts/instance", {
|
os.apiGet("charts/instance", {
|
||||||
host: props.instance.host,
|
host: props.instance.host,
|
||||||
|
@ -42,7 +44,7 @@ os.apiGet("charts/instance", {
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
||||||
res.requests.received.splice(0, 1);
|
res.requests.received.splice(0, 1);
|
||||||
chartValues = res.requests.received;
|
chartValues.value = res.requests.received;
|
||||||
});
|
});
|
||||||
|
|
||||||
function getInstanceIcon(instance): string {
|
function getInstanceIcon(instance): string {
|
||||||
|
|
|
@ -56,6 +56,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import MkInput from "@/components/form/input.vue";
|
import MkInput from "@/components/form/input.vue";
|
||||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
@ -68,47 +70,47 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let hostname = $ref("");
|
let hostname = ref("");
|
||||||
let instances: Instance[] = $ref([]);
|
let instances: Instance[] = ref([]);
|
||||||
let selected: Instance | null = $ref(null);
|
let selected: Instance | null = ref(null);
|
||||||
let dialogEl = $ref<InstanceType<typeof XModalWindow>>();
|
let dialogEl = ref<InstanceType<typeof XModalWindow>>();
|
||||||
|
|
||||||
let searchOrderLatch = 0;
|
let searchOrderLatch = 0;
|
||||||
const search = () => {
|
const search = () => {
|
||||||
if (hostname === "") {
|
if (hostname.value === "") {
|
||||||
instances = [];
|
instances.value = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchId = ++searchOrderLatch;
|
const searchId = ++searchOrderLatch;
|
||||||
os.api("federation/instances", {
|
os.api("federation/instances", {
|
||||||
host: hostname,
|
host: hostname.value,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
blocked: false,
|
blocked: false,
|
||||||
suspended: false,
|
suspended: false,
|
||||||
sort: "+pubSub",
|
sort: "+pubSub",
|
||||||
}).then((_instances) => {
|
}).then((_instances) => {
|
||||||
if (searchId !== searchOrderLatch) return;
|
if (searchId !== searchOrderLatch) return;
|
||||||
instances = _instances.map(
|
instances.value = _instances.map(
|
||||||
(x) =>
|
(x) =>
|
||||||
({
|
({
|
||||||
id: x.id,
|
id: x.id,
|
||||||
host: x.host,
|
host: x.host,
|
||||||
iconUrl: x.iconUrl,
|
iconUrl: x.iconUrl,
|
||||||
} as Instance)
|
}) as Instance,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const ok = () => {
|
const ok = () => {
|
||||||
if (selected == null) return;
|
if (selected.value == null) return;
|
||||||
emit("ok", selected);
|
emit("ok", selected.value);
|
||||||
dialogEl?.close();
|
dialogEl.value?.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
emit("cancel");
|
emit("cancel");
|
||||||
dialogEl?.close();
|
dialogEl.value?.close();
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { onMounted, ref, shallowRef } from "vue";
|
||||||
import { Chart } from "chart.js";
|
import { Chart } from "chart.js";
|
||||||
import MkSelect from "@/components/form/select.vue";
|
import MkSelect from "@/components/form/select.vue";
|
||||||
import MkChart from "@/components/MkChart.vue";
|
import MkChart from "@/components/MkChart.vue";
|
||||||
|
@ -116,11 +116,11 @@ import { initChart } from "@/scripts/init-chart";
|
||||||
initChart();
|
initChart();
|
||||||
|
|
||||||
const chartLimit = 500;
|
const chartLimit = 500;
|
||||||
let chartSpan = $ref<"hour" | "day">("hour");
|
let chartSpan = ref<"hour" | "day">("hour");
|
||||||
let chartSrc = $ref("active-users");
|
let chartSrc = ref("active-users");
|
||||||
let heatmapSrc = $ref("active-users");
|
let heatmapSrc = ref("active-users");
|
||||||
let subDoughnutEl = $shallowRef<HTMLCanvasElement>();
|
let subDoughnutEl = shallowRef<HTMLCanvasElement>();
|
||||||
let pubDoughnutEl = $shallowRef<HTMLCanvasElement>();
|
let pubDoughnutEl = shallowRef<HTMLCanvasElement>();
|
||||||
|
|
||||||
const { handler: externalTooltipHandler1 } = useChartTooltip({
|
const { handler: externalTooltipHandler1 } = useChartTooltip({
|
||||||
position: "middle",
|
position: "middle",
|
||||||
|
@ -138,7 +138,7 @@ function createDoughnut(chartEl, tooltip, data) {
|
||||||
{
|
{
|
||||||
backgroundColor: data.map((x) => x.color),
|
backgroundColor: data.map((x) => x.color),
|
||||||
borderColor: getComputedStyle(
|
borderColor: getComputedStyle(
|
||||||
document.documentElement
|
document.documentElement,
|
||||||
).getPropertyValue("--panel"),
|
).getPropertyValue("--panel"),
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
hoverOffset: 0,
|
hoverOffset: 0,
|
||||||
|
@ -161,7 +161,7 @@ function createDoughnut(chartEl, tooltip, data) {
|
||||||
ev,
|
ev,
|
||||||
"nearest",
|
"nearest",
|
||||||
{ intersect: true },
|
{ intersect: true },
|
||||||
false
|
false,
|
||||||
)[0];
|
)[0];
|
||||||
if (hit && data[hit.index].onClick) {
|
if (hit && data[hit.index].onClick) {
|
||||||
data[hit.index].onClick();
|
data[hit.index].onClick();
|
||||||
|
@ -189,7 +189,7 @@ function createDoughnut(chartEl, tooltip, data) {
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
os.apiGet("federation/stats", { limit: 30 }).then((fedStats) => {
|
os.apiGet("federation/stats", { limit: 30 }).then((fedStats) => {
|
||||||
createDoughnut(
|
createDoughnut(
|
||||||
subDoughnutEl,
|
subDoughnutEl.value,
|
||||||
externalTooltipHandler1,
|
externalTooltipHandler1,
|
||||||
fedStats.topSubInstances
|
fedStats.topSubInstances
|
||||||
.map((x) => ({
|
.map((x) => ({
|
||||||
|
@ -206,11 +206,11 @@ onMounted(() => {
|
||||||
color: "#80808080",
|
color: "#80808080",
|
||||||
value: fedStats.otherFollowersCount,
|
value: fedStats.otherFollowersCount,
|
||||||
},
|
},
|
||||||
])
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
createDoughnut(
|
createDoughnut(
|
||||||
pubDoughnutEl,
|
pubDoughnutEl.value,
|
||||||
externalTooltipHandler2,
|
externalTooltipHandler2,
|
||||||
fedStats.topPubInstances
|
fedStats.topPubInstances
|
||||||
.map((x) => ({
|
.map((x) => ({
|
||||||
|
@ -227,7 +227,7 @@ onMounted(() => {
|
||||||
color: "#80808080",
|
color: "#80808080",
|
||||||
value: fedStats.otherFollowingCount,
|
value: fedStats.otherFollowingCount,
|
||||||
},
|
},
|
||||||
])
|
]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
v-tooltip="
|
v-tooltip="
|
||||||
capitalize(
|
capitalize(
|
||||||
magTransProperty(instance, 'software_name', 'softwareName') ??
|
magTransProperty(instance, 'software_name', 'softwareName') ??
|
||||||
'?'
|
'?',
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
ref="ticker"
|
ref="ticker"
|
||||||
|
@ -16,6 +16,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import { instanceName } from "@/config";
|
import { instanceName } from "@/config";
|
||||||
import { instance as Instance } from "@/instance";
|
import { instance as Instance } from "@/instance";
|
||||||
import { getProxiedImageUrlNullable } from "@/scripts/media-proxy";
|
import { getProxiedImageUrlNullable } from "@/scripts/media-proxy";
|
||||||
|
@ -27,7 +29,7 @@ const props = defineProps<{
|
||||||
instance?: Misskey.entities.User["instance"] | types.InstanceTicker | null;
|
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
|
// if no instance data is given, this is for the local instance
|
||||||
const instance = props.instance ?? {
|
const instance = props.instance ?? {
|
||||||
|
@ -38,7 +40,7 @@ const instance = props.instance ?? {
|
||||||
name: instanceName,
|
name: instanceName,
|
||||||
themeColor: (
|
themeColor: (
|
||||||
document.querySelector(
|
document.querySelector(
|
||||||
'meta[name="theme-color-orig"]'
|
'meta[name="theme-color-orig"]',
|
||||||
) as HTMLMetaElement
|
) as HTMLMetaElement
|
||||||
)?.content,
|
)?.content,
|
||||||
softwareName: (Instance.softwareName || "Magnetar") as string | null,
|
softwareName: (Instance.softwareName || "Magnetar") as string | null,
|
||||||
|
@ -59,18 +61,18 @@ const bg = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function getInstanceIcon(
|
function getInstanceIcon(
|
||||||
instance?: Misskey.entities.User["instance"] | types.InstanceTicker | null
|
instance?: Misskey.entities.User["instance"] | types.InstanceTicker | null,
|
||||||
): string {
|
): string {
|
||||||
if (!instance) return "/client-assets/dummy.png";
|
if (!instance) return "/client-assets/dummy.png";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
getProxiedImageUrlNullable(
|
getProxiedImageUrlNullable(
|
||||||
magTransProperty(instance, "icon_url", "iconUrl"),
|
magTransProperty(instance, "icon_url", "iconUrl"),
|
||||||
"preview"
|
"preview",
|
||||||
) ??
|
) ??
|
||||||
getProxiedImageUrlNullable(
|
getProxiedImageUrlNullable(
|
||||||
magTransProperty(instance, "favicon_url", "faviconUrl"),
|
magTransProperty(instance, "favicon_url", "faviconUrl"),
|
||||||
"preview"
|
"preview",
|
||||||
) ??
|
) ??
|
||||||
"/client-assets/dummy.png"
|
"/client-assets/dummy.png"
|
||||||
);
|
);
|
||||||
|
@ -106,8 +108,11 @@ function getInstanceIcon(
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-shadow: -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),
|
||||||
|
-1px 1px 0 var(--bg),
|
||||||
|
1px 1px 0 var(--bg);
|
||||||
.article > .main &,
|
.article > .main &,
|
||||||
.header > .body & {
|
.header > .body & {
|
||||||
display: unset;
|
display: unset;
|
||||||
|
|
|
@ -32,7 +32,7 @@ const props = withDefaults(
|
||||||
{
|
{
|
||||||
copy: null,
|
copy: null,
|
||||||
oneline: false,
|
oneline: false,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const copy_ = () => {
|
const copy_ = () => {
|
||||||
|
|
|
@ -46,6 +46,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import MkModal from "@/components/MkModal.vue";
|
import MkModal from "@/components/MkModal.vue";
|
||||||
import { navbarItemDef } from "@/navbar";
|
import { navbarItemDef } from "@/navbar";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
|
@ -59,7 +61,7 @@ const props = withDefaults(
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
anchor: () => ({ x: "right", y: "center" }),
|
anchor: () => ({ x: "right", y: "center" }),
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -70,10 +72,10 @@ const preferedModalType =
|
||||||
deviceKind === "desktop" && props.src != null
|
deviceKind === "desktop" && props.src != null
|
||||||
? "popup"
|
? "popup"
|
||||||
: deviceKind === "smartphone"
|
: deviceKind === "smartphone"
|
||||||
? "drawer"
|
? "drawer"
|
||||||
: "dialog";
|
: "dialog";
|
||||||
|
|
||||||
const modal = $ref<InstanceType<typeof MkModal>>();
|
const modal = ref<InstanceType<typeof MkModal>>();
|
||||||
|
|
||||||
const menu = defaultStore.state.menu;
|
const menu = defaultStore.state.menu;
|
||||||
|
|
||||||
|
@ -91,7 +93,7 @@ const items = Object.keys(navbarItemDef)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
modal.close();
|
modal.value.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent } from "vue";
|
import { defineAsyncComponent, ref } from "vue";
|
||||||
import { url as local } from "@/config";
|
import { url as local } from "@/config";
|
||||||
import { useTooltip } from "@/scripts/use-tooltip";
|
import { useTooltip } from "@/scripts/use-tooltip";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
@ -28,27 +28,27 @@ const props = withDefaults(
|
||||||
url: string;
|
url: string;
|
||||||
rel?: null | string;
|
rel?: null | string;
|
||||||
}>(),
|
}>(),
|
||||||
{}
|
{},
|
||||||
);
|
);
|
||||||
|
|
||||||
const self = props.url.startsWith(local);
|
const self = props.url.startsWith(local);
|
||||||
const attr = self ? "to" : "href";
|
const attr = self ? "to" : "href";
|
||||||
const target = self ? null : "_blank";
|
const target = self ? null : "_blank";
|
||||||
|
|
||||||
const el = $ref();
|
const el = ref();
|
||||||
|
|
||||||
useTooltip($$(el), (showing) => {
|
useTooltip(el, (showing) => {
|
||||||
os.popup(
|
os.popup(
|
||||||
defineAsyncComponent(
|
defineAsyncComponent(
|
||||||
() => import("@/components/MkUrlPreviewPopup.vue")
|
() => import("@/components/MkUrlPreviewPopup.vue"),
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
showing,
|
showing,
|
||||||
url: props.url,
|
url: props.url,
|
||||||
source: el,
|
source: el.value,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
"closed"
|
"closed",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -63,9 +63,9 @@ export default {
|
||||||
: undefined,
|
: undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
$slots.default()
|
$slots.default(),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,30 +17,7 @@
|
||||||
"
|
"
|
||||||
class="audio"
|
class="audio"
|
||||||
>
|
>
|
||||||
<VuePlyr
|
<MagVideoPlayer :video="media"></MagVideoPlayer>
|
||||||
: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>
|
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
v-else
|
v-else
|
||||||
|
@ -58,11 +35,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import VuePlyr from "vue-plyr";
|
|
||||||
import type * as misskey from "calckey-js";
|
import type * as misskey from "calckey-js";
|
||||||
import { ColdDeviceStorage } from "@/store";
|
import { ColdDeviceStorage } from "@/store";
|
||||||
import "vue-plyr/dist/vue-plyr.css";
|
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { packed } from "magnetar-common";
|
import { packed } from "magnetar-common";
|
||||||
import { magTransProperty } from "@/scripts-mag/mag-util";
|
import { magTransProperty } from "@/scripts-mag/mag-util";
|
||||||
|
@ -74,16 +49,8 @@ const props = withDefaults(
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
const audioEl = $ref<HTMLAudioElement | null>();
|
const audioEl = ref<HTMLAudioElement | null>();
|
||||||
let hide = $ref(true);
|
let hide = ref(true);
|
||||||
|
|
||||||
function volumechange() {
|
|
||||||
if (audioEl) ColdDeviceStorage.set("mediaVolume", audioEl.volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (audioEl) audioEl.volume = ColdDeviceStorage.get("mediaVolume");
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -191,10 +191,10 @@ export default defineComponent({
|
||||||
|
|
||||||
caption() {
|
caption() {
|
||||||
const img = document.getElementById(
|
const img = document.getElementById(
|
||||||
"imgtocaption"
|
"imgtocaption",
|
||||||
) as HTMLImageElement;
|
) as HTMLImageElement;
|
||||||
const ta = document.getElementById(
|
const ta = document.getElementById(
|
||||||
"captioninput"
|
"captioninput",
|
||||||
) as HTMLTextAreaElement;
|
) as HTMLTextAreaElement;
|
||||||
os.api("drive/files/caption-image", {
|
os.api("drive/files/caption-image", {
|
||||||
url: img.src,
|
url: img.src,
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watch } from "vue";
|
import { watch, ref } from "vue";
|
||||||
import type * as misskey from "calckey-js";
|
import type * as misskey from "calckey-js";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { getStaticImageUrl } from "@/scripts/get-static-image-url";
|
import { getStaticImageUrl } from "@/scripts/get-static-image-url";
|
||||||
|
@ -78,7 +78,7 @@ const props = defineProps<{
|
||||||
raw?: boolean;
|
raw?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let hide = $ref(true);
|
let hide = ref(true);
|
||||||
|
|
||||||
function noAltTextPopup(event: MouseEvent) {
|
function noAltTextPopup(event: MouseEvent) {
|
||||||
os.alert({
|
os.alert({
|
||||||
|
@ -104,16 +104,16 @@ const url =
|
||||||
props.raw || defaultStore.state.loadRawImages
|
props.raw || defaultStore.state.loadRawImages
|
||||||
? props.image.url
|
? props.image.url
|
||||||
: defaultStore.state.disableShowingAnimatedImages
|
: defaultStore.state.disableShowingAnimatedImages
|
||||||
? getStaticImageUrl(
|
? getStaticImageUrl(
|
||||||
magTransProperty(props.image, "thumbnail_url", "thumbnailUrl")!
|
magTransProperty(props.image, "thumbnail_url", "thumbnailUrl")!,
|
||||||
)
|
)
|
||||||
: magTransProperty(props.image, "thumbnail_url", "thumbnailUrl")!;
|
: magTransProperty(props.image, "thumbnail_url", "thumbnailUrl")!;
|
||||||
|
|
||||||
// Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする
|
// Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする
|
||||||
watch(
|
watch(
|
||||||
() => props.image,
|
() => props.image,
|
||||||
() => {
|
() => {
|
||||||
hide =
|
hide.value =
|
||||||
defaultStore.state.nsfw === "force"
|
defaultStore.state.nsfw === "force"
|
||||||
? true
|
? true
|
||||||
: magTransProperty(props.image, "sensitive", "isSensitive") &&
|
: magTransProperty(props.image, "sensitive", "isSensitive") &&
|
||||||
|
@ -122,7 +122,7 @@ watch(
|
||||||
{
|
{
|
||||||
deep: true,
|
deep: true,
|
||||||
immediate: true,
|
immediate: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<div ref="gallery" @click.stop>
|
<div ref="gallery" @click.stop>
|
||||||
<template
|
<template
|
||||||
v-for="media in mediaList.filter((media) =>
|
v-for="media in mediaList.filter((media) =>
|
||||||
previewable(media)
|
previewable(media),
|
||||||
)"
|
)"
|
||||||
>
|
>
|
||||||
<XVideo
|
<XVideo
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
magTransProperty(
|
magTransProperty(
|
||||||
media,
|
media,
|
||||||
'mime_type',
|
'mime_type',
|
||||||
'type'
|
'type',
|
||||||
).startsWith('video')
|
).startsWith('video')
|
||||||
"
|
"
|
||||||
:key="media.id"
|
:key="media.id"
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
magTransProperty(
|
magTransProperty(
|
||||||
media,
|
media,
|
||||||
'mime_type',
|
'mime_type',
|
||||||
'type'
|
'type',
|
||||||
).startsWith('image')
|
).startsWith('image')
|
||||||
"
|
"
|
||||||
:key="media.id"
|
:key="media.id"
|
||||||
|
@ -87,7 +87,7 @@ onMounted(() => {
|
||||||
const properties = magTransProperty(
|
const properties = magTransProperty(
|
||||||
media,
|
media,
|
||||||
"media_metadata",
|
"media_metadata",
|
||||||
"properties"
|
"properties",
|
||||||
);
|
);
|
||||||
|
|
||||||
const item = {
|
const item = {
|
||||||
|
@ -142,7 +142,7 @@ onMounted(() => {
|
||||||
const properties = magTransProperty(
|
const properties = magTransProperty(
|
||||||
file,
|
file,
|
||||||
"media_metadata",
|
"media_metadata",
|
||||||
"properties"
|
"properties",
|
||||||
);
|
);
|
||||||
|
|
||||||
itemData.src = file.url;
|
itemData.src = file.url;
|
||||||
|
@ -205,7 +205,7 @@ onMounted(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const previewable = (
|
const previewable = (
|
||||||
file: packed.PackDriveFileBase | misskey.entities.DriveFile
|
file: packed.PackDriveFileBase | misskey.entities.DriveFile,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
const type = magTransProperty(file, "mime_type", "type");
|
const type = magTransProperty(file, "mime_type", "type");
|
||||||
|
|
||||||
|
@ -217,7 +217,7 @@ const previewable = (
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
const previewableCount = props.mediaList.filter((media) =>
|
const previewableCount = props.mediaList.filter((media) =>
|
||||||
previewable(media)
|
previewable(media),
|
||||||
).length;
|
).length;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -12,40 +12,8 @@
|
||||||
<span>{{ i18n.ts.clickToShow }}</span>
|
<span>{{ i18n.ts.clickToShow }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="video" :class="{ mini }">
|
<div v-else class="video">
|
||||||
<VuePlyr
|
<MagVideoPlayer :video="video"> </MagVideoPlayer>
|
||||||
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>
|
|
||||||
<button
|
<button
|
||||||
v-tooltip="i18n.ts.hide"
|
v-tooltip="i18n.ts.hide"
|
||||||
class="_button hide"
|
class="_button hide"
|
||||||
|
@ -57,43 +25,29 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref } from "vue";
|
import { ref } from "vue";
|
||||||
import VuePlyr from "vue-plyr";
|
|
||||||
import type * as misskey from "calckey-js";
|
import type * as misskey from "calckey-js";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
import "vue-plyr/dist/vue-plyr.css";
|
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { packed } from "magnetar-common";
|
import { packed } from "magnetar-common";
|
||||||
import { magTransProperty } from "@/scripts-mag/mag-util";
|
import { magTransProperty } from "@/scripts-mag/mag-util";
|
||||||
|
import MagVideoPlayer from "./MagVideoPlayer.vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
video: packed.PackDriveFileBase | misskey.entities.DriveFile;
|
video: packed.PackDriveFileBase | misskey.entities.DriveFile;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const plyr = ref();
|
|
||||||
const mini = ref(false);
|
|
||||||
|
|
||||||
const hide = ref(
|
const hide = ref(
|
||||||
defaultStore.state.nsfw === "force"
|
defaultStore.state.nsfw === "force"
|
||||||
? true
|
? true
|
||||||
: magTransProperty(props.video, "sensitive", "isSensitive") &&
|
: magTransProperty(props.video, "sensitive", "isSensitive") &&
|
||||||
defaultStore.state.nsfw !== "ignore"
|
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>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.video {
|
.video {
|
||||||
position: relative;
|
position: relative;
|
||||||
--plyr-color-main: var(--accent);
|
|
||||||
|
|
||||||
> .hide {
|
> .hide {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -113,31 +67,6 @@ onMounted(() => {
|
||||||
display: block;
|
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 {
|
.icozogqfvdetwohsdglrbswgrejoxbdj {
|
||||||
|
|
|
@ -213,19 +213,19 @@ const emit = defineEmits<{
|
||||||
(ev: "close", actioned?: boolean): void;
|
(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(
|
watch(
|
||||||
() => props.items,
|
() => props.items,
|
||||||
() => {
|
() => {
|
||||||
const items: (MenuItem | MenuPending)[] = [...props.items].filter(
|
const items: (MenuItem | MenuPending)[] = [...props.items].filter(
|
||||||
(item) => item !== undefined
|
(item) => item !== undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
@ -235,24 +235,24 @@ watch(
|
||||||
// if item is Promise
|
// if item is Promise
|
||||||
items[i] = { type: "pending" };
|
items[i] = { type: "pending" };
|
||||||
item.then((actualItem) => {
|
item.then((actualItem) => {
|
||||||
items2[i] = actualItem;
|
items2.value[i] = actualItem;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items2 = items as InnerMenuItem[];
|
items2.value = items as InnerMenuItem[];
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let childMenu = $ref<MenuItem[] | null>();
|
let childMenu = ref<MenuItem[] | null>();
|
||||||
let childTarget = $ref<HTMLElement | null>();
|
let childTarget = ref<HTMLElement | null>();
|
||||||
|
|
||||||
function closeChild() {
|
function closeChild() {
|
||||||
childMenu = null;
|
childMenu.value = null;
|
||||||
childShowingItem = null;
|
childShowingItem.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function childActioned() {
|
function childActioned() {
|
||||||
|
@ -262,11 +262,12 @@ function childActioned() {
|
||||||
|
|
||||||
function onGlobalMousedown(event: MouseEvent) {
|
function onGlobalMousedown(event: MouseEvent) {
|
||||||
if (
|
if (
|
||||||
childTarget &&
|
childTarget.value &&
|
||||||
(event.target === childTarget || childTarget.contains(event.target))
|
(event.target === childTarget.value ||
|
||||||
|
childTarget.value.contains(event.target))
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
if (child && child.checkHit(event)) return;
|
if (child.value && child.value.checkHit(event)) return;
|
||||||
closeChild();
|
closeChild();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,9 +286,9 @@ async function showChildren(item: MenuItem, ev: MouseEvent) {
|
||||||
os.popupMenu(item.children, ev.currentTarget ?? ev.target);
|
os.popupMenu(item.children, ev.currentTarget ?? ev.target);
|
||||||
close();
|
close();
|
||||||
} else {
|
} else {
|
||||||
childTarget = ev.currentTarget ?? ev.target;
|
childTarget.value = ev.currentTarget ?? ev.target;
|
||||||
childMenu = item.children;
|
childMenu.value = item.children;
|
||||||
childShowingItem = item;
|
childShowingItem.value = item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onUnmounted, watch } from "vue";
|
import { onUnmounted, watch, ref } from "vue";
|
||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
import tinycolor from "tinycolor2";
|
import tinycolor from "tinycolor2";
|
||||||
import { useInterval } from "@/scripts/use-interval";
|
import { useInterval } from "@/scripts/use-interval";
|
||||||
|
@ -37,13 +37,13 @@ const props = defineProps<{
|
||||||
const viewBoxX = 50;
|
const viewBoxX = 50;
|
||||||
const viewBoxY = 50;
|
const viewBoxY = 50;
|
||||||
const gradientId = uuid();
|
const gradientId = uuid();
|
||||||
let polylinePoints = $ref("");
|
let polylinePoints = ref("");
|
||||||
let polygonPoints = $ref("");
|
let polygonPoints = ref("");
|
||||||
let headX = $ref<number | null>(null);
|
let headX = ref<number | null>(null);
|
||||||
let headY = $ref<number | null>(null);
|
let headY = ref<number | null>(null);
|
||||||
let clock = $ref<number | null>(null);
|
let clock = ref<number | null>(null);
|
||||||
const accent = tinycolor(
|
const accent = tinycolor(
|
||||||
getComputedStyle(document.documentElement).getPropertyValue("--accent")
|
getComputedStyle(document.documentElement).getPropertyValue("--accent"),
|
||||||
);
|
);
|
||||||
const color = accent.toRgbString();
|
const color = accent.toRgbString();
|
||||||
|
|
||||||
|
@ -56,12 +56,14 @@ function draw(): void {
|
||||||
(1 - n / peak) * viewBoxY,
|
(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];
|
headX.value = _polylinePoints[_polylinePoints.length - 1][0];
|
||||||
headY = _polylinePoints[_polylinePoints.length - 1][1];
|
headY.value = _polylinePoints[_polylinePoints.length - 1][1];
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.src, draw, { immediate: true });
|
watch(() => props.src, draw, { immediate: true });
|
||||||
|
|
|
@ -77,7 +77,16 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 * as os from "@/os";
|
||||||
import { isTouchUsing } from "@/scripts/touch";
|
import { isTouchUsing } from "@/scripts/touch";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
|
@ -116,7 +125,7 @@ const props = withDefaults(
|
||||||
noOverlap: true,
|
noOverlap: true,
|
||||||
transparentBg: false,
|
transparentBg: false,
|
||||||
noReturnFocus: false,
|
noReturnFocus: false,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -130,14 +139,14 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
provide("modal", true);
|
provide("modal", true);
|
||||||
|
|
||||||
let maxHeight = $ref<number>();
|
let maxHeight = ref<number>();
|
||||||
let fixed = $ref(false);
|
let fixed = ref(false);
|
||||||
let transformOrigin = $ref("center");
|
let transformOrigin = ref("center");
|
||||||
let showing = $ref(true);
|
let showing = ref(true);
|
||||||
let content = $shallowRef<HTMLElement>();
|
let content = shallowRef<HTMLElement>();
|
||||||
const zIndex = os.claimZIndex(props.zPriority);
|
const zIndex = os.claimZIndex(props.zPriority);
|
||||||
let useSendAnime = $ref(false);
|
let useSendAnime = ref(false);
|
||||||
const type = $computed<ModalTypes>(() => {
|
const type = computed<ModalTypes>(() => {
|
||||||
if (props.preferType === "auto") {
|
if (props.preferType === "auto") {
|
||||||
if (
|
if (
|
||||||
!defaultStore.state.disableDrawer &&
|
!defaultStore.state.disableDrawer &&
|
||||||
|
@ -152,30 +161,30 @@ const type = $computed<ModalTypes>(() => {
|
||||||
return props.preferType!;
|
return props.preferType!;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const isEnableBgTransparent = $computed(
|
const isEnableBgTransparent = computed(
|
||||||
() => props.transparentBg && type === "popup"
|
() => props.transparentBg && type.value === "popup",
|
||||||
);
|
);
|
||||||
let transitionName = $computed(() =>
|
let transitionName = computed(() =>
|
||||||
defaultStore.state.animation
|
defaultStore.state.animation
|
||||||
? useSendAnime
|
? useSendAnime.value
|
||||||
? "send"
|
? "send"
|
||||||
: type === "drawer"
|
: type.value === "drawer"
|
||||||
? "modal-drawer"
|
? "modal-drawer"
|
||||||
: type === "popup"
|
: type.value === "popup"
|
||||||
? "modal-popup"
|
? "modal-popup"
|
||||||
: "modal"
|
: "modal"
|
||||||
: ""
|
: "",
|
||||||
);
|
);
|
||||||
let transitionDuration = $computed(() =>
|
let transitionDuration = computed(() =>
|
||||||
transitionName === "send"
|
transitionName.value === "send"
|
||||||
? 400
|
? 400
|
||||||
: transitionName === "modal-popup"
|
: transitionName.value === "modal-popup"
|
||||||
? 100
|
? 100
|
||||||
: transitionName === "modal"
|
: transitionName.value === "modal"
|
||||||
? 200
|
? 200
|
||||||
: transitionName === "modal-drawer"
|
: transitionName.value === "modal-drawer"
|
||||||
? 200
|
? 200
|
||||||
: 0
|
: 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
let contentClicking = false;
|
let contentClicking = false;
|
||||||
|
@ -187,12 +196,12 @@ function close(ev, opts: { useSendAnimation?: boolean } = {}) {
|
||||||
// history.forward();
|
// history.forward();
|
||||||
// }
|
// }
|
||||||
if (opts.useSendAnimation) {
|
if (opts.useSendAnimation) {
|
||||||
useSendAnime = true;
|
useSendAnime.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line vue/no-mutating-props
|
// eslint-disable-next-line vue/no-mutating-props
|
||||||
if (props.src) props.src.style.pointerEvents = "auto";
|
if (props.src) props.src.style.pointerEvents = "auto";
|
||||||
showing = false;
|
showing.value = false;
|
||||||
emit("close");
|
emit("close");
|
||||||
if (!props.noReturnFocus) {
|
if (!props.noReturnFocus) {
|
||||||
focusedElement.focus();
|
focusedElement.focus();
|
||||||
|
@ -204,8 +213,8 @@ function onBgClick() {
|
||||||
emit("click");
|
emit("click");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === "drawer") {
|
if (type.value === "drawer") {
|
||||||
maxHeight = window.innerHeight / 1.5;
|
maxHeight.value = window.innerHeight / 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
const keymap = {
|
const keymap = {
|
||||||
|
@ -216,21 +225,21 @@ const MARGIN = 16;
|
||||||
|
|
||||||
const align = () => {
|
const align = () => {
|
||||||
if (props.src == null) return;
|
if (props.src == null) return;
|
||||||
if (type === "drawer") return;
|
if (type.value === "drawer") return;
|
||||||
if (type === "dialog") return;
|
if (type.value === "dialog") return;
|
||||||
|
|
||||||
if (content == null) return;
|
if (content.value == null) return;
|
||||||
|
|
||||||
const srcRect = props.src.getBoundingClientRect();
|
const srcRect = props.src.getBoundingClientRect();
|
||||||
|
|
||||||
const width = content!.offsetWidth;
|
const width = content.value!.offsetWidth;
|
||||||
const height = content!.offsetHeight;
|
const height = content.value!.offsetHeight;
|
||||||
|
|
||||||
let left;
|
let left;
|
||||||
let top;
|
let top;
|
||||||
|
|
||||||
const x = srcRect.left + (fixed ? 0 : window.pageXOffset);
|
const x = srcRect.left + (fixed.value ? 0 : window.pageXOffset);
|
||||||
const y = srcRect.top + (fixed ? 0 : window.pageYOffset);
|
const y = srcRect.top + (fixed.value ? 0 : window.pageYOffset);
|
||||||
|
|
||||||
if (props.anchor.x === "center") {
|
if (props.anchor.x === "center") {
|
||||||
left = x + props.src.offsetWidth / 2 - width / 2;
|
left = x + props.src.offsetWidth / 2 - width / 2;
|
||||||
|
@ -248,7 +257,7 @@ const align = () => {
|
||||||
top = y + props.src.offsetHeight;
|
top = y + props.src.offsetHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fixed) {
|
if (fixed.value) {
|
||||||
// 画面から横にはみ出る場合
|
// 画面から横にはみ出る場合
|
||||||
if (left + width > window.innerWidth) {
|
if (left + width > window.innerWidth) {
|
||||||
left = window.innerWidth - width;
|
left = window.innerWidth - width;
|
||||||
|
@ -261,16 +270,16 @@ const align = () => {
|
||||||
if (top + height > window.innerHeight - MARGIN) {
|
if (top + height > window.innerHeight - MARGIN) {
|
||||||
if (props.noOverlap && props.anchor.x === "center") {
|
if (props.noOverlap && props.anchor.x === "center") {
|
||||||
if (underSpace >= upperSpace / 3) {
|
if (underSpace >= upperSpace / 3) {
|
||||||
maxHeight = underSpace;
|
maxHeight.value = underSpace;
|
||||||
} else {
|
} else {
|
||||||
maxHeight = upperSpace;
|
maxHeight.value = upperSpace;
|
||||||
top = upperSpace + MARGIN - height;
|
top = upperSpace + MARGIN - height;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
top = window.innerHeight - MARGIN - height;
|
top = window.innerHeight - MARGIN - height;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
maxHeight = underSpace;
|
maxHeight.value = underSpace;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 画面から横にはみ出る場合
|
// 画面から横にはみ出る場合
|
||||||
|
@ -286,9 +295,9 @@ const align = () => {
|
||||||
if (top + height - window.scrollY > window.innerHeight - MARGIN) {
|
if (top + height - window.scrollY > window.innerHeight - MARGIN) {
|
||||||
if (props.noOverlap && props.anchor.x === "center") {
|
if (props.noOverlap && props.anchor.x === "center") {
|
||||||
if (underSpace >= upperSpace / 3) {
|
if (underSpace >= upperSpace / 3) {
|
||||||
maxHeight = underSpace;
|
maxHeight.value = underSpace;
|
||||||
} else {
|
} else {
|
||||||
maxHeight = upperSpace;
|
maxHeight.value = upperSpace;
|
||||||
top = window.scrollY + (upperSpace + MARGIN - height);
|
top = window.scrollY + (upperSpace + MARGIN - height);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -300,7 +309,7 @@ const align = () => {
|
||||||
1;
|
1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
maxHeight = underSpace;
|
maxHeight.value = underSpace;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,36 +326,43 @@ const align = () => {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
top >=
|
top >=
|
||||||
srcRect.top + props.src.offsetHeight + (fixed ? 0 : window.pageYOffset)
|
srcRect.top +
|
||||||
|
props.src.offsetHeight +
|
||||||
|
(fixed.value ? 0 : window.pageYOffset)
|
||||||
) {
|
) {
|
||||||
transformOriginY = "top";
|
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";
|
transformOriginY = "bottom";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
left >=
|
left >=
|
||||||
srcRect.left + props.src.offsetWidth + (fixed ? 0 : window.pageXOffset)
|
srcRect.left +
|
||||||
|
props.src.offsetWidth +
|
||||||
|
(fixed.value ? 0 : window.pageXOffset)
|
||||||
) {
|
) {
|
||||||
transformOriginX = "left";
|
transformOriginX = "left";
|
||||||
} else if (
|
} else if (
|
||||||
left + width <=
|
left + width <=
|
||||||
srcRect.left + (fixed ? 0 : window.pageXOffset)
|
srcRect.left + (fixed.value ? 0 : window.pageXOffset)
|
||||||
) {
|
) {
|
||||||
transformOriginX = "right";
|
transformOriginX = "right";
|
||||||
}
|
}
|
||||||
|
|
||||||
transformOrigin = `${transformOriginX} ${transformOriginY}`;
|
transformOrigin.value = `${transformOriginX} ${transformOriginY}`;
|
||||||
|
|
||||||
content.style.left = left + "px";
|
content.value.style.left = left + "px";
|
||||||
content.style.top = top + "px";
|
content.value.style.top = top + "px";
|
||||||
};
|
};
|
||||||
|
|
||||||
const onOpened = () => {
|
const onOpened = () => {
|
||||||
emit("opened");
|
emit("opened");
|
||||||
|
|
||||||
// モーダルコンテンツにマウスボタンが押され、コンテンツ外でマウスボタンが離されたときにモーダルバックグラウンドクリックと判定させないためにマウスイベントを監視しフラグ管理する
|
// モーダルコンテンツにマウスボタンが押され、コンテンツ外でマウスボタンが離されたときにモーダルバックグラウンドクリックと判定させないためにマウスイベントを監視しフラグ管理する
|
||||||
const el = content!.children[0];
|
const el = content.value!.children[0];
|
||||||
el.addEventListener(
|
el.addEventListener(
|
||||||
"mousedown",
|
"mousedown",
|
||||||
(ev) => {
|
(ev) => {
|
||||||
|
@ -359,10 +375,10 @@ const onOpened = () => {
|
||||||
contentClicking = false;
|
contentClicking = false;
|
||||||
}, 100);
|
}, 100);
|
||||||
},
|
},
|
||||||
{ passive: true, once: true }
|
{ passive: true, once: true },
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
{ passive: true }
|
{ passive: true },
|
||||||
);
|
);
|
||||||
// if (props.preferType == "dialog") {
|
// if (props.preferType == "dialog") {
|
||||||
// history.pushState(null, "", location.href);
|
// history.pushState(null, "", location.href);
|
||||||
|
@ -378,19 +394,20 @@ onMounted(() => {
|
||||||
// eslint-disable-next-line vue/no-mutating-props
|
// eslint-disable-next-line vue/no-mutating-props
|
||||||
props.src.style.pointerEvents = "none";
|
props.src.style.pointerEvents = "none";
|
||||||
}
|
}
|
||||||
fixed = type === "drawer" || getFixedContainer(props.src) != null;
|
fixed.value =
|
||||||
|
type.value === "drawer" || getFixedContainer(props.src) != null;
|
||||||
|
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
align();
|
align();
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
new ResizeObserver((entries, observer) => {
|
new ResizeObserver((entries, observer) => {
|
||||||
align();
|
align();
|
||||||
}).observe(content!);
|
}).observe(content.value!);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
@ -414,7 +431,8 @@ defineExpose({
|
||||||
|
|
||||||
> .content {
|
> .content {
|
||||||
transform: translateY(0px);
|
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;
|
transform 0.3s cubic-bezier(0.5, -0.5, 1, 0.5) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -439,7 +457,9 @@ defineExpose({
|
||||||
|
|
||||||
> .content {
|
> .content {
|
||||||
transform-origin: var(--transformOrigin);
|
transform-origin: var(--transformOrigin);
|
||||||
transition: opacity 0.2s, transform 0.2s !important;
|
transition:
|
||||||
|
opacity 0.2s,
|
||||||
|
transform 0.2s !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.transition_modal_enterFrom,
|
.transition_modal_enterFrom,
|
||||||
|
@ -464,7 +484,8 @@ defineExpose({
|
||||||
|
|
||||||
> .content {
|
> .content {
|
||||||
transform-origin: var(--transformOrigin);
|
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;
|
transform 0.2s cubic-bezier(0, 0, 0.2, 1) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ComputedRef, provide } from "vue";
|
import { ComputedRef, provide, ref, computed } from "vue";
|
||||||
import MkModal from "@/components/MkModal.vue";
|
import MkModal from "@/components/MkModal.vue";
|
||||||
import { popout as _popout } from "@/scripts/popout";
|
import { popout as _popout } from "@/scripts/popout";
|
||||||
import copyToClipboard from "@/scripts/copy-to-clipboard";
|
import copyToClipboard from "@/scripts/copy-to-clipboard";
|
||||||
|
@ -76,27 +76,27 @@ const router = new Router(routes, props.initialPath);
|
||||||
|
|
||||||
router.addListener("push", (ctx) => {});
|
router.addListener("push", (ctx) => {});
|
||||||
|
|
||||||
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
|
let pageMetadata = ref<null | ComputedRef<PageMetadata>>();
|
||||||
let rootEl = $ref();
|
let rootEl = ref();
|
||||||
let modal = $ref<InstanceType<typeof MkModal>>();
|
let modal = ref<InstanceType<typeof MkModal>>();
|
||||||
let path = $ref(props.initialPath);
|
let path = ref(props.initialPath);
|
||||||
let width = $ref(860);
|
let width = ref(860);
|
||||||
let height = $ref(660);
|
let height = ref(660);
|
||||||
const history = [];
|
const history = [];
|
||||||
|
|
||||||
provide("router", router);
|
provide("router", router);
|
||||||
provideMetadataReceiver((info) => {
|
provideMetadataReceiver((info) => {
|
||||||
pageMetadata = info;
|
pageMetadata.value = info;
|
||||||
});
|
});
|
||||||
provide("shouldOmitHeaderTitle", true);
|
provide("shouldOmitHeaderTitle", true);
|
||||||
provide("shouldHeaderThin", true);
|
provide("shouldHeaderThin", true);
|
||||||
|
|
||||||
const pageUrl = $computed(() => url + path);
|
const pageUrl = computed(() => url + path.value);
|
||||||
const contextmenu = $computed(() => {
|
const contextmenu = computed(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
type: "label",
|
type: "label",
|
||||||
text: path,
|
text: path.value,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
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",
|
icon: "ph-arrow-square-out ph-bold ph-lg",
|
||||||
text: i18n.ts.openInNewTab,
|
text: i18n.ts.openInNewTab,
|
||||||
action: () => {
|
action: () => {
|
||||||
window.open(pageUrl, "_blank");
|
window.open(pageUrl.value, "_blank");
|
||||||
modal.close();
|
modal.value.close();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "ph-link-simple ph-bold ph-lg",
|
icon: "ph-link-simple ph-bold ph-lg",
|
||||||
text: i18n.ts.copyLink,
|
text: i18n.ts.copyLink,
|
||||||
action: () => {
|
action: () => {
|
||||||
copyToClipboard(pageUrl);
|
copyToClipboard(pageUrl.value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -137,17 +137,17 @@ function back() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function expand() {
|
function expand() {
|
||||||
mainRouter.push(path);
|
mainRouter.push(path.value);
|
||||||
modal.close();
|
modal.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function popout() {
|
function popout() {
|
||||||
_popout(path, rootEl);
|
_popout(path.value, rootEl.value);
|
||||||
modal.close();
|
modal.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onContextmenu(ev: MouseEvent) {
|
function onContextmenu(ev: MouseEvent) {
|
||||||
os.contextMenu(contextmenu, ev);
|
os.contextMenu(contextmenu.value, ev);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
? `${props.height}px`
|
? `${props.height}px`
|
||||||
: null
|
: null
|
||||||
: height
|
: height
|
||||||
? `min(${props.height}px, 100%)`
|
? `min(${props.height}px, 100%)`
|
||||||
: '100%',
|
: '100%',
|
||||||
}"
|
}"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
|
@ -62,6 +62,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { shallowRef } from "vue";
|
||||||
|
|
||||||
import { FocusTrap } from "focus-trap-vue";
|
import { FocusTrap } from "focus-trap-vue";
|
||||||
import MkModal from "./MkModal.vue";
|
import MkModal from "./MkModal.vue";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
@ -80,7 +82,7 @@ const props = withDefaults(
|
||||||
width: 400,
|
width: 400,
|
||||||
height: null,
|
height: null,
|
||||||
scroll: true,
|
scroll: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -90,12 +92,12 @@ const emit = defineEmits<{
|
||||||
(event: "ok"): void;
|
(event: "ok"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let modal = $shallowRef<InstanceType<typeof MkModal>>();
|
let modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
let rootEl = $shallowRef<HTMLElement>();
|
let rootEl = shallowRef<HTMLElement>();
|
||||||
let headerEl = $shallowRef<HTMLElement>();
|
let headerEl = shallowRef<HTMLElement>();
|
||||||
|
|
||||||
const close = (ev) => {
|
const close = (ev) => {
|
||||||
modal?.close(ev);
|
modal.value?.close(ev);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onBgClick = () => {
|
const onBgClick = () => {
|
||||||
|
|
|
@ -22,6 +22,6 @@ watch(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 RouterView from "@/components/global/RouterView.vue";
|
||||||
import XWindow from "@/components/MkWindow.vue";
|
import XWindow from "@/components/MkWindow.vue";
|
||||||
import { popout as _popout } from "@/scripts/popout";
|
import { popout as _popout } from "@/scripts/popout";
|
||||||
|
@ -56,18 +56,18 @@ defineEmits<{
|
||||||
|
|
||||||
const router = new Router(routes, props.initialPath);
|
const router = new Router(routes, props.initialPath);
|
||||||
|
|
||||||
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
|
let pageMetadata = ref<null | ComputedRef<PageMetadata>>();
|
||||||
let windowEl = $ref<InstanceType<typeof XWindow>>();
|
let windowEl = ref<InstanceType<typeof XWindow>>();
|
||||||
const history = $ref<{ path: string; key: any }[]>([
|
const history = ref<{ path: string; key: any }[]>([
|
||||||
{
|
{
|
||||||
path: router.getCurrentPath(),
|
path: router.getCurrentPath(),
|
||||||
key: router.getCurrentKey(),
|
key: router.getCurrentKey(),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const buttonsLeft = $computed(() => {
|
const buttonsLeft = computed(() => {
|
||||||
const buttons = [];
|
const buttons = [];
|
||||||
|
|
||||||
if (history.length > 1) {
|
if (history.value.length > 1) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
icon: "ph-caret-left ph-bold ph-lg",
|
icon: "ph-caret-left ph-bold ph-lg",
|
||||||
onClick: back,
|
onClick: back,
|
||||||
|
@ -76,7 +76,7 @@ const buttonsLeft = $computed(() => {
|
||||||
|
|
||||||
return buttons;
|
return buttons;
|
||||||
});
|
});
|
||||||
const buttonsRight = $computed(() => {
|
const buttonsRight = computed(() => {
|
||||||
const buttons = [
|
const buttons = [
|
||||||
{
|
{
|
||||||
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
||||||
|
@ -89,18 +89,18 @@ const buttonsRight = $computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
router.addListener("push", (ctx) => {
|
router.addListener("push", (ctx) => {
|
||||||
history.push({ path: ctx.path, key: ctx.key });
|
history.value.push({ path: ctx.path, key: ctx.key });
|
||||||
});
|
});
|
||||||
|
|
||||||
provide("router", router);
|
provide("router", router);
|
||||||
provideMetadataReceiver((info) => {
|
provideMetadataReceiver((info) => {
|
||||||
pageMetadata = info;
|
pageMetadata.value = info;
|
||||||
});
|
});
|
||||||
provide("shouldOmitHeaderTitle", true);
|
provide("shouldOmitHeaderTitle", true);
|
||||||
provide("shouldBackButton", false);
|
provide("shouldBackButton", false);
|
||||||
provide("shouldHeaderThin", true);
|
provide("shouldHeaderThin", true);
|
||||||
|
|
||||||
const contextmenu = $computed(() => [
|
const contextmenu = computed(() => [
|
||||||
{
|
{
|
||||||
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
||||||
text: i18n.ts.showInPage,
|
text: i18n.ts.showInPage,
|
||||||
|
@ -116,7 +116,7 @@ const contextmenu = $computed(() => [
|
||||||
text: i18n.ts.openInNewTab,
|
text: i18n.ts.openInNewTab,
|
||||||
action: () => {
|
action: () => {
|
||||||
window.open(url + router.getCurrentPath(), "_blank");
|
window.open(url + router.getCurrentPath(), "_blank");
|
||||||
windowEl.close();
|
windowEl.value.close();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -129,29 +129,29 @@ const contextmenu = $computed(() => [
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function menu(ev) {
|
function menu(ev) {
|
||||||
os.popupMenu(contextmenu, ev.currentTarget ?? ev.target);
|
os.popupMenu(contextmenu.value, ev.currentTarget ?? ev.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
function back() {
|
function back() {
|
||||||
history.pop();
|
history.value.pop();
|
||||||
router.replace(
|
router.replace(
|
||||||
history[history.length - 1].path,
|
history.value[history.value.length - 1].path,
|
||||||
history[history.length - 1].key
|
history.value[history.value.length - 1].key,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
windowEl.close();
|
windowEl.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function expand() {
|
function expand() {
|
||||||
mainRouter.push(router.getCurrentPath(), "forcePage");
|
mainRouter.push(router.getCurrentPath(), "forcePage");
|
||||||
windowEl.close();
|
windowEl.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function popout() {
|
function popout() {
|
||||||
_popout(router.getCurrentPath(), windowEl.$el);
|
_popout(router.getCurrentPath(), windowEl.value.$el);
|
||||||
windowEl.close();
|
windowEl.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
|
|
@ -85,7 +85,7 @@ import { magTransProperty } from "@/scripts-mag/mag-util";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
export type Paging<
|
export type Paging<
|
||||||
E extends keyof misskey.Endpoints = keyof misskey.Endpoints
|
E extends keyof misskey.Endpoints = keyof misskey.Endpoints,
|
||||||
> = {
|
> = {
|
||||||
endpoint: E;
|
endpoint: E;
|
||||||
limit: number;
|
limit: number;
|
||||||
|
@ -119,7 +119,7 @@ const props = withDefaults(
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
displayLimit: 30,
|
displayLimit: 30,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -166,7 +166,7 @@ const init = async (): Promise<void> => {
|
||||||
(err) => {
|
(err) => {
|
||||||
error.value = true;
|
error.value = true;
|
||||||
fetching.value = false;
|
fetching.value = false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ const refresh = async (): Promise<void> => {
|
||||||
(err) => {
|
(err) => {
|
||||||
error.value = true;
|
error.value = true;
|
||||||
fetching.value = false;
|
fetching.value = false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -241,8 +241,8 @@ const fetchMore = async (): Promise<void> => {
|
||||||
magTransProperty(
|
magTransProperty(
|
||||||
lastItem,
|
lastItem,
|
||||||
"createdAt",
|
"createdAt",
|
||||||
"created_at"
|
"created_at",
|
||||||
)
|
),
|
||||||
).getTime()
|
).getTime()
|
||||||
: undefined,
|
: undefined,
|
||||||
untilId: lastItem?.id ?? undefined,
|
untilId: lastItem?.id ?? undefined,
|
||||||
|
@ -259,7 +259,7 @@ const fetchMore = async (): Promise<void> => {
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
moreFetching.value = false;
|
moreFetching.value = false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -311,7 +311,7 @@ const prepend = (item: Item): void => {
|
||||||
queue.value = [];
|
queue.value = [];
|
||||||
items.value = [...queueRemoved, ...items.value].slice(
|
items.value = [...queueRemoved, ...items.value].slice(
|
||||||
0,
|
0,
|
||||||
props.displayLimit
|
props.displayLimit,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -355,7 +355,7 @@ watch(
|
||||||
if (a.length === 0 && b.length === 0) return;
|
if (a.length === 0 && b.length === 0) return;
|
||||||
emit("queue", queue.value.length);
|
emit("queue", queue.value.length);
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
|
|
@ -96,7 +96,7 @@ const emit = defineEmits<{
|
||||||
expiredAfter: number;
|
expiredAfter: number;
|
||||||
choices: string[];
|
choices: string[];
|
||||||
multiple: boolean;
|
multiple: boolean;
|
||||||
}
|
},
|
||||||
): void;
|
): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ const choices = ref(props.modelValue.choices);
|
||||||
const multiple = ref(props.modelValue.multiple);
|
const multiple = ref(props.modelValue.multiple);
|
||||||
const expiration = ref("infinite");
|
const expiration = ref("infinite");
|
||||||
const atDate = ref(
|
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 atTime = ref("00:00");
|
||||||
const after = ref(0);
|
const after = ref(0);
|
||||||
|
@ -166,8 +166,8 @@ function get() {
|
||||||
...(expiration.value === "at"
|
...(expiration.value === "at"
|
||||||
? { expiresAt: calcAt() }
|
? { expiresAt: calcAt() }
|
||||||
: expiration.value === "after"
|
: expiration.value === "after"
|
||||||
? { expiredAfter: calcAfter() }
|
? { expiredAfter: calcAfter() }
|
||||||
: {}),
|
: {}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ watch(
|
||||||
() => emit("update:modelValue", get()),
|
() => emit("update:modelValue", get()),
|
||||||
{
|
{
|
||||||
deep: true,
|
deep: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import MkModal from "./MkModal.vue";
|
import MkModal from "./MkModal.vue";
|
||||||
import MkMenu from "./MkMenu.vue";
|
import MkMenu from "./MkMenu.vue";
|
||||||
|
@ -42,7 +44,7 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let modal = $ref<InstanceType<typeof MkModal>>();
|
let modal = ref<InstanceType<typeof MkModal>>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -89,8 +89,8 @@
|
||||||
reply
|
reply
|
||||||
? 'ph-arrow-u-up-left ph-bold ph-lg'
|
? 'ph-arrow-u-up-left ph-bold ph-lg'
|
||||||
: renote
|
: renote
|
||||||
? 'ph-quotes ph-bold ph-lg'
|
? 'ph-quotes ph-bold ph-lg'
|
||||||
: 'ph-paper-plane-tilt ph-bold ph-lg'
|
: 'ph-paper-plane-tilt ph-bold ph-lg'
|
||||||
"
|
"
|
||||||
></i>
|
></i>
|
||||||
</button>
|
</button>
|
||||||
|
@ -235,7 +235,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 mfm from "mfm-js";
|
||||||
import * as misskey from "calckey-js";
|
import * as misskey from "calckey-js";
|
||||||
import insertTextAtCursor from "insert-text-at-cursor";
|
import insertTextAtCursor from "insert-text-at-cursor";
|
||||||
|
@ -295,7 +303,7 @@ const props = withDefaults(
|
||||||
initialVisibleUsers: () => [],
|
initialVisibleUsers: () => [],
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
showMfmCheatSheet: true,
|
showMfmCheatSheet: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -304,47 +312,47 @@ const emit = defineEmits<{
|
||||||
(ev: "esc"): void;
|
(ev: "esc"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const textareaEl = $ref<HTMLTextAreaElement | null>(null);
|
const textareaEl = ref<HTMLTextAreaElement | null>(null);
|
||||||
const cwInputEl = $ref<HTMLInputElement | null>(null);
|
const cwInputEl = ref<HTMLInputElement | null>(null);
|
||||||
const hashtagsInputEl = $ref<HTMLInputElement | null>(null);
|
const hashtagsInputEl = ref<HTMLInputElement | null>(null);
|
||||||
const visibilityButton = $ref<HTMLElement | null>(null);
|
const visibilityButton = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
let posting = $ref(false);
|
let posting = ref(false);
|
||||||
let text = $ref(props.initialText ?? "");
|
let text = ref(props.initialText ?? "");
|
||||||
let files = $ref(props.initialFiles ?? []);
|
let files = ref(props.initialFiles ?? []);
|
||||||
let poll = $ref<{
|
let poll = ref<{
|
||||||
choices: string[];
|
choices: string[];
|
||||||
multiple: boolean;
|
multiple: boolean;
|
||||||
expiresAt: string | null;
|
expiresAt: string | null;
|
||||||
expiredAfter: string | null;
|
expiredAfter: string | null;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
let useCw = $ref(false);
|
let useCw = ref(false);
|
||||||
let showPreview = $ref(false);
|
let showPreview = ref(false);
|
||||||
let cw = $ref<string | null>(null);
|
let cw = ref<string | null>(null);
|
||||||
let localOnly = $ref<boolean>(
|
let localOnly = ref<boolean>(
|
||||||
props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility
|
props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility
|
||||||
? defaultStore.state.localOnly
|
? defaultStore.state.localOnly
|
||||||
: defaultStore.state.defaultNoteLocalOnly
|
: defaultStore.state.defaultNoteLocalOnly,
|
||||||
);
|
);
|
||||||
let visibility = $ref(
|
let visibility = ref(
|
||||||
props.initialVisibility ??
|
props.initialVisibility ??
|
||||||
((defaultStore.state.rememberNoteVisibility
|
((defaultStore.state.rememberNoteVisibility
|
||||||
? defaultStore.state.visibility
|
? defaultStore.state.visibility
|
||||||
: defaultStore.state
|
: defaultStore.state
|
||||||
.defaultNoteVisibility) as (typeof misskey.noteVisibilities)[number])
|
.defaultNoteVisibility) as (typeof misskey.noteVisibilities)[number]),
|
||||||
);
|
);
|
||||||
let visibleUsers = $ref([]);
|
let visibleUsers = ref([]);
|
||||||
if (props.initialVisibleUsers) {
|
if (props.initialVisibleUsers) {
|
||||||
props.initialVisibleUsers.forEach(pushVisibleUser);
|
props.initialVisibleUsers.forEach(pushVisibleUser);
|
||||||
}
|
}
|
||||||
let autocomplete = $ref(null);
|
let autocomplete = ref(null);
|
||||||
let draghover = $ref(false);
|
let draghover = ref(false);
|
||||||
let quoteId = $ref<string | null>(null);
|
let quoteId = ref<string | null>(null);
|
||||||
let hasNotSpecifiedMentions = $ref(false);
|
let hasNotSpecifiedMentions = ref(false);
|
||||||
let recentHashtags = $ref(JSON.parse(localStorage.getItem("hashtags") || "[]"));
|
let recentHashtags = ref(JSON.parse(localStorage.getItem("hashtags") || "[]"));
|
||||||
let imeText = $ref("");
|
let imeText = ref("");
|
||||||
|
|
||||||
const draftKey = $computed((): string => {
|
const draftKey = computed((): string => {
|
||||||
if (props.editId) {
|
if (props.editId) {
|
||||||
return `edit:${props.editId}`;
|
return `edit:${props.editId}`;
|
||||||
}
|
}
|
||||||
|
@ -362,7 +370,7 @@ const draftKey = $computed((): string => {
|
||||||
return key;
|
return key;
|
||||||
});
|
});
|
||||||
|
|
||||||
const placeholder = $computed((): string => {
|
const placeholder = computed((): string => {
|
||||||
if (props.renote) {
|
if (props.renote) {
|
||||||
return i18n.ts._postForm.quotePlaceholder;
|
return i18n.ts._postForm.quotePlaceholder;
|
||||||
} else if (props.reply) {
|
} else if (props.reply) {
|
||||||
|
@ -380,57 +388,60 @@ const placeholder = $computed((): string => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const submitText = $computed((): string => {
|
const submitText = computed((): string => {
|
||||||
return props.editId
|
return props.editId
|
||||||
? i18n.ts.edit
|
? i18n.ts.edit
|
||||||
: props.renote
|
: props.renote
|
||||||
? i18n.ts.quote
|
? i18n.ts.quote
|
||||||
: props.reply
|
: props.reply
|
||||||
? i18n.ts.reply
|
? i18n.ts.reply
|
||||||
: i18n.ts.note;
|
: i18n.ts.note;
|
||||||
});
|
});
|
||||||
|
|
||||||
const textLength = $computed((): number => {
|
const textLength = computed((): number => {
|
||||||
return length((preprocess(text) + imeText).trim());
|
return length((preprocess(text.value) + imeText.value).trim());
|
||||||
});
|
});
|
||||||
|
|
||||||
const maxTextLength = $computed((): number => {
|
const maxTextLength = computed((): number => {
|
||||||
return instance ? instance.maxNoteTextLength : 1000;
|
return instance ? instance.maxNoteTextLength : 1000;
|
||||||
});
|
});
|
||||||
|
|
||||||
const canPost = $computed((): boolean => {
|
const canPost = computed((): boolean => {
|
||||||
return (
|
return (
|
||||||
!posting &&
|
!posting.value &&
|
||||||
(1 <= textLength || 1 <= files.length || !!poll || !!props.renote) &&
|
(1 <= textLength.value ||
|
||||||
textLength <= maxTextLength &&
|
1 <= files.value.length ||
|
||||||
(!poll || poll.choices.length >= 2)
|
!!poll.value ||
|
||||||
|
!!props.renote) &&
|
||||||
|
textLength.value <= maxTextLength.value &&
|
||||||
|
(!poll.value || poll.value.choices.length >= 2)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const withHashtags = $computed(
|
const withHashtags = computed(
|
||||||
defaultStore.makeGetterSetter("postFormWithHashtags")
|
defaultStore.makeGetterSetter("postFormWithHashtags"),
|
||||||
);
|
);
|
||||||
const hashtags = $computed(defaultStore.makeGetterSetter("postFormHashtags"));
|
const hashtags = computed(defaultStore.makeGetterSetter("postFormHashtags"));
|
||||||
|
|
||||||
watch($$(text), () => {
|
watch(text, () => {
|
||||||
checkMissingMention();
|
checkMissingMention();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
$$(visibleUsers),
|
visibleUsers,
|
||||||
() => {
|
() => {
|
||||||
checkMissingMention();
|
checkMissingMention();
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
deep: true,
|
deep: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (props.mention) {
|
if (props.mention) {
|
||||||
text = props.mention.host
|
text.value = props.mention.host
|
||||||
? `@${props.mention.username}@${toASCII(props.mention.host)}`
|
? `@${props.mention.username}@${toASCII(props.mention.host)}`
|
||||||
: `@${props.mention.username}`;
|
: `@${props.mention.username}`;
|
||||||
text += " ";
|
text.value += " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -438,7 +449,7 @@ if (
|
||||||
(props.reply.user.username !== $i.username ||
|
(props.reply.user.username !== $i.username ||
|
||||||
(props.reply.user.host != null && props.reply.user.host !== host))
|
(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
|
props.reply.user.host != null
|
||||||
? "@" + toASCII(props.reply.user.host)
|
? "@" + toASCII(props.reply.user.host)
|
||||||
: ""
|
: ""
|
||||||
|
@ -453,17 +464,17 @@ if (props.reply && props.reply.text != null) {
|
||||||
const mention = x.host
|
const mention = x.host
|
||||||
? `@${x.username}@${toASCII(x.host)}`
|
? `@${x.username}@${toASCII(x.host)}`
|
||||||
: otherHost == null || otherHost === host
|
: otherHost == null || otherHost === host
|
||||||
? `@${x.username}`
|
? `@${x.username}`
|
||||||
: `@${x.username}@${toASCII(otherHost)}`;
|
: `@${x.username}@${toASCII(otherHost)}`;
|
||||||
|
|
||||||
// 自分は除外
|
// 自分は除外
|
||||||
if ($i.username === x.username && (x.host == null || x.host === host))
|
if ($i.username === x.username && (x.host == null || x.host === host))
|
||||||
continue;
|
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 (
|
if (
|
||||||
props.reply &&
|
props.reply &&
|
||||||
["home", "followers", "specified"].includes(
|
["home", "followers", "specified"].includes(
|
||||||
magLegacyVisibility(props.reply.visibility)
|
magLegacyVisibility(props.reply.visibility),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
magLegacyVisibility(props.reply.visibility) === "home" &&
|
magLegacyVisibility(props.reply.visibility) === "home" &&
|
||||||
visibility === "followers"
|
visibility.value === "followers"
|
||||||
) {
|
) {
|
||||||
visibility = "followers";
|
visibility.value = "followers";
|
||||||
} else if (
|
} else if (
|
||||||
["home", "followers"].includes(
|
["home", "followers"].includes(
|
||||||
magLegacyVisibility(props.reply.visibility)
|
magLegacyVisibility(props.reply.visibility),
|
||||||
) &&
|
) &&
|
||||||
visibility === "specified"
|
visibility.value === "specified"
|
||||||
) {
|
) {
|
||||||
visibility = "specified";
|
visibility.value = "specified";
|
||||||
} else {
|
} 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;
|
const ids = props.reply.visible_user_ids;
|
||||||
if (ids) {
|
if (ids) {
|
||||||
os.api("users/show", {
|
os.api("users/show", {
|
||||||
userIds: ids.filter(
|
userIds: ids.filter(
|
||||||
(uid) => uid !== $i.id && uid !== props.reply!.user.id
|
(uid) => uid !== $i.id && uid !== props.reply!.user.id,
|
||||||
),
|
),
|
||||||
}).then((users) => {
|
}).then((users) => {
|
||||||
users.forEach(pushVisibleUser);
|
users.forEach(pushVisibleUser);
|
||||||
|
@ -505,74 +516,74 @@ if (
|
||||||
os.api("users/show", { userId: props.reply.user.id }).then(
|
os.api("users/show", { userId: props.reply.user.id }).then(
|
||||||
(user) => {
|
(user) => {
|
||||||
pushVisibleUser(user);
|
pushVisibleUser(user);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.specified) {
|
if (props.specified) {
|
||||||
visibility = "specified";
|
visibility.value = "specified";
|
||||||
pushVisibleUser(props.specified);
|
pushVisibleUser(props.specified);
|
||||||
}
|
}
|
||||||
|
|
||||||
// keep cw when reply
|
// keep cw when reply
|
||||||
if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
|
if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
|
||||||
useCw = true;
|
useCw.value = true;
|
||||||
cw = props.reply.cw;
|
cw.value = props.reply.cw;
|
||||||
}
|
}
|
||||||
|
|
||||||
function watchForDraft() {
|
function watchForDraft() {
|
||||||
watch($$(text), () => saveDraft());
|
watch(text, () => saveDraft());
|
||||||
watch($$(useCw), () => saveDraft());
|
watch(useCw, () => saveDraft());
|
||||||
watch($$(cw), () => saveDraft());
|
watch(cw, () => saveDraft());
|
||||||
watch($$(poll), () => saveDraft());
|
watch(poll, () => saveDraft());
|
||||||
watch($$(files), () => saveDraft(), { deep: true });
|
watch(files, () => saveDraft(), { deep: true });
|
||||||
watch($$(visibility), () => saveDraft());
|
watch(visibility, () => saveDraft());
|
||||||
watch($$(localOnly), () => saveDraft());
|
watch(localOnly, () => saveDraft());
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkMissingMention() {
|
function checkMissingMention() {
|
||||||
if (visibility === "specified") {
|
if (visibility.value === "specified") {
|
||||||
const ast = mfm.parse(text);
|
const ast = mfm.parse(text.value);
|
||||||
|
|
||||||
for (const x of extractMentions(ast)) {
|
for (const x of extractMentions(ast)) {
|
||||||
if (
|
if (
|
||||||
!visibleUsers.some(
|
!visibleUsers.value.some(
|
||||||
(u) => u.username === x.username && u.host === x.host
|
(u) => u.username === x.username && u.host === x.host,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
hasNotSpecifiedMentions = true;
|
hasNotSpecifiedMentions.value = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hasNotSpecifiedMentions = false;
|
hasNotSpecifiedMentions.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addMissingMention() {
|
function addMissingMention() {
|
||||||
const ast = mfm.parse(text);
|
const ast = mfm.parse(text.value);
|
||||||
|
|
||||||
for (const x of extractMentions(ast)) {
|
for (const x of extractMentions(ast)) {
|
||||||
if (
|
if (
|
||||||
!visibleUsers.some(
|
!visibleUsers.value.some(
|
||||||
(u) => u.username === x.username && u.host === x.host
|
(u) => u.username === x.username && u.host === x.host,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
os.api("users/show", { username: x.username, host: x.host }).then(
|
os.api("users/show", { username: x.username, host: x.host }).then(
|
||||||
(user) => {
|
(user) => {
|
||||||
visibleUsers.push(user);
|
visibleUsers.value.push(user);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePoll() {
|
function togglePoll() {
|
||||||
if (poll) {
|
if (poll.value) {
|
||||||
poll = null;
|
poll.value = null;
|
||||||
} else {
|
} else {
|
||||||
poll = {
|
poll.value = {
|
||||||
choices: ["", ""],
|
choices: ["", ""],
|
||||||
multiple: false,
|
multiple: false,
|
||||||
expiresAt: null,
|
expiresAt: null,
|
||||||
|
@ -582,15 +593,15 @@ function togglePoll() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function addTag(tag: string) {
|
function addTag(tag: string) {
|
||||||
insertTextAtCursor(textareaEl, ` #${tag} `);
|
insertTextAtCursor(textareaEl.value, ` #${tag} `);
|
||||||
}
|
}
|
||||||
|
|
||||||
function focus() {
|
function focus() {
|
||||||
if (textareaEl) {
|
if (textareaEl.value) {
|
||||||
textareaEl.focus();
|
textareaEl.value.focus();
|
||||||
textareaEl.setSelectionRange(
|
textareaEl.value.setSelectionRange(
|
||||||
textareaEl.value.length,
|
textareaEl.value.value.length,
|
||||||
textareaEl.value.length
|
textareaEl.value.value.length,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -599,72 +610,72 @@ function chooseFileFrom(ev) {
|
||||||
selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(
|
selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(
|
||||||
(files_) => {
|
(files_) => {
|
||||||
for (const file of files_) {
|
for (const file of files_) {
|
||||||
files.push(file);
|
files.value.push(file);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function detachFile(id) {
|
function detachFile(id) {
|
||||||
files = files.filter((x) => x.id !== id);
|
files.value = files.value.filter((x) => x.id !== id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateFiles(_files) {
|
function updateFiles(_files) {
|
||||||
files = _files;
|
files.value = _files;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateFileSensitive(file, sensitive) {
|
function updateFileSensitive(file, sensitive) {
|
||||||
const idx = files.findIndex((x) => x.id === file.id);
|
const idx = files.value.findIndex((x) => x.id === file.id);
|
||||||
const f = files[idx];
|
const f = files.value[idx];
|
||||||
if ("isSensitive" in f) f.isSensitive = sensitive;
|
if ("isSensitive" in f) f.isSensitive = sensitive;
|
||||||
else f.sensitive = sensitive;
|
else f.sensitive = sensitive;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateFileName(file, name) {
|
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) {
|
function upload(file: File, name?: string) {
|
||||||
uploadFile(file, defaultStore.state.uploadFolder, name).then((res) => {
|
uploadFile(file, defaultStore.state.uploadFolder, name).then((res) => {
|
||||||
files.push(res);
|
files.value.push(res);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function setVisibility() {
|
function setVisibility() {
|
||||||
os.popup(
|
os.popup(
|
||||||
defineAsyncComponent(
|
defineAsyncComponent(
|
||||||
() => import("@/components/MkVisibilityPicker.vue")
|
() => import("@/components/MkVisibilityPicker.vue"),
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
currentVisibility: visibility,
|
currentVisibility: visibility.value,
|
||||||
currentLocalOnly: localOnly,
|
currentLocalOnly: localOnly.value,
|
||||||
src: visibilityButton,
|
src: visibilityButton.value,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
changeVisibility: (v) => {
|
changeVisibility: (v) => {
|
||||||
visibility = v;
|
visibility.value = v;
|
||||||
if (defaultStore.state.rememberNoteVisibility) {
|
if (defaultStore.state.rememberNoteVisibility) {
|
||||||
defaultStore.set("visibility", visibility);
|
defaultStore.set("visibility", visibility.value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
changeLocalOnly: (v) => {
|
changeLocalOnly: (v) => {
|
||||||
localOnly = v;
|
localOnly.value = v;
|
||||||
if (defaultStore.state.rememberNoteVisibility) {
|
if (defaultStore.state.rememberNoteVisibility) {
|
||||||
defaultStore.set("localOnly", localOnly);
|
defaultStore.set("localOnly", localOnly.value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"closed"
|
"closed",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function pushVisibleUser(user) {
|
function pushVisibleUser(user) {
|
||||||
if (
|
if (
|
||||||
!visibleUsers.some(
|
!visibleUsers.value.some(
|
||||||
(u) => u.username === user.username && u.host === user.host
|
(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) {
|
function removeVisibleUser(user) {
|
||||||
visibleUsers = erase(user, visibleUsers);
|
visibleUsers.value = erase(user, visibleUsers.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
text = "";
|
text.value = "";
|
||||||
files = [];
|
files.value = [];
|
||||||
poll = null;
|
poll.value = null;
|
||||||
quoteId = null;
|
quoteId.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKeydown(ev: KeyboardEvent) {
|
function onKeydown(ev: KeyboardEvent) {
|
||||||
if (
|
if (
|
||||||
(ev.which === 10 || ev.which === 13) &&
|
(ev.which === 10 || ev.which === 13) &&
|
||||||
(ev.ctrlKey || ev.metaKey) &&
|
(ev.ctrlKey || ev.metaKey) &&
|
||||||
canPost
|
canPost.value
|
||||||
)
|
)
|
||||||
post();
|
post();
|
||||||
if (ev.which === 27) emit("esc");
|
if (ev.which === 27) emit("esc");
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCompositionUpdate(ev: CompositionEvent) {
|
function onCompositionUpdate(ev: CompositionEvent) {
|
||||||
imeText = ev.data;
|
imeText.value = ev.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCompositionEnd(ev: CompositionEvent) {
|
function onCompositionEnd(ev: CompositionEvent) {
|
||||||
imeText = "";
|
imeText.value = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onPaste(ev: ClipboardEvent) {
|
async function onPaste(ev: ClipboardEvent) {
|
||||||
for (const { item, i } of Array.from(ev.clipboardData.items).map(
|
for (const { item, i } of Array.from(ev.clipboardData.items).map(
|
||||||
(item, i) => ({ item, i })
|
(item, i) => ({ item, i }),
|
||||||
)) {
|
)) {
|
||||||
if (item.kind === "file") {
|
if (item.kind === "file") {
|
||||||
const file = item.getAsFile();
|
const file = item.getAsFile();
|
||||||
|
@ -713,7 +724,7 @@ async function onPaste(ev: ClipboardEvent) {
|
||||||
const ext = lio >= 0 ? file.name.slice(lio) : "";
|
const ext = lio >= 0 ? file.name.slice(lio) : "";
|
||||||
const formatted = `${formatTimeString(
|
const formatted = `${formatTimeString(
|
||||||
new Date(file.lastModified),
|
new Date(file.lastModified),
|
||||||
defaultStore.state.pastedFileName
|
defaultStore.state.pastedFileName,
|
||||||
).replace(/{{number}}/g, `${i + 1}`)}${ext}`;
|
).replace(/{{number}}/g, `${i + 1}`)}${ext}`;
|
||||||
upload(file, formatted);
|
upload(file, formatted);
|
||||||
}
|
}
|
||||||
|
@ -721,7 +732,7 @@ async function onPaste(ev: ClipboardEvent) {
|
||||||
|
|
||||||
const paste = ev.clipboardData.getData("text");
|
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();
|
ev.preventDefault();
|
||||||
|
|
||||||
os.yesno({
|
os.yesno({
|
||||||
|
@ -729,11 +740,11 @@ async function onPaste(ev: ClipboardEvent) {
|
||||||
text: i18n.ts.quoteQuestion,
|
text: i18n.ts.quoteQuestion,
|
||||||
}).then(({ canceled }) => {
|
}).then(({ canceled }) => {
|
||||||
if (canceled) {
|
if (canceled) {
|
||||||
insertTextAtCursor(textareaEl, paste);
|
insertTextAtCursor(textareaEl.value, paste);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
quoteId = paste
|
quoteId.value = paste
|
||||||
.substring(url.length)
|
.substring(url.length)
|
||||||
.match(/^\/notes\/(.+?)\/?$/)[1];
|
.match(/^\/notes\/(.+?)\/?$/)[1];
|
||||||
});
|
});
|
||||||
|
@ -746,7 +757,7 @@ function onDragover(ev) {
|
||||||
const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
|
const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
|
||||||
if (isFile || isDriveFile) {
|
if (isFile || isDriveFile) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
draghover = true;
|
draghover.value = true;
|
||||||
switch (ev.dataTransfer.effectAllowed) {
|
switch (ev.dataTransfer.effectAllowed) {
|
||||||
case "all":
|
case "all":
|
||||||
case "uninitialized":
|
case "uninitialized":
|
||||||
|
@ -767,15 +778,15 @@ function onDragover(ev) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDragenter(ev) {
|
function onDragenter(ev) {
|
||||||
draghover = true;
|
draghover.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDragleave(ev) {
|
function onDragleave(ev) {
|
||||||
draghover = false;
|
draghover.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDrop(ev): void {
|
function onDrop(ev): void {
|
||||||
draghover = false;
|
draghover.value = false;
|
||||||
|
|
||||||
// ファイルだったら
|
// ファイルだったら
|
||||||
if (ev.dataTransfer.files.length > 0) {
|
if (ev.dataTransfer.files.length > 0) {
|
||||||
|
@ -788,7 +799,7 @@ function onDrop(ev): void {
|
||||||
const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
|
const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
|
||||||
if (driveFile != null && driveFile !== "") {
|
if (driveFile != null && driveFile !== "") {
|
||||||
const file = JSON.parse(driveFile);
|
const file = JSON.parse(driveFile);
|
||||||
files.push(file);
|
files.value.push(file);
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
@ -797,16 +808,16 @@ function onDrop(ev): void {
|
||||||
function saveDraft() {
|
function saveDraft() {
|
||||||
const draftData = JSON.parse(localStorage.getItem("drafts") || "{}");
|
const draftData = JSON.parse(localStorage.getItem("drafts") || "{}");
|
||||||
|
|
||||||
draftData[draftKey] = {
|
draftData[draftKey.value] = {
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
data: {
|
data: {
|
||||||
text: text,
|
text: text.value,
|
||||||
useCw: useCw,
|
useCw: useCw.value,
|
||||||
cw: cw,
|
cw: cw.value,
|
||||||
visibility: visibility,
|
visibility: visibility.value,
|
||||||
localOnly: localOnly,
|
localOnly: localOnly.value,
|
||||||
files: files,
|
files: files.value,
|
||||||
poll: poll,
|
poll: poll.value,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -816,36 +827,37 @@ function saveDraft() {
|
||||||
function deleteDraft() {
|
function deleteDraft() {
|
||||||
const draftData = JSON.parse(localStorage.getItem("drafts") || "{}");
|
const draftData = JSON.parse(localStorage.getItem("drafts") || "{}");
|
||||||
|
|
||||||
delete draftData[draftKey];
|
delete draftData[draftKey.value];
|
||||||
|
|
||||||
localStorage.setItem("drafts", JSON.stringify(draftData));
|
localStorage.setItem("drafts", JSON.stringify(draftData));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function post() {
|
async function post() {
|
||||||
const processedText = preprocess(text);
|
const processedText = preprocess(text.value);
|
||||||
|
|
||||||
let postData = {
|
let postData = {
|
||||||
editId: props.editId ? props.editId : undefined,
|
editId: props.editId ? props.editId : undefined,
|
||||||
text: processedText === "" ? undefined : processedText,
|
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,
|
replyId: props.reply ? props.reply.id : undefined,
|
||||||
renoteId: props.renote
|
renoteId: props.renote
|
||||||
? props.renote.id
|
? props.renote.id
|
||||||
: quoteId
|
: quoteId.value
|
||||||
? quoteId
|
? quoteId.value
|
||||||
: undefined,
|
: undefined,
|
||||||
poll: poll,
|
poll: poll.value,
|
||||||
cw: useCw ? cw || "" : undefined,
|
cw: useCw.value ? cw.value || "" : undefined,
|
||||||
localOnly: localOnly,
|
localOnly: localOnly.value,
|
||||||
visibility: visibility,
|
visibility: visibility.value,
|
||||||
visibleUserIds:
|
visibleUserIds:
|
||||||
visibility === "specified"
|
visibility.value === "specified"
|
||||||
? visibleUsers.map((u) => u.id)
|
? visibleUsers.value.map((u) => u.id)
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (withHashtags && hashtags && hashtags.trim() !== "") {
|
if (withHashtags.value && hashtags.value && hashtags.value.trim() !== "") {
|
||||||
const hashtags_ = hashtags
|
const hashtags_ = hashtags.value
|
||||||
.trim()
|
.trim()
|
||||||
.split(" ")
|
.split(" ")
|
||||||
.map((x) => (x.startsWith("#") ? x : "#" + x))
|
.map((x) => (x.startsWith("#") ? x : "#" + x))
|
||||||
|
@ -857,12 +869,14 @@ async function post() {
|
||||||
|
|
||||||
let token = undefined;
|
let token = undefined;
|
||||||
|
|
||||||
if (postAccount) {
|
if (postAccount.value) {
|
||||||
const storedAccounts = await getAccounts();
|
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)
|
os.api(postData.editId ? "notes/edit" : "notes/create", postData, token)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
clear();
|
clear();
|
||||||
|
@ -875,19 +889,19 @@ async function post() {
|
||||||
.filter((x) => x.type === "hashtag")
|
.filter((x) => x.type === "hashtag")
|
||||||
.map((x) => x.props.hashtag);
|
.map((x) => x.props.hashtag);
|
||||||
const history = JSON.parse(
|
const history = JSON.parse(
|
||||||
localStorage.getItem("hashtags") || "[]"
|
localStorage.getItem("hashtags") || "[]",
|
||||||
) as string[];
|
) as string[];
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"hashtags",
|
"hashtags",
|
||||||
JSON.stringify(unique(hashtags_.concat(history)))
|
JSON.stringify(unique(hashtags_.concat(history))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
posting = false;
|
posting.value = false;
|
||||||
postAccount = null;
|
postAccount.value = null;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
posting = false;
|
posting.value = false;
|
||||||
os.alert({
|
os.alert({
|
||||||
type: "error",
|
type: "error",
|
||||||
text: err.message + "\n" + (err as any).id,
|
text: err.message + "\n" + (err as any).id,
|
||||||
|
@ -901,35 +915,35 @@ function cancel() {
|
||||||
|
|
||||||
function insertMention() {
|
function insertMention() {
|
||||||
os.selectUser().then((user) => {
|
os.selectUser().then((user) => {
|
||||||
insertTextAtCursor(textareaEl, "@" + Acct.toString(user) + " ");
|
insertTextAtCursor(textareaEl.value, "@" + Acct.toString(user) + " ");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function insertEmoji(ev: MouseEvent) {
|
async function insertEmoji(ev: MouseEvent) {
|
||||||
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl);
|
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openCheatSheet(ev: MouseEvent) {
|
async function openCheatSheet(ev: MouseEvent) {
|
||||||
os.popup(XCheatSheet, {}, {}, "closed");
|
os.popup(XCheatSheet, {}, {}, "closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
let postAccount = $ref<misskey.entities.UserDetailed | null>(null);
|
let postAccount = ref<misskey.entities.UserDetailed | null>(null);
|
||||||
|
|
||||||
function openAccountMenu(ev: MouseEvent) {
|
function openAccountMenu(ev: MouseEvent) {
|
||||||
openAccountMenu_(
|
openAccountMenu_(
|
||||||
{
|
{
|
||||||
withExtraOperation: false,
|
withExtraOperation: false,
|
||||||
includeCurrentAccount: true,
|
includeCurrentAccount: true,
|
||||||
active: postAccount != null ? postAccount.id : $i.id,
|
active: postAccount.value != null ? postAccount.value.id : $i.id,
|
||||||
onChoose: (account) => {
|
onChoose: (account) => {
|
||||||
if (account.id === $i.id) {
|
if (account.id === $i.id) {
|
||||||
postAccount = null;
|
postAccount.value = null;
|
||||||
} else {
|
} else {
|
||||||
postAccount = account;
|
postAccount.value = account;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ev
|
ev,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -943,27 +957,27 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: detach when unmount
|
// TODO: detach when unmount
|
||||||
new Autocomplete(textareaEl, $$(text));
|
new Autocomplete(textareaEl.value, text);
|
||||||
new Autocomplete(cwInputEl, $$(cw));
|
new Autocomplete(cwInputEl.value, cw);
|
||||||
new Autocomplete(hashtagsInputEl, $$(hashtags));
|
new Autocomplete(hashtagsInputEl.value, hashtags);
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
// 書きかけの投稿を復元
|
// 書きかけの投稿を復元
|
||||||
if (!props.instant && !props.mention && !props.specified) {
|
if (!props.instant && !props.mention && !props.specified) {
|
||||||
const draft = JSON.parse(localStorage.getItem("drafts") || "{}")[
|
const draft = JSON.parse(localStorage.getItem("drafts") || "{}")[
|
||||||
draftKey
|
draftKey.value
|
||||||
];
|
];
|
||||||
if (draft) {
|
if (draft) {
|
||||||
text = draft.data.text;
|
text.value = draft.data.text;
|
||||||
useCw = draft.data.useCw;
|
useCw.value = draft.data.useCw;
|
||||||
cw = draft.data.cw;
|
cw.value = draft.data.cw;
|
||||||
visibility = draft.data.visibility;
|
visibility.value = draft.data.visibility;
|
||||||
localOnly = draft.data.localOnly;
|
localOnly.value = draft.data.localOnly;
|
||||||
files = (draft.data.files || []).filter(
|
files.value = (draft.data.files || []).filter(
|
||||||
(draftFile) => draftFile
|
(draftFile) => draftFile,
|
||||||
);
|
);
|
||||||
if (draft.data.poll) {
|
if (draft.data.poll) {
|
||||||
poll = draft.data.poll;
|
poll.value = draft.data.poll;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -971,13 +985,13 @@ onMounted(() => {
|
||||||
// 削除して編集
|
// 削除して編集
|
||||||
if (props.initialNote) {
|
if (props.initialNote) {
|
||||||
const init = props.initialNote;
|
const init = props.initialNote;
|
||||||
text = init.text ? init.text : "";
|
text.value = init.text ? init.text : "";
|
||||||
|
|
||||||
files = init.attachments ?? [];
|
files.value = init.attachments ?? [];
|
||||||
cw = init.cw;
|
cw.value = init.cw;
|
||||||
useCw = init.cw != null;
|
useCw.value = init.cw != null;
|
||||||
if (init.poll) {
|
if (init.poll) {
|
||||||
poll = {
|
poll.value = {
|
||||||
choices: init.poll.options.map((x) => x.title),
|
choices: init.poll.options.map((x) => x.title),
|
||||||
multiple: init.poll.multiple_choice,
|
multiple: init.poll.multiple_choice,
|
||||||
expiresAt: init.poll.expires_at,
|
expiresAt: init.poll.expires_at,
|
||||||
|
@ -985,9 +999,9 @@ onMounted(() => {
|
||||||
expiredAfter: null,
|
expiredAfter: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
visibility = magLegacyVisibility(init.visibility);
|
visibility.value = magLegacyVisibility(init.visibility);
|
||||||
localOnly = init.local_only ?? false;
|
localOnly.value = init.local_only ?? false;
|
||||||
quoteId = init.renoted_note ? init.renoted_note.id : null;
|
quoteId.value = init.renoted_note ? init.renoted_note.id : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextTick(() => watchForDraft());
|
nextTick(() => watchForDraft());
|
||||||
|
|
|
@ -42,7 +42,7 @@ import { i18n } from "@/i18n";
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
XDraggable: defineAsyncComponent(() =>
|
XDraggable: defineAsyncComponent(() =>
|
||||||
import("vuedraggable").then((x) => x.default)
|
import("vuedraggable").then((x) => x.default),
|
||||||
),
|
),
|
||||||
MkDriveFileThumbnail,
|
MkDriveFileThumbnail,
|
||||||
},
|
},
|
||||||
|
@ -94,7 +94,7 @@ export default defineComponent({
|
||||||
this.$emit(
|
this.$emit(
|
||||||
"changeSensitive",
|
"changeSensitive",
|
||||||
file,
|
file,
|
||||||
!(file.sensitive ?? file.isSensitive)
|
!(file.sensitive ?? file.isSensitive),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -117,7 +117,7 @@ export default defineComponent({
|
||||||
async describe(file) {
|
async describe(file) {
|
||||||
os.popup(
|
os.popup(
|
||||||
defineAsyncComponent(
|
defineAsyncComponent(
|
||||||
() => import("@/components/MkMediaCaption.vue")
|
() => import("@/components/MkMediaCaption.vue"),
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
title: i18n.ts.describeFile,
|
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));
|
.then(() => (this.menu = null));
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { shallowRef } from "vue";
|
||||||
|
|
||||||
import * as misskey from "calckey-js";
|
import * as misskey from "calckey-js";
|
||||||
import MkModal from "@/components/MkModal.vue";
|
import MkModal from "@/components/MkModal.vue";
|
||||||
import MkPostForm from "@/components/MkPostForm.vue";
|
import MkPostForm from "@/components/MkPostForm.vue";
|
||||||
|
@ -45,11 +47,11 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let modal = $shallowRef<InstanceType<typeof MkModal>>();
|
let modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||||
let form = $shallowRef<InstanceType<typeof MkPostForm>>();
|
let form = shallowRef<InstanceType<typeof MkPostForm>>();
|
||||||
|
|
||||||
function onPosted() {
|
function onPosted() {
|
||||||
modal.close({
|
modal.value.close({
|
||||||
useSendAnimation: true,
|
useSendAnimation: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import { $i, getAccounts } from "@/account";
|
import { $i, getAccounts } from "@/account";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
import { instance } from "@/instance";
|
import { instance } from "@/instance";
|
||||||
|
@ -74,12 +76,12 @@ defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// ServiceWorker registration
|
// ServiceWorker registration
|
||||||
let registration = $ref<ServiceWorkerRegistration | undefined>();
|
let registration = ref<ServiceWorkerRegistration | undefined>();
|
||||||
// If this browser supports push notification
|
// If this browser supports push notification
|
||||||
let supported = $ref(false);
|
let supported = ref(false);
|
||||||
// If this browser has already subscribed to push notification
|
// If this browser has already subscribed to push notification
|
||||||
let pushSubscription = $ref<PushSubscription | null>(null);
|
let pushSubscription = ref<PushSubscription | null>(null);
|
||||||
let pushRegistrationInServer = $ref<
|
let pushRegistrationInServer = ref<
|
||||||
| {
|
| {
|
||||||
state?: string;
|
state?: string;
|
||||||
key?: string;
|
key?: string;
|
||||||
|
@ -91,23 +93,24 @@ let pushRegistrationInServer = $ref<
|
||||||
>();
|
>();
|
||||||
|
|
||||||
function subscribe() {
|
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
|
// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
|
||||||
return promiseDialog(
|
return promiseDialog(
|
||||||
registration.pushManager
|
registration.value.pushManager
|
||||||
.subscribe({
|
.subscribe({
|
||||||
userVisibleOnly: true,
|
userVisibleOnly: true,
|
||||||
applicationServerKey: urlBase64ToUint8Array(
|
applicationServerKey: urlBase64ToUint8Array(
|
||||||
instance.swPublickey
|
instance.swPublickey,
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
async (subscription) => {
|
async (subscription) => {
|
||||||
pushSubscription = subscription;
|
pushSubscription.value = subscription;
|
||||||
|
|
||||||
// Register
|
// Register
|
||||||
pushRegistrationInServer = await api("sw/register", {
|
pushRegistrationInServer.value = await api("sw/register", {
|
||||||
endpoint: subscription.endpoint,
|
endpoint: subscription.endpoint,
|
||||||
auth: encode(subscription.getKey("auth")),
|
auth: encode(subscription.getKey("auth")),
|
||||||
publickey: encode(subscription.getKey("p256dh")),
|
publickey: encode(subscription.getKey("p256dh")),
|
||||||
|
@ -118,7 +121,7 @@ function subscribe() {
|
||||||
// 通知が許可されていなかったとき
|
// 通知が許可されていなかったとき
|
||||||
if (err?.name === "NotAllowedError") {
|
if (err?.name === "NotAllowedError") {
|
||||||
console.info(
|
console.info(
|
||||||
"User denied the notification permission request."
|
"User denied the notification permission request.",
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -128,20 +131,20 @@ function subscribe() {
|
||||||
// そのサブスクリプションを解除しておく
|
// そのサブスクリプションを解除しておく
|
||||||
// (これは実行されなさそうだけど、おまじない的に古い実装から残してある)
|
// (これは実行されなさそうだけど、おまじない的に古い実装から残してある)
|
||||||
await unsubscribe();
|
await unsubscribe();
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
null,
|
null,
|
||||||
null
|
null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function unsubscribe() {
|
async function unsubscribe() {
|
||||||
if (!pushSubscription) return;
|
if (!pushSubscription.value) return;
|
||||||
|
|
||||||
const endpoint = pushSubscription.endpoint;
|
const endpoint = pushSubscription.value.endpoint;
|
||||||
const accounts = await getAccounts();
|
const accounts = await getAccounts();
|
||||||
|
|
||||||
pushRegistrationInServer = undefined;
|
pushRegistrationInServer.value = undefined;
|
||||||
|
|
||||||
if ($i && accounts.length >= 2) {
|
if ($i && accounts.length >= 2) {
|
||||||
apiWithDialog("sw/unregister", {
|
apiWithDialog("sw/unregister", {
|
||||||
|
@ -149,11 +152,11 @@ async function unsubscribe() {
|
||||||
endpoint,
|
endpoint,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
pushSubscription.unsubscribe();
|
pushSubscription.value.unsubscribe();
|
||||||
apiWithDialog("sw/unregister", {
|
apiWithDialog("sw/unregister", {
|
||||||
endpoint,
|
endpoint,
|
||||||
});
|
});
|
||||||
pushSubscription = null;
|
pushSubscription.value = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,20 +187,21 @@ if (navigator.serviceWorker == null) {
|
||||||
// TODO: よしなに?
|
// TODO: よしなに?
|
||||||
} else {
|
} else {
|
||||||
navigator.serviceWorker.ready.then(async (swr) => {
|
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) {
|
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", {
|
const res = await api("sw/show-registration", {
|
||||||
endpoint: pushSubscription.endpoint,
|
endpoint: pushSubscription.value.endpoint,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
pushRegistrationInServer = res;
|
pushRegistrationInServer.value = res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,6 +209,6 @@ if (navigator.serviceWorker == null) {
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
pushRegistrationInServer: $$(pushRegistrationInServer),
|
pushRegistrationInServer: pushRegistrationInServer,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, watch } from "vue";
|
import { onMounted, watch, ref } from "vue";
|
||||||
import * as misskey from "calckey-js";
|
import * as misskey from "calckey-js";
|
||||||
import MkUserCardMini from "@/components/MkUserCardMini.vue";
|
import MkUserCardMini from "@/components/MkUserCardMini.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
@ -48,26 +48,26 @@ const props = defineProps<{
|
||||||
noteId: string;
|
noteId: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let tab = $ref<string>();
|
let tab = ref<string>();
|
||||||
let note = $ref<packed.PackNoteBase>();
|
let note = ref<packed.PackNoteBase>();
|
||||||
let reactions = $ref<types.ReactionPair[]>();
|
let reactions = ref<types.ReactionPair[]>();
|
||||||
let users = $ref<misskey.entities.UserLite[]>();
|
let users = ref<misskey.entities.UserLite[]>();
|
||||||
|
|
||||||
watch($$(tab), async () => {
|
watch(tab, async () => {
|
||||||
const res = await os.api("notes/reactions", {
|
const res = await os.api("notes/reactions", {
|
||||||
noteId: props.noteId,
|
noteId: props.noteId,
|
||||||
type: tab,
|
type: tab.value,
|
||||||
limit: 30,
|
limit: 30,
|
||||||
});
|
});
|
||||||
|
|
||||||
users = res.map((x) => x.user);
|
users.value = res.map((x) => x.user);
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
os.magApi(endpoints.GetNoteById, {}, { id: props.noteId }).then((res) => {
|
os.magApi(endpoints.GetNoteById, {}, { id: props.noteId }).then((res) => {
|
||||||
reactions = res.reactions;
|
reactions.value = res.reactions;
|
||||||
tab = magReactionToLegacy(reactions.map((r) => r[0])[0]);
|
tab.value = magReactionToLegacy(reactions.value.map((r) => r[0])[0]);
|
||||||
note = res;
|
note.value = res;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -41,7 +41,7 @@ const props = defineProps<{
|
||||||
const buttonRef = ref<HTMLElement>();
|
const buttonRef = ref<HTMLElement>();
|
||||||
|
|
||||||
const canToggle = computed(
|
const canToggle = computed(
|
||||||
() => !magReactionToLegacy(props.reaction).match(/@\w/) && $i
|
() => !magReactionToLegacy(props.reaction).match(/@\w/) && $i,
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggleReaction = () => {
|
const toggleReaction = () => {
|
||||||
|
@ -90,10 +90,10 @@ useTooltip(
|
||||||
targetElement: buttonRef.value,
|
targetElement: buttonRef.value,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
"closed"
|
"closed",
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
100
|
100,
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ const initialReactions = new Set(
|
||||||
? props.note.reactions.map((v) => magReactionToLegacy(v[0]))
|
? props.note.reactions.map((v) => magReactionToLegacy(v[0]))
|
||||||
: Object.keys(props.note.reactions)
|
: Object.keys(props.note.reactions)
|
||||||
.map((v) => magConvertReaction(v))
|
.map((v) => magConvertReaction(v))
|
||||||
.map(magReactionToLegacy)
|
.map(magReactionToLegacy),
|
||||||
);
|
);
|
||||||
|
|
||||||
const isMe = computed(() => $i && $i.id === props.note.user.id);
|
const isMe = computed(() => $i && $i.id === props.note.user.id);
|
||||||
|
|
|
@ -85,7 +85,7 @@ const props = withDefaults(
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
particle: true,
|
particle: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|
|
@ -122,7 +122,7 @@ export default defineComponent({
|
||||||
action: () => {},
|
action: () => {},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
ev.currentTarget ?? ev.target
|
ev.currentTarget ?? ev.target,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -127,7 +127,7 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import Vue3OtpInput from "vue3-otp-input";
|
import Vue3OtpInput from "vue3-otp-input";
|
||||||
import { defineAsyncComponent } from "vue";
|
import { defineAsyncComponent, ref, computed } from "vue";
|
||||||
import { toUnicode } from "punycode/";
|
import { toUnicode } from "punycode/";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
import MkInput from "@/components/form/input.vue";
|
import MkInput from "@/components/form/input.vue";
|
||||||
|
@ -139,24 +139,24 @@ import { login } from "@/account";
|
||||||
import { instance } from "@/instance";
|
import { instance } from "@/instance";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
let signing = $ref(false);
|
let signing = ref(false);
|
||||||
let user = $ref(null);
|
let user = ref(null);
|
||||||
let username = $ref("");
|
let username = ref("");
|
||||||
let password = $ref("");
|
let password = ref("");
|
||||||
let token = $ref("");
|
let token = ref("");
|
||||||
let host = $ref(toUnicode(configHost));
|
let host = ref(toUnicode(configHost));
|
||||||
let totpLogin = $ref(false);
|
let totpLogin = ref(false);
|
||||||
let credential = $ref(null);
|
let credential = ref(null);
|
||||||
let challengeData = $ref(null);
|
let challengeData = ref(null);
|
||||||
let queryingKey = $ref(false);
|
let queryingKey = ref(false);
|
||||||
let hCaptchaResponse = $ref(null);
|
let hCaptchaResponse = ref(null);
|
||||||
let reCaptchaResponse = $ref(null);
|
let reCaptchaResponse = ref(null);
|
||||||
|
|
||||||
const updateToken = (value: string) => {
|
const updateToken = (value: string) => {
|
||||||
token = value.toString();
|
token.value = value.toString();
|
||||||
};
|
};
|
||||||
|
|
||||||
const meta = $computed(() => instance);
|
const meta = computed(() => instance);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: "login", v: any): void;
|
(ev: "login", v: any): void;
|
||||||
|
@ -182,14 +182,14 @@ const props = defineProps({
|
||||||
|
|
||||||
function onUsernameChange() {
|
function onUsernameChange() {
|
||||||
os.api("users/show", {
|
os.api("users/show", {
|
||||||
username: username,
|
username: username.value,
|
||||||
}).then(
|
}).then(
|
||||||
(userResponse) => {
|
(userResponse) => {
|
||||||
user = userResponse;
|
user.value = userResponse;
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
user = null;
|
user.value = null;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,38 +200,40 @@ function onLogin(res) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function queryKey() {
|
function queryKey() {
|
||||||
queryingKey = true;
|
queryingKey.value = true;
|
||||||
return navigator.credentials
|
return navigator.credentials
|
||||||
.get({
|
.get({
|
||||||
publicKey: {
|
publicKey: {
|
||||||
challenge: byteify(challengeData.challenge, "base64"),
|
challenge: byteify(challengeData.value.challenge, "base64"),
|
||||||
allowCredentials: challengeData.securityKeys.map((key) => ({
|
allowCredentials: challengeData.value.securityKeys.map(
|
||||||
id: byteify(key.id, "hex"),
|
(key) => ({
|
||||||
type: "public-key",
|
id: byteify(key.id, "hex"),
|
||||||
transports: ["usb", "nfc", "ble", "internal"],
|
type: "public-key",
|
||||||
})),
|
transports: ["usb", "nfc", "ble", "internal"],
|
||||||
|
}),
|
||||||
|
),
|
||||||
timeout: 60 * 1000,
|
timeout: 60 * 1000,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
queryingKey = false;
|
queryingKey.value = false;
|
||||||
return Promise.reject(null);
|
return Promise.reject(null);
|
||||||
})
|
})
|
||||||
.then((credential) => {
|
.then((credential) => {
|
||||||
queryingKey = false;
|
queryingKey.value = false;
|
||||||
signing = true;
|
signing.value = true;
|
||||||
return os.api("signin", {
|
return os.api("signin", {
|
||||||
username,
|
username: username.value,
|
||||||
password,
|
password: password.value,
|
||||||
signature: hexify(credential.response.signature),
|
signature: hexify(credential.response.signature),
|
||||||
authenticatorData: hexify(
|
authenticatorData: hexify(
|
||||||
credential.response.authenticatorData
|
credential.response.authenticatorData,
|
||||||
),
|
),
|
||||||
clientDataJSON: hexify(credential.response.clientDataJSON),
|
clientDataJSON: hexify(credential.response.clientDataJSON),
|
||||||
credentialId: credential.id,
|
credentialId: credential.id,
|
||||||
challengeId: challengeData.challengeId,
|
challengeId: challengeData.value.challengeId,
|
||||||
"hcaptcha-response": hCaptchaResponse,
|
"hcaptcha-response": hCaptchaResponse.value,
|
||||||
"g-recaptcha-response": reCaptchaResponse,
|
"g-recaptcha-response": reCaptchaResponse.value,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
@ -244,39 +246,42 @@ function queryKey() {
|
||||||
type: "error",
|
type: "error",
|
||||||
text: i18n.ts.signinFailed,
|
text: i18n.ts.signinFailed,
|
||||||
});
|
});
|
||||||
signing = false;
|
signing.value = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSubmit() {
|
function onSubmit() {
|
||||||
signing = true;
|
signing.value = true;
|
||||||
console.log("submit");
|
console.log("submit");
|
||||||
if (!totpLogin && user && user.twoFactorEnabled) {
|
if (!totpLogin.value && user.value && user.value.twoFactorEnabled) {
|
||||||
if (window.PublicKeyCredential && user.securityKeys) {
|
if (window.PublicKeyCredential && user.value.securityKeys) {
|
||||||
os.api("signin", {
|
os.api("signin", {
|
||||||
username,
|
username: username.value,
|
||||||
password,
|
password: password.value,
|
||||||
"hcaptcha-response": hCaptchaResponse,
|
"hcaptcha-response": hCaptchaResponse.value,
|
||||||
"g-recaptcha-response": reCaptchaResponse,
|
"g-recaptcha-response": reCaptchaResponse.value,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
totpLogin = true;
|
totpLogin.value = true;
|
||||||
signing = false;
|
signing.value = false;
|
||||||
challengeData = res;
|
challengeData.value = res;
|
||||||
return queryKey();
|
return queryKey();
|
||||||
})
|
})
|
||||||
.catch(loginFailed);
|
.catch(loginFailed);
|
||||||
} else {
|
} else {
|
||||||
totpLogin = true;
|
totpLogin.value = true;
|
||||||
signing = false;
|
signing.value = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
os.api("signin", {
|
os.api("signin", {
|
||||||
username,
|
username: username.value,
|
||||||
password,
|
password: password.value,
|
||||||
"hcaptcha-response": hCaptchaResponse,
|
"hcaptcha-response": hCaptchaResponse.value,
|
||||||
"g-recaptcha-response": reCaptchaResponse,
|
"g-recaptcha-response": reCaptchaResponse.value,
|
||||||
token: user && user.twoFactorEnabled ? token : undefined,
|
token:
|
||||||
|
user.value && user.value.twoFactorEnabled
|
||||||
|
? token.value
|
||||||
|
: undefined,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
emit("login", res);
|
emit("login", res);
|
||||||
|
@ -326,9 +331,9 @@ function loginFailed(err) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
challengeData = null;
|
challengeData.value = null;
|
||||||
totpLogin = false;
|
totpLogin.value = false;
|
||||||
signing = false;
|
signing.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetPassword() {
|
function resetPassword() {
|
||||||
|
@ -336,7 +341,7 @@ function resetPassword() {
|
||||||
defineAsyncComponent(() => import("@/components/MkForgotPassword.vue")),
|
defineAsyncComponent(() => import("@/components/MkForgotPassword.vue")),
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
"closed"
|
"closed",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import MkSignin from "@/components/MkSignin.vue";
|
import MkSignin from "@/components/MkSignin.vue";
|
||||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||||
|
@ -25,7 +27,7 @@ const props = withDefaults(
|
||||||
{
|
{
|
||||||
autoSet: false,
|
autoSet: false,
|
||||||
message: "",
|
message: "",
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -34,15 +36,15 @@ const emit = defineEmits<{
|
||||||
(ev: "cancelled"): void;
|
(ev: "cancelled"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
const dialog = ref<InstanceType<typeof XModalWindow>>();
|
||||||
|
|
||||||
function onClose() {
|
function onClose() {
|
||||||
emit("cancelled");
|
emit("cancelled");
|
||||||
dialog.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLogin(res) {
|
function onLogin(res) {
|
||||||
emit("done", res);
|
emit("done", res);
|
||||||
dialog.close();
|
dialog.value.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -284,6 +284,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import getPasswordStrength from "syuilo-password-strength";
|
import getPasswordStrength from "syuilo-password-strength";
|
||||||
import { toUnicode } from "punycode/";
|
import { toUnicode } from "punycode/";
|
||||||
|
@ -303,7 +305,7 @@ const props = withDefaults(
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
autoSet: false,
|
autoSet: false,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -313,14 +315,14 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
const host = toUnicode(config.host);
|
const host = toUnicode(config.host);
|
||||||
|
|
||||||
let hcaptcha = $ref();
|
let hcaptcha = ref();
|
||||||
let recaptcha = $ref();
|
let recaptcha = ref();
|
||||||
|
|
||||||
let username: string = $ref("");
|
let username: string = ref("");
|
||||||
let password: string = $ref("");
|
let password: string = ref("");
|
||||||
let retypedPassword: string = $ref("");
|
let retypedPassword: string = ref("");
|
||||||
let invitationCode: string = $ref("");
|
let invitationCode: string = ref("");
|
||||||
let email = $ref("");
|
let email = ref("");
|
||||||
let usernameState:
|
let usernameState:
|
||||||
| null
|
| null
|
||||||
| "wait"
|
| "wait"
|
||||||
|
@ -329,8 +331,8 @@ let usernameState:
|
||||||
| "error"
|
| "error"
|
||||||
| "invalid-format"
|
| "invalid-format"
|
||||||
| "min-range"
|
| "min-range"
|
||||||
| "max-range" = $ref(null);
|
| "max-range" = ref(null);
|
||||||
let invitationState: null | "entered" = $ref(null);
|
let invitationState: null | "entered" = ref(null);
|
||||||
let emailState:
|
let emailState:
|
||||||
| null
|
| null
|
||||||
| "wait"
|
| "wait"
|
||||||
|
@ -341,141 +343,142 @@ let emailState:
|
||||||
| "unavailable:mx"
|
| "unavailable:mx"
|
||||||
| "unavailable:smtp"
|
| "unavailable:smtp"
|
||||||
| "unavailable"
|
| "unavailable"
|
||||||
| "error" = $ref(null);
|
| "error" = ref(null);
|
||||||
let passwordStrength: "" | "low" | "medium" | "high" = $ref("");
|
let passwordStrength: "" | "low" | "medium" | "high" = ref("");
|
||||||
let passwordRetypeState: null | "match" | "not-match" = $ref(null);
|
let passwordRetypeState: null | "match" | "not-match" = ref(null);
|
||||||
let submitting: boolean = $ref(false);
|
let submitting: boolean = ref(false);
|
||||||
let ToSAgreement: boolean = $ref(false);
|
let ToSAgreement: boolean = ref(false);
|
||||||
let hCaptchaResponse = $ref(null);
|
let hCaptchaResponse = ref(null);
|
||||||
let reCaptchaResponse = $ref(null);
|
let reCaptchaResponse = ref(null);
|
||||||
|
|
||||||
const shouldDisableSubmitting = $computed((): boolean => {
|
const shouldDisableSubmitting = computed((): boolean => {
|
||||||
return (
|
return (
|
||||||
submitting ||
|
submitting.value ||
|
||||||
(instance.tosUrl && !ToSAgreement) ||
|
(instance.tosUrl && !ToSAgreement.value) ||
|
||||||
(instance.enableHcaptcha && !hCaptchaResponse) ||
|
(instance.enableHcaptcha && !hCaptchaResponse.value) ||
|
||||||
(instance.enableRecaptcha && !reCaptchaResponse) ||
|
(instance.enableRecaptcha && !reCaptchaResponse.value) ||
|
||||||
passwordRetypeState === "not-match"
|
passwordRetypeState.value === "not-match"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
function onChangeInvitationCode(): void {
|
function onChangeInvitationCode(): void {
|
||||||
if (invitationCode === "") {
|
if (invitationCode.value === "") {
|
||||||
invitationState = null;
|
invitationState.value = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
invitationState = "entered";
|
invitationState.value = "entered";
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangeUsername(): void {
|
function onChangeUsername(): void {
|
||||||
if (username === "") {
|
if (username.value === "") {
|
||||||
usernameState = null;
|
usernameState.value = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const err = !username.match(/^[a-zA-Z0-9_]+$/)
|
const err = !username.value.match(/^[a-zA-Z0-9_]+$/)
|
||||||
? "invalid-format"
|
? "invalid-format"
|
||||||
: username.length < 1
|
: username.value.length < 1
|
||||||
? "min-range"
|
? "min-range"
|
||||||
: username.length > 20
|
: username.value.length > 20
|
||||||
? "max-range"
|
? "max-range"
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
usernameState = err;
|
usernameState.value = err;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
usernameState = "wait";
|
usernameState.value = "wait";
|
||||||
|
|
||||||
os.api("username/available", {
|
os.api("username/available", {
|
||||||
username,
|
username: username.value,
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
usernameState = result.available ? "ok" : "unavailable";
|
usernameState.value = result.available ? "ok" : "unavailable";
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
usernameState = "error";
|
usernameState.value = "error";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangeEmail(): void {
|
function onChangeEmail(): void {
|
||||||
if (email === "") {
|
if (email.value === "") {
|
||||||
emailState = null;
|
emailState.value = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
emailState = "wait";
|
emailState.value = "wait";
|
||||||
|
|
||||||
os.api("email-address/available", {
|
os.api("email-address/available", {
|
||||||
emailAddress: email,
|
emailAddress: email.value,
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
emailState = result.available
|
emailState.value = result.available
|
||||||
? "ok"
|
? "ok"
|
||||||
: result.reason === "used"
|
: result.reason === "used"
|
||||||
? "unavailable:used"
|
? "unavailable:used"
|
||||||
: result.reason === "format"
|
: result.reason === "format"
|
||||||
? "unavailable:format"
|
? "unavailable:format"
|
||||||
: result.reason === "disposable"
|
: result.reason === "disposable"
|
||||||
? "unavailable:disposable"
|
? "unavailable:disposable"
|
||||||
: result.reason === "mx"
|
: result.reason === "mx"
|
||||||
? "unavailable:mx"
|
? "unavailable:mx"
|
||||||
: result.reason === "smtp"
|
: result.reason === "smtp"
|
||||||
? "unavailable:smtp"
|
? "unavailable:smtp"
|
||||||
: "unavailable";
|
: "unavailable";
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
emailState = "error";
|
emailState.value = "error";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangePassword(): void {
|
function onChangePassword(): void {
|
||||||
if (password === "") {
|
if (password.value === "") {
|
||||||
passwordStrength = "";
|
passwordStrength.value = "";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const strength = getPasswordStrength(password);
|
const strength = getPasswordStrength(password.value);
|
||||||
passwordStrength =
|
passwordStrength.value =
|
||||||
strength > 0.7 ? "high" : strength > 0.3 ? "medium" : "low";
|
strength > 0.7 ? "high" : strength > 0.3 ? "medium" : "low";
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangePasswordRetype(): void {
|
function onChangePasswordRetype(): void {
|
||||||
if (retypedPassword === "") {
|
if (retypedPassword.value === "") {
|
||||||
passwordRetypeState = null;
|
passwordRetypeState.value = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
passwordRetypeState = password === retypedPassword ? "match" : "not-match";
|
passwordRetypeState.value =
|
||||||
|
password.value === retypedPassword.value ? "match" : "not-match";
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSubmit(): void {
|
function onSubmit(): void {
|
||||||
if (submitting) return;
|
if (submitting.value) return;
|
||||||
submitting = true;
|
submitting.value = true;
|
||||||
|
|
||||||
os.api("signup", {
|
os.api("signup", {
|
||||||
username,
|
username: username.value,
|
||||||
password,
|
password: password.value,
|
||||||
emailAddress: email,
|
emailAddress: email.value,
|
||||||
invitationCode,
|
invitationCode: invitationCode.value,
|
||||||
"hcaptcha-response": hCaptchaResponse,
|
"hcaptcha-response": hCaptchaResponse.value,
|
||||||
"g-recaptcha-response": reCaptchaResponse,
|
"g-recaptcha-response": reCaptchaResponse.value,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (instance.emailRequiredForSignup) {
|
if (instance.emailRequiredForSignup) {
|
||||||
os.alert({
|
os.alert({
|
||||||
type: "success",
|
type: "success",
|
||||||
title: i18n.ts._signup.almostThere,
|
title: i18n.ts._signup.almostThere,
|
||||||
text: i18n.t("_signup.emailSent", { email }),
|
text: i18n.t("_signup.emailSent", { email: email.value }),
|
||||||
});
|
});
|
||||||
emit("signupEmailPending");
|
emit("signupEmailPending");
|
||||||
} else {
|
} else {
|
||||||
os.api("signin", {
|
os.api("signin", {
|
||||||
username,
|
username: username.value,
|
||||||
password,
|
password: password.value,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
emit("signup", res);
|
emit("signup", res);
|
||||||
|
|
||||||
|
@ -486,9 +489,9 @@ function onSubmit(): void {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
submitting = false;
|
submitting.value = false;
|
||||||
hcaptcha.reset?.();
|
hcaptcha.value.reset?.();
|
||||||
recaptcha.reset?.();
|
recaptcha.value.reset?.();
|
||||||
|
|
||||||
os.alert({
|
os.alert({
|
||||||
type: "error",
|
type: "error",
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import XSignup from "@/components/MkSignup.vue";
|
import XSignup from "@/components/MkSignup.vue";
|
||||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||||
|
@ -31,7 +33,7 @@ const props = withDefaults(
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
autoSet: false,
|
autoSet: false,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -39,14 +41,14 @@ const emit = defineEmits<{
|
||||||
(ev: "closed"): void;
|
(ev: "closed"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
const dialog = ref<InstanceType<typeof XModalWindow>>();
|
||||||
|
|
||||||
function onSignup(res) {
|
function onSignup(res) {
|
||||||
emit("done", res);
|
emit("done", res);
|
||||||
dialog?.close();
|
dialog.value?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSignupEmailPending() {
|
function onSignupEmailPending() {
|
||||||
dialog?.close();
|
dialog.value?.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -105,13 +105,16 @@ onMounted(() => {
|
||||||
particles.value.push(particle);
|
particles.value.push(particle);
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
particles.value = particles.value.filter(
|
particles.value = particles.value.filter(
|
||||||
(x) => x.id !== particle.id
|
(x) => x.id !== particle.id,
|
||||||
);
|
);
|
||||||
}, particle.dur - 100);
|
}, particle.dur - 100);
|
||||||
|
|
||||||
window.setTimeout(() => {
|
window.setTimeout(
|
||||||
add();
|
() => {
|
||||||
}, 500 + Math.random() * 500);
|
add();
|
||||||
|
},
|
||||||
|
500 + Math.random() * 500,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
add();
|
add();
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,7 @@ useTooltip(buttonRef, async (showing) => {
|
||||||
targetElement: buttonRef.value,
|
targetElement: buttonRef.value,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
"closed"
|
"closed",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -37,13 +37,13 @@ export default defineComponent({
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
this.$emit(
|
this.$emit(
|
||||||
"update:modelValue",
|
"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
Loading…
Reference in New Issue