summaryrefslogtreecommitdiff
path: root/packages/frontend/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/frontend/src')
-rw-r--r--packages/frontend/src/components/MkDialog.vue3
-rw-r--r--packages/frontend/src/components/MkLink.vue12
-rw-r--r--packages/frontend/src/components/MkMediaBanner.vue4
-rw-r--r--packages/frontend/src/components/MkNote.vue6
-rw-r--r--packages/frontend/src/components/MkNoteDetailed.vue6
-rw-r--r--packages/frontend/src/components/MkNoteSub.vue6
-rw-r--r--packages/frontend/src/components/MkPostFormDialog.vue14
-rw-r--r--packages/frontend/src/components/SkNote.vue6
-rw-r--r--packages/frontend/src/components/SkNoteDetailed.vue6
-rw-r--r--packages/frontend/src/components/SkNoteSub.vue6
-rw-r--r--packages/frontend/src/os.ts7
-rw-r--r--packages/frontend/src/pages/drive.file.info.vue6
-rw-r--r--packages/frontend/src/plugin.ts15
-rw-r--r--packages/frontend/src/ui/classic.sidebar.vue1
14 files changed, 72 insertions, 26 deletions
diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue
index 825c1d0513..7dc381b662 100644
--- a/packages/frontend/src/components/MkDialog.vue
+++ b/packages/frontend/src/components/MkDialog.vue
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkLoading v-else-if="type === 'waiting'" :class="$style.iconInner" :em="true"/>
</div>
<header v-if="title" :class="$style.title"><Mfm :text="title"/></header>
- <div v-if="text" :class="$style.text"><Mfm :text="text" :isBlock="true" /></div>
+ <div v-if="text" :class="$style.text"><Mfm :text="text" :isBlock="true" :plain="plain" /></div>
<MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" :autocomplete="input.autocomplete" @keydown="onInputKeydown">
<template v-if="input.type === 'password'" #prefix><i class="ti ti-lock"></i></template>
<template #caption>
@@ -105,6 +105,7 @@ const props = withDefaults(defineProps<{
cancelableByBgClick?: boolean;
okText?: string;
cancelText?: string;
+ plain?: boolean;
}>(), {
type: 'info',
showOkButton: true,
diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue
index 07cf9e0c37..d2819f9f4c 100644
--- a/packages/frontend/src/components/MkLink.vue
+++ b/packages/frontend/src/components/MkLink.vue
@@ -8,6 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:is="self ? 'MkA' : 'a'" ref="el" style="word-break: break-all;" class="_link" :[attr]="self ? url.substring(local.length) : url" :rel="rel ?? 'nofollow noopener'" :target="target"
:behavior="props.navigationBehavior"
:title="url"
+ @click.prevent="self ? true : promptConfirm()"
@click.stop
>
<slot></slot>
@@ -22,6 +23,7 @@ import { useTooltip } from '@/scripts/use-tooltip.js';
import * as os from '@/os.js';
import { isEnabledUrlPreview } from '@/instance.js';
import { MkABehavior } from '@/components/global/MkA.vue';
+import { i18n } from '@/i18n.js';
const props = withDefaults(defineProps<{
url: string;
@@ -47,6 +49,16 @@ if (isEnabledUrlPreview.value) {
});
});
}
+
+async function promptConfirm() {
+ const { canceled } = await os.confirm({
+ type: 'question',
+ text: i18n.tsx.confirmRemoteUrl({ x: props.url }),
+ plain: true,
+ });
+ if (canceled) return;
+ window.open(props.url, '_blank', 'nofollow noopener popup=false');
+}
</script>
<style lang="scss" module>
diff --git a/packages/frontend/src/components/MkMediaBanner.vue b/packages/frontend/src/components/MkMediaBanner.vue
index ed8d43273f..77a86ff2fb 100644
--- a/packages/frontend/src/components/MkMediaBanner.vue
+++ b/packages/frontend/src/components/MkMediaBanner.vue
@@ -5,12 +5,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="$style.root">
- <div v-if="media.isSensitive && hide" :class="$style.sensitive" @click="show">
+ <MkMediaAudio v-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" :audio="media"/>
+ <div v-else-if="media.isSensitive && hide" :class="$style.sensitive" @click="show">
<span style="font-size: 1.6em;"><i class="ti ti-alert-triangle"></i></span>
<b>{{ i18n.ts.sensitive }}</b>
<span>{{ i18n.ts.clickToShow }}</span>
</div>
- <MkMediaAudio v-else-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" :audio="media"/>
<a
v-else :class="$style.download"
:href="media.url"
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index e2f0a4e492..edae1e91b2 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -565,7 +565,8 @@ function quote() {
os.post({
renote: appearNote.value,
channel: appearNote.value.channel,
- }).then(() => {
+ }).then((cancelled) => {
+ if (cancelled) return;
misskeyApi('notes/renotes', {
noteId: appearNote.value.id,
userId: $i?.id,
@@ -589,7 +590,8 @@ function quote() {
} else {
os.post({
renote: appearNote.value,
- }).then(() => {
+ }).then((cancelled) => {
+ if (cancelled) return;
misskeyApi('notes/renotes', {
noteId: appearNote.value.id,
userId: $i?.id,
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 64559ef265..123e94c3e0 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -550,7 +550,8 @@ function quote() {
os.post({
renote: appearNote.value,
channel: appearNote.value.channel,
- }).then(() => {
+ }).then((cancelled) => {
+ if (cancelled) return;
misskeyApi('notes/renotes', {
noteId: appearNote.value.id,
userId: $i?.id,
@@ -574,7 +575,8 @@ function quote() {
} else {
os.post({
renote: appearNote.value,
- }).then(() => {
+ }).then((cancelled) => {
+ if (cancelled) return;
misskeyApi('notes/renotes', {
noteId: appearNote.value.id,
userId: $i?.id,
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 9caed62ce2..45276839ad 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -339,7 +339,8 @@ function quote() {
os.post({
renote: appearNote.value,
channel: appearNote.value.channel,
- }).then(() => {
+ }).then((cancelled) => {
+ if (cancelled) return;
misskeyApi('notes/renotes', {
noteId: props.note.id,
userId: $i.id,
@@ -363,7 +364,8 @@ function quote() {
} else {
os.post({
renote: appearNote.value,
- }).then(() => {
+ }).then((cancelled) => {
+ if (cancelled) return;
misskeyApi('notes/renotes', {
noteId: props.note.id,
userId: $i.id,
diff --git a/packages/frontend/src/components/MkPostFormDialog.vue b/packages/frontend/src/components/MkPostFormDialog.vue
index 947c0ee4d0..811a6378f2 100644
--- a/packages/frontend/src/components/MkPostFormDialog.vue
+++ b/packages/frontend/src/components/MkPostFormDialog.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<MkModal ref="modal" :preferType="'dialog'" @click="modal?.close()" @closed="onModalClosed()" @esc="modal?.close()">
- <MkPostForm ref="form" :class="$style.form" v-bind="props" autofocus freezeAfterPosted @posted="onPosted" @cancel="modal?.close()" @esc="modal?.close()"/>
+ <MkPostForm ref="form" :class="$style.form" v-bind="props" autofocus freezeAfterPosted @posted="onPosted" @cancel="onCancel" @esc="onCancel"/>
</MkModal>
</template>
@@ -37,7 +37,7 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (ev: 'closed'): void;
+ (ev: 'closed', cancelled: boolean): void;
}>();
const modal = shallowRef<InstanceType<typeof MkModal>>();
@@ -47,10 +47,18 @@ function onPosted() {
modal.value?.close({
useSendAnimation: true,
});
+ emit('closed', false);
+}
+
+function onCancel() {
+ // for some reason onModalClosed does not get called properly when closing the model through other functions.
+ modal.value?.close();
+ // emit is required so that the dialog gets properly disposed otherwise it will float around as a "zombie"
+ emit('closed', true);
}
function onModalClosed() {
- emit('closed');
+ emit('closed', true);
}
</script>
diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue
index b02d902482..3d5c5f5fae 100644
--- a/packages/frontend/src/components/SkNote.vue
+++ b/packages/frontend/src/components/SkNote.vue
@@ -565,7 +565,8 @@ function quote() {
os.post({
renote: appearNote.value,
channel: appearNote.value.channel,
- }).then(() => {
+ }).then((cancelled) => {
+ if (cancelled) return;
misskeyApi('notes/renotes', {
noteId: appearNote.value.id,
userId: $i?.id,
@@ -589,7 +590,8 @@ function quote() {
} else {
os.post({
renote: appearNote.value,
- }).then(() => {
+ }).then((cancelled) => {
+ if (cancelled) return;
misskeyApi('notes/renotes', {
noteId: appearNote.value.id,
userId: $i?.id,
diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue
index cca6c7a40c..5b85e21bac 100644
--- a/packages/frontend/src/components/SkNoteDetailed.vue
+++ b/packages/frontend/src/components/SkNoteDetailed.vue
@@ -559,7 +559,8 @@ function quote() {
os.post({
renote: appearNote.value,
channel: appearNote.value.channel,
- }).then(() => {
+ }).then((cancelled) => {
+ if (cancelled) return;
misskeyApi('notes/renotes', {
noteId: appearNote.value.id,
userId: $i?.id,
@@ -583,7 +584,8 @@ function quote() {
} else {
os.post({
renote: appearNote.value,
- }).then(() => {
+ }).then((cancelled) => {
+ if (cancelled) return;
misskeyApi('notes/renotes', {
noteId: appearNote.value.id,
userId: $i?.id,
diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index c2986b2524..fac35191b9 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -353,7 +353,8 @@ function quote() {
os.post({
renote: appearNote.value,
channel: appearNote.value.channel,
- }).then(() => {
+ }).then((cancelled) => {
+ if (cancelled) return;
misskeyApi('notes/renotes', {
noteId: props.note.id,
userId: $i.id,
@@ -377,7 +378,8 @@ function quote() {
} else {
os.post({
renote: appearNote.value,
- }).then(() => {
+ }).then((cancelled) => {
+ if (cancelled) return;
misskeyApi('notes/renotes', {
noteId: props.note.id,
userId: $i.id,
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index f6f4d62d50..bd96a0655e 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -245,6 +245,7 @@ export function confirm(props: {
text?: string;
okText?: string;
cancelText?: string;
+ plain?: boolean;
}): Promise<{ canceled: boolean }> {
return new Promise(resolve => {
const { dispose } = popup(MkDialog, {
@@ -691,7 +692,7 @@ export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> {
}));
}
-export function post(props: Record<string, any> = {}): Promise<void> {
+export function post(props: Record<string, any> = {}): Promise<void | boolean> {
pleaseLogin(undefined, (props.initialText || props.initialNote ? {
type: 'share',
params: {
@@ -709,8 +710,8 @@ export function post(props: Record<string, any> = {}): Promise<void> {
// 複数のpost formを開いたときに場合によってはエラーになる
// もちろん複数のpost formを開けること自体Misskeyサイドのバグなのだが
const { dispose } = popup(MkPostFormDialog, props, {
- closed: () => {
- resolve();
+ closed: (cancelled) => {
+ resolve(cancelled);
dispose();
},
});
diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue
index 4ed2a67678..39e61aa218 100644
--- a/packages/frontend/src/pages/drive.file.info.vue
+++ b/packages/frontend/src/pages/drive.file.info.vue
@@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkKeyValue>
</button>
<button class="_button" :class="$style.kvEditBtn" @click="describe()">
- <MkKeyValue>
+ <MkKeyValue :class="$style.multiline">
<template #key>{{ i18n.ts.description }}</template>
<template #value>{{ file.comment ? file.comment : `(${i18n.ts.none})` }}<i class="ti ti-pencil" :class="$style.kvEditIcon"></i></template>
</MkKeyValue>
@@ -313,6 +313,10 @@ onMounted(async () => {
padding: .5rem 1rem;
}
+.multiline {
+ white-space: pre-wrap;
+}
+
.kvEditBtn {
text-align: start;
display: block;
diff --git a/packages/frontend/src/plugin.ts b/packages/frontend/src/plugin.ts
index 81233a5a5e..9640c988eb 100644
--- a/packages/frontend/src/plugin.ts
+++ b/packages/frontend/src/plugin.ts
@@ -6,7 +6,8 @@
import { ref } from 'vue';
import { Interpreter, Parser, utils, values } from '@syuilo/aiscript';
import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js';
-import { inputText } from '@/os.js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
import { Plugin, noteActions, notePostInterruptors, noteViewInterruptors, postFormActions, userActions, pageViewInterruptors } from '@/store.js';
const parser = new Parser();
@@ -91,8 +92,16 @@ function createPluginEnv(opts: { plugin: Plugin; storageKey: string }): Record<s
registerPageViewInterruptor({ pluginId: opts.plugin.id, handler });
}),
'Plugin:open_url': values.FN_NATIVE(([url]) => {
- utils.assertString(url);
- window.open(url.value, '_blank', 'noopener');
+ (async () => {
+ utils.assertString(url);
+ const { canceled } = await os.confirm({
+ type: 'question',
+ text: i18n.tsx.confirmRemoteUrl({ x: url.value }),
+ plain: true,
+ });
+ if (canceled) return;
+ window.open(url.value, '_blank', 'noopener');
+ })();
}),
'Plugin:config': values.OBJ(config),
};
diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue
index d1cc10558e..dbb7590978 100644
--- a/packages/frontend/src/ui/classic.sidebar.vue
+++ b/packages/frontend/src/ui/classic.sidebar.vue
@@ -166,7 +166,6 @@ watch(defaultStore.reactiveState.menuDisplay, () => {
top: 0;
z-index: 1;
padding: 16px 0;
- background: var(--bg);
> .button {
min-width: 0;