summaryrefslogtreecommitdiff
path: root/packages/frontend
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2023-09-29 18:11:30 +0900
committerGitHub <noreply@github.com>2023-09-29 18:11:30 +0900
commit7adc8fcaf5e7a57edfd2c197fcccb510425bd82c (patch)
treee60179e48010b0a389f87687e934f71ead92b27f /packages/frontend
parentMerge pull request #11898 from misskey-dev/develop (diff)
parentfix (diff)
downloadmisskey-7adc8fcaf5e7a57edfd2c197fcccb510425bd82c.tar.gz
misskey-7adc8fcaf5e7a57edfd2c197fcccb510425bd82c.tar.bz2
misskey-7adc8fcaf5e7a57edfd2c197fcccb510425bd82c.zip
Merge pull request #11920 from misskey-dev/develop
Release: 2023.9.2
Diffstat (limited to 'packages/frontend')
-rw-r--r--packages/frontend/.storybook/preview-head.html2
-rw-r--r--packages/frontend/package.json66
-rw-r--r--packages/frontend/src/components/MkNoteDetailed.vue3
-rw-r--r--packages/frontend/src/components/MkNoteHeader.vue1
-rw-r--r--packages/frontend/src/components/MkNotificationSelectWindow.vue78
-rw-r--r--packages/frontend/src/components/MkNotificationSettingWindow.vue95
-rw-r--r--packages/frontend/src/components/MkNotifications.vue9
-rw-r--r--packages/frontend/src/components/MkPostForm.vue10
-rw-r--r--packages/frontend/src/components/MkPostFormDialog.vue1
-rw-r--r--packages/frontend/src/components/MkTimeline.vue49
-rw-r--r--packages/frontend/src/components/MkVisitorDashboard.vue3
-rw-r--r--packages/frontend/src/components/global/MkPageHeader.vue2
-rw-r--r--packages/frontend/src/const.ts1
-rw-r--r--packages/frontend/src/os.ts4
-rw-r--r--packages/frontend/src/pages/admin/ads.vue32
-rw-r--r--packages/frontend/src/pages/admin/modlog.ModLog.vue27
-rw-r--r--packages/frontend/src/pages/admin/roles.editor.vue20
-rw-r--r--packages/frontend/src/pages/admin/roles.vue8
-rw-r--r--packages/frontend/src/pages/notifications.vue5
-rw-r--r--packages/frontend/src/pages/settings/general.vue2
-rw-r--r--packages/frontend/src/pages/settings/notifications.notification-config.vue50
-rw-r--r--packages/frontend/src/pages/settings/notifications.vue48
-rw-r--r--packages/frontend/src/pages/timeline.vue35
-rw-r--r--packages/frontend/src/pages/user/index.timeline.vue9
-rw-r--r--packages/frontend/src/scripts/aiscript/api.ts10
-rw-r--r--packages/frontend/src/scripts/api.ts46
-rw-r--r--packages/frontend/src/scripts/get-note-menu.ts9
-rw-r--r--packages/frontend/src/scripts/use-note-capture.ts7
-rw-r--r--packages/frontend/src/store.ts4
-rw-r--r--packages/frontend/src/ui/_common_/common.vue4
-rw-r--r--packages/frontend/src/ui/deck/deck-store.ts5
-rw-r--r--packages/frontend/src/ui/deck/notifications-column.vue10
-rw-r--r--packages/frontend/src/ui/deck/tl-column.vue47
-rw-r--r--packages/frontend/src/widgets/WidgetActivity.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetAichan.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetAiscript.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetAiscriptApp.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetButton.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetCalendar.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetClicker.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetClock.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetDigitalClock.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetFederation.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetInstanceCloud.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetInstanceInfo.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetJobQueue.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetMemo.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetNotifications.vue16
-rw-r--r--packages/frontend/src/widgets/WidgetOnlineUsers.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetPhotos.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetPostForm.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetProfile.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetRss.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetRssTicker.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetSlideshow.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetTimeline.vue6
-rw-r--r--packages/frontend/src/widgets/WidgetTrends.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetUnixClock.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetUserList.vue2
59 files changed, 531 insertions, 241 deletions
diff --git a/packages/frontend/.storybook/preview-head.html b/packages/frontend/.storybook/preview-head.html
index ed38e49647..36ff34b48a 100644
--- a/packages/frontend/.storybook/preview-head.html
+++ b/packages/frontend/.storybook/preview-head.html
@@ -1,6 +1,6 @@
<link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true" as="image" type="image/png" crossorigin="anonymous">
<link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true" as="image" type="image/jpeg" crossorigin="anonymous">
-<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@2.32.0/tabler-icons.min.css">
+<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@2.37.0/tabler-icons.min.css">
<link rel="stylesheet" href="https://unpkg.com/@fontsource/m-plus-rounded-1c/index.css">
<style>
html {
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index f579da3cbb..1f704cd258 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -23,7 +23,7 @@
"@rollup/plugin-replace": "5.0.2",
"@rollup/pluginutils": "5.0.4",
"@syuilo/aiscript": "0.16.0",
- "@tabler/icons-webfont": "2.35.0",
+ "@tabler/icons-webfont": "2.37.0",
"@vitejs/plugin-vue": "4.3.4",
"@vue-macros/reactivity-transform": "0.3.23",
"@vue/compiler-sfc": "3.3.4",
@@ -32,7 +32,7 @@
"broadcast-channel": "5.3.0",
"browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3",
"buraha": "0.0.1",
- "canvas-confetti": "1.6.0",
+ "canvas-confetti": "1.6.1",
"chart.js": "4.4.0",
"chartjs-adapter-date-fns": "3.0.0",
"chartjs-chart-matrix": "2.0.1",
@@ -43,7 +43,7 @@
"cropperjs": "2.0.0-beta.4",
"date-fns": "2.30.0",
"escape-regexp": "0.0.1",
- "estree-walker": "^3.0.3",
+ "estree-walker": "3.0.3",
"eventemitter3": "5.0.1",
"gsap": "3.12.2",
"idb-keyval": "6.2.1",
@@ -57,12 +57,12 @@
"prismjs": "1.29.0",
"punycode": "2.3.0",
"querystring": "0.2.1",
- "rollup": "3.29.2",
+ "rollup": "3.29.4",
"sanitize-html": "2.11.0",
"sass": "1.68.0",
"strict-event-emitter-types": "2.0.0",
"textarea-caret": "3.1.0",
- "three": "0.156.1",
+ "three": "0.157.0",
"throttle-debounce": "5.0.0",
"tinycolor2": "1.6.0",
"tsc-alias": "1.8.8",
@@ -70,7 +70,7 @@
"twemoji-parser": "14.0.0",
"typescript": "5.2.2",
"uuid": "9.0.1",
- "v-code-diff": "^1.7.1",
+ "v-code-diff": "1.7.1",
"vanilla-tilt": "1.8.1",
"vite": "4.4.9",
"vue": "3.3.4",
@@ -78,44 +78,44 @@
"vuedraggable": "next"
},
"devDependencies": {
- "@storybook/addon-actions": "7.4.4",
- "@storybook/addon-essentials": "7.4.4",
- "@storybook/addon-interactions": "7.4.4",
- "@storybook/addon-links": "7.4.4",
- "@storybook/addon-storysource": "7.4.4",
- "@storybook/addons": "7.4.4",
- "@storybook/blocks": "7.4.4",
- "@storybook/core-events": "7.4.4",
+ "@storybook/addon-actions": "7.4.5",
+ "@storybook/addon-essentials": "7.4.5",
+ "@storybook/addon-interactions": "7.4.5",
+ "@storybook/addon-links": "7.4.5",
+ "@storybook/addon-storysource": "7.4.5",
+ "@storybook/addons": "7.4.5",
+ "@storybook/blocks": "7.4.5",
+ "@storybook/core-events": "7.4.5",
"@storybook/jest": "0.2.2",
- "@storybook/manager-api": "7.4.4",
- "@storybook/preview-api": "7.4.4",
- "@storybook/react": "7.4.4",
- "@storybook/react-vite": "7.4.4",
+ "@storybook/manager-api": "7.4.5",
+ "@storybook/preview-api": "7.4.5",
+ "@storybook/react": "7.4.5",
+ "@storybook/react-vite": "7.4.5",
"@storybook/testing-library": "0.2.1",
- "@storybook/theming": "7.4.4",
- "@storybook/types": "7.4.4",
- "@storybook/vue3": "7.4.4",
- "@storybook/vue3-vite": "7.4.4",
+ "@storybook/theming": "7.4.5",
+ "@storybook/types": "7.4.5",
+ "@storybook/vue3": "7.4.5",
+ "@storybook/vue3-vite": "7.4.5",
"@testing-library/vue": "7.0.0",
"@types/escape-regexp": "0.0.1",
"@types/estree": "1.0.2",
- "@types/matter-js": "0.19.0",
- "@types/micromatch": "4.0.2",
- "@types/node": "20.6.4",
+ "@types/matter-js": "0.19.1",
+ "@types/micromatch": "4.0.3",
+ "@types/node": "20.7.1",
"@types/punycode": "2.1.0",
- "@types/sanitize-html": "2.9.0",
+ "@types/sanitize-html": "2.9.1",
"@types/throttle-debounce": "5.0.0",
"@types/tinycolor2": "1.4.4",
"@types/uuid": "9.0.4",
- "@types/websocket": "1.0.6",
- "@types/ws": "8.5.5",
- "@typescript-eslint/eslint-plugin": "6.7.2",
- "@typescript-eslint/parser": "6.7.2",
+ "@types/websocket": "1.0.7",
+ "@types/ws": "8.5.6",
+ "@typescript-eslint/eslint-plugin": "6.7.3",
+ "@typescript-eslint/parser": "6.7.3",
"@vitest/coverage-v8": "0.34.5",
"@vue/runtime-core": "3.3.4",
"acorn": "8.10.0",
"cross-env": "7.0.3",
- "cypress": "13.2.0",
+ "cypress": "13.3.0",
"eslint": "8.50.0",
"eslint-plugin-import": "2.28.1",
"eslint-plugin-vue": "9.17.0",
@@ -129,13 +129,13 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"start-server-and-test": "2.0.1",
- "storybook": "7.4.4",
+ "storybook": "7.4.5",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"summaly": "github:misskey-dev/summaly",
"vite-plugin-turbosnap": "1.0.3",
"vitest": "0.34.5",
"vitest-fetch-mock": "0.2.2",
"vue-eslint-parser": "9.3.1",
- "vue-tsc": "1.8.13"
+ "vue-tsc": "1.8.15"
}
}
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 06663d0477..ab8886e8ba 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -93,6 +93,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<footer>
<div :class="$style.noteFooterInfo">
+ <div v-if="appearNote.updatedAt">
+ {{ i18n.ts.edited }}: <MkTime :time="appearNote.updatedAt" mode="detail"/>
+ </div>
<MkA :to="notePage(appearNote)">
<MkTime :time="appearNote.createdAt" mode="detail"/>
</MkA>
diff --git a/packages/frontend/src/components/MkNoteHeader.vue b/packages/frontend/src/components/MkNoteHeader.vue
index dda7238d27..05f98c638e 100644
--- a/packages/frontend/src/components/MkNoteHeader.vue
+++ b/packages/frontend/src/components/MkNoteHeader.vue
@@ -14,6 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/>
</div>
<div :class="$style.info">
+ <span v-if="note.updatedAt" style="margin-right: 0.5em;" :title="i18n.ts.edited"><i class="ti ti-pencil"></i></span>
<MkA :to="notePage(note)">
<MkTime :time="note.createdAt"/>
</MkA>
diff --git a/packages/frontend/src/components/MkNotificationSelectWindow.vue b/packages/frontend/src/components/MkNotificationSelectWindow.vue
new file mode 100644
index 0000000000..3d5a56975b
--- /dev/null
+++ b/packages/frontend/src/components/MkNotificationSelectWindow.vue
@@ -0,0 +1,78 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkModalWindow
+ ref="dialog"
+ :width="400"
+ :height="450"
+ :withOkButton="true"
+ :okButtonDisabled="false"
+ @ok="ok()"
+ @close="dialog?.close()"
+ @closed="emit('closed')"
+>
+ <template #header>{{ i18n.ts.notificationSetting }}</template>
+
+ <MkSpacer :marginMin="20" :marginMax="28">
+ <div class="_gaps_m">
+ <MkInfo>{{ i18n.ts.notificationSettingDesc }}</MkInfo>
+ <div class="_buttons">
+ <MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton>
+ <MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton>
+ </div>
+ <MkSwitch v-for="ntype in notificationTypes" :key="ntype" v-model="typesMap[ntype].value">{{ i18n.t(`_notification._types.${ntype}`) }}</MkSwitch>
+ </div>
+ </MkSpacer>
+</MkModalWindow>
+</template>
+
+<script lang="ts" setup>
+import { ref, Ref } from 'vue';
+import MkSwitch from './MkSwitch.vue';
+import MkInfo from './MkInfo.vue';
+import MkButton from './MkButton.vue';
+import MkModalWindow from '@/components/MkModalWindow.vue';
+import { notificationTypes } from '@/const.js';
+import { i18n } from '@/i18n.js';
+
+type TypesMap = Record<typeof notificationTypes[number], Ref<boolean>>
+
+const emit = defineEmits<{
+ (ev: 'done', v: { excludeTypes: string[] }): void,
+ (ev: 'closed'): void,
+}>();
+
+const props = withDefaults(defineProps<{
+ excludeTypes?: typeof notificationTypes[number][];
+}>(), {
+ excludeTypes: () => [],
+});
+
+const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
+
+const typesMap: TypesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(!props.excludeTypes.includes(t)) }), {} as any);
+
+function ok() {
+ emit('done', {
+ excludeTypes: (Object.keys(typesMap) as typeof notificationTypes[number][])
+ .filter(type => !typesMap[type].value),
+ });
+
+ if (dialog) dialog.close();
+}
+
+function disableAll() {
+ for (const type of notificationTypes) {
+ typesMap[type].value = false;
+ }
+}
+
+function enableAll() {
+ for (const type of notificationTypes) {
+ typesMap[type].value = true;
+ }
+}
+</script>
diff --git a/packages/frontend/src/components/MkNotificationSettingWindow.vue b/packages/frontend/src/components/MkNotificationSettingWindow.vue
deleted file mode 100644
index a25914b214..0000000000
--- a/packages/frontend/src/components/MkNotificationSettingWindow.vue
+++ /dev/null
@@ -1,95 +0,0 @@
-<!--
-SPDX-FileCopyrightText: syuilo and other misskey contributors
-SPDX-License-Identifier: AGPL-3.0-only
--->
-
-<template>
-<MkModalWindow
- ref="dialog"
- :width="400"
- :height="450"
- :withOkButton="true"
- :okButtonDisabled="false"
- @ok="ok()"
- @close="dialog?.close()"
- @closed="emit('closed')"
->
- <template #header>{{ i18n.ts.notificationSetting }}</template>
-
- <MkSpacer :marginMin="20" :marginMax="28">
- <div class="_gaps_m">
- <template v-if="showGlobalToggle">
- <MkSwitch v-model="useGlobalSetting">
- {{ i18n.ts.useGlobalSetting }}
- <template #caption>{{ i18n.ts.useGlobalSettingDesc }}</template>
- </MkSwitch>
- </template>
- <template v-if="!useGlobalSetting">
- <MkInfo>{{ i18n.ts.notificationSettingDesc }}</MkInfo>
- <div class="_buttons">
- <MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton>
- <MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton>
- </div>
- <MkSwitch v-for="ntype in notificationTypes" :key="ntype" v-model="typesMap[ntype].value">{{ i18n.t(`_notification._types.${ntype}`) }}</MkSwitch>
- </template>
- </div>
- </MkSpacer>
-</MkModalWindow>
-</template>
-
-<script lang="ts" setup>
-import { ref, Ref } from 'vue';
-import MkSwitch from './MkSwitch.vue';
-import MkInfo from './MkInfo.vue';
-import MkButton from './MkButton.vue';
-import MkModalWindow from '@/components/MkModalWindow.vue';
-import { notificationTypes } from '@/const';
-import { i18n } from '@/i18n.js';
-
-type TypesMap = Record<typeof notificationTypes[number], Ref<boolean>>
-
-const emit = defineEmits<{
- (ev: 'done', v: { includingTypes: string[] | null }): void,
- (ev: 'closed'): void,
-}>();
-
-const props = withDefaults(defineProps<{
- includingTypes?: typeof notificationTypes[number][] | null;
- showGlobalToggle?: boolean;
-}>(), {
- includingTypes: () => [],
- showGlobalToggle: true,
-});
-
-let includingTypes = $computed(() => props.includingTypes?.filter(x => notificationTypes.includes(x)) ?? []);
-
-const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
-
-const typesMap: TypesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(includingTypes.includes(t)) }), {} as any);
-let useGlobalSetting = $ref((includingTypes === null || includingTypes.length === 0) && props.showGlobalToggle);
-
-function ok() {
- if (useGlobalSetting) {
- emit('done', { includingTypes: null });
- } else {
- emit('done', {
- includingTypes: (Object.keys(typesMap) as typeof notificationTypes[number][])
- .filter(type => typesMap[type].value),
- });
- }
-
- if (dialog) dialog.close();
-}
-
-function disableAll() {
- for (const type of notificationTypes) {
- typesMap[type].value = false;
- }
-}
-
-function enableAll() {
- for (const type of notificationTypes) {
- typesMap[type].value = true;
- }
-}
-</script>
diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue
index ad1cb92ce1..6ba2e513c5 100644
--- a/packages/frontend/src/components/MkNotifications.vue
+++ b/packages/frontend/src/components/MkNotifications.vue
@@ -30,11 +30,11 @@ import MkNote from '@/components/MkNote.vue';
import { useStream } from '@/stream.js';
import { $i } from '@/account.js';
import { i18n } from '@/i18n.js';
-import { notificationTypes } from '@/const';
+import { notificationTypes } from '@/const.js';
import { infoImageUrl } from '@/instance.js';
const props = defineProps<{
- includeTypes?: typeof notificationTypes[number][];
+ excludeTypes?: typeof notificationTypes[number][];
}>();
const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
@@ -43,13 +43,12 @@ const pagination: Paging = {
endpoint: 'i/notifications' as const,
limit: 10,
params: computed(() => ({
- includeTypes: props.includeTypes ?? undefined,
- excludeTypes: props.includeTypes ? undefined : $i.mutingNotificationTypes,
+ excludeTypes: props.excludeTypes ?? undefined,
})),
};
const onNotification = (notification) => {
- const isMuted = props.includeTypes ? !props.includeTypes.includes(notification.type) : $i.mutingNotificationTypes.includes(notification.type);
+ const isMuted = props.excludeTypes ? props.excludeTypes.includes(notification.type) : false;
if (isMuted || document.visibilityState === 'visible') {
useStream().send('readNotification');
}
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 2b4dcc8ed4..b82ca3ef19 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -143,6 +143,7 @@ const props = withDefaults(defineProps<{
fixed?: boolean;
autofocus?: boolean;
freezeAfterPosted?: boolean;
+ updateMode?: boolean;
}>(), {
initialVisibleUsers: () => [],
autofocus: true,
@@ -698,17 +699,18 @@ async function post(ev?: MouseEvent) {
}
let postData = {
- text: text === '' ? undefined : text,
+ text: text === '' ? null : text,
fileIds: files.length > 0 ? files.map(f => f.id) : undefined,
replyId: props.reply ? props.reply.id : undefined,
renoteId: props.renote ? props.renote.id : quoteId ? quoteId : undefined,
channelId: props.channel ? props.channel.id : undefined,
poll: poll,
- cw: useCw ? cw ?? '' : undefined,
+ cw: useCw ? cw ?? '' : null,
localOnly: localOnly,
visibility: visibility,
visibleUserIds: visibility === 'specified' ? visibleUsers.map(u => u.id) : undefined,
reactionAcceptance,
+ noteId: props.updateMode ? props.initialNote?.id : undefined,
};
if (withHashtags && hashtags && hashtags.trim() !== '') {
@@ -731,7 +733,7 @@ async function post(ev?: MouseEvent) {
}
posting = true;
- os.api('notes/create', postData, token).then(() => {
+ os.api(props.updateMode ? 'notes/update' : 'notes/create', postData, token).then(() => {
if (props.freezeAfterPosted) {
posted = true;
} else {
@@ -819,8 +821,10 @@ function showActions(ev) {
action: () => {
action.handler({
text: text,
+ cw: cw,
}, (key, value) => {
if (key === 'text') { text = value; }
+ if (key === 'cw') { useCw = value !== null; cw = value; }
});
},
})), ev.currentTarget ?? ev.target);
diff --git a/packages/frontend/src/components/MkPostFormDialog.vue b/packages/frontend/src/components/MkPostFormDialog.vue
index c07a166a83..f33d498f93 100644
--- a/packages/frontend/src/components/MkPostFormDialog.vue
+++ b/packages/frontend/src/components/MkPostFormDialog.vue
@@ -30,6 +30,7 @@ const props = defineProps<{
instant?: boolean;
fixed?: boolean;
autofocus?: boolean;
+ updateMode?: boolean;
}>();
const emit = defineEmits<{
diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue
index d6712e7606..1dcafd6be1 100644
--- a/packages/frontend/src/components/MkTimeline.vue
+++ b/packages/frontend/src/components/MkTimeline.vue
@@ -15,14 +15,21 @@ import * as sound from '@/scripts/sound.js';
import { $i } from '@/account.js';
import { defaultStore } from '@/store.js';
-const props = defineProps<{
+const props = withDefaults(defineProps<{
src: string;
list?: string;
antenna?: string;
channel?: string;
role?: string;
sound?: boolean;
-}>();
+ withRenotes?: boolean;
+ withReplies?: boolean;
+ onlyFiles?: boolean;
+}>(), {
+ withRenotes: true,
+ withReplies: false,
+ onlyFiles: false,
+});
const emit = defineEmits<{
(ev: 'note'): void;
@@ -62,10 +69,14 @@ if (props.src === 'antenna') {
} else if (props.src === 'home') {
endpoint = 'notes/timeline';
query = {
- withReplies: defaultStore.state.showTimelineReplies,
+ withRenotes: props.withRenotes,
+ withReplies: props.withReplies,
+ withFiles: props.onlyFiles ? true : undefined,
};
connection = stream.useChannel('homeTimeline', {
- withReplies: defaultStore.state.showTimelineReplies,
+ withRenotes: props.withRenotes,
+ withReplies: props.withReplies,
+ withFiles: props.onlyFiles ? true : undefined,
});
connection.on('note', prepend);
@@ -73,28 +84,40 @@ if (props.src === 'antenna') {
} else if (props.src === 'local') {
endpoint = 'notes/local-timeline';
query = {
- withReplies: defaultStore.state.showTimelineReplies,
+ withRenotes: props.withRenotes,
+ withReplies: props.withReplies,
+ withFiles: props.onlyFiles ? true : undefined,
};
connection = stream.useChannel('localTimeline', {
- withReplies: defaultStore.state.showTimelineReplies,
+ withRenotes: props.withRenotes,
+ withReplies: props.withReplies,
+ withFiles: props.onlyFiles ? true : undefined,
});
connection.on('note', prepend);
} else if (props.src === 'social') {
endpoint = 'notes/hybrid-timeline';
query = {
- withReplies: defaultStore.state.showTimelineReplies,
+ withRenotes: props.withRenotes,
+ withReplies: props.withReplies,
+ withFiles: props.onlyFiles ? true : undefined,
};
connection = stream.useChannel('hybridTimeline', {
- withReplies: defaultStore.state.showTimelineReplies,
+ withRenotes: props.withRenotes,
+ withReplies: props.withReplies,
+ withFiles: props.onlyFiles ? true : undefined,
});
connection.on('note', prepend);
} else if (props.src === 'global') {
endpoint = 'notes/global-timeline';
query = {
- withReplies: defaultStore.state.showTimelineReplies,
+ withRenotes: props.withRenotes,
+ withReplies: props.withReplies,
+ withFiles: props.onlyFiles ? true : undefined,
};
connection = stream.useChannel('globalTimeline', {
- withReplies: defaultStore.state.showTimelineReplies,
+ withRenotes: props.withRenotes,
+ withReplies: props.withReplies,
+ withFiles: props.onlyFiles ? true : undefined,
});
connection.on('note', prepend);
} else if (props.src === 'mentions') {
@@ -116,9 +139,15 @@ if (props.src === 'antenna') {
} else if (props.src === 'list') {
endpoint = 'notes/user-list-timeline';
query = {
+ withRenotes: props.withRenotes,
+ withReplies: props.withReplies,
+ withFiles: props.onlyFiles ? true : undefined,
listId: props.list,
};
connection = stream.useChannel('userList', {
+ withRenotes: props.withRenotes,
+ withReplies: props.withReplies,
+ withFiles: props.onlyFiles ? true : undefined,
listId: props.list,
});
connection.on('note', prepend);
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index 7a8d7e6109..e4520bbb2d 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -114,8 +114,7 @@ function showMenu(ev) {
}
function exploreOtherServers() {
- // TODO: 言語をよしなに
- window.open('https://join.misskey.page/ja-JP/instances', '_blank');
+ window.open('https://join.misskey.page/instances', '_blank');
}
</script>
diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index ef8bfbbbfc..580816abaa 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -45,7 +45,7 @@ import { onMounted, onUnmounted, ref, inject } from 'vue';
import tinycolor from 'tinycolor2';
import XTabs, { Tab } from './MkPageHeader.tabs.vue';
import { scrollToTop } from '@/scripts/scroll.js';
-import { globalEvents } from '@/events';
+import { globalEvents } from '@/events.js';
import { injectPageMetadata } from '@/scripts/page-metadata.js';
import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts
index 15038b1063..9fd6d40d72 100644
--- a/packages/frontend/src/const.ts
+++ b/packages/frontend/src/const.ts
@@ -61,6 +61,7 @@ export const ROLE_POLICIES = [
'gtlAvailable',
'ltlAvailable',
'canPublicNote',
+ 'canEditNote',
'canInvite',
'inviteLimit',
'inviteLimitCycle',
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index 8aed5797e1..8093335a28 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -5,8 +5,8 @@
// TODO: なんでもかんでもos.tsに突っ込むのやめたいのでよしなに分割する
-import { pendingApiRequestsCount, api, apiGet } from '@/scripts/api.js';
-export { pendingApiRequestsCount, api, apiGet };
+import { pendingApiRequestsCount, api, apiExternal, apiGet } from '@/scripts/api.js';
+export { pendingApiRequestsCount, api, apiExternal, apiGet };
import { Component, markRaw, Ref, ref, defineAsyncComponent } from 'vue';
import { EventEmitter } from 'eventemitter3';
import insertTextAtCursor from 'insert-text-at-cursor';
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue
index cd9d86ca45..6aa0cf0427 100644
--- a/packages/frontend/src/pages/admin/ads.vue
+++ b/packages/frontend/src/pages/admin/ads.vue
@@ -5,11 +5,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<MkStickyContainer>
- <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
+ <template #header>
+ <XHeader :actions="headerActions" :tabs="headerTabs" />
+ </template>
<MkSpacer :contentMax="900">
+ <MkSwitch :modelValue="publishing" @update:modelValue="onChangePublishing">
+ {{ i18n.ts.publishing }}
+ </MkSwitch>
<div>
<div v-for="ad in ads" class="_panel _gaps_m" :class="$style.ad">
- <MkAd v-if="ad.url" :specify="ad"/>
+ <MkAd v-if="ad.url" :specify="ad" />
<MkInput v-model="ad.url" type="url">
<template #label>URL</template>
</MkInput>
@@ -46,7 +51,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<span>
{{ i18n.ts._ad.timezoneinfo }}
<div v-for="(day, index) in daysOfWeek" :key="index">
- <input :id="`ad${ad.id}-${index}`" type="checkbox" :checked="(ad.dayOfWeek & (1 << index)) !== 0" @change="toggleDayOfWeek(ad, index)">
+ <input :id="`ad${ad.id}-${index}`" type="checkbox" :checked="(ad.dayOfWeek & (1 << index)) !== 0"
+ @change="toggleDayOfWeek(ad, index)">
<label :for="`ad${ad.id}-${index}`">{{ day }}</label>
</div>
</span>
@@ -55,8 +61,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.memo }}</template>
</MkTextarea>
<div class="buttons">
- <MkButton class="button" inline primary style="margin-right: 12px;" @click="save(ad)"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
- <MkButton class="button" inline danger @click="remove(ad)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton>
+ <MkButton class="button" inline primary style="margin-right: 12px;" @click="save(ad)"><i
+ class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
+ <MkButton class="button" inline danger @click="remove(ad)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}
+ </MkButton>
</div>
</div>
<MkButton class="button" @click="more()">
@@ -75,6 +83,7 @@ import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkRadios from '@/components/MkRadios.vue';
import MkFolder from '@/components/MkFolder.vue';
+import MkSwitch from '@/components/MkSwitch.vue';
import FormSplit from '@/components/form/split.vue';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
@@ -86,8 +95,9 @@ let ads: any[] = $ref([]);
const localTime = new Date();
const localTimeDiff = localTime.getTimezoneOffset() * 60 * 1000;
const daysOfWeek: string[] = [i18n.ts._weekday.sunday, i18n.ts._weekday.monday, i18n.ts._weekday.tuesday, i18n.ts._weekday.wednesday, i18n.ts._weekday.thursday, i18n.ts._weekday.friday, i18n.ts._weekday.saturday];
+let publishing = false;
-os.api('admin/ad/list').then(adsResponse => {
+os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
ads = adsResponse.map(r => {
const exdate = new Date(r.expiresAt);
const stdate = new Date(r.startsAt);
@@ -101,6 +111,10 @@ os.api('admin/ad/list').then(adsResponse => {
});
});
+const onChangePublishing = (v) => {
+ publishing = v;
+ refresh();
+};
// 選択された曜日(index)のビットフラグを操作する
function toggleDayOfWeek(ad, index) {
ad.dayOfWeek ^= 1 << index;
@@ -131,6 +145,8 @@ function remove(ad) {
if (ad.id == null) return;
os.apiWithDialog('admin/ad/delete', {
id: ad.id,
+ }).then(() => {
+ refresh();
});
});
}
@@ -172,7 +188,7 @@ function save(ad) {
}
}
function more() {
- os.api('admin/ad/list', { untilId: ads.reduce((acc, ad) => ad.id != null ? ad : acc).id }).then(adsResponse => {
+ os.api('admin/ad/list', { untilId: ads.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => {
ads = ads.concat(adsResponse.map(r => {
const exdate = new Date(r.expiresAt);
const stdate = new Date(r.startsAt);
@@ -188,7 +204,7 @@ function more() {
}
function refresh() {
- os.api('admin/ad/list').then(adsResponse => {
+ os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
ads = adsResponse.map(r => {
const exdate = new Date(r.expiresAt);
const stdate = new Date(r.startsAt);
diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue
index 14f94479f1..99b8544f33 100644
--- a/packages/frontend/src/pages/admin/modlog.ModLog.vue
+++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue
@@ -6,7 +6,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<MkFolder>
<template #label>
- <b>{{ i18n.ts._moderationLogTypes[log.type] }}</b>
+ <b
+ :class="{
+ [$style.logGreen]: ['createRole', 'addCustomEmoji', 'createGlobalAnnouncement', 'createUserAnnouncement', 'createAd', 'createInvitation'].includes(log.type),
+ [$style.logYellow]: ['markSensitiveDriveFile', 'resetPassword'].includes(log.type),
+ [$style.logRed]: ['suspend', 'deleteRole', 'suspendRemoteInstance', 'deleteGlobalAnnouncement', 'deleteUserAnnouncement', 'deleteCustomEmoji', 'deleteNote', 'deleteDriveFile', 'deleteAd'].includes(log.type)
+ }"
+ >{{ i18n.ts._moderationLogTypes[log.type] }}</b>
<span v-if="log.type === 'updateUserNote'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
<span v-else-if="log.type === 'suspend'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
<span v-else-if="log.type === 'unsuspend'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
@@ -16,7 +22,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<span v-else-if="log.type === 'createRole'">: {{ log.info.role.name }}</span>
<span v-else-if="log.type === 'updateRole'">: {{ log.info.before.name }}</span>
<span v-else-if="log.type === 'deleteRole'">: {{ log.info.role.name }}</span>
+ <span v-else-if="log.type === 'addCustomEmoji'">: {{ log.info.emoji.name }}</span>
<span v-else-if="log.type === 'updateCustomEmoji'">: {{ log.info.before.name }}</span>
+ <span v-else-if="log.type === 'deleteCustomEmoji'">: {{ log.info.emoji.name }}</span>
<span v-else-if="log.type === 'markSensitiveDriveFile'">: @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }}</span>
<span v-else-if="log.type === 'unmarkSensitiveDriveFile'">: @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }}</span>
<span v-else-if="log.type === 'suspendRemoteInstance'">: {{ log.info.host }}</span>
@@ -75,6 +83,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
</div>
</template>
+ <template v-else-if="log.type === 'updateAd'">
+ <div :class="$style.diff">
+ <CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
+ </div>
+ </template>
<details>
<summary>raw</summary>
@@ -113,4 +126,16 @@ const props = defineProps<{
border-radius: 6px;
overflow: clip;
}
+
+.logYellow {
+ color: var(--warning);
+}
+
+.logRed {
+ color: var(--error);
+}
+
+.logGreen {
+ color: var(--success);
+}
</style>
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index 2ef3e254cd..1b72e1d332 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -160,6 +160,26 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkFolder>
+ <MkFolder v-if="matchQuery([i18n.ts._role._options.canEditNote, 'canEditNote'])">
+ <template #label>{{ i18n.ts._role._options.canEditNote }}</template>
+ <template #suffix>
+ <span v-if="role.policies.canEditNote.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
+ <span v-else>{{ role.policies.canEditNote.value ? i18n.ts.yes : i18n.ts.no }}</span>
+ <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canEditNote)"></i></span>
+ </template>
+ <div class="_gaps">
+ <MkSwitch v-model="role.policies.canEditNote.useDefault" :readonly="readonly">
+ <template #label>{{ i18n.ts._role.useBaseValue }}</template>
+ </MkSwitch>
+ <MkSwitch v-model="role.policies.canEditNote.value" :disabled="role.policies.canEditNote.useDefault" :readonly="readonly">
+ <template #label>{{ i18n.ts.enable }}</template>
+ </MkSwitch>
+ <MkRange v-model="role.policies.canEditNote.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+ <template #label>{{ i18n.ts._role.priority }}</template>
+ </MkRange>
+ </div>
+ </MkFolder>
+
<MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])">
<template #label>{{ i18n.ts._role._options.canInvite }}</template>
<template #suffix>
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index 8d23335430..e1306d04b9 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -48,6 +48,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch>
</MkFolder>
+ <MkFolder v-if="matchQuery([i18n.ts._role._options.canEditNote, 'canEditNote'])">
+ <template #label>{{ i18n.ts._role._options.canEditNote }}</template>
+ <template #suffix>{{ policies.canEditNote ? i18n.ts.yes : i18n.ts.no }}</template>
+ <MkSwitch v-model="policies.canEditNote">
+ <template #label>{{ i18n.ts.enable }}</template>
+ </MkSwitch>
+ </MkFolder>
+
<MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])">
<template #label>{{ i18n.ts._role._options.canInvite }}</template>
<template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template>
diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue
index 3ae6319c7e..8d2475b085 100644
--- a/packages/frontend/src/pages/notifications.vue
+++ b/packages/frontend/src/pages/notifications.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :contentMax="800">
<div v-if="tab === 'all'">
- <XNotifications class="notifications" :includeTypes="includeTypes"/>
+ <XNotifications class="notifications" :excludeTypes="excludeTypes"/>
</div>
<div v-else-if="tab === 'mentions'">
<MkNotes :pagination="mentionsPagination"/>
@@ -27,10 +27,11 @@ import MkNotes from '@/components/MkNotes.vue';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { notificationTypes } from '@/const';
+import { notificationTypes } from '@/const.js';
let tab = $ref('all');
let includeTypes = $ref<string[] | null>(null);
+const excludeTypes = $computed(() => includeTypes ? notificationTypes.filter(t => !includeTypes.includes(t)) : null);
const mentionsPagination = {
endpoint: 'notes/mentions' as const,
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index a536bd1baa..55de53fb07 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -29,7 +29,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_s">
<MkSwitch v-model="showFixedPostForm">{{ i18n.ts.showFixedPostForm }}</MkSwitch>
<MkSwitch v-model="showFixedPostFormInChannel">{{ i18n.ts.showFixedPostFormInChannel }}</MkSwitch>
- <MkSwitch v-model="showTimelineReplies">{{ i18n.ts.flagShowTimelineReplies }}<template #caption>{{ i18n.ts.flagShowTimelineRepliesDescription }} {{ i18n.ts.reflectMayTakeTime }}</template></MkSwitch>
<MkFolder>
<template #label>{{ i18n.ts.pinnedList }}</template>
<!-- 複数ピン止め管理できるようにしたいけどめんどいので一旦ひとつのみ -->
@@ -249,7 +248,6 @@ const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars'));
const mediaListWithOneImageAppearance = computed(defaultStore.makeGetterSetter('mediaListWithOneImageAppearance'));
const notificationPosition = computed(defaultStore.makeGetterSetter('notificationPosition'));
const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis'));
-const showTimelineReplies = computed(defaultStore.makeGetterSetter('showTimelineReplies'));
const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn'));
watch(lang, () => {
diff --git a/packages/frontend/src/pages/settings/notifications.notification-config.vue b/packages/frontend/src/pages/settings/notifications.notification-config.vue
new file mode 100644
index 0000000000..c1f107c2b8
--- /dev/null
+++ b/packages/frontend/src/pages/settings/notifications.notification-config.vue
@@ -0,0 +1,50 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div class="_gaps_m">
+ <MkSelect v-model="type">
+ <option value="all">{{ i18n.ts.all }}</option>
+ <option value="following">{{ i18n.ts.following }}</option>
+ <option value="follower">{{ i18n.ts.followers }}</option>
+ <option value="mutualFollow">{{ i18n.ts.mutualFollow }}</option>
+ <option value="list">{{ i18n.ts.userList }}</option>
+ <option value="never">{{ i18n.ts.none }}</option>
+ </MkSelect>
+
+ <MkSelect v-if="type === 'list'" v-model="userListId">
+ <template #label>{{ i18n.ts.userList }}</template>
+ <option v-for="list in props.userLists" :key="list.id" :value="list.id">{{ list.name }}</option>
+ </MkSelect>
+
+ <div class="_buttons">
+ <MkButton inline primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
+ </div>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { } from 'vue';
+import * as Misskey from 'misskey-js';
+import MkSelect from '@/components/MkSelect.vue';
+import MkButton from '@/components/MkButton.vue';
+import { i18n } from '@/i18n.js';
+
+const props = defineProps<{
+ value: any;
+ userLists: Misskey.entities.UserList[];
+}>();
+
+const emit = defineEmits<{
+ (ev: 'update', result: any): void;
+}>();
+
+let type = $ref(props.value.type);
+let userListId = $ref(props.value.userListId);
+
+function save() {
+ emit('update', { type, userListId });
+}
+</script>
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index 5b3c29e7c8..88e02af1f4 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -5,7 +5,26 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div class="_gaps_m">
- <FormLink @click="configure"><template #icon><i class="ti ti-settings"></i></template>{{ i18n.ts.notificationSetting }}</FormLink>
+ <FormSection first>
+ <template #label>{{ i18n.ts.notificationRecieveConfig }}</template>
+ <div class="_gaps_s">
+ <MkFolder v-for="type in notificationTypes" :key="type">
+ <template #label>{{ i18n.t('_notification._types.' + type) }}</template>
+ <template #suffix>
+ {{
+ $i.notificationRecieveConfig[type]?.type === 'never' ? i18n.ts.none :
+ $i.notificationRecieveConfig[type]?.type === 'following' ? i18n.ts.following :
+ $i.notificationRecieveConfig[type]?.type === 'follower' ? i18n.ts.followers :
+ $i.notificationRecieveConfig[type]?.type === 'mutualFollow' ? i18n.ts.mutualFollow :
+ $i.notificationRecieveConfig[type]?.type === 'list' ? i18n.ts.userList :
+ i18n.ts.all
+ }}
+ </template>
+
+ <XNotificationConfig :userLists="userLists" :value="$i.notificationRecieveConfig[type] ?? { type: 'all' }" @update="(res) => updateReceiveConfig(type, res)"/>
+ </MkFolder>
+ </div>
+ </FormSection>
<FormSection>
<div class="_gaps_m">
<FormLink @click="readAllNotifications">{{ i18n.ts.markAsReadAllNotifications }}</FormLink>
@@ -37,19 +56,22 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { defineAsyncComponent } from 'vue';
+import XNotificationConfig from './notifications.notification-config.vue';
import FormLink from '@/components/form/link.vue';
import FormSection from '@/components/form/section.vue';
+import MkFolder from '@/components/MkFolder.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import * as os from '@/os.js';
import { $i } from '@/account.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
-import { notificationTypes } from '@/const';
+import { notificationTypes } from '@/const.js';
let allowButton = $shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
let pushRegistrationInServer = $computed(() => allowButton?.pushRegistrationInServer);
let sendReadMessage = $computed(() => pushRegistrationInServer?.sendReadMessage || false);
+const userLists = await os.api('users/lists/list');
async function readAllUnreadNotes() {
await os.api('i/read-all-unread-notes');
@@ -59,21 +81,15 @@ async function readAllNotifications() {
await os.api('notifications/mark-all-as-read');
}
-function configure() {
- const includingTypes = notificationTypes.filter(x => !$i!.mutingNotificationTypes.includes(x));
- os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSettingWindow.vue')), {
- includingTypes,
- showGlobalToggle: false,
- }, {
- done: async (res) => {
- const { includingTypes: value } = res;
- await os.apiWithDialog('i/update', {
- mutingNotificationTypes: notificationTypes.filter(x => !value.includes(x)),
- }).then(i => {
- $i!.mutingNotificationTypes = i.mutingNotificationTypes;
- });
+async function updateReceiveConfig(type, value) {
+ await os.apiWithDialog('i/update', {
+ notificationRecieveConfig: {
+ ...$i!.notificationRecieveConfig,
+ [type]: value,
},
- }, 'closed');
+ }).then(i => {
+ $i!.notificationRecieveConfig = i.notificationRecieveConfig;
+ });
}
function onChangeSendReadMessage(v: boolean) {
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index cce7360b9b..5bad689aee 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -15,9 +15,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.tl">
<MkTimeline
ref="tlComponent"
- :key="src"
+ :key="src + withRenotes + withReplies + onlyFiles"
:src="src.split(':')[0]"
:list="src.split(':')[1]"
+ :withRenotes="withRenotes"
+ :withReplies="withReplies"
+ :onlyFiles="onlyFiles"
:sound="true"
@queue="queueUpdated"
/>
@@ -58,6 +61,9 @@ const rootEl = $shallowRef<HTMLElement>();
let queue = $ref(0);
let srcWhenNotSignin = $ref(isLocalTimelineAvailable ? 'local' : 'global');
const src = $computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin), set: (x) => saveSrc(x) });
+const withRenotes = $ref(true);
+const withReplies = $ref(false);
+const onlyFiles = $ref(false);
watch($$(src), () => queue = 0);
@@ -129,7 +135,28 @@ function focus(): void {
tlComponent.focus();
}
-const headerActions = $computed(() => []);
+const headerActions = $computed(() => [{
+ icon: 'ti ti-dots',
+ text: i18n.ts.options,
+ handler: (ev) => {
+ os.popupMenu([{
+ type: 'switch',
+ text: i18n.ts.showRenotes,
+ icon: 'ti ti-repeat',
+ ref: $$(withRenotes),
+ }, {
+ type: 'switch',
+ text: i18n.ts.withReplies,
+ icon: 'ti ti-arrow-back-up',
+ ref: $$(withReplies),
+ }, {
+ type: 'switch',
+ text: i18n.ts.fileAttachedOnly,
+ icon: 'ti ti-photo',
+ ref: $$(onlyFiles),
+ }], ev.currentTarget ?? ev.target);
+ },
+}]);
const headerTabs = $computed(() => [...(defaultStore.reactiveState.pinnedUserLists.value.map(l => ({
key: 'list:' + l.id,
@@ -149,7 +176,7 @@ const headerTabs = $computed(() => [...(defaultStore.reactiveState.pinnedUserLis
}, {
key: 'social',
title: i18n.ts._timelines.social,
- icon: 'ti ti-rocket',
+ icon: 'ti ti-universe',
iconOnly: true,
}] : []), ...(isGlobalTimelineAvailable ? [{
key: 'global',
@@ -190,7 +217,7 @@ const headerTabsWhenNotLogin = $computed(() => [
definePageMetadata(computed(() => ({
title: i18n.ts.timeline,
- icon: src === 'local' ? 'ti ti-planet' : src === 'social' ? 'ti ti-rocket' : src === 'global' ? 'ti ti-whirl' : 'ti ti-home',
+ icon: src === 'local' ? 'ti ti-planet' : src === 'social' ? 'ti ti-universe' : src === 'global' ? 'ti ti-whirl' : 'ti ti-home',
})));
</script>
diff --git a/packages/frontend/src/pages/user/index.timeline.vue b/packages/frontend/src/pages/user/index.timeline.vue
index 3a2a2ade81..42040f5304 100644
--- a/packages/frontend/src/pages/user/index.timeline.vue
+++ b/packages/frontend/src/pages/user/index.timeline.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #header>
<MkTab v-model="include" :class="$style.tab">
<option :value="null">{{ i18n.ts.notes }}</option>
- <option value="replies">{{ i18n.ts.notesAndReplies }}</option>
+ <option value="all">{{ i18n.ts.all }}</option>
<option value="files">{{ i18n.ts.withFiles }}</option>
</MkTab>
</template>
@@ -36,7 +36,8 @@ const pagination = {
limit: 10,
params: computed(() => ({
userId: props.user.id,
- includeReplies: include.value === 'replies' || include.value === 'files',
+ withRenotes: include.value === 'all',
+ withReplies: include.value === 'all' || include.value === 'files',
withFiles: include.value === 'files',
})),
};
@@ -51,7 +52,7 @@ const pagination = {
.tl {
background: var(--bg);
- border-radius: var(--radius);
- overflow: clip;
+ border-radius: var(--radius);
+ overflow: clip;
}
</style>
diff --git a/packages/frontend/src/scripts/aiscript/api.ts b/packages/frontend/src/scripts/aiscript/api.ts
index 9f60e52cea..f049a51b93 100644
--- a/packages/frontend/src/scripts/aiscript/api.ts
+++ b/packages/frontend/src/scripts/aiscript/api.ts
@@ -48,6 +48,16 @@ export function createAiScriptEnv(opts) {
return values.ERROR('request_failed', utils.jsToVal(err));
});
}),
+ 'Mk:apiExternal': values.FN_NATIVE(async ([host, ep, param, token]) => {
+ utils.assertString(host);
+ utils.assertString(ep);
+ if (token) utils.assertString(token);
+ return os.apiExternal(host.value, ep.value, utils.valToJs(param), token?.value).then(res => {
+ return utils.jsToVal(res);
+ }, err => {
+ return values.ERROR('request_failed', utils.jsToVal(err));
+ });
+ }),
'Mk:save': values.FN_NATIVE(([key, value]) => {
utils.assertString(key);
miLocalStorage.setItem(`aiscript:${opts.storageKey}:${key.value}`, JSON.stringify(utils.valToJs(value)));
diff --git a/packages/frontend/src/scripts/api.ts b/packages/frontend/src/scripts/api.ts
index 9259c88013..080977e5e4 100644
--- a/packages/frontend/src/scripts/api.ts
+++ b/packages/frontend/src/scripts/api.ts
@@ -11,6 +11,7 @@ export const pendingApiRequestsCount = ref(0);
// Implements Misskey.api.ApiClient.request
export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(endpoint: E, data: P = {} as any, token?: string | null | undefined, signal?: AbortSignal): Promise<Misskey.Endpoints[E]['res']> {
+ if (endpoint.includes('://')) throw new Error('invalid endpoint');
pendingApiRequestsCount.value++;
const onFinally = () => {
@@ -23,7 +24,50 @@ export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoin
if (token !== undefined) (data as any).i = token;
// Send request
- window.fetch(endpoint.indexOf('://') > -1 ? endpoint : `${apiUrl}/${endpoint}`, {
+ window.fetch(`${apiUrl}/${endpoint}`, {
+ method: 'POST',
+ body: JSON.stringify(data),
+ credentials: 'omit',
+ cache: 'no-cache',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ signal,
+ }).then(async (res) => {
+ const body = res.status === 204 ? null : await res.json();
+
+ if (res.status === 200) {
+ resolve(body);
+ } else if (res.status === 204) {
+ resolve();
+ } else {
+ reject(body.error);
+ }
+ }).catch(reject);
+ });
+
+ promise.then(onFinally, onFinally);
+
+ return promise;
+}
+
+export function apiExternal<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(hostUrl: string, endpoint: E, data: P = {} as any, token?: string | null | undefined, signal?: AbortSignal): Promise<Misskey.Endpoints[E]['res']> {
+ if (!/^https?:\/\//.test(hostUrl)) throw new Error('invalid host name');
+ if (endpoint.includes('://')) throw new Error('invalid endpoint');
+ pendingApiRequestsCount.value++;
+
+ const onFinally = () => {
+ pendingApiRequestsCount.value--;
+ };
+
+ const promise = new Promise<Misskey.Endpoints[E]['res'] | void>((resolve, reject) => {
+ // Append a credential
+ (data as any).i = token;
+
+ const fullUrl = (hostUrl.slice(-1) === '/' ? hostUrl.slice(0, -1) : hostUrl)
+ + '/api/' + (endpoint.slice(0, 1) === '/' ? endpoint.slice(1) : endpoint);
+ // Send request
+ window.fetch(fullUrl, {
method: 'POST',
body: JSON.stringify(data),
credentials: 'omit',
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index 0948741fc5..45fb622069 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -172,6 +172,10 @@ export function getNoteMenu(props: {
});
}
+ function edit(): void {
+ os.post({ initialNote: appearNote, renote: appearNote.renote, reply: appearNote.reply, channel: appearNote.channel, updateMode: true });
+ }
+
function toggleFavorite(favorite: boolean): void {
claimAchievement('noteFavorited1');
os.apiWithDialog(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', {
@@ -352,6 +356,11 @@ export function getNoteMenu(props: {
),
...(appearNote.userId === $i.id || $i.isModerator || $i.isAdmin ? [
null,
+ appearNote.userId === $i.id && $i.policies.canEditNote ? {
+ icon: 'ti ti-edit',
+ text: i18n.ts.edit,
+ action: edit,
+ } : undefined,
appearNote.userId === $i.id ? {
icon: 'ti ti-edit',
text: i18n.ts.deleteAndEdit,
diff --git a/packages/frontend/src/scripts/use-note-capture.ts b/packages/frontend/src/scripts/use-note-capture.ts
index c618532570..a4c913749e 100644
--- a/packages/frontend/src/scripts/use-note-capture.ts
+++ b/packages/frontend/src/scripts/use-note-capture.ts
@@ -71,6 +71,13 @@ export function useNoteCapture(props: {
break;
}
+ case 'updated': {
+ note.value.updatedAt = new Date().toISOString();
+ note.value.cw = body.cw;
+ note.value.text = body.text;
+ break;
+ }
+
case 'deleted': {
props.isDeletedRef.value = true;
break;
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 8a7ee62eff..e715088d03 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -109,10 +109,6 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'account',
default: [] as string[],
},
- showTimelineReplies: {
- where: 'account',
- default: false,
- },
menu: {
where: 'deviceAccount',
diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue
index 10972f3bc1..732a4cfeaa 100644
--- a/packages/frontend/src/ui/_common_/common.vue
+++ b/packages/frontend/src/ui/_common_/common.vue
@@ -65,9 +65,7 @@ const dev = _DEV_;
let notifications = $ref<Misskey.entities.Notification[]>([]);
-function onNotification(notification: Misskey.entities.Notification, isClient: boolean = false) {
- if ($i.mutingNotificationTypes.includes(notification.type)) return;
-
+function onNotification(notification: Misskey.entities.Notification, isClient = false) {
if (document.visibilityState === 'visible') {
if (!isClient) {
useStream().send('readNotification');
diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts
index f910c5181d..49fdf4d314 100644
--- a/packages/frontend/src/ui/deck/deck-store.ts
+++ b/packages/frontend/src/ui/deck/deck-store.ts
@@ -28,8 +28,11 @@ export type Column = {
listId?: string;
channelId?: string;
roleId?: string;
- includingTypes?: typeof notificationTypes[number][];
+ excludeTypes?: typeof notificationTypes[number][];
tl?: 'home' | 'local' | 'social' | 'global';
+ withRenotes?: boolean;
+ withReplies?: boolean;
+ onlyFiles?: boolean;
};
export const deckStore = markRaw(new Storage('deck', {
diff --git a/packages/frontend/src/ui/deck/notifications-column.vue b/packages/frontend/src/ui/deck/notifications-column.vue
index 0455d43deb..b6bbf1fb55 100644
--- a/packages/frontend/src/ui/deck/notifications-column.vue
+++ b/packages/frontend/src/ui/deck/notifications-column.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<XColumn :column="column" :isStacked="isStacked" :menu="menu">
<template #header><i class="ti ti-bell" style="margin-right: 8px;"></i>{{ column.name }}</template>
- <XNotifications :includeTypes="column.includingTypes"/>
+ <XNotifications :excludeTypes="props.column.excludeTypes"/>
</XColumn>
</template>
@@ -25,13 +25,13 @@ const props = defineProps<{
}>();
function func() {
- os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSettingWindow.vue')), {
- includingTypes: props.column.includingTypes,
+ os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSelectWindow.vue')), {
+ excludeTypes: props.column.excludeTypes,
}, {
done: async (res) => {
- const { includingTypes } = res;
+ const { excludeTypes } = res;
updateColumn(props.column.id, {
- includingTypes: includingTypes,
+ excludeTypes: excludeTypes,
});
},
}, 'closed');
diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue
index 813b801d21..aad73d73a1 100644
--- a/packages/frontend/src/ui/deck/tl-column.vue
+++ b/packages/frontend/src/ui/deck/tl-column.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #header>
<i v-if="column.tl === 'home'" class="ti ti-home"></i>
<i v-else-if="column.tl === 'local'" class="ti ti-planet"></i>
- <i v-else-if="column.tl === 'social'" class="ti ti-rocket"></i>
+ <i v-else-if="column.tl === 'social'" class="ti ti-universe"></i>
<i v-else-if="column.tl === 'global'" class="ti ti-whirl"></i>
<span style="margin-left: 8px;">{{ column.name }}</span>
</template>
@@ -20,12 +20,20 @@ SPDX-License-Identifier: AGPL-3.0-only
</p>
<p :class="$style.disabledDescription">{{ i18n.ts._disabledTimeline.description }}</p>
</div>
- <MkTimeline v-else-if="column.tl" ref="timeline" :key="column.tl" :src="column.tl"/>
+ <MkTimeline
+ v-else-if="column.tl"
+ ref="timeline"
+ :key="column.tl + withRenotes + withReplies + onlyFiles"
+ :src="column.tl"
+ :withRenotes="withRenotes"
+ :withReplies="withReplies"
+ :onlyFiles="onlyFiles"
+ />
</XColumn>
</template>
<script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, watch } from 'vue';
import XColumn from './column.vue';
import { removeColumn, updateColumn, Column } from './deck-store.js';
import MkTimeline from '@/components/MkTimeline.vue';
@@ -43,6 +51,27 @@ let disabled = $ref(false);
const isLocalTimelineAvailable = (($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable));
const isGlobalTimelineAvailable = (($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable));
+const withRenotes = $ref(props.column.withRenotes ?? true);
+const withReplies = $ref(props.column.withReplies ?? false);
+const onlyFiles = $ref(props.column.onlyFiles ?? false);
+
+watch($$(withRenotes), v => {
+ updateColumn(props.column.id, {
+ withRenotes: v,
+ });
+});
+
+watch($$(withReplies), v => {
+ updateColumn(props.column.id, {
+ withReplies: v,
+ });
+});
+
+watch($$(onlyFiles), v => {
+ updateColumn(props.column.id, {
+ onlyFiles: v,
+ });
+});
onMounted(() => {
if (props.column.tl == null) {
@@ -82,6 +111,18 @@ const menu = [{
icon: 'ti ti-pencil',
text: i18n.ts.timeline,
action: setType,
+}, {
+ type: 'switch',
+ text: i18n.ts.showRenotes,
+ ref: $$(withRenotes),
+}, {
+ type: 'switch',
+ text: i18n.ts.withReplies,
+ ref: $$(withReplies),
+}, {
+ type: 'switch',
+ text: i18n.ts.fileAttachedOnly,
+ ref: $$(onlyFiles),
}];
</script>
diff --git a/packages/frontend/src/widgets/WidgetActivity.vue b/packages/frontend/src/widgets/WidgetActivity.vue
index ed969abcfc..6b890d41a8 100644
--- a/packages/frontend/src/widgets/WidgetActivity.vue
+++ b/packages/frontend/src/widgets/WidgetActivity.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import XCalendar from './WidgetActivity.calendar.vue';
import XChart from './WidgetActivity.chart.vue';
import { GetFormResultType } from '@/scripts/form.js';
diff --git a/packages/frontend/src/widgets/WidgetAichan.vue b/packages/frontend/src/widgets/WidgetAichan.vue
index ce71413328..76b35f6fed 100644
--- a/packages/frontend/src/widgets/WidgetAichan.vue
+++ b/packages/frontend/src/widgets/WidgetAichan.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, onUnmounted, shallowRef } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
const name = 'ai';
diff --git a/packages/frontend/src/widgets/WidgetAiscript.vue b/packages/frontend/src/widgets/WidgetAiscript.vue
index 4b927563f8..1b8c8ad9bc 100644
--- a/packages/frontend/src/widgets/WidgetAiscript.vue
+++ b/packages/frontend/src/widgets/WidgetAiscript.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
import { Interpreter, Parser, utils } from '@syuilo/aiscript';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import * as os from '@/os.js';
import MkContainer from '@/components/MkContainer.vue';
diff --git a/packages/frontend/src/widgets/WidgetAiscriptApp.vue b/packages/frontend/src/widgets/WidgetAiscriptApp.vue
index a41a8bdbd4..53b6020ffc 100644
--- a/packages/frontend/src/widgets/WidgetAiscriptApp.vue
+++ b/packages/frontend/src/widgets/WidgetAiscriptApp.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, Ref, ref, watch } from 'vue';
import { Interpreter, Parser } from '@syuilo/aiscript';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import * as os from '@/os.js';
import { createAiScriptEnv } from '@/scripts/aiscript/api.js';
diff --git a/packages/frontend/src/widgets/WidgetButton.vue b/packages/frontend/src/widgets/WidgetButton.vue
index b80117a9c5..a7bdd4c49c 100644
--- a/packages/frontend/src/widgets/WidgetButton.vue
+++ b/packages/frontend/src/widgets/WidgetButton.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { Interpreter, Parser } from '@syuilo/aiscript';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import * as os from '@/os.js';
import { createAiScriptEnv } from '@/scripts/aiscript/api.js';
diff --git a/packages/frontend/src/widgets/WidgetCalendar.vue b/packages/frontend/src/widgets/WidgetCalendar.vue
index 4b592203ec..7fabd09a24 100644
--- a/packages/frontend/src/widgets/WidgetCalendar.vue
+++ b/packages/frontend/src/widgets/WidgetCalendar.vue
@@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import { i18n } from '@/i18n.js';
import { useInterval } from '@/scripts/use-interval.js';
diff --git a/packages/frontend/src/widgets/WidgetClicker.vue b/packages/frontend/src/widgets/WidgetClicker.vue
index 9b19c007de..5e7464f3a4 100644
--- a/packages/frontend/src/widgets/WidgetClicker.vue
+++ b/packages/frontend/src/widgets/WidgetClicker.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import MkContainer from '@/components/MkContainer.vue';
import MkClickerGame from '@/components/MkClickerGame.vue';
diff --git a/packages/frontend/src/widgets/WidgetClock.vue b/packages/frontend/src/widgets/WidgetClock.vue
index 6e6e1fba96..e4ea2c97dd 100644
--- a/packages/frontend/src/widgets/WidgetClock.vue
+++ b/packages/frontend/src/widgets/WidgetClock.vue
@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import MkContainer from '@/components/MkContainer.vue';
import MkAnalogClock from '@/components/MkAnalogClock.vue';
diff --git a/packages/frontend/src/widgets/WidgetDigitalClock.vue b/packages/frontend/src/widgets/WidgetDigitalClock.vue
index a250d28230..9ff5f8dcef 100644
--- a/packages/frontend/src/widgets/WidgetDigitalClock.vue
+++ b/packages/frontend/src/widgets/WidgetDigitalClock.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import { timezones } from '@/scripts/timezones.js';
import MkDigitalClock from '@/components/MkDigitalClock.vue';
diff --git a/packages/frontend/src/widgets/WidgetFederation.vue b/packages/frontend/src/widgets/WidgetFederation.vue
index 1d067aac69..47f94402fb 100644
--- a/packages/frontend/src/widgets/WidgetFederation.vue
+++ b/packages/frontend/src/widgets/WidgetFederation.vue
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import MkContainer from '@/components/MkContainer.vue';
import MkMiniChart from '@/components/MkMiniChart.vue';
diff --git a/packages/frontend/src/widgets/WidgetInstanceCloud.vue b/packages/frontend/src/widgets/WidgetInstanceCloud.vue
index ffcf059a42..4ae77e86fc 100644
--- a/packages/frontend/src/widgets/WidgetInstanceCloud.vue
+++ b/packages/frontend/src/widgets/WidgetInstanceCloud.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import MkContainer from '@/components/MkContainer.vue';
import MkTagCloud from '@/components/MkTagCloud.vue';
diff --git a/packages/frontend/src/widgets/WidgetInstanceInfo.vue b/packages/frontend/src/widgets/WidgetInstanceInfo.vue
index 48a1849090..ff4a1b46c0 100644
--- a/packages/frontend/src/widgets/WidgetInstanceInfo.vue
+++ b/packages/frontend/src/widgets/WidgetInstanceInfo.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import { host } from '@/config.js';
import { instance } from '@/instance.js';
diff --git a/packages/frontend/src/widgets/WidgetJobQueue.vue b/packages/frontend/src/widgets/WidgetJobQueue.vue
index e0025a8074..89770b2216 100644
--- a/packages/frontend/src/widgets/WidgetJobQueue.vue
+++ b/packages/frontend/src/widgets/WidgetJobQueue.vue
@@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onUnmounted, reactive } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import { useStream } from '@/stream.js';
import number from '@/filters/number.js';
diff --git a/packages/frontend/src/widgets/WidgetMemo.vue b/packages/frontend/src/widgets/WidgetMemo.vue
index c12afd50b0..1f5666b3ef 100644
--- a/packages/frontend/src/widgets/WidgetMemo.vue
+++ b/packages/frontend/src/widgets/WidgetMemo.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, watch } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import MkContainer from '@/components/MkContainer.vue';
import { defaultStore } from '@/store.js';
diff --git a/packages/frontend/src/widgets/WidgetNotifications.vue b/packages/frontend/src/widgets/WidgetNotifications.vue
index b9999d8011..796578395f 100644
--- a/packages/frontend/src/widgets/WidgetNotifications.vue
+++ b/packages/frontend/src/widgets/WidgetNotifications.vue
@@ -10,14 +10,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #func="{ buttonStyleClass }"><button class="_button" :class="buttonStyleClass" @click="configureNotification()"><i class="ti ti-settings"></i></button></template>
<div>
- <XNotifications :includeTypes="widgetProps.includingTypes"/>
+ <XNotifications :excludeTypes="widgetProps.excludeTypes"/>
</div>
</MkContainer>
</template>
<script lang="ts" setup>
import { defineAsyncComponent } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import MkContainer from '@/components/MkContainer.vue';
import XNotifications from '@/components/MkNotifications.vue';
@@ -35,10 +35,10 @@ const widgetPropsDef = {
type: 'number' as const,
default: 300,
},
- includingTypes: {
+ excludeTypes: {
type: 'array' as const,
hidden: true,
- default: null,
+ default: [],
},
};
@@ -54,12 +54,12 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name,
);
const configureNotification = () => {
- os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSettingWindow.vue')), {
- includingTypes: widgetProps.includingTypes,
+ os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSelectWindow.vue')), {
+ excludeTypes: widgetProps.excludeTypes,
}, {
done: async (res) => {
- const { includingTypes } = res;
- widgetProps.includingTypes = includingTypes;
+ const { excludeTypes } = res;
+ widgetProps.excludeTypes = excludeTypes;
save();
},
}, 'closed');
diff --git a/packages/frontend/src/widgets/WidgetOnlineUsers.vue b/packages/frontend/src/widgets/WidgetOnlineUsers.vue
index a77468009e..46fe991f37 100644
--- a/packages/frontend/src/widgets/WidgetOnlineUsers.vue
+++ b/packages/frontend/src/widgets/WidgetOnlineUsers.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import * as os from '@/os.js';
import { useInterval } from '@/scripts/use-interval.js';
diff --git a/packages/frontend/src/widgets/WidgetPhotos.vue b/packages/frontend/src/widgets/WidgetPhotos.vue
index 8c01d3cce9..9af4f80873 100644
--- a/packages/frontend/src/widgets/WidgetPhotos.vue
+++ b/packages/frontend/src/widgets/WidgetPhotos.vue
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onUnmounted, ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import { useStream } from '@/stream.js';
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
diff --git a/packages/frontend/src/widgets/WidgetPostForm.vue b/packages/frontend/src/widgets/WidgetPostForm.vue
index d20ea3f8f4..320b47a4ff 100644
--- a/packages/frontend/src/widgets/WidgetPostForm.vue
+++ b/packages/frontend/src/widgets/WidgetPostForm.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import MkPostForm from '@/components/MkPostForm.vue';
diff --git a/packages/frontend/src/widgets/WidgetProfile.vue b/packages/frontend/src/widgets/WidgetProfile.vue
index 8cd5ffa9e1..fc54af2d71 100644
--- a/packages/frontend/src/widgets/WidgetProfile.vue
+++ b/packages/frontend/src/widgets/WidgetProfile.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import { $i } from '@/account.js';
import { userPage } from '@/filters/user.js';
diff --git a/packages/frontend/src/widgets/WidgetRss.vue b/packages/frontend/src/widgets/WidgetRss.vue
index 22833415dd..5540a09c71 100644
--- a/packages/frontend/src/widgets/WidgetRss.vue
+++ b/packages/frontend/src/widgets/WidgetRss.vue
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, watch, computed } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import MkContainer from '@/components/MkContainer.vue';
import { url as base } from '@/config.js';
diff --git a/packages/frontend/src/widgets/WidgetRssTicker.vue b/packages/frontend/src/widgets/WidgetRssTicker.vue
index ce8142f1ad..2b2a5233be 100644
--- a/packages/frontend/src/widgets/WidgetRssTicker.vue
+++ b/packages/frontend/src/widgets/WidgetRssTicker.vue
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, watch, computed } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import MarqueeText from '@/components/MkMarquee.vue';
import { GetFormResultType } from '@/scripts/form.js';
import MkContainer from '@/components/MkContainer.vue';
diff --git a/packages/frontend/src/widgets/WidgetSlideshow.vue b/packages/frontend/src/widgets/WidgetSlideshow.vue
index fe7ac8303d..82b6246add 100644
--- a/packages/frontend/src/widgets/WidgetSlideshow.vue
+++ b/packages/frontend/src/widgets/WidgetSlideshow.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, ref, shallowRef } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import * as os from '@/os.js';
import { useInterval } from '@/scripts/use-interval.js';
diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue
index 51623023c7..b8842d7b52 100644
--- a/packages/frontend/src/widgets/WidgetTimeline.vue
+++ b/packages/frontend/src/widgets/WidgetTimeline.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #icon>
<i v-if="widgetProps.src === 'home'" class="ti ti-home"></i>
<i v-else-if="widgetProps.src === 'local'" class="ti ti-planet"></i>
- <i v-else-if="widgetProps.src === 'social'" class="ti ti-rocket"></i>
+ <i v-else-if="widgetProps.src === 'social'" class="ti ti-universe"></i>
<i v-else-if="widgetProps.src === 'global'" class="ti ti-whirl"></i>
<i v-else-if="widgetProps.src === 'list'" class="ti ti-list"></i>
<i v-else-if="widgetProps.src === 'antenna'" class="ti ti-antenna"></i>
@@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import * as os from '@/os.js';
import MkContainer from '@/components/MkContainer.vue';
@@ -124,7 +124,7 @@ const choose = async (ev) => {
action: () => { setSrc('local'); },
}, {
text: i18n.ts._timelines.social,
- icon: 'ti ti-rocket',
+ icon: 'ti ti-universe',
action: () => { setSrc('social'); },
}, {
text: i18n.ts._timelines.global,
diff --git a/packages/frontend/src/widgets/WidgetTrends.vue b/packages/frontend/src/widgets/WidgetTrends.vue
index 0a57305526..0d4df28a95 100644
--- a/packages/frontend/src/widgets/WidgetTrends.vue
+++ b/packages/frontend/src/widgets/WidgetTrends.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import MkContainer from '@/components/MkContainer.vue';
import MkMiniChart from '@/components/MkMiniChart.vue';
diff --git a/packages/frontend/src/widgets/WidgetUnixClock.vue b/packages/frontend/src/widgets/WidgetUnixClock.vue
index 69bceb7572..33585cd721 100644
--- a/packages/frontend/src/widgets/WidgetUnixClock.vue
+++ b/packages/frontend/src/widgets/WidgetUnixClock.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onUnmounted, ref, watch } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
const name = 'unixClock';
diff --git a/packages/frontend/src/widgets/WidgetUserList.vue b/packages/frontend/src/widgets/WidgetUserList.vue
index 343e9a4292..a0d460c704 100644
--- a/packages/frontend/src/widgets/WidgetUserList.vue
+++ b/packages/frontend/src/widgets/WidgetUserList.vue
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import { GetFormResultType } from '@/scripts/form.js';
import MkContainer from '@/components/MkContainer.vue';
import * as os from '@/os.js';