summaryrefslogtreecommitdiff
path: root/packages/frontend
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2023-02-22 18:06:25 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2023-02-22 18:06:25 +0900
commit76583510419a7a21dfc3c43c9a6e29de791da0fa (patch)
tree07c05b0e5331256140becb55dc85928b6618e272 /packages/frontend
parentMerge branch 'develop' (diff)
parent13.7.0 (diff)
downloadmisskey-76583510419a7a21dfc3c43c9a6e29de791da0fa.tar.gz
misskey-76583510419a7a21dfc3c43c9a6e29de791da0fa.tar.bz2
misskey-76583510419a7a21dfc3c43c9a6e29de791da0fa.zip
Merge branch 'develop'
Diffstat (limited to 'packages/frontend')
-rw-r--r--packages/frontend/.eslintrc.js1
-rw-r--r--packages/frontend/assets/label-red.svg12
-rw-r--r--packages/frontend/assets/label.svg12
-rw-r--r--packages/frontend/assets/unread.svg14
-rw-r--r--packages/frontend/package.json33
-rw-r--r--packages/frontend/src/components/MkAbuseReport.vue1
-rw-r--r--packages/frontend/src/components/MkAbuseReportWindow.vue2
-rw-r--r--packages/frontend/src/components/MkAnalogClock.vue2
-rw-r--r--packages/frontend/src/components/MkAsUi.vue2
-rw-r--r--packages/frontend/src/components/MkAutocomplete.vue3
-rw-r--r--packages/frontend/src/components/MkCaptcha.vue2
-rw-r--r--packages/frontend/src/components/MkChart.vue2
-rw-r--r--packages/frontend/src/components/MkChartLegend.vue1
-rw-r--r--packages/frontend/src/components/MkClickerGame.vue5
-rw-r--r--packages/frontend/src/components/MkCropperDialog.vue2
-rw-r--r--packages/frontend/src/components/MkCwButton.vue3
-rw-r--r--packages/frontend/src/components/MkDialog.vue39
-rw-r--r--packages/frontend/src/components/MkDonation.vue3
-rw-r--r--packages/frontend/src/components/MkDrive.vue2
-rw-r--r--packages/frontend/src/components/MkEmojiPicker.vue1
-rw-r--r--packages/frontend/src/components/MkEmojiPickerWindow.vue2
-rw-r--r--packages/frontend/src/components/MkFileListForAdmin.vue2
-rw-r--r--packages/frontend/src/components/MkFlashPreview.vue2
-rw-r--r--packages/frontend/src/components/MkFolder.vue35
-rw-r--r--packages/frontend/src/components/MkGalleryPostPreview.vue2
-rw-r--r--packages/frontend/src/components/MkHeatmap.vue5
-rw-r--r--packages/frontend/src/components/MkInput.vue8
-rw-r--r--packages/frontend/src/components/MkLaunchPad.vue3
-rw-r--r--packages/frontend/src/components/MkMediaList.vue9
-rw-r--r--packages/frontend/src/components/MkMenu.child.vue4
-rw-r--r--packages/frontend/src/components/MkMenu.vue2
-rw-r--r--packages/frontend/src/components/MkMiniChart.vue2
-rw-r--r--packages/frontend/src/components/MkModalPageWindow.vue2
-rw-r--r--packages/frontend/src/components/MkNote.vue6
-rw-r--r--packages/frontend/src/components/MkNoteDetailed.vue2
-rw-r--r--packages/frontend/src/components/MkNotification.vue20
-rw-r--r--packages/frontend/src/components/MkNotificationSettingWindow.vue2
-rw-r--r--packages/frontend/src/components/MkNotifications.vue3
-rw-r--r--packages/frontend/src/components/MkNumber.vue2
-rw-r--r--packages/frontend/src/components/MkObjectView.value.vue2
-rw-r--r--packages/frontend/src/components/MkOmit.vue2
-rw-r--r--packages/frontend/src/components/MkPagePreview.vue1
-rw-r--r--packages/frontend/src/components/MkPageWindow.vue16
-rw-r--r--packages/frontend/src/components/MkPagination.vue2
-rw-r--r--packages/frontend/src/components/MkPoll.vue2
-rw-r--r--packages/frontend/src/components/MkPostForm.vue23
-rw-r--r--packages/frontend/src/components/MkPostFormAttaches.vue3
-rw-r--r--packages/frontend/src/components/MkReactedUsersDialog.vue22
-rw-r--r--packages/frontend/src/components/MkReactionsViewer.reaction.vue2
-rw-r--r--packages/frontend/src/components/MkReactionsViewer.vue2
-rw-r--r--packages/frontend/src/components/MkRetentionHeatmap.vue5
-rw-r--r--packages/frontend/src/components/MkRolePreview.vue21
-rw-r--r--packages/frontend/src/components/MkSelect.vue6
-rw-r--r--packages/frontend/src/components/MkSignin.vue8
-rw-r--r--packages/frontend/src/components/MkSuperMenu.vue2
-rw-r--r--packages/frontend/src/components/MkSwitch.vue1
-rw-r--r--packages/frontend/src/components/MkTagCloud.vue2
-rw-r--r--packages/frontend/src/components/MkTextarea.vue2
-rw-r--r--packages/frontend/src/components/MkTimeline.vue9
-rw-r--r--packages/frontend/src/components/MkToast.vue2
-rw-r--r--packages/frontend/src/components/MkTooltip.vue2
-rw-r--r--packages/frontend/src/components/MkUrlPreview.vue2
-rw-r--r--packages/frontend/src/components/MkUserList.vue13
-rw-r--r--packages/frontend/src/components/MkUserPreview.vue8
-rw-r--r--packages/frontend/src/components/MkUserSelectDialog.vue7
-rw-r--r--packages/frontend/src/components/MkWidgets.vue3
-rw-r--r--packages/frontend/src/components/global/MkA.vue1
-rw-r--r--packages/frontend/src/components/global/MkAvatar.vue2
-rw-r--r--packages/frontend/src/components/global/MkCustomEmoji.vue4
-rw-r--r--packages/frontend/src/components/global/MkError.vue6
-rw-r--r--packages/frontend/src/components/global/MkPageHeader.tabs.vue50
-rw-r--r--packages/frontend/src/components/global/MkPageHeader.vue12
-rw-r--r--packages/frontend/src/components/global/MkSpacer.vue2
-rw-r--r--packages/frontend/src/components/global/RouterView.vue2
-rw-r--r--packages/frontend/src/components/mfm.ts4
-rw-r--r--packages/frontend/src/components/page/page.canvas.vue1
-rw-r--r--packages/frontend/src/components/page/page.counter.vue1
-rw-r--r--packages/frontend/src/components/page/page.image.vue3
-rw-r--r--packages/frontend/src/components/page/page.number-input.vue1
-rw-r--r--packages/frontend/src/components/page/page.radio-button.vue1
-rw-r--r--packages/frontend/src/components/page/page.section.vue1
-rw-r--r--packages/frontend/src/components/page/page.switch.vue1
-rw-r--r--packages/frontend/src/components/page/page.text-input.vue1
-rw-r--r--packages/frontend/src/components/page/page.textarea-input.vue2
-rw-r--r--packages/frontend/src/components/page/page.vue3
-rw-r--r--packages/frontend/src/init.ts11
-rw-r--r--packages/frontend/src/instance.ts2
-rw-r--r--packages/frontend/src/navbar.ts17
-rw-r--r--packages/frontend/src/nirax.ts2
-rw-r--r--packages/frontend/src/os.ts8
-rw-r--r--packages/frontend/src/pages/about-misskey.vue10
-rw-r--r--packages/frontend/src/pages/about.emojis.vue5
-rw-r--r--packages/frontend/src/pages/about.federation.vue3
-rw-r--r--packages/frontend/src/pages/about.vue4
-rw-r--r--packages/frontend/src/pages/achievements.vue2
-rw-r--r--packages/frontend/src/pages/admin-file.vue1
-rw-r--r--packages/frontend/src/pages/admin/RolesEditorFormula.vue5
-rw-r--r--packages/frontend/src/pages/admin/_header_.vue6
-rw-r--r--packages/frontend/src/pages/admin/abuses.vue2
-rw-r--r--packages/frontend/src/pages/admin/ads.vue34
-rw-r--r--packages/frontend/src/pages/admin/federation.vue3
-rw-r--r--packages/frontend/src/pages/admin/files.vue5
-rw-r--r--packages/frontend/src/pages/admin/index.vue5
-rw-r--r--packages/frontend/src/pages/admin/object-storage.vue1
-rw-r--r--packages/frontend/src/pages/admin/overview.active-users.vue4
-rw-r--r--packages/frontend/src/pages/admin/overview.ap-requests.vue7
-rw-r--r--packages/frontend/src/pages/admin/overview.federation.vue3
-rw-r--r--packages/frontend/src/pages/admin/overview.instances.vue2
-rw-r--r--packages/frontend/src/pages/admin/overview.moderators.vue4
-rw-r--r--packages/frontend/src/pages/admin/overview.pie.vue4
-rw-r--r--packages/frontend/src/pages/admin/overview.queue.chart.vue4
-rw-r--r--packages/frontend/src/pages/admin/overview.queue.vue2
-rw-r--r--packages/frontend/src/pages/admin/overview.stats.vue4
-rw-r--r--packages/frontend/src/pages/admin/overview.users.vue1
-rw-r--r--packages/frontend/src/pages/admin/overview.vue6
-rw-r--r--packages/frontend/src/pages/admin/queue.chart.chart.vue4
-rw-r--r--packages/frontend/src/pages/admin/queue.vue2
-rw-r--r--packages/frontend/src/pages/admin/roles.edit.vue7
-rw-r--r--packages/frontend/src/pages/admin/roles.editor.vue2
-rw-r--r--packages/frontend/src/pages/admin/roles.role.vue36
-rw-r--r--packages/frontend/src/pages/admin/roles.vue4
-rw-r--r--packages/frontend/src/pages/admin/security.vue1
-rw-r--r--packages/frontend/src/pages/admin/settings.vue1
-rw-r--r--packages/frontend/src/pages/antenna-timeline.vue10
-rw-r--r--packages/frontend/src/pages/auth.vue15
-rw-r--r--packages/frontend/src/pages/channel-editor.vue2
-rw-r--r--packages/frontend/src/pages/channel.vue7
-rw-r--r--packages/frontend/src/pages/channels.vue2
-rw-r--r--packages/frontend/src/pages/clicker.vue2
-rw-r--r--packages/frontend/src/pages/clip.vue4
-rw-r--r--packages/frontend/src/pages/custom-emojis-manager.vue3
-rw-r--r--packages/frontend/src/pages/drive.vue1
-rw-r--r--packages/frontend/src/pages/emoji-edit-dialog.vue1
-rw-r--r--packages/frontend/src/pages/explore.featured.vue6
-rw-r--r--packages/frontend/src/pages/explore.roles.vue22
-rw-r--r--packages/frontend/src/pages/explore.users.vue38
-rw-r--r--packages/frontend/src/pages/explore.vue16
-rw-r--r--packages/frontend/src/pages/favorites.vue1
-rw-r--r--packages/frontend/src/pages/flash/flash-edit.vue121
-rw-r--r--packages/frontend/src/pages/flash/flash-index.vue2
-rw-r--r--packages/frontend/src/pages/flash/flash.vue8
-rw-r--r--packages/frontend/src/pages/gallery/edit.vue2
-rw-r--r--packages/frontend/src/pages/gallery/index.vue8
-rw-r--r--packages/frontend/src/pages/gallery/post.vue5
-rw-r--r--packages/frontend/src/pages/instance-info.vue1
-rw-r--r--packages/frontend/src/pages/messaging/index.vue305
-rw-r--r--packages/frontend/src/pages/messaging/messaging-room.form.vue366
-rw-r--r--packages/frontend/src/pages/messaging/messaging-room.message.vue338
-rw-r--r--packages/frontend/src/pages/messaging/messaging-room.vue415
-rw-r--r--packages/frontend/src/pages/my-antennas/create.vue2
-rw-r--r--packages/frontend/src/pages/my-antennas/edit.vue1
-rw-r--r--packages/frontend/src/pages/my-antennas/editor.vue15
-rw-r--r--packages/frontend/src/pages/note.vue11
-rw-r--r--packages/frontend/src/pages/notifications.vue6
-rw-r--r--packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue4
-rw-r--r--packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue2
-rw-r--r--packages/frontend/src/pages/page-editor/page-editor.blocks.vue2
-rw-r--r--packages/frontend/src/pages/page-editor/page-editor.vue3
-rw-r--r--packages/frontend/src/pages/pages.vue2
-rw-r--r--packages/frontend/src/pages/registry.keys.vue2
-rw-r--r--packages/frontend/src/pages/registry.value.vue4
-rw-r--r--packages/frontend/src/pages/registry.vue1
-rw-r--r--packages/frontend/src/pages/role.vue47
-rw-r--r--packages/frontend/src/pages/scratchpad.vue2
-rw-r--r--packages/frontend/src/pages/search.vue4
-rw-r--r--packages/frontend/src/pages/settings/2fa.qrdialog.vue82
-rw-r--r--packages/frontend/src/pages/settings/2fa.vue366
-rw-r--r--packages/frontend/src/pages/settings/deck.vue6
-rw-r--r--packages/frontend/src/pages/settings/drive.vue2
-rw-r--r--packages/frontend/src/pages/settings/email.vue7
-rw-r--r--packages/frontend/src/pages/settings/index.vue5
-rw-r--r--packages/frontend/src/pages/settings/mute-block.vue1
-rw-r--r--packages/frontend/src/pages/settings/notifications.vue6
-rw-r--r--packages/frontend/src/pages/settings/profile.vue11
-rw-r--r--packages/frontend/src/pages/settings/reaction.vue1
-rw-r--r--packages/frontend/src/pages/settings/security.vue12
-rw-r--r--packages/frontend/src/pages/settings/sounds.vue5
-rw-r--r--packages/frontend/src/pages/settings/statusbar.statusbar.vue3
-rw-r--r--packages/frontend/src/pages/settings/statusbar.vue4
-rw-r--r--packages/frontend/src/pages/settings/webhook.vue3
-rw-r--r--packages/frontend/src/pages/tag.vue4
-rw-r--r--packages/frontend/src/pages/timeline.vue12
-rw-r--r--packages/frontend/src/pages/user-info.vue7
-rw-r--r--packages/frontend/src/pages/user-list-timeline.vue10
-rw-r--r--packages/frontend/src/pages/user-tag.vue38
-rw-r--r--packages/frontend/src/pages/user/achievements.vue3
-rw-r--r--packages/frontend/src/pages/user/activity.following.vue5
-rw-r--r--packages/frontend/src/pages/user/activity.heatmap.vue4
-rw-r--r--packages/frontend/src/pages/user/activity.notes.vue5
-rw-r--r--packages/frontend/src/pages/user/activity.pv.vue4
-rw-r--r--packages/frontend/src/pages/user/activity.vue1
-rw-r--r--packages/frontend/src/pages/user/followers.vue2
-rw-r--r--packages/frontend/src/pages/user/following.vue2
-rw-r--r--packages/frontend/src/pages/user/home.vue19
-rw-r--r--packages/frontend/src/pages/user/index.timeline.vue7
-rw-r--r--packages/frontend/src/pages/user/index.vue1
-rw-r--r--packages/frontend/src/pages/welcome.entrance.a.vue278
-rw-r--r--packages/frontend/src/pages/welcome.timeline.vue100
-rw-r--r--packages/frontend/src/pizzax.ts30
-rw-r--r--packages/frontend/src/router.ts21
-rw-r--r--packages/frontend/src/scripts/aiscript/ui.ts2
-rw-r--r--packages/frontend/src/scripts/get-note-menu.ts14
-rw-r--r--packages/frontend/src/scripts/get-user-menu.ts36
-rw-r--r--packages/frontend/src/scripts/get-user-name.ts2
-rw-r--r--packages/frontend/src/scripts/hpml/evaluator.ts5
-rw-r--r--packages/frontend/src/scripts/hpml/index.ts14
-rw-r--r--packages/frontend/src/scripts/hpml/lib.ts57
-rw-r--r--packages/frontend/src/scripts/hpml/type-checker.ts4
-rw-r--r--packages/frontend/src/scripts/popup-position.ts1
-rw-r--r--packages/frontend/src/scripts/scroll.ts16
-rw-r--r--packages/frontend/src/scripts/search.ts2
-rw-r--r--packages/frontend/src/scripts/use-leave-guard.ts4
-rw-r--r--packages/frontend/src/store.ts1
-rw-r--r--packages/frontend/src/theme-store.ts6
-rw-r--r--packages/frontend/src/ui/_common_/common.vue4
-rw-r--r--packages/frontend/src/ui/_common_/navbar-for-mobile.vue2
-rw-r--r--packages/frontend/src/ui/_common_/statusbar-federation.vue4
-rw-r--r--packages/frontend/src/ui/_common_/statusbar-rss.vue3
-rw-r--r--packages/frontend/src/ui/_common_/statusbar-user-list.vue2
-rw-r--r--packages/frontend/src/ui/_common_/statusbars.vue3
-rw-r--r--packages/frontend/src/ui/classic.vue10
-rw-r--r--packages/frontend/src/ui/deck.vue10
-rw-r--r--packages/frontend/src/ui/deck/antenna-column.vue6
-rw-r--r--packages/frontend/src/ui/deck/channel-column.vue10
-rw-r--r--packages/frontend/src/ui/deck/column.vue4
-rw-r--r--packages/frontend/src/ui/deck/deck-store.ts1
-rw-r--r--packages/frontend/src/ui/deck/direct-column.vue4
-rw-r--r--packages/frontend/src/ui/deck/list-column.vue6
-rw-r--r--packages/frontend/src/ui/deck/main-column.vue2
-rw-r--r--packages/frontend/src/ui/deck/mentions-column.vue4
-rw-r--r--packages/frontend/src/ui/deck/tl-column.vue5
-rw-r--r--packages/frontend/src/ui/universal.vue5
-rw-r--r--packages/frontend/src/ui/visitor.vue2
-rw-r--r--packages/frontend/src/ui/visitor/b.vue11
-rw-r--r--packages/frontend/src/ui/visitor/kanban.vue2
-rw-r--r--packages/frontend/src/ui/zen.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetActivity.vue4
-rw-r--r--packages/frontend/src/widgets/WidgetAichan.vue4
-rw-r--r--packages/frontend/src/widgets/WidgetAiscript.vue4
-rw-r--r--packages/frontend/src/widgets/WidgetAiscriptApp.vue8
-rw-r--r--packages/frontend/src/widgets/WidgetButton.vue5
-rw-r--r--packages/frontend/src/widgets/WidgetCalendar.vue4
-rw-r--r--packages/frontend/src/widgets/WidgetClicker.vue4
-rw-r--r--packages/frontend/src/widgets/WidgetClock.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetDigitalClock.vue3
-rw-r--r--packages/frontend/src/widgets/WidgetFederation.vue4
-rw-r--r--packages/frontend/src/widgets/WidgetInstanceCloud.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetInstanceInfo.vue3
-rw-r--r--packages/frontend/src/widgets/WidgetJobQueue.vue5
-rw-r--r--packages/frontend/src/widgets/WidgetMemo.vue5
-rw-r--r--packages/frontend/src/widgets/WidgetNotifications.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetOnlineUsers.vue4
-rw-r--r--packages/frontend/src/widgets/WidgetPhotos.vue4
-rw-r--r--packages/frontend/src/widgets/WidgetPostForm.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetProfile.vue3
-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.vue4
-rw-r--r--packages/frontend/src/widgets/WidgetTimeline.vue9
-rw-r--r--packages/frontend/src/widgets/WidgetTrends.vue4
-rw-r--r--packages/frontend/src/widgets/WidgetUnixClock.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetUserList.vue3
-rw-r--r--packages/frontend/src/widgets/server-metric/index.vue4
-rw-r--r--packages/frontend/src/widgets/server-metric/mem.vue2
-rw-r--r--packages/frontend/vite.json5.ts16
265 files changed, 1406 insertions, 2603 deletions
diff --git a/packages/frontend/.eslintrc.js b/packages/frontend/.eslintrc.js
index 6c3bfb5a6e..e8e0e57d2a 100644
--- a/packages/frontend/.eslintrc.js
+++ b/packages/frontend/.eslintrc.js
@@ -55,6 +55,7 @@ module.exports = {
'vue/multi-word-component-names': 'warn',
'vue/require-v-for-key': 'warn',
'vue/no-unused-components': 'warn',
+ 'vue/no-unused-vars': 'warn',
'vue/valid-v-for': 'warn',
'vue/return-in-computed-property': 'warn',
'vue/no-setup-props-destructure': 'warn',
diff --git a/packages/frontend/assets/label-red.svg b/packages/frontend/assets/label-red.svg
index 45996aa9ce..c89d3f5f3a 100644
--- a/packages/frontend/assets/label-red.svg
+++ b/packages/frontend/assets/label-red.svg
@@ -1,6 +1,6 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
- y="0px" width="96px" height="96px" viewBox="0 0 96 96" enable-background="new 0 0 96 96" xml:space="preserve">
-<polygon fill="#ea2412" points="0,45.255 45.254,0 84.854,0 0,84.854 "/>
-</svg>
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
+ y="0px" width="96px" height="96px" viewBox="0 0 96 96" enable-background="new 0 0 96 96" xml:space="preserve">
+<polygon fill="#ea2412" points="0,45.255 45.254,0 84.854,0 0,84.854 "/>
+</svg>
diff --git a/packages/frontend/assets/label.svg b/packages/frontend/assets/label.svg
index b1f85f3c07..997335f505 100644
--- a/packages/frontend/assets/label.svg
+++ b/packages/frontend/assets/label.svg
@@ -1,6 +1,6 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
- y="0px" width="96px" height="96px" viewBox="0 0 96 96" enable-background="new 0 0 96 96" xml:space="preserve">
-<polygon fill="#0B8AEA" points="0,45.255 45.254,0 84.854,0 0,84.854 "/>
-</svg>
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
+ y="0px" width="96px" height="96px" viewBox="0 0 96 96" enable-background="new 0 0 96 96" xml:space="preserve">
+<polygon fill="#0B8AEA" points="0,45.255 45.254,0 84.854,0 0,84.854 "/>
+</svg>
diff --git a/packages/frontend/assets/unread.svg b/packages/frontend/assets/unread.svg
index 8c3cc9f475..8bd4156e51 100644
--- a/packages/frontend/assets/unread.svg
+++ b/packages/frontend/assets/unread.svg
@@ -1,7 +1,7 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 16.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
- y="0px" width="32px" height="32px" viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
-<circle fill="#3AA2DC" cx="16.5" cy="16.5" r="6"/>
-</svg>
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
+ y="0px" width="32px" height="32px" viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
+<circle fill="#3AA2DC" cx="16.5" cy="16.5" r="6"/>
+</svg>
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index bf22f7aaad..f89a946282 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -4,7 +4,9 @@
"scripts": {
"watch": "vite",
"build": "vite build",
- "lint": "vue-tsc --noEmit && eslint --quiet \"src/**/*.{ts,vue}\""
+ "typecheck": "vue-tsc --noEmit",
+ "eslint": "eslint --quiet \"src/**/*.{ts,vue}\"",
+ "lint": "pnpm typecheck && pnpm eslint"
},
"dependencies": {
"@discordapp/twemoji": "14.0.2",
@@ -17,11 +19,11 @@
"@vue/compiler-sfc": "3.2.47",
"autobind-decorator": "2.4.0",
"autosize": "5.0.2",
- "blurhash": "2.0.4",
+ "blurhash": "2.0.5",
"broadcast-channel": "4.20.2",
"browser-image-resizer": "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.3",
"canvas-confetti": "1.6.0",
- "chart.js": "4.2.0",
+ "chart.js": "4.2.1",
"chartjs-adapter-date-fns": "3.0.0",
"chartjs-chart-matrix": "2.0.1",
"chartjs-plugin-gradient": "0.6.1",
@@ -36,7 +38,7 @@
"insert-text-at-cursor": "0.3.0",
"is-file-animated": "1.0.2",
"json5": "2.2.3",
- "matter-js": "0.18.0",
+ "matter-js": "0.19.0",
"mfm-js": "0.23.3",
"misskey-js": "0.0.15",
"photoswipe": "5.3.5",
@@ -44,13 +46,12 @@
"punycode": "2.3.0",
"querystring": "0.2.1",
"rndstr": "1.0.0",
- "rollup": "3.14.0",
+ "rollup": "3.17.2",
"s-age": "1.1.2",
- "sanitize-html": "2.9.0",
- "sass": "1.58.0",
+ "sanitize-html": "2.10.0",
+ "sass": "1.58.3",
"seedrandom": "3.0.5",
"strict-event-emitter-types": "2.0.0",
- "stringz": "2.1.0",
"syuilo-password-strength": "0.0.1",
"textarea-caret": "3.1.0",
"three": "0.149.0",
@@ -63,7 +64,7 @@
"uuid": "9.0.0",
"vanilla-tilt": "1.8.0",
"vue-plyr": "7.0.0",
- "vite": "4.1.1",
+ "vite": "4.1.2",
"vue": "3.2.47",
"vue-prism-editor": "2.0.0-alpha.2",
"vuedraggable": "next"
@@ -74,7 +75,7 @@
"@types/gulp": "4.0.10",
"@types/gulp-rename": "2.0.1",
"@types/matter-js": "0.18.2",
- "@types/node": "18.13.0",
+ "@types/node": "18.14.0",
"@types/punycode": "2.1.0",
"@types/sanitize-html": "2.8.0",
"@types/seedrandom": "3.0.4",
@@ -83,16 +84,16 @@
"@types/uuid": "9.0.0",
"@types/websocket": "1.0.5",
"@types/ws": "8.5.4",
- "@typescript-eslint/eslint-plugin": "5.51.0",
- "@typescript-eslint/parser": "5.51.0",
+ "@typescript-eslint/eslint-plugin": "5.52.0",
+ "@typescript-eslint/parser": "5.52.0",
"@vue/runtime-core": "3.2.47",
"cross-env": "7.0.3",
- "cypress": "12.5.1",
- "eslint": "8.33.0",
+ "cypress": "12.6.0",
+ "eslint": "8.34.0",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-vue": "9.9.0",
- "start-server-and-test": "1.15.3",
+ "start-server-and-test": "1.15.4",
"vue-eslint-parser": "9.1.0",
- "vue-tsc": "1.0.24"
+ "vue-tsc": "1.1.4"
}
}
diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue
index 0e18a5a83d..dee80378e6 100644
--- a/packages/frontend/src/components/MkAbuseReport.vue
+++ b/packages/frontend/src/components/MkAbuseReport.vue
@@ -39,7 +39,6 @@
import MkButton from '@/components/MkButton.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
-import { acct, userPage } from '@/filters/user';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { dateString } from '@/filters/date';
diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue
index a76a1e0f54..9f2bf99338 100644
--- a/packages/frontend/src/components/MkAbuseReportWindow.vue
+++ b/packages/frontend/src/components/MkAbuseReportWindow.vue
@@ -43,7 +43,7 @@ const emit = defineEmits<{
}>();
const uiWindow = shallowRef<InstanceType<typeof MkWindow>>();
-const comment = ref(props.initialComment || '');
+const comment = ref(props.initialComment ?? '');
function send() {
os.apiWithDialog('users/report-abuse', {
diff --git a/packages/frontend/src/components/MkAnalogClock.vue b/packages/frontend/src/components/MkAnalogClock.vue
index 139e49cc40..1218202616 100644
--- a/packages/frontend/src/components/MkAnalogClock.vue
+++ b/packages/frontend/src/components/MkAnalogClock.vue
@@ -73,7 +73,7 @@
</template>
<script lang="ts" setup>
-import { ref, computed, onMounted, onBeforeUnmount, shallowRef, nextTick } from 'vue';
+import { computed, onMounted, onBeforeUnmount } from 'vue';
import tinycolor from 'tinycolor2';
import { globalEvents } from '@/events.js';
diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue
index 4f463d73d9..6ade5316c6 100644
--- a/packages/frontend/src/components/MkAsUi.vue
+++ b/packages/frontend/src/components/MkAsUi.vue
@@ -48,7 +48,7 @@
</template>
<script lang="ts" setup>
-import { computed, defineAsyncComponent, onMounted, onUnmounted, Ref } from 'vue';
+import { Ref } from 'vue';
import * as os from '@/os';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue
index e523b988b0..663c57623d 100644
--- a/packages/frontend/src/components/MkAutocomplete.vue
+++ b/packages/frontend/src/components/MkAutocomplete.vue
@@ -43,7 +43,6 @@ import * as os from '@/os';
import { MFM_TAGS } from '@/scripts/mfm-tags';
import { defaultStore } from '@/store';
import { emojilist } from '@/scripts/emojilist';
-import { instance } from '@/instance';
import { i18n } from '@/i18n';
import { miLocalStorage } from '@/local-storage';
import { customEmojis } from '@/custom-emojis';
@@ -210,7 +209,7 @@ function exec() {
}
} else if (props.type === 'hashtag') {
if (!props.q || props.q === '') {
- hashtags.value = JSON.parse(miLocalStorage.getItem('hashtags') || '[]');
+ hashtags.value = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]');
fetching.value = false;
} else {
const cacheKey = `autocomplete:hashtag:${props.q}`;
diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue
index 8db2e54e88..c72cc2ab1b 100644
--- a/packages/frontend/src/components/MkCaptcha.vue
+++ b/packages/frontend/src/components/MkCaptcha.vue
@@ -69,7 +69,7 @@ const captcha = computed<Captcha>(() => window[variable.value] || {} as unknown
if (loaded) {
available.value = true;
} else {
- (document.getElementById(scriptId.value) || document.head.appendChild(Object.assign(document.createElement('script'), {
+ (document.getElementById(scriptId.value) ?? document.head.appendChild(Object.assign(document.createElement('script'), {
async: true,
id: scriptId.value,
src: src.value,
diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue
index 57efda44b1..06d5b9949a 100644
--- a/packages/frontend/src/components/MkChart.vue
+++ b/packages/frontend/src/components/MkChart.vue
@@ -14,7 +14,7 @@
id-denylist violation when setting it. This is causing about 60+ lint issues.
As this is part of Chart.js's API it makes sense to disable the check here.
*/
-import { onMounted, ref, shallowRef, watch, PropType, onUnmounted } from 'vue';
+import { onMounted, ref, shallowRef, watch, PropType } from 'vue';
import { Chart } from 'chart.js';
import gradient from 'chartjs-plugin-gradient';
import * as os from '@/os';
diff --git a/packages/frontend/src/components/MkChartLegend.vue b/packages/frontend/src/components/MkChartLegend.vue
index b950f2836e..7bc6194b7a 100644
--- a/packages/frontend/src/components/MkChartLegend.vue
+++ b/packages/frontend/src/components/MkChartLegend.vue
@@ -8,7 +8,6 @@
</template>
<script lang="ts" setup>
-import { onMounted, ref, shallowRef, watch, PropType, onUnmounted } from 'vue';
import { Chart, LegendItem } from 'chart.js';
const props = defineProps({
diff --git a/packages/frontend/src/components/MkClickerGame.vue b/packages/frontend/src/components/MkClickerGame.vue
index 68e0f8185d..da6439fd2c 100644
--- a/packages/frontend/src/components/MkClickerGame.vue
+++ b/packages/frontend/src/components/MkClickerGame.vue
@@ -14,7 +14,7 @@
</template>
<script lang="ts" setup>
-import { computed, defineAsyncComponent, onMounted, onUnmounted } from 'vue';
+import { computed, onMounted, onUnmounted } from 'vue';
import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue';
import * as os from '@/os';
import { useInterval } from '@/scripts/use-interval';
@@ -22,9 +22,6 @@ import * as game from '@/scripts/clicker-game';
import number from '@/filters/number';
import { claimAchievement } from '@/scripts/achievements';
-defineProps<{
-}>();
-
const saveData = game.saveData;
const cookies = computed(() => saveData.value?.cookies);
let cps = $ref(0);
diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue
index 7e4d2016be..d1b5cc5118 100644
--- a/packages/frontend/src/components/MkCropperDialog.vue
+++ b/packages/frontend/src/components/MkCropperDialog.vue
@@ -26,7 +26,7 @@
</template>
<script lang="ts" setup>
-import { nextTick, onMounted } from 'vue';
+import { onMounted } from 'vue';
import * as misskey from 'misskey-js';
import Cropper from 'cropperjs';
import tinycolor from 'tinycolor2';
diff --git a/packages/frontend/src/components/MkCwButton.vue b/packages/frontend/src/components/MkCwButton.vue
index e0885f5550..7d5579040a 100644
--- a/packages/frontend/src/components/MkCwButton.vue
+++ b/packages/frontend/src/components/MkCwButton.vue
@@ -7,7 +7,6 @@
<script lang="ts" setup>
import { computed } from 'vue';
-import { length } from 'stringz';
import * as misskey from 'misskey-js';
import { concat } from '@/scripts/array';
import { i18n } from '@/i18n';
@@ -23,7 +22,7 @@ const emit = defineEmits<{
const label = computed(() => {
return concat([
- props.note.text ? [i18n.t('_cw.chars', { count: length(props.note.text) })] : [],
+ props.note.text ? [i18n.t('_cw.chars', { count: props.note.text.length })] : [],
props.note.files && props.note.files.length !== 0 ? [i18n.t('_cw.files', { count: props.note.files.length })] : [],
props.note.poll != null ? [i18n.ts.poll] : [],
] as string[][]).join(' / ');
diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue
index 9690353432..863ea702cd 100644
--- a/packages/frontend/src/components/MkDialog.vue
+++ b/packages/frontend/src/components/MkDialog.vue
@@ -14,8 +14,12 @@
</div>
<header v-if="title" :class="$style.title"><Mfm :text="title"/></header>
<div v-if="text" :class="$style.text"><Mfm :text="text"/></div>
- <MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" @keydown="onInputKeydown">
+ <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>
+ <span v-if="okButtonDisabled && disabledReason === 'charactersExceeded'" v-text="i18n.t('_dialog.charactersExceeded', { current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })" />
+ <span v-else-if="okButtonDisabled && disabledReason === 'charactersBelow'" v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })" />
+ </template>
</MkInput>
<MkSelect v-if="select" v-model="selectedValue" autofocus>
<template v-if="select.items">
@@ -28,7 +32,7 @@
</template>
</MkSelect>
<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons">
- <MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
+ <MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" :disabled="okButtonDisabled" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
<MkButton v-if="showCancelButton || input || select" inline @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton>
</div>
<div v-if="actions" :class="$style.buttons">
@@ -47,9 +51,12 @@ import MkSelect from '@/components/MkSelect.vue';
import { i18n } from '@/i18n';
type Input = {
- type: HTMLInputElement['type'];
+ type: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local';
placeholder?: string | null;
- default: any | null;
+ autocomplete?: string;
+ default: string | number | null;
+ minLength?: number;
+ maxLength?: number;
};
type Select = {
@@ -98,8 +105,28 @@ const emit = defineEmits<{
const modal = shallowRef<InstanceType<typeof MkModal>>();
-const inputValue = ref(props.input?.default || null);
-const selectedValue = ref(props.select?.default || null);
+const inputValue = ref<string | number | null>(props.input?.default ?? null);
+const selectedValue = ref(props.select?.default ?? null);
+
+let disabledReason = $ref<null | 'charactersExceeded' | 'charactersBelow'>(null);
+const okButtonDisabled = $computed<boolean>(() => {
+ if (props.input) {
+ if (props.input.minLength) {
+ if ((inputValue.value || inputValue.value === '') && (inputValue.value as string).length < props.input.minLength) {
+ disabledReason = 'charactersBelow';
+ return true;
+ }
+ }
+ if (props.input.maxLength) {
+ if (inputValue.value && (inputValue.value as string).length > props.input.maxLength) {
+ disabledReason = 'charactersExceeded';
+ return true;
+ }
+ }
+ }
+
+ return false;
+});
function done(canceled: boolean, result?) {
emit('done', { canceled, result });
diff --git a/packages/frontend/src/components/MkDonation.vue b/packages/frontend/src/components/MkDonation.vue
index 707444abc9..9baa90ebfe 100644
--- a/packages/frontend/src/components/MkDonation.vue
+++ b/packages/frontend/src/components/MkDonation.vue
@@ -31,7 +31,6 @@
</template>
<script lang="ts" setup>
-import { onMounted, shallowRef } from 'vue';
import MkButton from '@/components/MkButton.vue';
import MkLink from '@/components/MkLink.vue';
import { host } from '@/config';
@@ -51,7 +50,7 @@ function close() {
}
function neverShow() {
- miLocalStorage.setItem('neverShowDonationInfo', 'true')
+ miLocalStorage.setItem('neverShowDonationInfo', 'true');
close();
}
</script>
diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue
index af7175e5cd..bfec57d6a0 100644
--- a/packages/frontend/src/components/MkDrive.vue
+++ b/packages/frontend/src/components/MkDrive.vue
@@ -88,7 +88,7 @@
</template>
<script lang="ts" setup>
-import { markRaw, nextTick, onActivated, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
+import { nextTick, onActivated, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
import * as Misskey from 'misskey-js';
import MkButton from './MkButton.vue';
import XNavFolder from '@/components/MkDrive.navFolder.vue';
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index c7556ec36e..7d280f2f4b 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -95,7 +95,6 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue';
import * as os from '@/os';
import { isTouchUsing } from '@/scripts/touch';
import { deviceKind } from '@/scripts/device-kind';
-import { instance } from '@/instance';
import { i18n } from '@/i18n';
import { defaultStore } from '@/store';
import { customEmojiCategories, customEmojis } from '@/custom-emojis';
diff --git a/packages/frontend/src/components/MkEmojiPickerWindow.vue b/packages/frontend/src/components/MkEmojiPickerWindow.vue
index ca7dbccdc8..84970410e9 100644
--- a/packages/frontend/src/components/MkEmojiPickerWindow.vue
+++ b/packages/frontend/src/components/MkEmojiPickerWindow.vue
@@ -7,7 +7,7 @@
:front="true"
@closed="emit('closed')"
>
- <MkEmojiPicker :show-pinned="showPinned" :as-reaction-picker="asReactionPicker" as-window @chosen="chosen" :class="$style.picker"/>
+ <MkEmojiPicker :show-pinned="showPinned" :as-reaction-picker="asReactionPicker" as-window :class="$style.picker" @chosen="chosen"/>
</MkWindow>
</template>
diff --git a/packages/frontend/src/components/MkFileListForAdmin.vue b/packages/frontend/src/components/MkFileListForAdmin.vue
index f340acaf2d..71a35ae6e8 100644
--- a/packages/frontend/src/components/MkFileListForAdmin.vue
+++ b/packages/frontend/src/components/MkFileListForAdmin.vue
@@ -32,12 +32,10 @@
</template>
<script lang="ts" setup>
-import { computed } from 'vue';
import * as Acct from 'misskey-js/built/acct';
import MkPagination from '@/components/MkPagination.vue';
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
import bytes from '@/filters/bytes';
-import * as os from '@/os';
import { i18n } from '@/i18n';
import { dateString } from '@/filters/date';
diff --git a/packages/frontend/src/components/MkFlashPreview.vue b/packages/frontend/src/components/MkFlashPreview.vue
index a96934ddb1..7c9ae155ab 100644
--- a/packages/frontend/src/components/MkFlashPreview.vue
+++ b/packages/frontend/src/components/MkFlashPreview.vue
@@ -15,9 +15,7 @@
<script lang="ts" setup>
import { } from 'vue';
-import * as misskey from 'misskey-js';
import { userName } from '@/filters/user';
-import * as os from '@/os';
const props = defineProps<{
//flash: misskey.entities.Flash;
diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue
index a1d7210d7e..b97e36cd5f 100644
--- a/packages/frontend/src/components/MkFolder.vue
+++ b/packages/frontend/src/components/MkFolder.vue
@@ -1,13 +1,20 @@
<template>
<div ref="rootEl" :class="[$style.root, { [$style.opened]: opened }]">
<div :class="$style.header" class="_button" @click="toggle">
- <span :class="$style.headerIcon"><slot name="icon"></slot></span>
- <span :class="$style.headerText"><slot name="label"></slot></span>
- <span :class="$style.headerRight">
+ <div :class="$style.headerIcon"><slot name="icon"></slot></div>
+ <div :class="$style.headerText">
+ <div :class="$style.headerTextMain">
+ <slot name="label"></slot>
+ </div>
+ <div :class="$style.headerTextSub">
+ <slot name="caption"></slot>
+ </div>
+ </div>
+ <div :class="$style.headerRight">
<span :class="$style.headerRightText"><slot name="suffix"></slot></span>
<i v-if="opened" class="ti ti-chevron-up icon"></i>
<i v-else class="ti ti-chevron-down icon"></i>
- </span>
+ </div>
</div>
<div v-if="openedAtLeastOnce" :class="[$style.body, { [$style.bgSame]: bgSame }]" :style="{ maxHeight: maxHeight ? `${maxHeight}px` : null }">
<Transition
@@ -139,6 +146,17 @@ onMounted(() => {
}
}
+.headerUpper {
+ display: flex;
+ align-items: center;
+}
+
+.headerLower {
+ color: var(--fgTransparentWeak);
+ font-size: .85em;
+ padding-left: 4px;
+}
+
.headerIcon {
margin-right: 0.75em;
flex-shrink: 0;
@@ -161,6 +179,15 @@ onMounted(() => {
padding-right: 12px;
}
+.headerTextMain {
+
+}
+
+.headerTextSub {
+ color: var(--fgTransparentWeak);
+ font-size: .85em;
+}
+
.headerRight {
margin-left: auto;
opacity: 0.7;
diff --git a/packages/frontend/src/components/MkGalleryPostPreview.vue b/packages/frontend/src/components/MkGalleryPostPreview.vue
index 42f8853bd4..2c5032119f 100644
--- a/packages/frontend/src/components/MkGalleryPostPreview.vue
+++ b/packages/frontend/src/components/MkGalleryPostPreview.vue
@@ -16,9 +16,7 @@
<script lang="ts" setup>
import { } from 'vue';
-import { userName } from '@/filters/user';
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
-import * as os from '@/os';
const props = defineProps<{
post: any;
diff --git a/packages/frontend/src/components/MkHeatmap.vue b/packages/frontend/src/components/MkHeatmap.vue
index f222fca9a1..e5a39a759b 100644
--- a/packages/frontend/src/components/MkHeatmap.vue
+++ b/packages/frontend/src/components/MkHeatmap.vue
@@ -8,14 +8,11 @@
</template>
<script lang="ts" setup>
-import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
+import { onMounted, nextTick, watch } from 'vue';
import { Chart } from 'chart.js';
-import tinycolor from 'tinycolor2';
-import { MatrixController, MatrixElement } from 'chartjs-chart-matrix';
import * as os from '@/os';
import { defaultStore } from '@/store';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { chartVLine } from '@/scripts/chart-vline';
import { alpha } from '@/scripts/color';
import { initChart } from '@/scripts/init-chart';
diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue
index e3f68caa9b..3e3d7354c1 100644
--- a/packages/frontend/src/components/MkInput.vue
+++ b/packages/frontend/src/components/MkInput.vue
@@ -23,7 +23,7 @@
@input="onInput"
>
<datalist v-if="datalist" :id="id">
- <option v-for="data in datalist" :value="data"/>
+ <option v-for="data in datalist" :key="data" :value="data"/>
</datalist>
<div ref="suffixEl" class="suffix"><slot name="suffix"></slot></div>
</div>
@@ -34,14 +34,14 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue';
+import { onMounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue';
import { debounce } from 'throttle-debounce';
import MkButton from '@/components/MkButton.vue';
import { useInterval } from '@/scripts/use-interval';
import { i18n } from '@/i18n';
const props = defineProps<{
- modelValue: string | number;
+ modelValue: string | number | null;
type?: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local';
required?: boolean;
readonly?: boolean;
@@ -49,7 +49,7 @@ const props = defineProps<{
pattern?: string;
placeholder?: string;
autofocus?: boolean;
- autocomplete?: boolean;
+ autocomplete?: string;
spellcheck?: boolean;
step?: any;
datalist?: string[];
diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue
index 40c99b60ec..80e5cc8270 100644
--- a/packages/frontend/src/components/MkLaunchPad.vue
+++ b/packages/frontend/src/components/MkLaunchPad.vue
@@ -23,11 +23,8 @@
import { } from 'vue';
import MkModal from '@/components/MkModal.vue';
import { navbarItemDef } from '@/navbar';
-import { instanceName } from '@/config';
import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
import { deviceKind } from '@/scripts/device-kind';
-import * as os from '@/os';
const props = withDefaults(defineProps<{
src?: HTMLElement;
diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue
index 29d15989b7..a12bb78e35 100644
--- a/packages/frontend/src/components/MkMediaList.vue
+++ b/packages/frontend/src/components/MkMediaList.vue
@@ -23,7 +23,6 @@ import XImage from '@/components/MkMediaImage.vue';
import XVideo from '@/components/MkMediaVideo.vue';
import * as os from '@/os';
import { FILE_TYPE_BROWSERSAFE } from '@/const';
-import { defaultStore } from '@/store';
const props = defineProps<{
mediaList: misskey.entities.DriveFile[];
@@ -46,8 +45,8 @@ onMounted(() => {
src: media.url,
w: media.properties.width,
h: media.properties.height,
- alt: media.comment || media.name,
- comment: media.comment || media.name,
+ alt: media.comment ?? media.name,
+ comment: media.comment ?? media.name,
};
if (media.properties.orientation != null && media.properties.orientation >= 5) {
[item.w, item.h] = [item.h, item.w];
@@ -91,8 +90,8 @@ onMounted(() => {
[itemData.w, itemData.h] = [itemData.h, itemData.w];
}
itemData.msrc = file.thumbnailUrl;
- itemData.alt = file.comment || file.name;
- itemData.comment = file.comment || file.name;
+ itemData.alt = file.comment ?? file.name;
+ itemData.comment = file.comment ?? file.name;
itemData.thumbCropped = true;
});
diff --git a/packages/frontend/src/components/MkMenu.child.vue b/packages/frontend/src/components/MkMenu.child.vue
index 0ff8794c5d..cdd9d96b96 100644
--- a/packages/frontend/src/components/MkMenu.child.vue
+++ b/packages/frontend/src/components/MkMenu.child.vue
@@ -5,11 +5,9 @@
</template>
<script lang="ts" setup>
-import { on } from 'events';
-import { nextTick, onBeforeUnmount, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue';
+import { nextTick, onMounted, shallowRef, watch } from 'vue';
import MkMenu from './MkMenu.vue';
import { MenuItem } from '@/types/menu';
-import * as os from '@/os';
const props = defineProps<{
items: MenuItem[];
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index eee77a9475..52aba58455 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -56,7 +56,7 @@
</template>
<script lang="ts" setup>
-import { defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
+import { defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, watch } from 'vue';
import { focusPrev, focusNext } from '@/scripts/focus';
import MkSwitch from '@/components/MkSwitch.vue';
import { MenuItem, InnerMenuItem, MenuPending, MenuAction } from '@/types/menu';
diff --git a/packages/frontend/src/components/MkMiniChart.vue b/packages/frontend/src/components/MkMiniChart.vue
index c64ce163f9..89050e10f0 100644
--- a/packages/frontend/src/components/MkMiniChart.vue
+++ b/packages/frontend/src/components/MkMiniChart.vue
@@ -26,7 +26,7 @@
</template>
<script lang="ts" setup>
-import { onUnmounted, watch } from 'vue';
+import { watch } from 'vue';
import { v4 as uuid } from 'uuid';
import tinycolor from 'tinycolor2';
import { useInterval } from '@/scripts/use-interval';
diff --git a/packages/frontend/src/components/MkModalPageWindow.vue b/packages/frontend/src/components/MkModalPageWindow.vue
index ed892bc007..68a3eda3d8 100644
--- a/packages/frontend/src/components/MkModalPageWindow.vue
+++ b/packages/frontend/src/components/MkModalPageWindow.vue
@@ -29,7 +29,7 @@ import { url } from '@/config';
import * as os from '@/os';
import { mainRouter, routes } from '@/router';
import { i18n } from '@/i18n';
-import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata';
+import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
import { Router } from '@/nirax';
const props = defineProps<{
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index c9c512c36e..4986f1b646 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -9,7 +9,7 @@
>
<MkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
<div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div>
- <!--<div v-if="appearNote._prId_" class="tip"><i class="fas fa-bullhorn"></i> {{ i18n.ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ i18n.ts.hideThisNote }} <i class="ti ti-x"></i></button></div>-->
+ <!--<div v-if="appearNote._prId_" class="tip"><i class="ti ti-speakerphone"></i> {{ i18n.ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ i18n.ts.hideThisNote }} <i class="ti ti-x"></i></button></div>-->
<!--<div v-if="appearNote._featuredId_" class="tip"><i class="ti ti-bolt"></i> {{ i18n.ts.featured }}</div>-->
<div v-if="isRenote" :class="$style.renote">
<MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/>
@@ -126,7 +126,7 @@
</template>
<script lang="ts" setup>
-import { computed, inject, onMounted, onUnmounted, reactive, ref, shallowRef, Ref, defineAsyncComponent } from 'vue';
+import { computed, inject, onMounted, ref, shallowRef, Ref, defineAsyncComponent } from 'vue';
import * as mfm from 'mfm-js';
import * as misskey from 'misskey-js';
import MkNoteSub from '@/components/MkNoteSub.vue';
@@ -195,6 +195,8 @@ const isMyRenote = $i && ($i.id === note.userId);
const showContent = ref(false);
const urls = appearNote.text ? extractUrlFromMfm(mfm.parse(appearNote.text)) : null;
const isLong = (appearNote.cw == null && appearNote.text != null && (
+ (appearNote.text.includes('$[x3')) ||
+ (appearNote.text.includes('$[x4')) ||
(appearNote.text.split('\n').length > 9) ||
(appearNote.text.length > 500) ||
(appearNote.files.length >= 5) ||
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 92bdadc562..82e0f3e689 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -133,7 +133,7 @@
</template>
<script lang="ts" setup>
-import { computed, inject, onMounted, onUnmounted, reactive, ref, shallowRef } from 'vue';
+import { computed, inject, onMounted, ref, shallowRef } from 'vue';
import * as mfm from 'mfm-js';
import * as misskey from 'misskey-js';
import MkNoteSub from '@/components/MkNoteSub.vue';
diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue
index 0d42e8ffbf..38bf416ea8 100644
--- a/packages/frontend/src/components/MkNotification.vue
+++ b/packages/frontend/src/components/MkNotification.vue
@@ -9,7 +9,6 @@
<i v-if="notification.type === 'follow'" class="ti ti-plus"></i>
<i v-else-if="notification.type === 'receiveFollowRequest'" class="ti ti-clock"></i>
<i v-else-if="notification.type === 'followRequestAccepted'" class="ti ti-check"></i>
- <i v-else-if="notification.type === 'groupInvited'" class="ti ti-certificate-2"></i>
<i v-else-if="notification.type === 'renote'" class="ti ti-repeat"></i>
<i v-else-if="notification.type === 'reply'" class="ti ti-arrow-back-up"></i>
<i v-else-if="notification.type === 'mention'" class="ti ti-at"></i>
@@ -74,12 +73,6 @@
<button class="_textButton" @click="acceptFollowRequest()">{{ i18n.ts.accept }}</button> | <button class="_textButton" @click="rejectFollowRequest()">{{ i18n.ts.reject }}</button>
</div>
</template>
- <template v-else-if="notification.type === 'groupInvited'">
- <span :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.groupInvited }}: <b>{{ notification.invitation.group.name }}</b></span>
- <div v-if="full && !groupInviteDone">
- <button class="_textButton" @click="acceptGroupInvitation()">{{ i18n.ts.accept }}</button> | <button class="_textButton" @click="rejectGroupInvitation()">{{ i18n.ts.reject }}</button>
- </div>
- </template>
<span v-else-if="notification.type === 'app'" :class="$style.text">
<Mfm :text="notification.body" :nowrap="false"/>
</span>
@@ -145,7 +138,6 @@ onUnmounted(() => {
});
const followRequestDone = ref(false);
-const groupInviteDone = ref(false);
const acceptFollowRequest = () => {
followRequestDone.value = true;
@@ -157,16 +149,6 @@ const rejectFollowRequest = () => {
os.api('following/requests/reject', { userId: props.notification.user.id });
};
-const acceptGroupInvitation = () => {
- groupInviteDone.value = true;
- os.apiWithDialog('users/groups/invitations/accept', { invitationId: props.notification.invitation.id });
-};
-
-const rejectGroupInvitation = () => {
- groupInviteDone.value = true;
- os.api('users/groups/invitations/reject', { invitationId: props.notification.invitation.id });
-};
-
useTooltip(reactionRef, (showing) => {
os.popup(XReactionTooltip, {
showing,
@@ -224,7 +206,7 @@ useTooltip(reactionRef, (showing) => {
}
}
-.t_follow, .t_followRequestAccepted, .t_receiveFollowRequest, .t_groupInvited {
+.t_follow, .t_followRequestAccepted, .t_receiveFollowRequest {
padding: 3px;
background: #36aed2;
pointer-events: none;
diff --git a/packages/frontend/src/components/MkNotificationSettingWindow.vue b/packages/frontend/src/components/MkNotificationSettingWindow.vue
index e303403872..2d8d30e337 100644
--- a/packages/frontend/src/components/MkNotificationSettingWindow.vue
+++ b/packages/frontend/src/components/MkNotificationSettingWindow.vue
@@ -54,7 +54,7 @@ const props = withDefaults(defineProps<{
showGlobalToggle: true,
});
-let includingTypes = $computed(() => props.includingTypes || []);
+let includingTypes = $computed(() => props.includingTypes ?? []);
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue
index ab5dff8db5..37ce7635a3 100644
--- a/packages/frontend/src/components/MkNotifications.vue
+++ b/packages/frontend/src/components/MkNotifications.vue
@@ -17,13 +17,12 @@
</template>
<script lang="ts" setup>
-import { defineComponent, markRaw, onUnmounted, onMounted, computed, shallowRef } from 'vue';
+import { onUnmounted, onMounted, computed, shallowRef } from 'vue';
import { notificationTypes } from 'misskey-js';
import MkPagination, { Paging } from '@/components/MkPagination.vue';
import XNotification from '@/components/MkNotification.vue';
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
import XNote from '@/components/MkNote.vue';
-import * as os from '@/os';
import { stream } from '@/stream';
import { $i } from '@/account';
import { i18n } from '@/i18n';
diff --git a/packages/frontend/src/components/MkNumber.vue b/packages/frontend/src/components/MkNumber.vue
index 58279be0b6..51fc4d02bb 100644
--- a/packages/frontend/src/components/MkNumber.vue
+++ b/packages/frontend/src/components/MkNumber.vue
@@ -3,7 +3,7 @@
</template>
<script lang="ts" setup>
-import { ref, reactive, watch } from 'vue';
+import { reactive, watch } from 'vue';
import gsap from 'gsap';
import number from '@/filters/number';
diff --git a/packages/frontend/src/components/MkObjectView.value.vue b/packages/frontend/src/components/MkObjectView.value.vue
index 0c7230d783..e7fc73bce3 100644
--- a/packages/frontend/src/components/MkObjectView.value.vue
+++ b/packages/frontend/src/components/MkObjectView.value.vue
@@ -29,7 +29,7 @@
</template>
<script lang="ts">
-import { computed, defineComponent, reactive, ref } from 'vue';
+import { defineComponent, reactive } from 'vue';
import number from '@/filters/number';
export default defineComponent({
diff --git a/packages/frontend/src/components/MkOmit.vue b/packages/frontend/src/components/MkOmit.vue
index 5a834c9800..a806d92b22 100644
--- a/packages/frontend/src/components/MkOmit.vue
+++ b/packages/frontend/src/components/MkOmit.vue
@@ -8,7 +8,7 @@
</template>
<script lang="ts" setup>
-import { nextTick, onMounted } from 'vue';
+import { onMounted } from 'vue';
const props = withDefaults(defineProps<{
maxHeight: number;
diff --git a/packages/frontend/src/components/MkPagePreview.vue b/packages/frontend/src/components/MkPagePreview.vue
index a78431e2a7..4fda6df5e9 100644
--- a/packages/frontend/src/components/MkPagePreview.vue
+++ b/packages/frontend/src/components/MkPagePreview.vue
@@ -18,7 +18,6 @@
import { } from 'vue';
import * as misskey from 'misskey-js';
import { userName } from '@/filters/user';
-import * as os from '@/os';
const props = defineProps<{
page: misskey.entities.Page;
diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue
index d12aafd06d..02ce58451d 100644
--- a/packages/frontend/src/components/MkPageWindow.vue
+++ b/packages/frontend/src/components/MkPageWindow.vue
@@ -18,23 +18,22 @@
</template>
<div :class="$style.root" :style="{ background: pageMetadata?.value?.bg }" style="container-type: inline-size;">
- <RouterView :router="router"/>
+ <RouterView :key="reloadCount" :router="router"/>
</div>
</MkWindow>
</template>
<script lang="ts" setup>
-import { ComputedRef, inject, onMounted, onUnmounted, provide } from 'vue';
+import { ComputedRef, onMounted, onUnmounted, provide } from 'vue';
import RouterView from '@/components/global/RouterView.vue';
import MkWindow from '@/components/MkWindow.vue';
import { popout as _popout } from '@/scripts/popout';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import { url } from '@/config';
-import * as os from '@/os';
import { mainRouter, routes } from '@/router';
import { Router } from '@/nirax';
import { i18n } from '@/i18n';
-import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata';
+import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
import { openingWindowsCount } from '@/os';
import { claimAchievement } from '@/scripts/achievements';
@@ -68,6 +67,10 @@ const buttonsLeft = $computed(() => {
});
const buttonsRight = $computed(() => {
const buttons = [{
+ icon: 'ti ti-reload',
+ title: i18n.ts.reload,
+ onClick: reload,
+ }, {
icon: 'ti ti-player-eject',
title: i18n.ts.showInPage,
onClick: expand,
@@ -75,6 +78,7 @@ const buttonsRight = $computed(() => {
return buttons;
});
+let reloadCount = $ref(0);
router.addListener('push', ctx => {
history.push({ path: ctx.path, key: ctx.key });
@@ -116,6 +120,10 @@ function back() {
router.replace(history[history.length - 1].path, history[history.length - 1].key);
}
+function reload() {
+ reloadCount++;
+}
+
function close() {
windowEl.close();
}
diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue
index 224a42cdc2..84ba94361e 100644
--- a/packages/frontend/src/components/MkPagination.vue
+++ b/packages/frontend/src/components/MkPagination.vue
@@ -104,7 +104,7 @@ const {
enableInfiniteScroll,
} = defaultStore.reactiveState;
-const contentEl = $computed(() => props.pagination.pageEl || rootEl);
+const contentEl = $computed(() => props.pagination.pageEl ?? rootEl);
const scrollableElement = $computed(() => getScrollContainer(contentEl));
// 先頭が表示されているかどうかを検出
diff --git a/packages/frontend/src/components/MkPoll.vue b/packages/frontend/src/components/MkPoll.vue
index 33e625a058..fcbd8ad351 100644
--- a/packages/frontend/src/components/MkPoll.vue
+++ b/packages/frontend/src/components/MkPoll.vue
@@ -22,7 +22,7 @@
</template>
<script lang="ts" setup>
-import { computed, onUnmounted, ref, toRef } from 'vue';
+import { computed, ref } from 'vue';
import * as misskey from 'misskey-js';
import { sum } from '@/scripts/array';
import { pleaseLogin } from '@/scripts/please-login';
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index f15906c1c1..54512aa4d8 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -73,10 +73,8 @@ import { inject, watch, nextTick, onMounted, defineAsyncComponent } from 'vue';
import * as mfm from 'mfm-js';
import * as misskey from 'misskey-js';
import insertTextAtCursor from 'insert-text-at-cursor';
-import { length } from 'stringz';
import { toASCII } from 'punycode/';
import * as Acct from 'misskey-js/built/acct';
-import { throttle } from 'throttle-debounce';
import MkNoteSimple from '@/components/MkNoteSimple.vue';
import XNotePreview from '@/components/MkNotePreview.vue';
import XPostFormAttaches from '@/components/MkPostFormAttaches.vue';
@@ -87,7 +85,6 @@ import { extractMentions } from '@/scripts/extract-mentions';
import { formatTimeString } from '@/scripts/format-time-string';
import { Autocomplete } from '@/scripts/autocomplete';
import * as os from '@/os';
-import { stream } from '@/stream';
import { selectFiles } from '@/scripts/select-file';
import { defaultStore, notePostInterruptors, postFormActions } from '@/store';
import MkInfo from '@/components/MkInfo.vue';
@@ -157,15 +154,9 @@ let autocomplete = $ref(null);
let draghover = $ref(false);
let quoteId = $ref(null);
let hasNotSpecifiedMentions = $ref(false);
-let recentHashtags = $ref(JSON.parse(miLocalStorage.getItem('hashtags') || '[]'));
+let recentHashtags = $ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'));
let imeText = $ref('');
-const typing = throttle(3000, () => {
- if (props.channel) {
- stream.send('typingOnChannel', { channel: props.channel.id });
- }
-});
-
const draftKey = $computed((): string => {
let key = props.channel ? `channel:${props.channel.id}` : '';
@@ -209,7 +200,7 @@ const submitText = $computed((): string => {
});
const textLength = $computed((): number => {
- return length((text + imeText).trim());
+ return (text + imeText).trim().length;
});
const maxTextLength = $computed((): number => {
@@ -228,7 +219,7 @@ const hashtags = $computed(defaultStore.makeGetterSetter('postFormHashtags'));
watch($$(text), () => {
checkMissingMention();
-});
+}, { immediate: true });
watch($$(visibleUsers), () => {
checkMissingMention();
@@ -447,12 +438,10 @@ function clear() {
function onKeydown(ev: KeyboardEvent) {
if ((ev.which === 10 || ev.which === 13) && (ev.ctrlKey || ev.metaKey) && canPost) post();
if (ev.which === 27) emit('esc');
- typing();
}
function onCompositionUpdate(ev: CompositionEvent) {
imeText = ev.data;
- typing();
}
function onCompositionEnd(ev: CompositionEvent) {
@@ -544,7 +533,7 @@ function onDrop(ev): void {
}
function saveDraft() {
- const draftData = JSON.parse(miLocalStorage.getItem('drafts') || '{}');
+ const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}');
draftData[draftKey] = {
updatedAt: new Date(),
@@ -653,7 +642,7 @@ async function post(ev?: MouseEvent) {
emit('posted');
if (postData.text && postData.text !== '') {
const hashtags_ = mfm.parse(postData.text).filter(x => x.type === 'hashtag').map(x => x.props.hashtag);
- const history = JSON.parse(miLocalStorage.getItem('hashtags') || '[]') as string[];
+ const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[];
miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history))));
}
posting = false;
@@ -757,7 +746,7 @@ onMounted(() => {
nextTick(() => {
// 書きかけの投稿を復元
if (!props.instant && !props.mention && !props.specified) {
- const draft = JSON.parse(miLocalStorage.getItem('drafts') || '{}')[draftKey];
+ const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey];
if (draft) {
text = draft.data.text;
useCw = draft.data.useCw;
diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue
index 766cc9a06c..5fb820f03f 100644
--- a/packages/frontend/src/components/MkPostFormAttaches.vue
+++ b/packages/frontend/src/components/MkPostFormAttaches.vue
@@ -15,10 +15,9 @@
</template>
<script lang="ts" setup>
-import { defineAsyncComponent, watch } from 'vue';
+import { defineAsyncComponent } from 'vue';
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
import * as os from '@/os';
-import { deepClone } from '@/scripts/clone';
import { i18n } from '@/i18n';
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
diff --git a/packages/frontend/src/components/MkReactedUsersDialog.vue b/packages/frontend/src/components/MkReactedUsersDialog.vue
index 2a8dffc014..1506e24ce8 100644
--- a/packages/frontend/src/components/MkReactedUsersDialog.vue
+++ b/packages/frontend/src/components/MkReactedUsersDialog.vue
@@ -10,15 +10,21 @@
<MkSpacer :margin-min="20" :margin-max="28">
<div v-if="note" class="_gaps">
- <div :class="$style.tabs">
- <button v-for="reaction in reactions" :key="reaction" :class="[$style.tab, { [$style.tabActive]: tab === reaction }]" class="_button" @click="tab = reaction">
- <MkReactionIcon :reaction="reaction"/>
- <span style="margin-left: 4px;">{{ note.reactions[reaction] }}</span>
- </button>
+ <div v-if="reactions.length === 0" class="_fullinfo">
+ <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <div>{{ i18n.ts.nothing }}</div>
</div>
- <MkA v-for="user in users" :key="user.id" :to="userPage(user)">
- <MkUserCardMini :user="user" :with-chart="false"/>
- </MkA>
+ <template v-else>
+ <div :class="$style.tabs">
+ <button v-for="reaction in reactions" :key="reaction" :class="[$style.tab, { [$style.tabActive]: tab === reaction }]" class="_button" @click="tab = reaction">
+ <MkReactionIcon :reaction="reaction"/>
+ <span style="margin-left: 4px;">{{ note.reactions[reaction] }}</span>
+ </button>
+ </div>
+ <MkA v-for="user in users" :key="user.id" :to="userPage(user)">
+ <MkUserCardMini :user="user" :with-chart="false"/>
+ </MkA>
+ </template>
</div>
<div v-else>
<MkLoading/>
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index 4abd2562df..fd0f42e9fc 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -12,7 +12,7 @@
</template>
<script lang="ts" setup>
-import { computed, onMounted, ref, shallowRef, watch } from 'vue';
+import { computed, onMounted, shallowRef, watch } from 'vue';
import * as misskey from 'misskey-js';
import XDetails from '@/components/MkReactionsViewer.details.vue';
import MkReactionIcon from '@/components/MkReactionIcon.vue';
diff --git a/packages/frontend/src/components/MkReactionsViewer.vue b/packages/frontend/src/components/MkReactionsViewer.vue
index cdd6f528e7..76faffe926 100644
--- a/packages/frontend/src/components/MkReactionsViewer.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.vue
@@ -51,7 +51,7 @@ watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumbe
...Object.entries(newSource)
.sort(([, a], [, b]) => b - a)
.filter(([y], i) => i < maxNumber && !newReactionsNames.includes(y)),
- ]
+ ];
newReactions = newReactions.slice(0, props.maxNumber);
diff --git a/packages/frontend/src/components/MkRetentionHeatmap.vue b/packages/frontend/src/components/MkRetentionHeatmap.vue
index 52c8b6d026..8326ec7ef3 100644
--- a/packages/frontend/src/components/MkRetentionHeatmap.vue
+++ b/packages/frontend/src/components/MkRetentionHeatmap.vue
@@ -8,14 +8,11 @@
</template>
<script lang="ts" setup>
-import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue';
+import { onMounted, nextTick } from 'vue';
import { Chart } from 'chart.js';
-import tinycolor from 'tinycolor2';
-import { MatrixController, MatrixElement } from 'chartjs-chart-matrix';
import * as os from '@/os';
import { defaultStore } from '@/store';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { chartVLine } from '@/scripts/chart-vline';
import { alpha } from '@/scripts/color';
import { initChart } from '@/scripts/init-chart';
diff --git a/packages/frontend/src/components/MkRolePreview.vue b/packages/frontend/src/components/MkRolePreview.vue
index 2edc224b1c..2f5866f340 100644
--- a/packages/frontend/src/components/MkRolePreview.vue
+++ b/packages/frontend/src/components/MkRolePreview.vue
@@ -1,10 +1,15 @@
<template>
-<MkA v-adaptive-bg :to="`/admin/roles/${role.id}`" class="_panel" :class="$style.root" tabindex="-1" :style="{ '--color': role.color }">
+<MkA v-adaptive-bg :to="forModeration ? `/admin/roles/${role.id}` : `/roles/${role.id}`" class="_panel" :class="$style.root" tabindex="-1" :style="{ '--color': role.color }">
<div :class="$style.title">
<span :class="$style.icon">
- <i v-if="role.isAdministrator" class="ti ti-crown" style="color: var(--accent);"></i>
- <i v-else-if="role.isModerator" class="ti ti-shield" style="color: var(--accent);"></i>
- <i v-else class="ti ti-user" style="opacity: 0.7;"></i>
+ <template v-if="role.iconUrl">
+ <img :class="$style.badge" :src="role.iconUrl"/>
+ </template>
+ <template v-else>
+ <i v-if="role.isAdministrator" class="ti ti-crown" style="color: var(--accent);"></i>
+ <i v-else-if="role.isModerator" class="ti ti-shield" style="color: var(--accent);"></i>
+ <i v-else class="ti ti-user" style="opacity: 0.7;"></i>
+ </template>
</span>
<span :class="$style.name">{{ role.name }}</span>
<span v-if="role.target === 'manual'" :class="$style.users">{{ role.usersCount }} users</span>
@@ -16,12 +21,11 @@
<script lang="ts" setup>
import { } from 'vue';
-import * as misskey from 'misskey-js';
-import * as os from '@/os';
import { i18n } from '@/i18n';
const props = defineProps<{
role: any;
+ forModeration: boolean;
}>();
</script>
@@ -40,6 +44,11 @@ const props = defineProps<{
margin-right: 8px;
}
+.badge {
+ height: 1.3em;
+ vertical-align: -20%;
+}
+
.name {
font-weight: bold;
}
diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue
index 12099153eb..2de890186a 100644
--- a/packages/frontend/src/components/MkSelect.vue
+++ b/packages/frontend/src/components/MkSelect.vue
@@ -27,14 +27,14 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs, VNode, useSlots } from 'vue';
+import { onMounted, nextTick, ref, watch, computed, toRefs, VNode, useSlots } from 'vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os';
import { useInterval } from '@/scripts/use-interval';
import { i18n } from '@/i18n';
const props = defineProps<{
- modelValue: string;
+ modelValue: string | null;
required?: boolean;
readonly?: boolean;
disabled?: boolean;
@@ -48,7 +48,7 @@ const props = defineProps<{
const emit = defineEmits<{
(ev: 'change', _ev: KeyboardEvent): void;
- (ev: 'update:modelValue', value: string): void;
+ (ev: 'update:modelValue', value: string | null): void;
}>();
const slots = useSlots();
diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue
index 8cce1d43f6..ffc5e82b56 100644
--- a/packages/frontend/src/components/MkSignin.vue
+++ b/packages/frontend/src/components/MkSignin.vue
@@ -10,7 +10,7 @@
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
</MkInput>
- <MkInput v-if="!user || user && !user.usePasswordLessLogin" v-model="password" :placeholder="i18n.ts.password" type="password" :with-password-toggle="true" required data-cy-signin-password>
+ <MkInput v-if="!user || user && !user.usePasswordLessLogin" v-model="password" :placeholder="i18n.ts.password" type="password" autocomplete="current-password" :with-password-toggle="true" required data-cy-signin-password>
<template #prefix><i class="ti ti-lock"></i></template>
<template #caption><button class="_textButton" type="button" @click="resetPassword">{{ i18n.ts.forgotPassword }}</button></template>
</MkInput>
@@ -28,11 +28,11 @@
</div>
<div class="twofa-group totp-group">
<p style="margin-bottom:0;">{{ i18n.ts.twoStepAuthentication }}</p>
- <MkInput v-if="user && user.usePasswordLessLogin" v-model="password" type="password" :with-password-toggle="true" required>
+ <MkInput v-if="user && user.usePasswordLessLogin" v-model="password" type="password" autocomplete="current-password" :with-password-toggle="true" required>
<template #label>{{ i18n.ts.password }}</template>
<template #prefix><i class="ti ti-lock"></i></template>
</MkInput>
- <MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" :spellcheck="false" required>
+ <MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="one-time-code" :spellcheck="false" required>
<template #label>{{ i18n.ts.token }}</template>
<template #prefix><i class="ti ti-123"></i></template>
</MkInput>
@@ -50,7 +50,7 @@ import { showSuspendedDialog } from '../scripts/show-suspended-dialog';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkInfo from '@/components/MkInfo.vue';
-import { apiUrl, host as configHost } from '@/config';
+import { host as configHost } from '@/config';
import { byteify, hexify } from '@/scripts/2fa';
import * as os from '@/os';
import { login } from '@/account';
diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue
index 5d33ad0ad3..2a8e43c570 100644
--- a/packages/frontend/src/components/MkSuperMenu.vue
+++ b/packages/frontend/src/components/MkSuperMenu.vue
@@ -24,7 +24,7 @@
</template>
<script lang="ts">
-import { defineComponent, ref, unref } from 'vue';
+import { defineComponent } from 'vue';
export default defineComponent({
props: {
diff --git a/packages/frontend/src/components/MkSwitch.vue b/packages/frontend/src/components/MkSwitch.vue
index 133f437ca1..8bb8637dda 100644
--- a/packages/frontend/src/components/MkSwitch.vue
+++ b/packages/frontend/src/components/MkSwitch.vue
@@ -22,7 +22,6 @@
<script lang="ts" setup>
import { toRefs, Ref } from 'vue';
-import * as os from '@/os';
import { i18n } from '@/i18n';
const props = defineProps<{
diff --git a/packages/frontend/src/components/MkTagCloud.vue b/packages/frontend/src/components/MkTagCloud.vue
index 9f7e76f18e..4e8d5bab7f 100644
--- a/packages/frontend/src/components/MkTagCloud.vue
+++ b/packages/frontend/src/components/MkTagCloud.vue
@@ -10,7 +10,7 @@
</template>
<script lang="ts" setup>
-import { onMounted, ref, watch, PropType, onBeforeUnmount } from 'vue';
+import { onMounted, watch, onBeforeUnmount } from 'vue';
import tinycolor from 'tinycolor2';
const loaded = !!window.TagCanvas;
diff --git a/packages/frontend/src/components/MkTextarea.vue b/packages/frontend/src/components/MkTextarea.vue
index 0147ac7f83..82b631edda 100644
--- a/packages/frontend/src/components/MkTextarea.vue
+++ b/packages/frontend/src/components/MkTextarea.vue
@@ -27,7 +27,7 @@
</template>
<script lang="ts">
-import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
+import { defineComponent, onMounted, nextTick, ref, watch, computed, toRefs } from 'vue';
import { debounce } from 'throttle-debounce';
import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n';
diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue
index 831a194ce3..87f7c61a92 100644
--- a/packages/frontend/src/components/MkTimeline.vue
+++ b/packages/frontend/src/components/MkTimeline.vue
@@ -1,11 +1,10 @@
<template>
-<XNotes ref="tlComponent" :no-gap="!$store.state.showGapBetweenNotesInTimeline" :pagination="pagination" @queue="emit('queue', $event)"/>
+<MkNotes ref="tlComponent" :no-gap="!$store.state.showGapBetweenNotesInTimeline" :pagination="pagination" @queue="emit('queue', $event)"/>
</template>
<script lang="ts" setup>
-import { ref, computed, provide, onUnmounted } from 'vue';
-import XNotes from '@/components/MkNotes.vue';
-import * as os from '@/os';
+import { computed, provide, onUnmounted } from 'vue';
+import MkNotes from '@/components/MkNotes.vue';
import { stream } from '@/stream';
import * as sound from '@/scripts/sound';
import { $i } from '@/account';
@@ -25,7 +24,7 @@ const emit = defineEmits<{
provide('inChannel', computed(() => props.src === 'channel'));
-const tlComponent: InstanceType<typeof XNotes> = $ref();
+const tlComponent: InstanceType<typeof MkNotes> = $ref();
const prepend = note => {
tlComponent.pagingComponent?.prepend(note);
diff --git a/packages/frontend/src/components/MkToast.vue b/packages/frontend/src/components/MkToast.vue
index 95ab23f76d..1aa48f88e6 100644
--- a/packages/frontend/src/components/MkToast.vue
+++ b/packages/frontend/src/components/MkToast.vue
@@ -17,7 +17,7 @@
</template>
<script lang="ts" setup>
-import { onMounted, ref } from 'vue';
+import { onMounted } from 'vue';
import * as os from '@/os';
defineProps<{
diff --git a/packages/frontend/src/components/MkTooltip.vue b/packages/frontend/src/components/MkTooltip.vue
index 17c8ea0128..0b0556de39 100644
--- a/packages/frontend/src/components/MkTooltip.vue
+++ b/packages/frontend/src/components/MkTooltip.vue
@@ -16,7 +16,7 @@
</template>
<script lang="ts" setup>
-import { nextTick, onMounted, onUnmounted, ref, shallowRef } from 'vue';
+import { nextTick, onMounted, onUnmounted, shallowRef } from 'vue';
import * as os from '@/os';
import { calcPopupPosition } from '@/scripts/popup-position';
diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue
index 62e58e1553..b97b7cf07b 100644
--- a/packages/frontend/src/components/MkUrlPreview.vue
+++ b/packages/frontend/src/components/MkUrlPreview.vue
@@ -45,7 +45,7 @@
</template>
<script lang="ts" setup>
-import { defineAsyncComponent, onMounted, onUnmounted } from 'vue';
+import { defineAsyncComponent, onUnmounted } from 'vue';
import { url as local } from '@/config';
import { i18n } from '@/i18n';
import * as os from '@/os';
diff --git a/packages/frontend/src/components/MkUserList.vue b/packages/frontend/src/components/MkUserList.vue
index b7bc200a55..51eb426e97 100644
--- a/packages/frontend/src/components/MkUserList.vue
+++ b/packages/frontend/src/components/MkUserList.vue
@@ -7,25 +7,26 @@
</div>
</template>
- <template #default="{ items: users }">
+ <template #default="{ items }">
<div class="efvhhmdq">
- <MkUserInfo v-for="user in users" :key="user.id" class="user" :user="user"/>
+ <MkUserInfo v-for="item in items" :key="item.id" class="user" :user="extractor(item)"/>
</div>
</template>
</MkPagination>
</template>
<script lang="ts" setup>
-import { shallowRef } from 'vue';
import MkUserInfo from '@/components/MkUserInfo.vue';
import MkPagination, { Paging } from '@/components/MkPagination.vue';
-import { userPage } from '@/filters/user';
import { i18n } from '@/i18n';
-const props = defineProps<{
+const props = withDefaults(defineProps<{
pagination: Paging;
noGap?: boolean;
-}>();
+ extractor?: (item: any) => any;
+}>(), {
+ extractor: (item) => item,
+});
</script>
<style lang="scss" scoped>
diff --git a/packages/frontend/src/components/MkUserPreview.vue b/packages/frontend/src/components/MkUserPreview.vue
index eacc66de4f..1086a2c651 100644
--- a/packages/frontend/src/components/MkUserPreview.vue
+++ b/packages/frontend/src/components/MkUserPreview.vue
@@ -89,7 +89,7 @@ onMounted(() => {
<style lang="scss" scoped>
.popup-enter-active, .popup-leave-active {
- transition: opacity 0.3s, transform 0.3s !important;
+ transition: opacity 0.15s, transform 0.15s !important;
}
.popup-enter-from, .popup-leave-to {
opacity: 0;
@@ -183,8 +183,10 @@ onMounted(() => {
> .menu {
position: absolute;
top: 8px;
- right: 42px;
- padding: 8px;
+ right: 44px;
+ padding: 6px;
+ background: var(--panel);
+ border-radius: 999px;
}
> .koudoku-button {
diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue
index c17b97f283..dc78bbf42d 100644
--- a/packages/frontend/src/components/MkUserSelectDialog.vue
+++ b/packages/frontend/src/components/MkUserSelectDialog.vue
@@ -16,7 +16,7 @@
<template #label>{{ i18n.ts.username }}</template>
<template #prefix>@</template>
</MkInput>
- <MkInput v-model="host" @update:model-value="search">
+ <MkInput v-model="host" :datalist="[hostname]" @update:model-value="search">
<template #label>{{ i18n.ts.host }}</template>
<template #prefix>@</template>
</MkInput>
@@ -52,7 +52,7 @@
</template>
<script lang="ts" setup>
-import { nextTick, onMounted } from 'vue';
+import { onMounted } from 'vue';
import * as misskey from 'misskey-js';
import MkInput from '@/components/MkInput.vue';
import FormSplit from '@/components/form/split.vue';
@@ -61,6 +61,7 @@ import * as os from '@/os';
import { defaultStore } from '@/store';
import { i18n } from '@/i18n';
import { $i } from '@/account';
+import { hostname } from '@/config';
const emit = defineEmits<{
(ev: 'ok', selected: misskey.entities.UserDetailed): void;
@@ -115,7 +116,7 @@ onMounted(() => {
os.api('users/show', {
userIds: defaultStore.state.recentlyUsedUsers,
}).then(users => {
- if (props.includeSelf) {
+ if (props.includeSelf && users.find(x => $i ? x.id === $i.id : true) == null) {
recentUsers = [$i, ...users];
} else {
recentUsers = users;
diff --git a/packages/frontend/src/components/MkWidgets.vue b/packages/frontend/src/components/MkWidgets.vue
index eff64c12e5..19c735c5f8 100644
--- a/packages/frontend/src/components/MkWidgets.vue
+++ b/packages/frontend/src/components/MkWidgets.vue
@@ -43,14 +43,13 @@ export type DefaultStoredWidget = {
} & Widget;
</script>
<script lang="ts" setup>
-import { defineAsyncComponent, reactive, ref, computed } from 'vue';
+import { defineAsyncComponent, ref } from 'vue';
import { v4 as uuid } from 'uuid';
import MkSelect from '@/components/MkSelect.vue';
import MkButton from '@/components/MkButton.vue';
import { widgets as widgetDefs } from '@/widgets';
import * as os from '@/os';
import { i18n } from '@/i18n';
-import { deepClone } from '@/scripts/clone';
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue
index 5a0ba0d8d3..40d134dffb 100644
--- a/packages/frontend/src/components/global/MkA.vue
+++ b/packages/frontend/src/components/global/MkA.vue
@@ -5,7 +5,6 @@
</template>
<script lang="ts" setup>
-import { inject } from 'vue';
import * as os from '@/os';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import { url } from '@/config';
diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue
index 9ad06545f2..d392ec6d6f 100644
--- a/packages/frontend/src/components/global/MkAvatar.vue
+++ b/packages/frontend/src/components/global/MkAvatar.vue
@@ -18,7 +18,7 @@
</template>
<script lang="ts" setup>
-import { onMounted, watch } from 'vue';
+import { watch } from 'vue';
import * as misskey from 'misskey-js';
import { getStaticImageUrl } from '@/scripts/media-proxy';
import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash';
diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue
index e6dedd0354..82aad44c1f 100644
--- a/packages/frontend/src/components/global/MkCustomEmoji.vue
+++ b/packages/frontend/src/components/global/MkCustomEmoji.vue
@@ -24,7 +24,7 @@ const rawUrl = computed(() => {
return props.url;
}
if (props.host == null && !customEmojiName.value.includes('@')) {
- return customEmojis.value.find(x => x.name === customEmojiName.value)?.url || null;
+ return customEmojis.value.find(x => x.name === customEmojiName.value)?.url ?? null;
}
return props.host ? `/emoji/${customEmojiName.value}@${props.host}.webp` : `/emoji/${customEmojiName.value}.webp`;
});
@@ -32,7 +32,7 @@ const rawUrl = computed(() => {
const url = computed(() =>
defaultStore.reactiveState.disableShowingAnimatedImages.value && rawUrl.value
? getStaticImageUrl(rawUrl.value)
- : rawUrl.value
+ : rawUrl.value,
);
const alt = computed(() => `:${customEmojiName.value}:`);
diff --git a/packages/frontend/src/components/global/MkError.vue b/packages/frontend/src/components/global/MkError.vue
index d412934a31..7390a9dfb9 100644
--- a/packages/frontend/src/components/global/MkError.vue
+++ b/packages/frontend/src/components/global/MkError.vue
@@ -3,7 +3,7 @@
<div :class="$style.root">
<img :class="$style.img" src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/>
<p :class="$style.text"><i class="ti ti-alert-triangle"></i> {{ i18n.ts.somethingHappened }}</p>
- <MkButton :class="$style.button" @click="() => $emit('retry')">{{ i18n.ts.retry }}</MkButton>
+ <MkButton :class="$style.button" @click="() => emit('retry')">{{ i18n.ts.retry }}</MkButton>
</div>
</Transition>
</template>
@@ -11,6 +11,10 @@
<script lang="ts" setup>
import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n';
+
+const emit = defineEmits<{
+ (ev: 'retry'): void;
+}>();
</script>
<style lang="scss" module>
diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
index dae68c7e9c..3beedf34b2 100644
--- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
@@ -1,23 +1,33 @@
<template>
- <div ref="el" :class="$style.tabs" @wheel="onTabWheel">
- <div :class="$style.tabsInner">
- <button v-for="t in tabs" :ref="(el) => tabRefs[t.key] = (el as HTMLElement)" v-tooltip.noDelay="t.title"
- class="_button" :class="[$style.tab, { [$style.active]: t.key != null && t.key === props.tab, [$style.animate]: defaultStore.reactiveState.animation.value }]"
- @mousedown="(ev) => onTabMousedown(t, ev)" @click="(ev) => onTabClick(t, ev)">
- <div :class="$style.tabInner">
- <i v-if="t.icon" :class="[$style.tabIcon, t.icon]"></i>
- <div v-if="!t.iconOnly || (!defaultStore.reactiveState.animation.value && t.key === tab)"
- :class="$style.tabTitle">{{ t.title }}</div>
- <Transition v-else @enter="enter" @after-enter="afterEnter" @leave="leave" @after-leave="afterLeave"
- mode="in-out">
- <div v-show="t.key === tab" :class="[$style.tabTitle, $style.animate]">{{ t.title }}</div>
- </Transition>
+<div ref="el" :class="$style.tabs" @wheel="onTabWheel">
+ <div :class="$style.tabsInner">
+ <button
+ v-for="t in tabs" :ref="(el) => tabRefs[t.key] = (el as HTMLElement)" v-tooltip.noDelay="t.title"
+ class="_button" :class="[$style.tab, { [$style.active]: t.key != null && t.key === props.tab, [$style.animate]: defaultStore.reactiveState.animation.value }]"
+ @mousedown="(ev) => onTabMousedown(t, ev)" @click="(ev) => onTabClick(t, ev)"
+ >
+ <div :class="$style.tabInner">
+ <i v-if="t.icon" :class="[$style.tabIcon, t.icon]"></i>
+ <div
+ v-if="!t.iconOnly || (!defaultStore.reactiveState.animation.value && t.key === tab)"
+ :class="$style.tabTitle"
+ >
+ {{ t.title }}
</div>
- </button>
- </div>
- <div ref="tabHighlightEl"
- :class="[$style.tabHighlight, { [$style.animate]: defaultStore.reactiveState.animation.value }]"></div>
+ <Transition
+ v-else mode="in-out" @enter="enter" @after-enter="afterEnter" @leave="leave"
+ @after-leave="afterLeave"
+ >
+ <div v-show="t.key === tab" :class="[$style.tabTitle, $style.animate]">{{ t.title }}</div>
+ </Transition>
+ </div>
+ </button>
</div>
+ <div
+ ref="tabHighlightEl"
+ :class="[$style.tabHighlight, { [$style.animate]: defaultStore.reactiveState.animation.value }]"
+ ></div>
+</div>
</template>
<script lang="ts">
@@ -34,7 +44,7 @@ export type Tab = {
</script>
<script lang="ts" setup>
-import { onMounted, onUnmounted, watch, nextTick, Transition, shallowRef } from 'vue';
+import { onMounted, onUnmounted, watch, nextTick, shallowRef } from 'vue';
import { defaultStore } from '@/store';
const props = withDefaults(defineProps<{
@@ -206,8 +216,8 @@ onUnmounted(() => {
align-items: center;
}
-.tabIcon+.tabTitle {
- padding-left: 8px;
+.tabIcon + .tabTitle {
+ padding-left: 4px;
}
.tabTitle {
diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index 803efb1690..a4e25bbe1a 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -2,9 +2,9 @@
<div v-if="show" ref="el" :class="[$style.root]" :style="{ background: bg }">
<div :class="[$style.upper, { [$style.slim]: narrow, [$style.thin]: thin_ }]">
<div v-if="!thin_ && narrow && props.displayMyAvatar && $i" class="_button" :class="$style.buttonsLeft" @click="openAccountMenu">
- <MkAvatar :class="$style.avatar" :user="$i" />
+ <MkAvatar :class="$style.avatar" :user="$i"/>
</div>
- <div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttonsLeft" />
+ <div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttonsLeft"/>
<template v-if="metadata">
<div v-if="!hideTitle" :class="$style.titleContainer" @click="top">
@@ -19,7 +19,7 @@
</div>
</div>
</div>
- <XTabs v-if="!narrow || hideTitle" :class="$style.tabs" :tab="tab" @update:tab="key => emit('update:tab', key)" :tabs="tabs" :root-el="el" @tab-click="onTabClick"/>
+ <XTabs v-if="!narrow || hideTitle" :class="$style.tabs" :tab="tab" :tabs="tabs" :root-el="el" @update:tab="key => emit('update:tab', key)" @tab-click="onTabClick"/>
</template>
<div v-if="(!thin_ && narrow && !hideTitle) || (actions && actions.length > 0)" :class="$style.buttonsRight">
<template v-for="action in actions">
@@ -28,7 +28,7 @@
</div>
</div>
<div v-if="(narrow && !hideTitle) && hasTabs" :class="[$style.lower, { [$style.slim]: narrow, [$style.thin]: thin_ }]">
- <XTabs :class="$style.tabs" :tab="tab" @update:tab="key => emit('update:tab', key)" :tabs="tabs" :root-el="el" @tab-click="onTabClick"/>
+ <XTabs :class="$style.tabs" :tab="tab" :tabs="tabs" :root-el="el" @update:tab="key => emit('update:tab', key)" @tab-click="onTabClick"/>
</div>
</div>
</template>
@@ -36,11 +36,11 @@
<script lang="ts" setup>
import { onMounted, onUnmounted, ref, inject } from 'vue';
import tinycolor from 'tinycolor2';
+import XTabs, { Tab } from './MkPageHeader.tabs.vue';
import { scrollToTop } from '@/scripts/scroll';
import { globalEvents } from '@/events';
import { injectPageMetadata } from '@/scripts/page-metadata';
import { $i, openAccountMenu as openAccountMenu_ } from '@/account';
-import XTabs, { Tab } from './MkPageHeader.tabs.vue'
const props = withDefaults(defineProps<{
tabs?: Tab[];
@@ -96,7 +96,7 @@ function onTabClick(): void {
}
const calcBg = () => {
- const rawBg = metadata?.bg || 'var(--bg)';
+ const rawBg = metadata?.bg ?? 'var(--bg)';
const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
tinyBg.setAlpha(0.85);
bg.value = tinyBg.toRgbString();
diff --git a/packages/frontend/src/components/global/MkSpacer.vue b/packages/frontend/src/components/global/MkSpacer.vue
index 78e9a1a9c2..ba7c0400c7 100644
--- a/packages/frontend/src/components/global/MkSpacer.vue
+++ b/packages/frontend/src/components/global/MkSpacer.vue
@@ -7,7 +7,7 @@
</template>
<script lang="ts" setup>
-import { inject, onMounted, onUnmounted, ref } from 'vue';
+import { inject } from 'vue';
import { deviceKind } from '@/scripts/device-kind';
const props = withDefaults(defineProps<{
diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue
index e5270ffefa..5763c84e81 100644
--- a/packages/frontend/src/components/global/RouterView.vue
+++ b/packages/frontend/src/components/global/RouterView.vue
@@ -11,7 +11,7 @@
</template>
<script lang="ts" setup>
-import { inject, nextTick, onBeforeUnmount, onMounted, onUnmounted, provide, watch } from 'vue';
+import { inject, onBeforeUnmount, provide } from 'vue';
import { Resolved, Router } from '@/nirax';
import { defaultStore } from '@/store';
diff --git a/packages/frontend/src/components/mfm.ts b/packages/frontend/src/components/mfm.ts
index 816a42a5fb..e84eabcbcc 100644
--- a/packages/frontend/src/components/mfm.ts
+++ b/packages/frontend/src/components/mfm.ts
@@ -5,13 +5,11 @@ import MkLink from '@/components/MkLink.vue';
import MkMention from '@/components/MkMention.vue';
import MkEmoji from '@/components/global/MkEmoji.vue';
import MkCustomEmoji from '@/components/global/MkCustomEmoji.vue';
-import { concat } from '@/scripts/array';
import MkCode from '@/components/MkCode.vue';
import MkGoogle from '@/components/MkGoogle.vue';
import MkSparkle from '@/components/MkSparkle.vue';
import MkA from '@/components/global/MkA.vue';
import { host } from '@/config';
-import { MFM_TAGS } from '@/scripts/mfm-tags';
import { defaultStore } from '@/store';
const QUOTE_STYLE = `
@@ -280,7 +278,7 @@ export default defineComponent({
case 'hashtag': {
return [h(MkA, {
key: Math.random(),
- to: this.isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/explore/tags/${encodeURIComponent(token.props.hashtag)}`,
+ to: this.isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/user-tags/${encodeURIComponent(token.props.hashtag)}`,
style: 'color:var(--hashtag);',
}, `#${token.props.hashtag}`)];
}
diff --git a/packages/frontend/src/components/page/page.canvas.vue b/packages/frontend/src/components/page/page.canvas.vue
index 80f6c8339c..82ff36ec36 100644
--- a/packages/frontend/src/components/page/page.canvas.vue
+++ b/packages/frontend/src/components/page/page.canvas.vue
@@ -6,7 +6,6 @@
<script lang="ts">
import { defineComponent, onMounted, PropType, Ref, ref } from 'vue';
-import * as os from '@/os';
import { CanvasBlock } from '@/scripts/hpml/block';
import { Hpml } from '@/scripts/hpml/evaluator';
diff --git a/packages/frontend/src/components/page/page.counter.vue b/packages/frontend/src/components/page/page.counter.vue
index a9e1f41a54..63fde6a120 100644
--- a/packages/frontend/src/components/page/page.counter.vue
+++ b/packages/frontend/src/components/page/page.counter.vue
@@ -7,7 +7,6 @@
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
import MkButton from '../MkButton.vue';
-import * as os from '@/os';
import { CounterVarBlock } from '@/scripts/hpml/block';
import { Hpml } from '@/scripts/hpml/evaluator';
diff --git a/packages/frontend/src/components/page/page.image.vue b/packages/frontend/src/components/page/page.image.vue
index 8ba70c5855..0237644d29 100644
--- a/packages/frontend/src/components/page/page.image.vue
+++ b/packages/frontend/src/components/page/page.image.vue
@@ -5,9 +5,8 @@
</template>
<script lang="ts" setup>
-import { defineComponent, PropType } from 'vue';
+import { PropType } from 'vue';
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
-import * as os from '@/os';
import { ImageBlock } from '@/scripts/hpml/block';
import { Hpml } from '@/scripts/hpml/evaluator';
diff --git a/packages/frontend/src/components/page/page.number-input.vue b/packages/frontend/src/components/page/page.number-input.vue
index 4c5aae1040..72c1b6deb0 100644
--- a/packages/frontend/src/components/page/page.number-input.vue
+++ b/packages/frontend/src/components/page/page.number-input.vue
@@ -9,7 +9,6 @@
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
import MkInput from '../MkInput.vue';
-import * as os from '@/os';
import { Hpml } from '@/scripts/hpml/evaluator';
import { NumberInputVarBlock } from '@/scripts/hpml/block';
diff --git a/packages/frontend/src/components/page/page.radio-button.vue b/packages/frontend/src/components/page/page.radio-button.vue
index 2ae8d00ffc..ce8f252e44 100644
--- a/packages/frontend/src/components/page/page.radio-button.vue
+++ b/packages/frontend/src/components/page/page.radio-button.vue
@@ -8,7 +8,6 @@
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
import MkRadio from '../MkRadio.vue';
-import * as os from '@/os';
import { Hpml } from '@/scripts/hpml/evaluator';
import { RadioButtonVarBlock } from '@/scripts/hpml/block';
diff --git a/packages/frontend/src/components/page/page.section.vue b/packages/frontend/src/components/page/page.section.vue
index 630c1f5179..50181b3905 100644
--- a/packages/frontend/src/components/page/page.section.vue
+++ b/packages/frontend/src/components/page/page.section.vue
@@ -10,7 +10,6 @@
<script lang="ts">
import { defineComponent, defineAsyncComponent, PropType } from 'vue';
-import * as os from '@/os';
import { SectionBlock } from '@/scripts/hpml/block';
import { Hpml } from '@/scripts/hpml/evaluator';
diff --git a/packages/frontend/src/components/page/page.switch.vue b/packages/frontend/src/components/page/page.switch.vue
index d0d2637afa..b5f3464512 100644
--- a/packages/frontend/src/components/page/page.switch.vue
+++ b/packages/frontend/src/components/page/page.switch.vue
@@ -7,7 +7,6 @@
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
import MkSwitch from '../MkSwitch.vue';
-import * as os from '@/os';
import { Hpml } from '@/scripts/hpml/evaluator';
import { SwitchVarBlock } from '@/scripts/hpml/block';
diff --git a/packages/frontend/src/components/page/page.text-input.vue b/packages/frontend/src/components/page/page.text-input.vue
index 50731a9c9d..d020a99de8 100644
--- a/packages/frontend/src/components/page/page.text-input.vue
+++ b/packages/frontend/src/components/page/page.text-input.vue
@@ -9,7 +9,6 @@
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
import MkInput from '../MkInput.vue';
-import * as os from '@/os';
import { Hpml } from '@/scripts/hpml/evaluator';
import { TextInputVarBlock } from '@/scripts/hpml/block';
diff --git a/packages/frontend/src/components/page/page.textarea-input.vue b/packages/frontend/src/components/page/page.textarea-input.vue
index 7905c7eded..db3a96dd1b 100644
--- a/packages/frontend/src/components/page/page.textarea-input.vue
+++ b/packages/frontend/src/components/page/page.textarea-input.vue
@@ -9,9 +9,7 @@
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
import MkTextarea from '../MkTextarea.vue';
-import * as os from '@/os';
import { Hpml } from '@/scripts/hpml/evaluator';
-import { HpmlTextInput } from '@/scripts/hpml';
import { TextInputVarBlock } from '@/scripts/hpml/block';
export default defineComponent({
diff --git a/packages/frontend/src/components/page/page.vue b/packages/frontend/src/components/page/page.vue
index 87a288befe..5f1f62581e 100644
--- a/packages/frontend/src/components/page/page.vue
+++ b/packages/frontend/src/components/page/page.vue
@@ -5,12 +5,11 @@
</template>
<script lang="ts">
-import { defineComponent, onMounted, nextTick, onUnmounted, PropType } from 'vue';
+import { defineComponent, onMounted, nextTick, PropType } from 'vue';
import XBlock from './page.block.vue';
import { Hpml } from '@/scripts/hpml/evaluator';
import { url } from '@/config';
import { $i } from '@/account';
-import { defaultStore } from '@/store';
export default defineComponent({
components: {
diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts
index 64c252ce55..8c657295f9 100644
--- a/packages/frontend/src/init.ts
+++ b/packages/frontend/src/init.ts
@@ -25,7 +25,7 @@ import JSON5 from 'json5';
import widgets from '@/widgets';
import directives from '@/directives';
import components from '@/components';
-import { version, ui, lang, host, updateLocale } from '@/config';
+import { version, ui, lang, updateLocale } from '@/config';
import { applyTheme } from '@/scripts/theme';
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode';
import { i18n, updateI18n } from '@/i18n';
@@ -505,15 +505,6 @@ if ($i) {
updateAccount({ hasUnreadSpecifiedNotes: false });
});
- main.on('readAllMessagingMessages', () => {
- updateAccount({ hasUnreadMessagingMessage: false });
- });
-
- main.on('unreadMessagingMessage', () => {
- updateAccount({ hasUnreadMessagingMessage: true });
- sound.play('chatBg');
- });
-
main.on('readAllAntennas', () => {
updateAccount({ hasUnreadAntenna: false });
});
diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts
index 08dbd9737c..f4c1988704 100644
--- a/packages/frontend/src/instance.ts
+++ b/packages/frontend/src/instance.ts
@@ -1,4 +1,4 @@
-import { computed, reactive } from 'vue';
+import { reactive } from 'vue';
import * as Misskey from 'misskey-js';
import { api } from './os';
import { miLocalStorage } from './local-storage';
diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts
index 4f809d888e..95bf6e8181 100644
--- a/packages/frontend/src/navbar.ts
+++ b/packages/frontend/src/navbar.ts
@@ -1,4 +1,4 @@
-import { computed, ref, reactive } from 'vue';
+import { computed, reactive } from 'vue';
import { $i } from './account';
import { miLocalStorage } from './local-storage';
import { search } from '@/scripts/search';
@@ -15,13 +15,6 @@ export const navbarItemDef = reactive({
indicated: computed(() => $i != null && $i.hasUnreadNotification),
to: '/my/notifications',
},
- messaging: {
- title: i18n.ts.messaging,
- icon: 'ti ti-messages',
- show: computed(() => $i != null),
- indicated: computed(() => $i != null && $i.hasUnreadMessagingMessage),
- to: '/my/messaging',
- },
drive: {
title: i18n.ts.drive,
icon: 'ti ti-cloud',
@@ -57,14 +50,6 @@ export const navbarItemDef = reactive({
show: computed(() => $i != null),
to: '/my/lists',
},
- /*
- groups: {
- title: i18n.ts.groups,
- icon: 'ti ti-users',
- show: computed(() => $i != null),
- to: '/my/groups',
- },
- */
antennas: {
title: i18n.ts.antennas,
icon: 'ti ti-antenna',
diff --git a/packages/frontend/src/nirax.ts b/packages/frontend/src/nirax.ts
index 53e73a8d48..68977ed796 100644
--- a/packages/frontend/src/nirax.ts
+++ b/packages/frontend/src/nirax.ts
@@ -1,7 +1,7 @@
// NIRAX --- A lightweight router
import { EventEmitter } from 'eventemitter3';
-import { Ref, Component, ref, shallowRef, ShallowRef } from 'vue';
+import { Component, shallowRef, ShallowRef } from 'vue';
import { pleaseLogin } from '@/scripts/please-login';
import { safeURIDecode } from '@/scripts/safe-uri-decode';
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index 6bff12661f..a69fe73f30 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -246,7 +246,10 @@ export function inputText(props: {
title?: string | null;
text?: string | null;
placeholder?: string | null;
+ autocomplete?: string;
default?: string | null;
+ minLength?: number;
+ maxLength?: number;
}): Promise<{ canceled: true; result: undefined; } | {
canceled: false; result: string;
}> {
@@ -257,7 +260,10 @@ export function inputText(props: {
input: {
type: props.type,
placeholder: props.placeholder,
+ autocomplete: props.autocomplete,
default: props.default,
+ minLength: props.minLength,
+ maxLength: props.maxLength,
},
}, {
done: result => {
@@ -271,6 +277,7 @@ export function inputNumber(props: {
title?: string | null;
text?: string | null;
placeholder?: string | null;
+ autocomplete?: string;
default?: number | null;
}): Promise<{ canceled: true; result: undefined; } | {
canceled: false; result: number;
@@ -282,6 +289,7 @@ export function inputNumber(props: {
input: {
type: 'number',
placeholder: props.placeholder,
+ autocomplete: props.autocomplete,
default: props.default,
},
}, {
diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index bc3d248193..782fe9fdb2 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -84,6 +84,10 @@
</div>
<p>{{ i18n.ts._aboutMisskey.morePatrons }}</p>
</FormSection>
+ <FormSection>
+ <template #label>Credits</template>
+ <p>Misskeyで使われる画像の一部は、許可を得て「あの子がこっちを見てるメーカー」で作成したものが含まれます。</p>
+ </FormSection>
</div>
</MkSpacer>
</div>
@@ -111,6 +115,12 @@ const patronsWithIcon = [{
}, {
name: 'だれかさん',
icon: 'https://misskey-hub.net/patrons/f7409b5e5a88477a9b9d740c408de125.jpg',
+}, {
+ name: 'narazaka',
+ icon: 'https://misskey-hub.net/patrons/e3affff31ffb4877b1196c7360abc3e5.jpg',
+}, {
+ name: 'ひとぅ',
+ icon: 'https://misskey-hub.net/patrons/8cc0d0a0a6d84c88bca1aedabf6ed5ab.jpg',
}];
const patrons = [
diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue
index d964e48b31..7f3b4fd937 100644
--- a/packages/frontend/src/pages/about.emojis.vue
+++ b/packages/frontend/src/pages/about.emojis.vue
@@ -31,14 +31,11 @@
</template>
<script lang="ts" setup>
-import { defineComponent, computed, watch } from 'vue';
+import { watch } from 'vue';
import XEmoji from './emojis.emoji.vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
-import MkSelect from '@/components/MkSelect.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
-import MkTab from '@/components/MkTab.vue';
-import * as os from '@/os';
import { customEmojis, customEmojiCategories, getCustomEmojiTags } from '@/custom-emojis';
import { i18n } from '@/i18n';
import * as Misskey from 'misskey-js';
diff --git a/packages/frontend/src/pages/about.federation.vue b/packages/frontend/src/pages/about.federation.vue
index 90d6893f37..8fe613a9a8 100644
--- a/packages/frontend/src/pages/about.federation.vue
+++ b/packages/frontend/src/pages/about.federation.vue
@@ -46,15 +46,12 @@
<script lang="ts" setup>
import { computed } from 'vue';
-import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkPagination from '@/components/MkPagination.vue';
import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
import FormSplit from '@/components/form/split.vue';
-import * as os from '@/os';
import { i18n } from '@/i18n';
-import { dateString } from '@/filters/date';
let host = $ref('');
let state = $ref('federating');
diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue
index e5b9aecc61..be0c1828a3 100644
--- a/packages/frontend/src/pages/about.vue
+++ b/packages/frontend/src/pages/about.vue
@@ -86,10 +86,10 @@
</template>
<script lang="ts" setup>
-import { ref, computed, watch } from 'vue';
+import { computed, watch } from 'vue';
import XEmojis from './about.emojis.vue';
import XFederation from './about.federation.vue';
-import { version, instanceName, host } from '@/config';
+import { version, host } from '@/config';
import FormLink from '@/components/form/link.vue';
import FormSection from '@/components/form/section.vue';
import FormSuspense from '@/components/form/suspense.vue';
diff --git a/packages/frontend/src/pages/achievements.vue b/packages/frontend/src/pages/achievements.vue
index effa2fcaf4..1eef7a53fe 100644
--- a/packages/frontend/src/pages/achievements.vue
+++ b/packages/frontend/src/pages/achievements.vue
@@ -8,7 +8,7 @@
</template>
<script lang="ts" setup>
-import { onActivated, onDeactivated, onMounted, onUnmounted, ref } from 'vue';
+import { onActivated, onDeactivated, onMounted, onUnmounted } from 'vue';
import MkAchievements from '@/components/MkAchievements.vue';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue
index 6aac064c36..1d309a7377 100644
--- a/packages/frontend/src/pages/admin-file.vue
+++ b/packages/frontend/src/pages/admin-file.vue
@@ -75,7 +75,6 @@ import bytes from '@/filters/bytes';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
-import { acct } from '@/filters/user';
import { iAmAdmin, iAmModerator } from '@/account';
let tab = $ref('overview');
diff --git a/packages/frontend/src/pages/admin/RolesEditorFormula.vue b/packages/frontend/src/pages/admin/RolesEditorFormula.vue
index 5bd3803486..07729b8cf9 100644
--- a/packages/frontend/src/pages/admin/RolesEditorFormula.vue
+++ b/packages/frontend/src/pages/admin/RolesEditorFormula.vue
@@ -52,12 +52,7 @@ import { computed, defineAsyncComponent, ref, watch } from 'vue';
import { v4 as uuid } from 'uuid';
import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
-import MkTextarea from '@/components/MkTextarea.vue';
-import MkFolder from '@/components/MkFolder.vue';
-import MkSwitch from '@/components/MkSwitch.vue';
import MkButton from '@/components/MkButton.vue';
-import FormSlot from '@/components/form/slot.vue';
-import * as os from '@/os';
import { i18n } from '@/i18n';
import { deepClone } from '@/scripts/clone';
diff --git a/packages/frontend/src/pages/admin/_header_.vue b/packages/frontend/src/pages/admin/_header_.vue
index a342644516..372c63ff4c 100644
--- a/packages/frontend/src/pages/admin/_header_.vue
+++ b/packages/frontend/src/pages/admin/_header_.vue
@@ -28,13 +28,11 @@
</template>
<script lang="ts" setup>
-import { computed, onMounted, onUnmounted, ref, shallowRef, inject, watch, nextTick } from 'vue';
+import { computed, onMounted, onUnmounted, ref, shallowRef, watch, nextTick } from 'vue';
import tinycolor from 'tinycolor2';
import { popupMenu } from '@/os';
-import { url } from '@/config';
import { scrollToTop } from '@/scripts/scroll';
import MkButton from '@/components/MkButton.vue';
-import { i18n } from '@/i18n';
import { globalEvents } from '@/events';
import { injectPageMetadata } from '@/scripts/page-metadata';
@@ -115,7 +113,7 @@ function onTabClick(tab: Tab, ev: MouseEvent): void {
}
const calcBg = () => {
- const rawBg = metadata?.bg || 'var(--bg)';
+ const rawBg = metadata?.bg ?? 'var(--bg)';
const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
tinyBg.setAlpha(0.85);
bg.value = tinyBg.toRgbString();
diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue
index 1c8557400f..9e8af43024 100644
--- a/packages/frontend/src/pages/admin/abuses.vue
+++ b/packages/frontend/src/pages/admin/abuses.vue
@@ -50,11 +50,9 @@
import { computed } from 'vue';
import XHeader from './_header_.vue';
-import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkPagination from '@/components/MkPagination.vue';
import XAbuseReport from '@/components/MkAbuseReport.vue';
-import * as os from '@/os';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue
index 701ec31b65..828bfe6007 100644
--- a/packages/frontend/src/pages/admin/ads.vue
+++ b/packages/frontend/src/pages/admin/ads.vue
@@ -29,6 +29,9 @@
<MkInput v-model="ad.ratio" type="number">
<template #label>{{ i18n.ts.ratio }}</template>
</MkInput>
+ <MkInput v-model="ad.startsAt" type="datetime-local">
+ <template #label>{{ i18n.ts.startingperiod }}</template>
+ </MkInput>
<MkInput v-model="ad.expiresAt" type="datetime-local">
<template #label>{{ i18n.ts.expiration }}</template>
</MkInput>
@@ -41,6 +44,9 @@
<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()">
+ <i class="ti ti-reload"></i>{{ i18n.ts.more }}
+ </MkButton>
</div>
</MkSpacer>
</MkStickyContainer>
@@ -66,11 +72,14 @@ const localTimeDiff = localTime.getTimezoneOffset() * 60 * 1000;
os.api('admin/ad/list').then(adsResponse => {
ads = adsResponse.map(r => {
- const date = new Date(r.expiresAt);
- date.setMilliseconds(date.getMilliseconds() - localTimeDiff);
+ const exdate = new Date(r.expiresAt);
+ const stdate = new Date(r.startsAt);
+ exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
+ stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff);
return {
...r,
- expiresAt: date.toISOString().slice(0, 16),
+ expiresAt: exdate.toISOString().slice(0, 16),
+ startsAt: stdate.toISOString().slice(0, 16),
};
});
});
@@ -85,6 +94,7 @@ function add() {
url: '',
imageUrl: null,
expiresAt: null,
+ startsAt: null,
});
}
@@ -106,15 +116,31 @@ function save(ad) {
os.apiWithDialog('admin/ad/create', {
...ad,
expiresAt: new Date(ad.expiresAt).getTime(),
+ startsAt: new Date(ad.startsAt).getTime(),
});
} else {
os.apiWithDialog('admin/ad/update', {
...ad,
expiresAt: new Date(ad.expiresAt).getTime(),
+ startsAt: new Date(ad.startsAt).getTime(),
});
}
}
-
+function more() {
+ os.api('admin/ad/list', { untilId: ads.reduce((acc, ad) => ad.id != null ? ad : acc).id }).then(adsResponse => {
+ ads = ads.concat(adsResponse.map(r => {
+ const exdate = new Date(r.expiresAt);
+ const stdate = new Date(r.startsAt);
+ exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
+ stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff);
+ return {
+ ...r,
+ expiresAt: exdate.toISOString().slice(0, 16),
+ startsAt: stdate.toISOString().slice(0, 16),
+ };
+ }));
+ });
+}
const headerActions = $computed(() => [{
asFullButton: true,
icon: 'ti ti-plus',
diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue
index f2c114ca21..6af1610431 100644
--- a/packages/frontend/src/pages/admin/federation.vue
+++ b/packages/frontend/src/pages/admin/federation.vue
@@ -54,15 +54,12 @@
<script lang="ts" setup>
import { computed } from 'vue';
import XHeader from './_header_.vue';
-import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkPagination from '@/components/MkPagination.vue';
import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
import FormSplit from '@/components/form/split.vue';
-import * as os from '@/os';
import { i18n } from '@/i18n';
-import { dateString } from '@/filters/date';
import { definePageMetadata } from '@/scripts/page-metadata';
let host = $ref('');
diff --git a/packages/frontend/src/pages/admin/files.vue b/packages/frontend/src/pages/admin/files.vue
index 1d61cdf8e4..c189437246 100644
--- a/packages/frontend/src/pages/admin/files.vue
+++ b/packages/frontend/src/pages/admin/files.vue
@@ -33,14 +33,11 @@
</template>
<script lang="ts" setup>
-import { computed, defineAsyncComponent } from 'vue';
-import * as Acct from 'misskey-js/built/acct';
+import { computed } from 'vue';
import XHeader from './_header_.vue';
-import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
-import bytes from '@/filters/bytes';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index 9a07d3c959..b054999303 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -23,16 +23,15 @@
</template>
<script lang="ts" setup>
-import { defineAsyncComponent, inject, nextTick, onMounted, onUnmounted, provide, watch } from 'vue';
+import { onMounted, onUnmounted, provide, watch } from 'vue';
import { i18n } from '@/i18n';
import MkSuperMenu from '@/components/MkSuperMenu.vue';
import MkInfo from '@/components/MkInfo.vue';
-import { scroll } from '@/scripts/scroll';
import { instance } from '@/instance';
import * as os from '@/os';
import { lookupUser } from '@/scripts/lookup-user';
import { useRouter } from '@/router';
-import { definePageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata';
+import { definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
const isEmpty = (x: string | null) => x == null || x === '';
diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue
index 6a6af637ed..bd7c203512 100644
--- a/packages/frontend/src/pages/admin/object-storage.vue
+++ b/packages/frontend/src/pages/admin/object-storage.vue
@@ -75,7 +75,6 @@ import MkSwitch from '@/components/MkSwitch.vue';
import MkInput from '@/components/MkInput.vue';
import FormSuspense from '@/components/form/suspense.vue';
import FormSplit from '@/components/form/split.vue';
-import FormSection from '@/components/form/section.vue';
import * as os from '@/os';
import { fetchInstance } from '@/instance';
import { i18n } from '@/i18n';
diff --git a/packages/frontend/src/pages/admin/overview.active-users.vue b/packages/frontend/src/pages/admin/overview.active-users.vue
index 3ec7694f26..fc10ad75f8 100644
--- a/packages/frontend/src/pages/admin/overview.active-users.vue
+++ b/packages/frontend/src/pages/admin/overview.active-users.vue
@@ -8,15 +8,13 @@
</template>
<script lang="ts" setup>
-import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue';
+import { onMounted } from 'vue';
import { Chart } from 'chart.js';
-import tinycolor from 'tinycolor2';
import gradient from 'chartjs-plugin-gradient';
import * as os from '@/os';
import { defaultStore } from '@/store';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
import { chartVLine } from '@/scripts/chart-vline';
-import { alpha } from '@/scripts/color';
import { initChart } from '@/scripts/init-chart';
initChart();
diff --git a/packages/frontend/src/pages/admin/overview.ap-requests.vue b/packages/frontend/src/pages/admin/overview.ap-requests.vue
index efb335fff1..ad8e623415 100644
--- a/packages/frontend/src/pages/admin/overview.ap-requests.vue
+++ b/packages/frontend/src/pages/admin/overview.ap-requests.vue
@@ -15,15 +15,10 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, ref } from 'vue';
+import { onMounted } from 'vue';
import { Chart } from 'chart.js';
import gradient from 'chartjs-plugin-gradient';
-import tinycolor from 'tinycolor2';
-import MkMiniChart from '@/components/MkMiniChart.vue';
import * as os from '@/os';
-import number from '@/filters/number';
-import MkNumberDiff from '@/components/MkNumberDiff.vue';
-import { i18n } from '@/i18n';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
import { chartVLine } from '@/scripts/chart-vline';
import { defaultStore } from '@/store';
diff --git a/packages/frontend/src/pages/admin/overview.federation.vue b/packages/frontend/src/pages/admin/overview.federation.vue
index 2789adf643..ab78c4c393 100644
--- a/packages/frontend/src/pages/admin/overview.federation.vue
+++ b/packages/frontend/src/pages/admin/overview.federation.vue
@@ -41,9 +41,8 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, ref } from 'vue';
+import { onMounted } from 'vue';
import XPie from './overview.pie.vue';
-import MkMiniChart from '@/components/MkMiniChart.vue';
import * as os from '@/os';
import number from '@/filters/number';
import MkNumberDiff from '@/components/MkNumberDiff.vue';
diff --git a/packages/frontend/src/pages/admin/overview.instances.vue b/packages/frontend/src/pages/admin/overview.instances.vue
index 15dbdc4639..7d530d6b95 100644
--- a/packages/frontend/src/pages/admin/overview.instances.vue
+++ b/packages/frontend/src/pages/admin/overview.instances.vue
@@ -12,7 +12,7 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, ref } from 'vue';
+import { ref } from 'vue';
import * as os from '@/os';
import { useInterval } from '@/scripts/use-interval';
import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
diff --git a/packages/frontend/src/pages/admin/overview.moderators.vue b/packages/frontend/src/pages/admin/overview.moderators.vue
index 445217d825..ff689b8bf9 100644
--- a/packages/frontend/src/pages/admin/overview.moderators.vue
+++ b/packages/frontend/src/pages/admin/overview.moderators.vue
@@ -12,10 +12,8 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, ref } from 'vue';
+import { onMounted } from 'vue';
import * as os from '@/os';
-import number from '@/filters/number';
-import { i18n } from '@/i18n';
let moderators: any = $ref(null);
let fetching = $ref(true);
diff --git a/packages/frontend/src/pages/admin/overview.pie.vue b/packages/frontend/src/pages/admin/overview.pie.vue
index 416e963356..08a29bf550 100644
--- a/packages/frontend/src/pages/admin/overview.pie.vue
+++ b/packages/frontend/src/pages/admin/overview.pie.vue
@@ -3,10 +3,8 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, ref, shallowRef } from 'vue';
+import { onMounted, shallowRef } from 'vue';
import { Chart } from 'chart.js';
-import number from '@/filters/number';
-import { defaultStore } from '@/store';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
import { initChart } from '@/scripts/init-chart';
diff --git a/packages/frontend/src/pages/admin/overview.queue.chart.vue b/packages/frontend/src/pages/admin/overview.queue.chart.vue
index 0162d53665..6a11e8b768 100644
--- a/packages/frontend/src/pages/admin/overview.queue.chart.vue
+++ b/packages/frontend/src/pages/admin/overview.queue.chart.vue
@@ -3,10 +3,8 @@
</template>
<script lang="ts" setup>
-import { watch, onMounted, onUnmounted, ref, shallowRef } from 'vue';
+import { onMounted, shallowRef } from 'vue';
import { Chart } from 'chart.js';
-import number from '@/filters/number';
-import * as os from '@/os';
import { defaultStore } from '@/store';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
import { chartVLine } from '@/scripts/chart-vline';
diff --git a/packages/frontend/src/pages/admin/overview.queue.vue b/packages/frontend/src/pages/admin/overview.queue.vue
index 7e58882938..1f56a2826a 100644
--- a/packages/frontend/src/pages/admin/overview.queue.vue
+++ b/packages/frontend/src/pages/admin/overview.queue.vue
@@ -33,9 +33,7 @@
import { markRaw, onMounted, onUnmounted, ref } from 'vue';
import XChart from './overview.queue.chart.vue';
import number from '@/filters/number';
-import * as os from '@/os';
import { stream } from '@/stream';
-import { i18n } from '@/i18n';
const connection = markRaw(stream.useChannel('queueStats'));
diff --git a/packages/frontend/src/pages/admin/overview.stats.vue b/packages/frontend/src/pages/admin/overview.stats.vue
index bd636cc3ef..3dc1ed8ec5 100644
--- a/packages/frontend/src/pages/admin/overview.stats.vue
+++ b/packages/frontend/src/pages/admin/overview.stats.vue
@@ -56,10 +56,8 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, ref } from 'vue';
-import MkMiniChart from '@/components/MkMiniChart.vue';
+import { onMounted } from 'vue';
import * as os from '@/os';
-import number from '@/filters/number';
import MkNumberDiff from '@/components/MkNumberDiff.vue';
import MkNumber from '@/components/MkNumber.vue';
import { i18n } from '@/i18n';
diff --git a/packages/frontend/src/pages/admin/overview.users.vue b/packages/frontend/src/pages/admin/overview.users.vue
index 5390d9d8cb..3379d064cd 100644
--- a/packages/frontend/src/pages/admin/overview.users.vue
+++ b/packages/frontend/src/pages/admin/overview.users.vue
@@ -12,7 +12,6 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, ref } from 'vue';
import * as os from '@/os';
import { useInterval } from '@/scripts/use-interval';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
diff --git a/packages/frontend/src/pages/admin/overview.vue b/packages/frontend/src/pages/admin/overview.vue
index 0166724e01..5c96c07bfb 100644
--- a/packages/frontend/src/pages/admin/overview.vue
+++ b/packages/frontend/src/pages/admin/overview.vue
@@ -60,7 +60,7 @@
</template>
<script lang="ts" setup>
-import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue';
+import { markRaw, onMounted, onBeforeUnmount, nextTick } from 'vue';
import XFederation from './overview.federation.vue';
import XInstances from './overview.instances.vue';
import XQueue from './overview.queue.vue';
@@ -71,14 +71,10 @@ import XStats from './overview.stats.vue';
import XRetention from './overview.retention.vue';
import XModerators from './overview.moderators.vue';
import XHeatmap from './overview.heatmap.vue';
-import MkTagCloud from '@/components/MkTagCloud.vue';
-import { version, url } from '@/config';
import * as os from '@/os';
import { stream } from '@/stream';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
-import { defaultStore } from '@/store';
-import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
const rootEl = $shallowRef<HTMLElement>();
diff --git a/packages/frontend/src/pages/admin/queue.chart.chart.vue b/packages/frontend/src/pages/admin/queue.chart.chart.vue
index a0c05df983..1a1f6a9db4 100644
--- a/packages/frontend/src/pages/admin/queue.chart.chart.vue
+++ b/packages/frontend/src/pages/admin/queue.chart.chart.vue
@@ -3,10 +3,8 @@
</template>
<script lang="ts" setup>
-import { watch, onMounted, onUnmounted, ref, shallowRef } from 'vue';
+import { onMounted, shallowRef } from 'vue';
import { Chart } from 'chart.js';
-import number from '@/filters/number';
-import * as os from '@/os';
import { defaultStore } from '@/store';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
import { chartVLine } from '@/scripts/chart-vline';
diff --git a/packages/frontend/src/pages/admin/queue.vue b/packages/frontend/src/pages/admin/queue.vue
index 8d19b49fc5..80e97fed93 100644
--- a/packages/frontend/src/pages/admin/queue.vue
+++ b/packages/frontend/src/pages/admin/queue.vue
@@ -9,10 +9,8 @@
</template>
<script lang="ts" setup>
-import { markRaw, onMounted, onBeforeUnmount, nextTick } from 'vue';
import XQueue from './queue.chart.vue';
import XHeader from './_header_.vue';
-import MkButton from '@/components/MkButton.vue';
import * as os from '@/os';
import * as config from '@/config';
import { i18n } from '@/i18n';
diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue
index 3cb4e2deb9..ae884c0111 100644
--- a/packages/frontend/src/pages/admin/roles.edit.vue
+++ b/packages/frontend/src/pages/admin/roles.edit.vue
@@ -13,13 +13,6 @@
import { computed } from 'vue';
import XHeader from './_header_.vue';
import XEditor from './roles.editor.vue';
-import MkInput from '@/components/MkInput.vue';
-import MkSelect from '@/components/MkSelect.vue';
-import MkTextarea from '@/components/MkTextarea.vue';
-import MkFolder from '@/components/MkFolder.vue';
-import MkSwitch from '@/components/MkSwitch.vue';
-import MkButton from '@/components/MkButton.vue';
-import FormSlot from '@/components/form/slot.vue';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index d89a68f982..4eea827de7 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -384,7 +384,7 @@
</template>
<script lang="ts" setup>
-import { computed, reactive, watch } from 'vue';
+import { reactive, watch } from 'vue';
import { v4 as uuid } from 'uuid';
import RolesEditorFormula from './RolesEditorFormula.vue';
import MkInput from '@/components/MkInput.vue';
diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue
index 0365165b5d..1b9f0e7c53 100644
--- a/packages/frontend/src/pages/admin/roles.role.vue
+++ b/packages/frontend/src/pages/admin/roles.role.vue
@@ -16,16 +16,29 @@
<MkFolder v-if="role.target === 'manual'" default-open>
<template #icon><i class="ti ti-users"></i></template>
<template #label>{{ i18n.ts.users }}</template>
- <template #suffix>{{ role.users.length }}</template>
+ <template #suffix>{{ role.usersCount }}</template>
<div class="_gaps">
<MkButton primary rounded @click="assign"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton>
- <div v-for="user in role.users" :key="user.id" :class="$style.userItem">
- <MkA :class="$style.user" :to="`/user-info/${user.id}`">
- <MkUserCardMini :user="user"/>
- </MkA>
- <button class="_button" :class="$style.unassign" @click="unassign(user, $event)"><i class="ti ti-x"></i></button>
- </div>
+ <MkPagination :pagination="usersPagination">
+ <template #empty>
+ <div class="_fullinfo">
+ <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <div>{{ i18n.ts.noUsers }}</div>
+ </div>
+ </template>
+
+ <template #default="{ items }">
+ <div class="_gaps_s">
+ <div v-for="item in items" :key="item.user.id" :class="$style.userItem">
+ <MkA :class="$style.user" :to="`/user-info/${item.user.id}`">
+ <MkUserCardMini :user="item.user"/>
+ </MkA>
+ <button class="_button" :class="$style.unassign" @click="unassign(item.user, $event)"><i class="ti ti-x"></i></button>
+ </div>
+ </div>
+ </template>
+ </MkPagination>
</div>
</MkFolder>
<MkInfo v-else>{{ i18n.ts._role.isConditionalRole }}</MkInfo>
@@ -47,6 +60,7 @@ import { useRouter } from '@/router';
import MkButton from '@/components/MkButton.vue';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import MkInfo from '@/components/MkInfo.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
const router = useRouter();
@@ -54,6 +68,14 @@ const props = defineProps<{
id?: string;
}>();
+const usersPagination = {
+ endpoint: 'admin/roles/users' as const,
+ limit: 20,
+ params: computed(() => ({
+ roleId: props.id,
+ })),
+};
+
const role = reactive(await os.api('admin/roles/show', {
roleId: props.id,
}));
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index ff8f8a356f..d89f0d2a7d 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -133,7 +133,7 @@
</div>
</MkFolder>
<div class="_gaps_s">
- <MkRolePreview v-for="role in roles" :key="role.id" :role="role"/>
+ <MkRolePreview v-for="role in roles" :key="role.id" :role="role" :for-moderation="true"/>
</div>
</div>
</MkSpacer>
@@ -145,8 +145,6 @@
import { computed, reactive } from 'vue';
import XHeader from './_header_.vue';
import MkInput from '@/components/MkInput.vue';
-import MkSelect from '@/components/MkSelect.vue';
-import MkPagination from '@/components/MkPagination.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkButton from '@/components/MkButton.vue';
diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index 191da506e9..cd8ef9e68b 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -108,7 +108,6 @@ import XHeader from './_header_.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkRadios from '@/components/MkRadios.vue';
import MkSwitch from '@/components/MkSwitch.vue';
-import FormInfo from '@/components/MkInfo.vue';
import FormSuspense from '@/components/form/suspense.vue';
import MkRange from '@/components/MkRange.vue';
import MkInput from '@/components/MkInput.vue';
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index 12d852a90e..7840c55ee4 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -143,7 +143,6 @@ import XHeader from './_header_.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
-import FormInfo from '@/components/MkInfo.vue';
import FormSection from '@/components/form/section.vue';
import FormSplit from '@/components/form/split.vue';
import FormSuspense from '@/components/form/suspense.vue';
diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue
index ff31c3ab2c..cf803d6c7f 100644
--- a/packages/frontend/src/pages/antenna-timeline.vue
+++ b/packages/frontend/src/pages/antenna-timeline.vue
@@ -4,7 +4,7 @@
<div ref="rootEl" v-hotkey.global="keymap" class="tqmomfks">
<div v-if="queue > 0" class="new"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div>
<div class="tl">
- <XTimeline
+ <MkTimeline
ref="tlEl" :key="antennaId"
class="tl"
src="antenna"
@@ -18,8 +18,8 @@
</template>
<script lang="ts" setup>
-import { computed, inject, watch } from 'vue';
-import XTimeline from '@/components/MkTimeline.vue';
+import { computed, watch } from 'vue';
+import MkTimeline from '@/components/MkTimeline.vue';
import { scroll } from '@/scripts/scroll';
import * as os from '@/os';
import { useRouter } from '@/router';
@@ -35,7 +35,7 @@ const props = defineProps<{
let antenna = $ref(null);
let queue = $ref(0);
let rootEl = $shallowRef<HTMLElement>();
-let tlEl = $shallowRef<InstanceType<typeof XTimeline>>();
+let tlEl = $shallowRef<InstanceType<typeof MkTimeline>>();
const keymap = $computed(() => ({
't': focus,
}));
@@ -72,7 +72,7 @@ watch(() => props.antennaId, async () => {
}, { immediate: true });
const headerActions = $computed(() => antenna ? [{
- icon: 'fas fa-calendar-alt',
+ icon: 'ti ti-calendar-time',
text: i18n.ts.jumpToSpecifiedDate,
handler: timetravel,
}, {
diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue
index 50afffc460..4f8afb9ea2 100644
--- a/packages/frontend/src/pages/auth.vue
+++ b/packages/frontend/src/pages/auth.vue
@@ -1,12 +1,12 @@
<template>
<MkStickyContainer>
- <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs" /></template>
+ <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :content-max="500">
<div v-if="state == 'fetch-session-error'">
<p>{{ i18n.ts.somethingHappened }}</p>
</div>
<div v-else-if="$i && !session">
- <MkLoading />
+ <MkLoading/>
</div>
<div v-else-if="$i && session">
<XForm
@@ -21,15 +21,16 @@
</div>
<div v-if="state == 'accepted' && session">
<h1>{{ session.app.isAuthorized ? $t('already-authorized') : i18n.ts.allowed }}</h1>
- <p v-if="session.app.callbackUrl">{{ i18n.ts._auth.callback }}
- <MkEllipsis />
+ <p v-if="session.app.callbackUrl">
+ {{ i18n.ts._auth.callback }}
+ <MkEllipsis/>
</p>
<p v-if="!session.app.callbackUrl">{{ i18n.ts._auth.pleaseGoBack }}</p>
</div>
</div>
<div v-else>
<p :class="$style.loginMessage">{{ i18n.ts._auth.pleaseLogin }}</p>
- <MkSignin @login="onLogin" />
+ <MkSignin @login="onLogin"/>
</div>
</MkSpacer>
</MkStickyContainer>
@@ -37,12 +38,12 @@
<script lang="ts" setup>
import { onMounted } from 'vue';
+import { AuthSession } from 'misskey-js/built/entities';
import XForm from './auth.form.vue';
import MkSignin from '@/components/MkSignin.vue';
import * as os from '@/os';
import { $i, login } from '@/account';
import { definePageMetadata } from '@/scripts/page-metadata';
-import { AuthSession } from 'misskey-js/built/entities';
import { i18n } from '@/i18n';
const props = defineProps<{
@@ -82,7 +83,7 @@ onMounted(async () => {
} else {
state = 'waiting';
}
- } catch (e) {
+ } catch (err) {
state = 'fetch-session-error';
}
});
diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue
index df8417e4ad..38c5b1e082 100644
--- a/packages/frontend/src/pages/channel-editor.vue
+++ b/packages/frontend/src/pages/channel-editor.vue
@@ -27,7 +27,7 @@
</template>
<script lang="ts" setup>
-import { computed, inject, watch } from 'vue';
+import { computed, watch } from 'vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue
index 0fb33e30f7..0e6d0e2691 100644
--- a/packages/frontend/src/pages/channel.vue
+++ b/packages/frontend/src/pages/channel.vue
@@ -25,17 +25,16 @@
<MkPostForm v-if="$i" :channel="channel" class="post-form _panel _margin" fixed/>
- <XTimeline :key="channelId" class="_margin" src="channel" :channel="channelId" @before="before" @after="after"/>
+ <MkTimeline :key="channelId" class="_margin" src="channel" :channel="channelId" @before="before" @after="after"/>
</div>
</MkSpacer>
</MkStickyContainer>
</template>
<script lang="ts" setup>
-import { computed, inject, watch } from 'vue';
-import MkContainer from '@/components/MkContainer.vue';
+import { computed, watch } from 'vue';
import MkPostForm from '@/components/MkPostForm.vue';
-import XTimeline from '@/components/MkTimeline.vue';
+import MkTimeline from '@/components/MkTimeline.vue';
import XChannelFollowButton from '@/components/MkChannelFollowButton.vue';
import * as os from '@/os';
import { useRouter } from '@/router';
diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue
index 9043d06c52..3550c7f436 100644
--- a/packages/frontend/src/pages/channels.vue
+++ b/packages/frontend/src/pages/channels.vue
@@ -23,7 +23,7 @@
</template>
<script lang="ts" setup>
-import { computed, defineComponent, inject } from 'vue';
+import { computed } from 'vue';
import MkChannelPreview from '@/components/MkChannelPreview.vue';
import MkPagination from '@/components/MkPagination.vue';
import MkButton from '@/components/MkButton.vue';
diff --git a/packages/frontend/src/pages/clicker.vue b/packages/frontend/src/pages/clicker.vue
index 082a303e6f..24eae32e13 100644
--- a/packages/frontend/src/pages/clicker.vue
+++ b/packages/frontend/src/pages/clicker.vue
@@ -8,9 +8,7 @@
</template>
<script lang="ts" setup>
-import { ref } from 'vue';
import MkClickerGame from '@/components/MkClickerGame.vue';
-import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
definePageMetadata({
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index cd9cec0d4f..d4e8f27005 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -12,7 +12,7 @@
</div>
</div>
- <XNotes :pagination="pagination" :detail="true"/>
+ <MkNotes :pagination="pagination" :detail="true"/>
</div>
</MkSpacer>
</MkStickyContainer>
@@ -21,7 +21,7 @@
<script lang="ts" setup>
import { computed, watch, provide } from 'vue';
import * as misskey from 'misskey-js';
-import XNotes from '@/components/MkNotes.vue';
+import MkNotes from '@/components/MkNotes.vue';
import { $i } from '@/account';
import { i18n } from '@/i18n';
import * as os from '@/os';
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index 87d205ed78..59cb3262b7 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -68,11 +68,10 @@
</template>
<script lang="ts" setup>
-import { computed, defineAsyncComponent, defineComponent, ref, shallowRef } from 'vue';
+import { computed, defineAsyncComponent, ref, shallowRef } from 'vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkPagination from '@/components/MkPagination.vue';
-import MkTab from '@/components/MkTab.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import FormSplit from '@/components/form/split.vue';
import { selectFile, selectFiles } from '@/scripts/select-file';
diff --git a/packages/frontend/src/pages/drive.vue b/packages/frontend/src/pages/drive.vue
index 04ade5c207..0b398684ca 100644
--- a/packages/frontend/src/pages/drive.vue
+++ b/packages/frontend/src/pages/drive.vue
@@ -7,7 +7,6 @@
<script lang="ts" setup>
import { computed } from 'vue';
import XDrive from '@/components/MkDrive.vue';
-import * as os from '@/os';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index 4d84ed7f16..9be30f76a0 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -34,7 +34,6 @@ import MkModalWindow from '@/components/MkModalWindow.vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import * as os from '@/os';
-import { unique } from '@/scripts/array';
import { i18n } from '@/i18n';
import { customEmojiCategories } from '@/custom-emojis';
diff --git a/packages/frontend/src/pages/explore.featured.vue b/packages/frontend/src/pages/explore.featured.vue
index 18a371a086..a972ae04ec 100644
--- a/packages/frontend/src/pages/explore.featured.vue
+++ b/packages/frontend/src/pages/explore.featured.vue
@@ -4,13 +4,13 @@
<option value="notes">{{ i18n.ts.notes }}</option>
<option value="polls">{{ i18n.ts.poll }}</option>
</MkTab>
- <XNotes v-if="tab === 'notes'" :pagination="paginationForNotes"/>
- <XNotes v-else-if="tab === 'polls'" :pagination="paginationForPolls"/>
+ <MkNotes v-if="tab === 'notes'" :pagination="paginationForNotes"/>
+ <MkNotes v-else-if="tab === 'polls'" :pagination="paginationForPolls"/>
</MkSpacer>
</template>
<script lang="ts" setup>
-import XNotes from '@/components/MkNotes.vue';
+import MkNotes from '@/components/MkNotes.vue';
import MkTab from '@/components/MkTab.vue';
import { i18n } from '@/i18n';
diff --git a/packages/frontend/src/pages/explore.roles.vue b/packages/frontend/src/pages/explore.roles.vue
new file mode 100644
index 0000000000..8be11008c2
--- /dev/null
+++ b/packages/frontend/src/pages/explore.roles.vue
@@ -0,0 +1,22 @@
+<template>
+<MkSpacer :content-max="1200">
+ <div class="_gaps_s">
+ <MkRolePreview v-for="role in roles" :key="role.id" :role="role" :for-moderation="false"/>
+ </div>
+</MkSpacer>
+</template>
+
+<script lang="ts" setup>
+import { } from 'vue';
+import MkRolePreview from '@/components/MkRolePreview.vue';
+import * as os from '@/os';
+
+let roles = $ref();
+
+os.api('roles/list', {
+ limit: 30,
+}).then(res => {
+ roles = res;
+});
+</script>
+
diff --git a/packages/frontend/src/pages/explore.users.vue b/packages/frontend/src/pages/explore.users.vue
index 3a74e8518d..c441407d97 100644
--- a/packages/frontend/src/pages/explore.users.vue
+++ b/packages/frontend/src/pages/explore.users.vue
@@ -7,20 +7,20 @@
<div v-if="origin === 'local'">
<template v-if="tag == null">
<MkFoldableSection class="_margin" persist-key="explore-pinned-users">
- <template #header><i class="fas fa-bookmark ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.pinnedUsers }}</template>
- <XUserList :pagination="pinnedUsers"/>
+ <template #header><i class="ti ti-bookmark ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.pinnedUsers }}</template>
+ <MkUserList :pagination="pinnedUsers"/>
</MkFoldableSection>
<MkFoldableSection class="_margin" persist-key="explore-popular-users">
- <template #header><i class="fas fa-chart-line ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template>
- <XUserList :pagination="popularUsers"/>
+ <template #header><i class="ti ti-chart-line ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template>
+ <MkUserList :pagination="popularUsers"/>
</MkFoldableSection>
<MkFoldableSection class="_margin" persist-key="explore-recently-updated-users">
- <template #header><i class="fas fa-comment-alt ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template>
- <XUserList :pagination="recentlyUpdatedUsers"/>
+ <template #header><i class="ti ti-message ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template>
+ <MkUserList :pagination="recentlyUpdatedUsers"/>
</MkFoldableSection>
<MkFoldableSection class="_margin" persist-key="explore-recently-registered-users">
<template #header><i class="ti ti-plus ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyRegisteredUsers }}</template>
- <XUserList :pagination="recentlyRegisteredUsers"/>
+ <MkUserList :pagination="recentlyRegisteredUsers"/>
</MkFoldableSection>
</template>
</div>
@@ -29,28 +29,28 @@
<template #header><i class="ti ti-hash ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularTags }}</template>
<div class="vxjfqztj">
- <MkA v-for="tag in tagsLocal" :key="'local:' + tag.tag" :to="`/explore/tags/${tag.tag}`" class="local">{{ tag.tag }}</MkA>
- <MkA v-for="tag in tagsRemote" :key="'remote:' + tag.tag" :to="`/explore/tags/${tag.tag}`">{{ tag.tag }}</MkA>
+ <MkA v-for="tag in tagsLocal" :key="'local:' + tag.tag" :to="`/user-tags/${tag.tag}`" class="local">{{ tag.tag }}</MkA>
+ <MkA v-for="tag in tagsRemote" :key="'remote:' + tag.tag" :to="`/user-tags/${tag.tag}`">{{ tag.tag }}</MkA>
</div>
</MkFoldableSection>
<MkFoldableSection v-if="tag != null" :key="`${tag}`" class="_margin">
<template #header><i class="ti ti-hash ti-fw" style="margin-right: 0.5em;"></i>{{ tag }}</template>
- <XUserList :pagination="tagUsers"/>
+ <MkUserList :pagination="tagUsers"/>
</MkFoldableSection>
<template v-if="tag == null">
<MkFoldableSection class="_margin">
- <template #header><i class="fas fa-chart-line ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template>
- <XUserList :pagination="popularUsersF"/>
+ <template #header><i class="ti ti-chart-line ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template>
+ <MkUserList :pagination="popularUsersF"/>
</MkFoldableSection>
<MkFoldableSection class="_margin">
- <template #header><i class="fas fa-comment-alt ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template>
- <XUserList :pagination="recentlyUpdatedUsersF"/>
+ <template #header><i class="ti ti-message ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template>
+ <MkUserList :pagination="recentlyUpdatedUsersF"/>
</MkFoldableSection>
<MkFoldableSection class="_margin">
- <template #header><i class="fas fa-rocket ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyDiscoveredUsers }}</template>
- <XUserList :pagination="recentlyRegisteredUsersF"/>
+ <template #header><i class="ti ti-rocket ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyDiscoveredUsers }}</template>
+ <MkUserList :pagination="recentlyRegisteredUsersF"/>
</MkFoldableSection>
</template>
</div>
@@ -58,14 +58,12 @@
</template>
<script lang="ts" setup>
-import { computed, watch } from 'vue';
-import XUserList from '@/components/MkUserList.vue';
+import { watch } from 'vue';
+import MkUserList from '@/components/MkUserList.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
import MkTab from '@/components/MkTab.vue';
-import number from '@/filters/number';
import * as os from '@/os';
import { i18n } from '@/i18n';
-import { instance } from '@/instance';
const props = defineProps<{
tag?: string;
diff --git a/packages/frontend/src/pages/explore.vue b/packages/frontend/src/pages/explore.vue
index 57df58bbb8..0ed0a7ebc2 100644
--- a/packages/frontend/src/pages/explore.vue
+++ b/packages/frontend/src/pages/explore.vue
@@ -8,6 +8,9 @@
<div v-else-if="tab === 'users'">
<XUsers/>
</div>
+ <div v-else-if="tab === 'roles'">
+ <XRoles/>
+ </div>
<div v-else-if="tab === 'search'">
<MkSpacer :content-max="1200">
<div>
@@ -22,7 +25,7 @@
</MkRadios>
</div>
- <XUserList v-if="searchQuery" ref="searchEl" class="_margin" :pagination="searchPagination"/>
+ <MkUserList v-if="searchQuery" ref="searchEl" class="_margin" :pagination="searchPagination"/>
</MkSpacer>
</div>
</div>
@@ -33,15 +36,13 @@
import { computed, watch } from 'vue';
import XFeatured from './explore.featured.vue';
import XUsers from './explore.users.vue';
+import XRoles from './explore.roles.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
import MkInput from '@/components/MkInput.vue';
import MkRadios from '@/components/MkRadios.vue';
-import number from '@/filters/number';
-import * as os from '@/os';
import { definePageMetadata } from '@/scripts/page-metadata';
import { i18n } from '@/i18n';
-import { instance } from '@/instance';
-import XUserList from '@/components/MkUserList.vue';
+import MkUserList from '@/components/MkUserList.vue';
const props = withDefaults(defineProps<{
tag?: string;
@@ -79,7 +80,12 @@ const headerTabs = $computed(() => [{
icon: 'ti ti-users',
title: i18n.ts.users,
}, {
+ key: 'roles',
+ icon: 'ti ti-badges',
+ title: i18n.ts.roles,
+}, {
key: 'search',
+ icon: 'ti ti-search',
title: i18n.ts.search,
}]);
diff --git a/packages/frontend/src/pages/favorites.vue b/packages/frontend/src/pages/favorites.vue
index 0bbed411b1..07dd768499 100644
--- a/packages/frontend/src/pages/favorites.vue
+++ b/packages/frontend/src/pages/favorites.vue
@@ -21,7 +21,6 @@
</template>
<script lang="ts" setup>
-import { ref } from 'vue';
import MkPagination from '@/components/MkPagination.vue';
import XNote from '@/components/MkNote.vue';
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index 734c467e3b..2b7fcf74e1 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -24,10 +24,9 @@
</template>
<script lang="ts" setup>
-import { computed, onDeactivated, onUnmounted, Ref, ref, watch } from 'vue';
+import { computed } from 'vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os';
-import { url } from '@/config';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
import MkTextarea from '@/components/MkTextarea.vue';
@@ -174,6 +173,119 @@ var cursor = 0
do()
`;
+const PRESET_QUIZ = `/// @ 0.12.4
+let title = '地理クイズ'
+
+let qas = [{
+ q: 'オーストラリアの首都は?'
+ choices: ['シドニー' 'キャンベラ' 'メルボルン']
+ a: 'キャンベラ'
+ aDescription: '最大の都市はシドニーですが首都はキャンベラです。'
+} {
+ q: '国土面積2番目の国は?'
+ choices: ['カナダ' 'アメリカ' '中国']
+ a: 'カナダ'
+ aDescription: '大きい順にロシア、カナダ、アメリカ、中国です。'
+} {
+ q: '二重内陸国ではないのは?'
+ choices: ['リヒテンシュタイン' 'ウズベキスタン' 'レソト']
+ a: 'レソト'
+ aDescription: 'レソトは(一重)内陸国です。'
+} {
+ q: '閘門がない運河は?'
+ choices: ['キール運河' 'スエズ運河' 'パナマ運河']
+ a: 'スエズ運河'
+ aDescription: 'スエズ運河は高低差がないので閘門はありません。'
+}]
+
+let qaEls = [Ui:C:container({
+ align: 'center'
+ children: [
+ Ui:C:text({
+ size: 1.5
+ bold: true
+ text: title
+ })
+ ]
+})]
+
+var qn = 0
+each (let qa, qas) {
+ qn += 1
+ qa.id = Util:uuid()
+ qaEls.push(Ui:C:container({
+ align: 'center'
+ bgColor: '#000'
+ fgColor: '#fff'
+ padding: 16
+ rounded: true
+ children: [
+ Ui:C:text({
+ text: \`Q{qn} {qa.q}\`
+ })
+ Ui:C:select({
+ items: qa.choices.map(@(c) {{ text: c, value: c }})
+ onChange: @(v) { qa.userAnswer = v }
+ })
+ Ui:C:container({
+ children: []
+ } \`{qa.id}:a\`)
+ ]
+ } qa.id))
+}
+
+@finish() {
+ var score = 0
+
+ each (let qa, qas) {
+ let correct = qa.userAnswer == qa.a
+ if (correct) score += 1
+ let el = Ui:get(\`{qa.id}:a\`)
+ el.update({
+ children: [
+ Ui:C:text({
+ size: 1.2
+ bold: true
+ color: if (correct) '#f00' else '#00f'
+ text: if (correct) '🎉正解' else '不正解'
+ })
+ Ui:C:text({
+ text: qa.aDescription
+ })
+ ]
+ })
+ }
+
+ let result = \`{title}の結果は{qas.len}問中{score}問正解でした。\`
+ Ui:get('footer').update({
+ children: [
+ Ui:C:postFormButton({
+ text: '結果を共有'
+ rounded: true
+ primary: true
+ form: {
+ text: \`{result}{Str:lf}{THIS_URL}\`
+ }
+ })
+ ]
+ })
+}
+
+qaEls.push(Ui:C:container({
+ align: 'center'
+ children: [
+ Ui:C:button({
+ text: '答え合わせ'
+ primary: true
+ rounded: true
+ onClick: finish
+ })
+ ]
+} 'footer'))
+
+Ui:render(qaEls)
+`;
+
const PRESET_TIMELINE = `/// @ 0.12.4
// APIリクエストを行いローカルタイムラインを表示するプリセット
@@ -260,6 +372,11 @@ function selectPreset(ev: MouseEvent) {
script = PRESET_SHUFFLE;
},
}, {
+ text: 'Quiz',
+ action: () => {
+ script = PRESET_QUIZ;
+ },
+ }, {
text: 'Timeline viewer',
action: () => {
script = PRESET_TIMELINE;
diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue
index a3a48d3b97..f1dca5f240 100644
--- a/packages/frontend/src/pages/flash/flash-index.vue
+++ b/packages/frontend/src/pages/flash/flash-index.vue
@@ -33,7 +33,7 @@
</template>
<script lang="ts" setup>
-import { computed, inject } from 'vue';
+import { computed } from 'vue';
import MkFlashPreview from '@/components/MkFlashPreview.vue';
import MkPagination from '@/components/MkPagination.vue';
import MkButton from '@/components/MkButton.vue';
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index c82559d55a..3528e7e145 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -52,18 +52,14 @@
<script lang="ts" setup>
import { computed, onDeactivated, onUnmounted, Ref, ref, watch } from 'vue';
-import { Interpreter, Parser, utils, values } from '@syuilo/aiscript';
+import { Interpreter, Parser, values } from '@syuilo/aiscript';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os';
import { url } from '@/config';
-import MkFollowButton from '@/components/MkFollowButton.vue';
-import MkContainer from '@/components/MkContainer.vue';
-import MkPagination from '@/components/MkPagination.vue';
-import MkPagePreview from '@/components/MkPagePreview.vue';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
import MkAsUi from '@/components/MkAsUi.vue';
-import { AsUiComponent, AsUiRoot, patch, registerAsUiLib, render } from '@/scripts/aiscript/ui';
+import { AsUiComponent, AsUiRoot, registerAsUiLib } from '@/scripts/aiscript/ui';
import { createAiScriptEnv } from '@/scripts/aiscript/api';
import MkFolder from '@/components/MkFolder.vue';
import MkTextarea from '@/components/MkTextarea.vue';
diff --git a/packages/frontend/src/pages/gallery/edit.vue b/packages/frontend/src/pages/gallery/edit.vue
index f47632c85f..1fae7686e5 100644
--- a/packages/frontend/src/pages/gallery/edit.vue
+++ b/packages/frontend/src/pages/gallery/edit.vue
@@ -31,7 +31,7 @@
</template>
<script lang="ts" setup>
-import { computed, inject, watch } from 'vue';
+import { computed, watch } from 'vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
diff --git a/packages/frontend/src/pages/gallery/index.vue b/packages/frontend/src/pages/gallery/index.vue
index 1001c3b2e7..de8f448da1 100644
--- a/packages/frontend/src/pages/gallery/index.vue
+++ b/packages/frontend/src/pages/gallery/index.vue
@@ -42,16 +42,10 @@
</template>
<script lang="ts" setup>
-import { computed, defineComponent, watch } from 'vue';
-import XUserList from '@/components/MkUserList.vue';
+import { watch } from 'vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
-import MkInput from '@/components/MkInput.vue';
-import MkButton from '@/components/MkButton.vue';
-import MkTab from '@/components/MkTab.vue';
import MkPagination from '@/components/MkPagination.vue';
import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue';
-import number from '@/filters/number';
-import * as os from '@/os';
import { definePageMetadata } from '@/scripts/page-metadata';
import { i18n } from '@/i18n';
import { useRouter } from '@/router';
diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue
index 31e2727bb5..4bf7c8c514 100644
--- a/packages/frontend/src/pages/gallery/post.vue
+++ b/packages/frontend/src/pages/gallery/post.vue
@@ -47,7 +47,7 @@
</MkPagination>
</MkContainer>
</div>
- <MkError v-else-if="error" @retry="fetch()"/>
+ <MkError v-else-if="error" @retry="fetchPost()"/>
<MkLoading v-else/>
</Transition>
</div>
@@ -56,11 +56,10 @@
</template>
<script lang="ts" setup>
-import { computed, defineComponent, inject, watch } from 'vue';
+import { computed, watch } from 'vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os';
import MkContainer from '@/components/MkContainer.vue';
-import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
import MkPagination from '@/components/MkPagination.vue';
import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue';
import MkFollowButton from '@/components/MkFollowButton.vue';
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index 714f95add9..ba5fda137a 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -124,7 +124,6 @@ import MkSelect from '@/components/MkSelect.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import * as os from '@/os';
import number from '@/filters/number';
-import bytes from '@/filters/bytes';
import { iAmModerator } from '@/account';
import { definePageMetadata } from '@/scripts/page-metadata';
import { i18n } from '@/i18n';
diff --git a/packages/frontend/src/pages/messaging/index.vue b/packages/frontend/src/pages/messaging/index.vue
deleted file mode 100644
index 3d11cf13e9..0000000000
--- a/packages/frontend/src/pages/messaging/index.vue
+++ /dev/null
@@ -1,305 +0,0 @@
-<template>
-<MkStickyContainer>
- <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
- <MkSpacer :content-max="800">
- <div class="yweeujhr">
- <MkButton primary class="start" @click="start"><i class="ti ti-plus"></i> {{ $ts.startMessaging }}</MkButton>
-
- <div v-if="messages.length > 0" class="history">
- <MkA
- v-for="(message, i) in messages"
- :key="message.id"
- v-anim="i"
- class="message _panel"
- :class="{ isMe: isMe(message), isRead: message.groupId ? message.reads.includes($i.id) : message.isRead }"
- :to="message.groupId ? `/my/messaging/group/${message.groupId}` : `/my/messaging/${getAcct(isMe(message) ? message.recipient : message.user)}`"
- :data-index="i"
- >
- <div>
- <MkAvatar class="avatar" :user="message.groupId ? message.user : isMe(message) ? message.recipient : message.user" indicator link preview/>
- <header v-if="message.groupId">
- <span class="name">{{ message.group.name }}</span>
- <MkTime :time="message.createdAt" class="time"/>
- </header>
- <header v-else>
- <span class="name"><MkUserName :user="isMe(message) ? message.recipient : message.user"/></span>
- <span class="username">@{{ acct(isMe(message) ? message.recipient : message.user) }}</span>
- <MkTime :time="message.createdAt" class="time"/>
- </header>
- <div class="body">
- <p class="text"><span v-if="isMe(message)" class="me">{{ $ts.you }}:</span>{{ message.text }}</p>
- </div>
- </div>
- </MkA>
- </div>
- <div v-if="!fetching && messages.length == 0" class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
- <div>{{ $ts.noHistory }}</div>
- </div>
- <MkLoading v-if="fetching"/>
- </div>
- </MkSpacer>
-</MkStickyContainer>
-</template>
-
-<script lang="ts" setup>
-import { defineAsyncComponent, defineComponent, inject, markRaw, onMounted, onUnmounted } from 'vue';
-import * as Acct from 'misskey-js/built/acct';
-import MkButton from '@/components/MkButton.vue';
-import { acct } from '@/filters/user';
-import * as os from '@/os';
-import { stream } from '@/stream';
-import { useRouter } from '@/router';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { $i } from '@/account';
-
-const router = useRouter();
-
-let fetching = $ref(true);
-let moreFetching = $ref(false);
-let messages = $ref([]);
-let connection = $ref(null);
-
-const getAcct = Acct.toString;
-
-function isMe(message) {
- return message.userId === $i.id;
-}
-
-function onMessage(message) {
- if (message.recipientId) {
- messages = messages.filter(m => !(
- (m.recipientId === message.recipientId && m.userId === message.userId) ||
- (m.recipientId === message.userId && m.userId === message.recipientId)));
-
- messages.unshift(message);
- } else if (message.groupId) {
- messages = messages.filter(m => m.groupId !== message.groupId);
- messages.unshift(message);
- }
-}
-
-function onRead(ids) {
- for (const id of ids) {
- const found = messages.find(m => m.id === id);
- if (found) {
- if (found.recipientId) {
- found.isRead = true;
- } else if (found.groupId) {
- found.reads.push($i.id);
- }
- }
- }
-}
-
-function start(ev) {
- os.popupMenu([{
- text: i18n.ts.messagingWithUser,
- icon: 'ti ti-user',
- action: () => { startUser(); },
- }, {
- text: i18n.ts.messagingWithGroup,
- icon: 'ti ti-users',
- action: () => { startGroup(); },
- }], ev.currentTarget ?? ev.target);
-}
-
-async function startUser() {
- os.selectUser().then(user => {
- router.push(`/my/messaging/${Acct.toString(user)}`);
- });
-}
-
-async function startGroup() {
- const groups1 = await os.api('users/groups/owned');
- const groups2 = await os.api('users/groups/joined');
- if (groups1.length === 0 && groups2.length === 0) {
- os.alert({
- type: 'warning',
- title: i18n.ts.youHaveNoGroups,
- text: i18n.ts.joinOrCreateGroup,
- });
- return;
- }
- const { canceled, result: group } = await os.select({
- title: i18n.ts.group,
- items: groups1.concat(groups2).map(group => ({
- value: group, text: group.name,
- })),
- });
- if (canceled) return;
- router.push(`/my/messaging/group/${group.id}`);
-}
-
-onMounted(() => {
- connection = markRaw(stream.useChannel('messagingIndex'));
-
- connection.on('message', onMessage);
- connection.on('read', onRead);
-
- os.api('messaging/history', { group: false }).then(userMessages => {
- os.api('messaging/history', { group: true }).then(groupMessages => {
- const _messages = userMessages.concat(groupMessages);
- _messages.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
- messages = _messages;
- fetching = false;
- });
- });
-});
-
-onUnmounted(() => {
- if (connection) connection.dispose();
-});
-
-const headerActions = $computed(() => []);
-
-const headerTabs = $computed(() => []);
-
-definePageMetadata({
- title: i18n.ts.messaging,
- icon: 'ti ti-messages',
-});
-</script>
-
-<style lang="scss" scoped>
-.yweeujhr {
-
- > .start {
- margin: 0 auto var(--margin) auto;
- }
-
- > .history {
- > .message {
- display: block;
- text-decoration: none;
- margin-bottom: var(--margin);
-
- * {
- pointer-events: none;
- user-select: none;
- }
-
- &:hover {
- .avatar {
- filter: saturate(200%);
- }
- }
-
- &:active {
- }
-
- &.isRead,
- &.isMe {
- opacity: 0.8;
- }
-
- &:not(.isMe):not(.isRead) {
- > div {
- background-image: url("/client-assets/unread.svg");
- background-repeat: no-repeat;
- background-position: 0 center;
- }
- }
-
- &:after {
- content: "";
- display: block;
- clear: both;
- }
-
- > div {
- padding: 20px 30px;
-
- &:after {
- content: "";
- display: block;
- clear: both;
- }
-
- > header {
- display: flex;
- align-items: center;
- margin-bottom: 2px;
- white-space: nowrap;
- overflow: hidden;
-
- > .name {
- margin: 0;
- padding: 0;
- overflow: hidden;
- text-overflow: ellipsis;
- font-size: 1em;
- font-weight: bold;
- transition: all 0.1s ease;
- }
-
- > .username {
- margin: 0 8px;
- }
-
- > .time {
- margin: 0 0 0 auto;
- }
- }
-
- > .avatar {
- float: left;
- width: 54px;
- height: 54px;
- margin: 0 16px 0 0;
- border-radius: 8px;
- transition: all 0.1s ease;
- }
-
- > .body {
-
- > .text {
- display: block;
- margin: 0 0 0 0;
- padding: 0;
- overflow: hidden;
- overflow-wrap: break-word;
- font-size: 1.1em;
- color: var(--faceText);
-
- .me {
- opacity: 0.7;
- }
- }
-
- > .image {
- display: block;
- max-width: 100%;
- max-height: 512px;
- }
- }
- }
- }
- }
-}
-
-@container (max-width: 400px) {
- .yweeujhr {
- > .history {
- > .message {
- &:not(.isMe):not(.isRead) {
- > div {
- background-image: none;
- border-left: solid 4px #3aa2dc;
- }
- }
-
- > div {
- padding: 16px;
- font-size: 0.9em;
-
- > .avatar {
- margin: 0 12px 0 0;
- }
- }
- }
- }
- }
-}
-</style>
diff --git a/packages/frontend/src/pages/messaging/messaging-room.form.vue b/packages/frontend/src/pages/messaging/messaging-room.form.vue
deleted file mode 100644
index d6113668dd..0000000000
--- a/packages/frontend/src/pages/messaging/messaging-room.form.vue
+++ /dev/null
@@ -1,366 +0,0 @@
-<template>
-<div
- :class="$style['root']"
- @dragover.stop="onDragover"
- @drop.stop="onDrop"
->
- <textarea
- :class="$style['textarea']"
- class="_acrylic"
- ref="textEl"
- v-model="text"
- :placeholder="i18n.ts.inputMessageHere"
- @keydown="onKeydown"
- @compositionupdate="onCompositionUpdate"
- @paste="onPaste"
- ></textarea>
- <footer :class="$style['footer']">
- <div v-if="file" :class="$style['file']" @click="file = null">{{ file.name }}</div>
- <div :class="$style['buttons']">
- <button class="_button" :class="$style['button']" @click="chooseFile"><i class="ti ti-photo-plus"></i></button>
- <button class="_button" :class="$style['button']" @click="insertEmoji"><i class="ti ti-mood-happy"></i></button>
- <button class="_button" :class="[$style['button'], $style['send']]" :disabled="!canSend || sending" :title="i18n.ts.send" @click="send">
- <template v-if="!sending"><i class="ti ti-send"></i></template><template v-if="sending"><MkLoading :em="true"/></template>
- </button>
- </div>
- </footer>
- <input :class="$style['file-input']" ref="fileEl" type="file" @change="onChangeFile"/>
-</div>
-</template>
-
-<script lang="ts" setup>
-import { onMounted, watch } from 'vue';
-import * as Misskey from 'misskey-js';
-import autosize from 'autosize';
-//import insertTextAtCursor from 'insert-text-at-cursor';
-import { throttle } from 'throttle-debounce';
-import { formatTimeString } from '@/scripts/format-time-string';
-import { selectFile } from '@/scripts/select-file';
-import * as os from '@/os';
-import { stream } from '@/stream';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
-//import { Autocomplete } from '@/scripts/autocomplete';
-import { uploadFile } from '@/scripts/upload';
-import { miLocalStorage } from '@/local-storage';
-
-const props = defineProps<{
- user?: Misskey.entities.UserDetailed | null;
- group?: Misskey.entities.UserGroup | null;
-}>();
-
-let textEl = $shallowRef<HTMLTextAreaElement>();
-let fileEl = $shallowRef<HTMLInputElement>();
-
-let text = $ref<string>('');
-let file = $ref<Misskey.entities.DriveFile | null>(null);
-let sending = $ref(false);
-const typing = throttle(3000, () => {
- stream.send('typingOnMessaging', props.user ? { partner: props.user.id } : { group: props.group?.id });
-});
-
-let draftKey = $computed(() => props.user ? 'user:' + props.user.id : 'group:' + props.group?.id);
-let canSend = $computed(() => (text != null && text !== '') || file != null);
-
-watch([$$(text), $$(file)], saveDraft);
-
-async function onPaste(ev: ClipboardEvent) {
- if (!ev.clipboardData) return;
-
- const clipboardData = ev.clipboardData;
- const items = clipboardData.items;
-
- if (items.length === 1) {
- if (items[0].kind === 'file') {
- const pastedFile = items[0].getAsFile();
- if (!pastedFile) return;
- const lio = pastedFile.name.lastIndexOf('.');
- const ext = lio >= 0 ? pastedFile.name.slice(lio) : '';
- const formatted = formatTimeString(new Date(pastedFile.lastModified), defaultStore.state.pastedFileName).replace(/{{number}}/g, '1') + ext;
- if (formatted) upload(pastedFile, formatted);
- }
- } else {
- if (items[0].kind === 'file') {
- os.alert({
- type: 'error',
- text: i18n.ts.onlyOneFileCanBeAttached,
- });
- }
- }
-}
-
-function onDragover(ev: DragEvent) {
- if (!ev.dataTransfer) return;
-
- const isFile = ev.dataTransfer.items[0].kind === 'file';
- const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
- if (isFile || isDriveFile) {
- ev.preventDefault();
- switch (ev.dataTransfer.effectAllowed) {
- case 'all':
- case 'uninitialized':
- case 'copy':
- case 'copyLink':
- case 'copyMove':
- ev.dataTransfer.dropEffect = 'copy';
- break;
- case 'linkMove':
- case 'move':
- ev.dataTransfer.dropEffect = 'move';
- break;
- default:
- ev.dataTransfer.dropEffect = 'none';
- break;
- }
- }
-}
-
-function onDrop(ev: DragEvent): void {
- if (!ev.dataTransfer) return;
-
- // ファイルだったら
- if (ev.dataTransfer.files.length === 1) {
- ev.preventDefault();
- upload(ev.dataTransfer.files[0]);
- return;
- } else if (ev.dataTransfer.files.length > 1) {
- ev.preventDefault();
- os.alert({
- type: 'error',
- text: i18n.ts.onlyOneFileCanBeAttached,
- });
- return;
- }
-
- //#region ドライブのファイル
- const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
- if (driveFile != null && driveFile !== '') {
- file = JSON.parse(driveFile);
- ev.preventDefault();
- }
- //#endregion
-}
-
-function onKeydown(ev: KeyboardEvent) {
- typing();
- if ((ev.key === 'Enter') && (ev.ctrlKey || ev.metaKey) && canSend) {
- send();
- }
-}
-
-function onCompositionUpdate() {
- typing();
-}
-
-function chooseFile(ev: MouseEvent) {
- selectFile(ev.currentTarget ?? ev.target, i18n.ts.selectFile).then(selectedFile => {
- file = selectedFile;
- });
-}
-
-function onChangeFile() {
- if (fileEl.files![0]) upload(fileEl.files[0]);
-}
-
-function upload(fileToUpload: File, name?: string) {
- uploadFile(fileToUpload, defaultStore.state.uploadFolder, name).then(res => {
- file = res;
- });
-}
-
-function send() {
- sending = true;
- os.api('messaging/messages/create', {
- userId: props.user ? props.user.id : undefined,
- groupId: props.group ? props.group.id : undefined,
- text: text ? text : undefined,
- fileId: file ? file.id : undefined,
- }).then(message => {
- clear();
- }).catch(err => {
- console.error(err);
- }).then(() => {
- sending = false;
- });
-}
-
-function clear() {
- text = '';
- file = null;
- deleteDraft();
-}
-
-function saveDraft() {
- const drafts = JSON.parse(miLocalStorage.getItem('message_drafts') || '{}');
-
- drafts[draftKey] = {
- updatedAt: new Date(),
- // eslint-disable-next-line id-denylist
- data: {
- text: text,
- file: file,
- },
- };
-
- miLocalStorage.setItem('message_drafts', JSON.stringify(drafts));
-}
-
-function deleteDraft() {
- const drafts = JSON.parse(miLocalStorage.getItem('message_drafts') || '{}');
-
- delete drafts[draftKey];
-
- miLocalStorage.setItem('message_drafts', JSON.stringify(drafts));
-}
-
-async function insertEmoji(ev: MouseEvent) {
- os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textEl);
-}
-
-onMounted(() => {
- autosize(textEl);
-
- // TODO: detach when unmount
- // TODO
- //new Autocomplete(textEl, this, { model: 'text' });
-
- // 書きかけの投稿を復元
- const draft = JSON.parse(miLocalStorage.getItem('message_drafts') || '{}')[draftKey];
- if (draft) {
- text = draft.data.text;
- file = draft.data.file;
- }
-});
-
-defineExpose({
- file,
- upload,
-});
-</script>
-
-<style lang="scss" module>
-.root {
- position: relative;
-}
-
-.textarea {
- cursor: auto;
- display: block;
- width: 100%;
- min-width: 100%;
- max-width: 100%;
- min-height: 80px;
- margin: 0;
- padding: 16px 16px 0 16px;
- resize: none;
- font-size: 1em;
- font-family: inherit;
- outline: none;
- border: none;
- border-radius: 0;
- box-shadow: none;
- box-sizing: border-box;
- color: var(--fg);
-}
-
-.footer {
- position: sticky;
- bottom: 0;
- background: var(--panel);
-}
-
-.file {
- padding: 8px;
- color: var(--fg);
- background: transparent;
- cursor: pointer;
-}
-/*
-.files {
- display: block;
- margin: 0;
- padding: 0 8px;
- list-style: none;
-
- &:after {
- content: '';
- display: block;
- clear: both;
- }
-
- > li {
- display: block;
- float: left;
- margin: 4px;
- padding: 0;
- width: 64px;
- height: 64px;
- background-color: #eee;
- background-repeat: no-repeat;
- background-position: center center;
- background-size: cover;
- cursor: move;
-
- &:hover {
- > .remove {
- display: block;
- }
- }
- }
-}
-
-.file-remove {
- display: none;
- position: absolute;
- right: -6px;
- top: -6px;
- margin: 0;
- padding: 0;
- background: transparent;
- outline: none;
- border: none;
- border-radius: 0;
- box-shadow: none;
- cursor: pointer;
-}
-*/
-
-.buttons {
- display: flex;
-}
-
-.button {
- margin: 0;
- padding: 16px;
- font-size: 1em;
- font-weight: normal;
- text-decoration: none;
- transition: color 0.1s ease;
-
- &:hover {
- color: var(--accent);
- }
-
- &:active {
- color: var(--accentDarken);
- transition: color 0s ease;
- }
-}
-.send {
- margin-left: auto;
- color: var(--accent);
-
- &:hover {
- color: var(--accentLighten);
- }
-
- &:active {
- color: var(--accentDarken);
- transition: color 0s ease;
- }
-}
-
-.file-input {
- display: none;
-}
-</style>
diff --git a/packages/frontend/src/pages/messaging/messaging-room.message.vue b/packages/frontend/src/pages/messaging/messaging-room.message.vue
deleted file mode 100644
index d10798b92e..0000000000
--- a/packages/frontend/src/pages/messaging/messaging-room.message.vue
+++ /dev/null
@@ -1,338 +0,0 @@
-<template>
-<div class="thvuemwp" :class="{ isMe }">
- <MkAvatar class="avatar" :user="message.user" indicator link preview/>
- <div class="content">
- <div class="balloon" :class="{ noText: message.text == null }">
- <button v-if="isMe" class="delete-button" :title="$ts.delete" @click="del">
- <img src="/client-assets/remove.png" alt="Delete"/>
- </button>
- <div v-if="!message.isDeleted" class="content">
- <Mfm v-if="message.text" ref="text" class="text" :text="message.text" :i="$i"/>
- <div v-if="message.file" class="file">
- <a :href="message.file.url" rel="noopener" target="_blank" :title="message.file.name">
- <img v-if="message.file.type.split('/')[0] == 'image'" :src="message.file.url" :alt="message.file.name"/>
- <p v-else>{{ message.file.name }}</p>
- </a>
- </div>
- </div>
- <div v-else class="content">
- <p class="is-deleted">{{ $ts.deleted }}</p>
- </div>
- </div>
- <div></div>
- <MkUrlPreview v-for="url in urls" :key="url" :url="url" style="margin: 8px 0;"/>
- <footer>
- <template v-if="isGroup">
- <span v-if="message.reads.length > 0" class="read">{{ $ts.messageRead }} {{ message.reads.length }}</span>
- </template>
- <template v-else>
- <span v-if="isMe && message.isRead" class="read">{{ $ts.messageRead }}</span>
- </template>
- <MkTime :time="message.createdAt"/>
- <template v-if="message.is_edited"><i class="ti ti-pencil"></i></template>
- </footer>
- </div>
-</div>
-</template>
-
-<script lang="ts" setup>
-import { } from 'vue';
-import * as mfm from 'mfm-js';
-import * as Misskey from 'misskey-js';
-import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
-import MkUrlPreview from '@/components/MkUrlPreview.vue';
-import * as os from '@/os';
-import { $i } from '@/account';
-
-const props = defineProps<{
- message: Misskey.entities.MessagingMessage;
- isGroup?: boolean;
-}>();
-
-const isMe = $computed(() => props.message.userId === $i?.id);
-const urls = $computed(() => props.message.text ? extractUrlFromMfm(mfm.parse(props.message.text)) : []);
-
-function del(): void {
- os.api('messaging/messages/delete', {
- messageId: props.message.id,
- });
-}
-</script>
-
-<style lang="scss" scoped>
-.thvuemwp {
- $me-balloon-color: var(--accent);
-
- position: relative;
- background-color: transparent;
- display: flex;
-
- > .avatar {
- position: sticky;
- top: calc(var(--stickyTop, 0px) + 16px);
- display: block;
- width: 54px;
- height: 54px;
- transition: all 0.1s ease;
- }
-
- > .content {
- min-width: 0;
-
- > .balloon {
- position: relative;
- display: inline-flex;
- align-items: center;
- padding: 0;
- min-height: 38px;
- border-radius: 16px;
- max-width: 100%;
-
- &:before {
- content: "";
- pointer-events: none;
- display: block;
- position: absolute;
- top: 12px;
- }
-
- & + * {
- clear: both;
- }
-
- &:hover {
- > .delete-button {
- display: block;
- }
- }
-
- > .delete-button {
- display: none;
- position: absolute;
- z-index: 1;
- top: -4px;
- right: -4px;
- margin: 0;
- padding: 0;
- cursor: pointer;
- outline: none;
- border: none;
- border-radius: 0;
- box-shadow: none;
- background: transparent;
-
- > img {
- vertical-align: bottom;
- width: 16px;
- height: 16px;
- cursor: pointer;
- }
- }
-
- > .content {
- max-width: 100%;
-
- > .is-deleted {
- display: block;
- margin: 0;
- padding: 0;
- overflow: hidden;
- overflow-wrap: break-word;
- font-size: 1em;
- color: rgba(#000, 0.5);
- }
-
- > .text {
- display: block;
- margin: 0;
- padding: 12px 18px;
- overflow: hidden;
- overflow-wrap: break-word;
- word-break: break-word;
- font-size: 1em;
- color: rgba(#000, 0.8);
-
- & + .file {
- > a {
- border-radius: 0 0 16px 16px;
- }
- }
- }
-
- > .file {
- > a {
- display: block;
- max-width: 100%;
- border-radius: 16px;
- overflow: hidden;
- text-decoration: none;
-
- &:hover {
- text-decoration: none;
-
- > p {
- background: #ccc;
- }
- }
-
- > * {
- display: block;
- margin: 0;
- width: 100%;
- max-height: 512px;
- object-fit: contain;
- box-sizing: border-box;
- }
-
- > p {
- padding: 30px;
- text-align: center;
- color: #555;
- background: #ddd;
- }
- }
- }
- }
- }
-
- > footer {
- display: block;
- margin: 2px 0 0 0;
- font-size: 0.65em;
-
- > .read {
- margin: 0 8px;
- }
-
- > i {
- margin-left: 4px;
- }
- }
- }
-
- &:not(.isMe) {
- padding-left: var(--margin);
-
- > .content {
- padding-left: 16px;
- padding-right: 32px;
-
- > .balloon {
- $color: var(--messageBg);
- background: $color;
-
- &.noText {
- background: transparent;
- }
-
- &:not(.noText):before {
- left: -14px;
- border-top: solid 8px transparent;
- border-right: solid 8px $color;
- border-bottom: solid 8px transparent;
- border-left: solid 8px transparent;
- }
-
- > .content {
- > .text {
- color: var(--fg);
- }
- }
- }
-
- > footer {
- text-align: left;
- }
- }
- }
-
- &.isMe {
- flex-direction: row-reverse;
- padding-right: var(--margin);
- right: var(--margin); // 削除時にposition: absoluteになったときに使う
-
- > .content {
- padding-right: 16px;
- padding-left: 32px;
- text-align: right;
-
- > .balloon {
- background: $me-balloon-color;
- text-align: left;
-
- ::selection {
- color: var(--accent);
- background-color: #fff;
- }
-
- &.noText {
- background: transparent;
- }
-
- &:not(.noText):before {
- right: -14px;
- left: auto;
- border-top: solid 8px transparent;
- border-right: solid 8px transparent;
- border-bottom: solid 8px transparent;
- border-left: solid 8px $me-balloon-color;
- }
-
- > .content {
-
- > p.is-deleted {
- color: rgba(#fff, 0.5);
- }
-
- > .text {
- &, ::v-deep(*) {
- color: var(--fgOnAccent) !important;
- }
- }
- }
- }
-
- > footer {
- text-align: right;
-
- > .read {
- user-select: none;
- }
- }
- }
- }
-}
-
-@container (max-width: 400px) {
- .thvuemwp {
- > .avatar {
- width: 48px;
- height: 48px;
- }
-
- > .content {
- > .balloon {
- > .content {
- > .text {
- font-size: 0.9em;
- }
- }
- }
- }
- }
-}
-
-@container (max-width: 500px) {
- .thvuemwp {
- > .content {
- > .balloon {
- > .content {
- > .text {
- padding: 8px 16px;
- }
- }
- }
- }
- }
-}
-</style>
diff --git a/packages/frontend/src/pages/messaging/messaging-room.vue b/packages/frontend/src/pages/messaging/messaging-room.vue
deleted file mode 100644
index 0867f003a3..0000000000
--- a/packages/frontend/src/pages/messaging/messaging-room.vue
+++ /dev/null
@@ -1,415 +0,0 @@
-<template>
-<MkStickyContainer>
-<template #header>
- <MkPageHeader />
-</template>
-<div
- ref="rootEl"
- :class="$style['root']"
- @dragover.prevent.stop="onDragover"
- @drop.prevent.stop="onDrop"
->
- <div :class="$style['body']">
- <MkPagination v-if="pagination" ref="pagingComponent" :key="userAcct || groupId" :pagination="pagination">
- <template #empty>
- <div class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
- <div>{{ i18n.ts.noMessagesYet }}</div>
- </div>
- </template>
- <template #default="{ items: messages, fetching: pFetching }">
- <MkDateSeparatedList
- v-if="messages.length > 0"
- v-slot="{ item: message }"
- :class="{ [$style['messages']]: true, 'deny-move-transition': pFetching }"
- :items="messages"
- direction="up"
- reversed
- >
- <XMessage :key="message.id" :message="message" :is-group="group != null"/>
- </MkDateSeparatedList>
- </template>
- </MkPagination>
- </div>
- <footer :class="$style['footer']">
- <div v-if="typers.length > 0" :class="$style['typers']">
- <I18n :src="i18n.ts.typingUsers" text-tag="span">
- <template #users>
- <b v-for="typer in typers" :key="typer.id" :class="$style['user']">{{ typer.username }}</b>
- </template>
- </I18n>
- <MkEllipsis/>
- </div>
- <Transition :name="animation ? 'fade' : ''">
- <div v-show="showIndicator" :class="$style['new-message']">
- <button class="_buttonPrimary" @click="onIndicatorClick" :class="$style['new-message-button']">
- <i class="fas ti-fw fa-arrow-circle-down" :class="$style['new-message-icon']"></i>{{ i18n.ts.newMessageExists }}
- </button>
- </div>
- </Transition>
- <XForm v-if="!fetching" ref="formEl" :user="user" :group="group" :class="$style['form']"/>
- </footer>
-</div>
-</MkStickyContainer>
-</template>
-
-<script lang="ts" setup>
-import { computed, watch, onMounted, nextTick, onBeforeUnmount } from 'vue';
-import * as Misskey from 'misskey-js';
-import * as Acct from 'misskey-js/built/acct';
-import XMessage from './messaging-room.message.vue';
-import XForm from './messaging-room.form.vue';
-import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
-import { isBottomVisible, onScrollBottom, scrollToBottom } from '@/scripts/scroll';
-import * as os from '@/os';
-import { stream } from '@/stream';
-import * as sound from '@/scripts/sound';
-import { i18n } from '@/i18n';
-import { $i } from '@/account';
-import { defaultStore } from '@/store';
-import { definePageMetadata } from '@/scripts/page-metadata';
-
-const props = defineProps<{
- userAcct?: string;
- groupId?: string;
-}>();
-
-let rootEl = $shallowRef<HTMLDivElement>();
-let formEl = $shallowRef<InstanceType<typeof XForm>>();
-let pagingComponent = $shallowRef<InstanceType<typeof MkPagination>>();
-
-let fetching = $ref(true);
-let user: Misskey.entities.UserDetailed | null = $ref(null);
-let group: Misskey.entities.UserGroup | null = $ref(null);
-let typers: Misskey.entities.User[] = $ref([]);
-let connection: Misskey.ChannelConnection<Misskey.Channels['messaging']> | null = $ref(null);
-let showIndicator = $ref(false);
-const {
- animation,
-} = defaultStore.reactiveState;
-
-let pagination: Paging | null = $ref(null);
-
-watch([() => props.userAcct, () => props.groupId], () => {
- if (connection) connection.dispose();
- fetch();
-});
-
-async function fetch() {
- fetching = true;
-
- if (props.userAcct) {
- const acct = Acct.parse(props.userAcct);
- user = await os.api('users/show', { username: acct.username, host: acct.host || undefined });
- group = null;
-
- pagination = {
- endpoint: 'messaging/messages',
- limit: 20,
- params: {
- userId: user.id,
- },
- reversed: true,
- pageEl: $$(rootEl).value,
- };
- connection = stream.useChannel('messaging', {
- otherparty: user.id,
- });
- } else {
- user = null;
- group = await os.api('users/groups/show', { groupId: props.groupId });
-
- pagination = {
- endpoint: 'messaging/messages',
- limit: 20,
- params: {
- groupId: group?.id,
- },
- reversed: true,
- pageEl: $$(rootEl).value,
- };
- connection = stream.useChannel('messaging', {
- group: group?.id,
- });
- }
-
- connection.on('message', onMessage);
- connection.on('read', onRead);
- connection.on('deleted', onDeleted);
- connection.on('typers', _typers => {
- typers = _typers.filter(u => u.id !== $i?.id);
- });
-
- document.addEventListener('visibilitychange', onVisibilitychange);
-
- nextTick(() => {
- pagingComponent.inited.then(() => {
- thisScrollToBottom();
- });
- window.setTimeout(() => {
- fetching = false;
- }, 300);
- });
-}
-
-function onDragover(ev: DragEvent) {
- if (!ev.dataTransfer) return;
-
- const isFile = ev.dataTransfer.items[0].kind === 'file';
- const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
-
- if (isFile || isDriveFile) {
- switch (ev.dataTransfer.effectAllowed) {
- case 'all':
- case 'uninitialized':
- case 'copy':
- case 'copyLink':
- case 'copyMove':
- ev.dataTransfer.dropEffect = 'copy';
- break;
- case 'linkMove':
- case 'move':
- ev.dataTransfer.dropEffect = 'move';
- break;
- default:
- ev.dataTransfer.dropEffect = 'none';
- break;
- }
- } else {
- ev.dataTransfer.dropEffect = 'none';
- }
-}
-
-function onDrop(ev: DragEvent): void {
- if (!ev.dataTransfer) return;
-
- // ファイルだったら
- if (ev.dataTransfer.files.length === 1) {
- formEl.upload(ev.dataTransfer.files[0]);
- return;
- } else if (ev.dataTransfer.files.length > 1) {
- os.alert({
- type: 'error',
- text: i18n.ts.onlyOneFileCanBeAttached,
- });
- return;
- }
-
- //#region ドライブのファイル
- const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
- if (driveFile != null && driveFile !== '') {
- const file = JSON.parse(driveFile);
- formEl.file = file;
- }
- //#endregion
-}
-
-function onMessage(message) {
- sound.play('chat');
-
- const _isBottom = isBottomVisible(rootEl, 64);
-
- pagingComponent.prepend(message);
- if (message.userId !== $i?.id && !document.hidden) {
- connection?.send('read', {
- id: message.id,
- });
- }
-
- if (_isBottom) {
- // Scroll to bottom
- nextTick(() => {
- thisScrollToBottom();
- });
- } else if (message.userId !== $i?.id) {
- // Notify
- notifyNewMessage();
- }
-}
-
-function onRead(x) {
- if (user) {
- if (!Array.isArray(x)) x = [x];
- for (const id of x) {
- if (pagingComponent.items.some(y => y.id === id)) {
- const exist = pagingComponent.items.map(y => y.id).indexOf(id);
- pagingComponent.items[exist] = {
- ...pagingComponent.items[exist],
- isRead: true,
- };
- }
- }
- } else if (group) {
- for (const id of x.ids) {
- if (pagingComponent.items.some(y => y.id === id)) {
- const exist = pagingComponent.items.map(y => y.id).indexOf(id);
- pagingComponent.items[exist] = {
- ...pagingComponent.items[exist],
- reads: [...pagingComponent.items[exist].reads, x.userId],
- };
- }
- }
- }
-}
-
-function onDeleted(id) {
- const msg = pagingComponent.items.find(m => m.id === id);
- if (msg) {
- pagingComponent.items = pagingComponent.items.filter(m => m.id !== msg.id);
- }
-}
-
-function thisScrollToBottom() {
- scrollToBottom($$(rootEl).value, { behavior: 'smooth' });
-}
-
-function onIndicatorClick() {
- showIndicator = false;
- thisScrollToBottom();
-}
-
-let scrollRemove: (() => void) | null = $ref(null);
-
-function notifyNewMessage() {
- showIndicator = true;
-
- scrollRemove = onScrollBottom(rootEl, () => {
- showIndicator = false;
- scrollRemove = null;
- });
-}
-
-function onVisibilitychange() {
- if (document.hidden) return;
- for (const message of pagingComponent.items) {
- if (message.userId !== $i?.id && !message.isRead) {
- connection?.send('read', {
- id: message.id,
- });
- }
- }
-}
-
-onMounted(() => {
- fetch();
-});
-
-onBeforeUnmount(() => {
- connection?.dispose();
- document.removeEventListener('visibilitychange', onVisibilitychange);
- if (scrollRemove) scrollRemove();
-});
-
-definePageMetadata(computed(() => !fetching ? user ? {
- userName: user,
- avatar: user,
-} : {
- title: group?.name,
- icon: 'ti ti-users',
-} : null));
-</script>
-
-<style lang="scss" module>
-.root {
- display: content;
-}
-
-.body {
- min-height: 80%;
-}
-
-.more {
- display: block;
- margin: 16px auto;
- padding: 0 12px;
- line-height: 24px;
- color: #fff;
- background: rgba(#000, 0.3);
- border-radius: 12px;
- &:hover {
- background: rgba(#000, 0.4);
- }
- &:active {
- background: rgba(#000, 0.5);
- }
- > i {
- margin-right: 4px;
- }
-}
-
-.fetching {
- cursor: wait;
-}
-
-.messages {
- padding: 16px 0 0;
-
- > * {
- margin-bottom: 16px;
- }
-}
-
-.footer {
- width: 100%;
- position: sticky;
- z-index: 2;
- padding-top: 8px;
- bottom: var(--minBottomSpacing);
-}
-
-.new-message {
- width: 100%;
- padding-bottom: 8px;
- text-align: center;
-}
-
-.new-message-button {
- display: inline-block;
- margin: 0;
- padding: 0 12px;
- line-height: 32px;
- font-size: 12px;
- border-radius: 16px;
-}
-
-.new-message-icon {
- display: inline-block;
- margin-right: 8px;
-}
-
-.typers {
- position: absolute;
- bottom: 100%;
- padding: 0 8px 0 8px;
- font-size: 0.9em;
- color: var(--fgTransparentWeak);
-}
-
-
-.user + .user:before {
- content: ", ";
- font-weight: normal;
-}
-
-.user:last-of-type:after {
- content: " ";
-}
-
-.form {
- max-height: 12em;
- overflow-y: scroll;
- border-top: solid 0.5px var(--divider);
- border-bottom-left-radius: 0;
- border-bottom-right-radius: 0;
-}
-
-.fade-enter-active, .fade-leave-active {
- transition: opacity 0.1s;
-}
-
-.fade-enter-from, .fade-leave-to {
- transition: opacity 0.5s;
- opacity: 0;
-}
-</style>
diff --git a/packages/frontend/src/pages/my-antennas/create.vue b/packages/frontend/src/pages/my-antennas/create.vue
index 005b036696..c35af3e22a 100644
--- a/packages/frontend/src/pages/my-antennas/create.vue
+++ b/packages/frontend/src/pages/my-antennas/create.vue
@@ -5,7 +5,6 @@
</template>
<script lang="ts" setup>
-import { inject } from 'vue';
import XAntenna from './editor.vue';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
@@ -17,7 +16,6 @@ let draft = $ref({
name: '',
src: 'all',
userListId: null,
- userGroupId: null,
users: [],
keywords: [],
excludeKeywords: [],
diff --git a/packages/frontend/src/pages/my-antennas/edit.vue b/packages/frontend/src/pages/my-antennas/edit.vue
index cb583faaeb..913fbde8e9 100644
--- a/packages/frontend/src/pages/my-antennas/edit.vue
+++ b/packages/frontend/src/pages/my-antennas/edit.vue
@@ -5,7 +5,6 @@
</template>
<script lang="ts" setup>
-import { inject, watch } from 'vue';
import XAntenna from './editor.vue';
import * as os from '@/os';
import { i18n } from '@/i18n';
diff --git a/packages/frontend/src/pages/my-antennas/editor.vue b/packages/frontend/src/pages/my-antennas/editor.vue
index 5c1eb66947..26b7bcc71b 100644
--- a/packages/frontend/src/pages/my-antennas/editor.vue
+++ b/packages/frontend/src/pages/my-antennas/editor.vue
@@ -11,16 +11,11 @@
<!--<option value="home">{{ i18n.ts._antennaSources.homeTimeline }}</option>-->
<option value="users">{{ i18n.ts._antennaSources.users }}</option>
<!--<option value="list">{{ i18n.ts._antennaSources.userList }}</option>-->
- <!--<option value="group">{{ i18n.ts._antennaSources.userGroup }}</option>-->
</MkSelect>
<MkSelect v-if="src === 'list'" v-model="userListId">
<template #label>{{ i18n.ts.userList }}</template>
<option v-for="list in userLists" :key="list.id" :value="list.id">{{ list.name }}</option>
</MkSelect>
- <MkSelect v-else-if="src === 'group'" v-model="userGroupId">
- <template #label>{{ i18n.ts.userGroup }}</template>
- <option v-for="group in userGroups" :key="group.id" :value="group.id">{{ group.name }}</option>
- </MkSelect>
<MkTextarea v-else-if="src === 'users'" v-model="users">
<template #label>{{ i18n.ts.users }}</template>
<template #caption>{{ i18n.ts.antennaUsersDescription }} <button class="_textButton" @click="addUser">{{ i18n.ts.addUser }}</button></template>
@@ -70,7 +65,6 @@ const emit = defineEmits<{
let name: string = $ref(props.antenna.name);
let src: string = $ref(props.antenna.src);
let userListId: any = $ref(props.antenna.userListId);
-let userGroupId: any = $ref(props.antenna.userGroupId);
let users: string = $ref(props.antenna.users.join('\n'));
let keywords: string = $ref(props.antenna.keywords.map(x => x.join(' ')).join('\n'));
let excludeKeywords: string = $ref(props.antenna.excludeKeywords.map(x => x.join(' ')).join('\n'));
@@ -79,19 +73,11 @@ let withReplies: boolean = $ref(props.antenna.withReplies);
let withFile: boolean = $ref(props.antenna.withFile);
let notify: boolean = $ref(props.antenna.notify);
let userLists: any = $ref(null);
-let userGroups: any = $ref(null);
watch(() => src, async () => {
if (src === 'list' && userLists === null) {
userLists = await os.api('users/lists/list');
}
-
- if (src === 'group' && userGroups === null) {
- const groups1 = await os.api('users/groups/owned');
- const groups2 = await os.api('users/groups/joined');
-
- userGroups = [...groups1, ...groups2];
- }
});
async function saveAntenna() {
@@ -99,7 +85,6 @@ async function saveAntenna() {
name,
src,
userListId,
- userGroupId,
withReplies,
withFile,
notify,
diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue
index 86b3fce3c5..165e357ebd 100644
--- a/packages/frontend/src/pages/note.vue
+++ b/packages/frontend/src/pages/note.vue
@@ -6,7 +6,7 @@
<Transition :name="$store.state.animation ? 'fade' : ''" mode="out-in">
<div v-if="note" class="note">
<div v-if="showNext" class="_margin">
- <XNotes class="" :pagination="nextPagination" :no-gap="true"/>
+ <MkNotes class="" :pagination="nextPagination" :no-gap="true"/>
</div>
<div class="main _margin">
@@ -29,10 +29,10 @@
</div>
<div v-if="showPrev" class="_margin">
- <XNotes class="" :pagination="prevPagination" :no-gap="true"/>
+ <MkNotes class="" :pagination="prevPagination" :no-gap="true"/>
</div>
</div>
- <MkError v-else-if="error" @retry="fetch()"/>
+ <MkError v-else-if="error" @retry="fetchNote()"/>
<MkLoading v-else/>
</Transition>
</div>
@@ -41,11 +41,10 @@
</template>
<script lang="ts" setup>
-import { computed, defineComponent, watch } from 'vue';
+import { computed, watch } from 'vue';
import * as misskey from 'misskey-js';
-import XNote from '@/components/MkNote.vue';
import XNoteDetailed from '@/components/MkNoteDetailed.vue';
-import XNotes from '@/components/MkNotes.vue';
+import MkNotes from '@/components/MkNotes.vue';
import MkRemoteCaution from '@/components/MkRemoteCaution.vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os';
diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue
index 7106951de2..0fcf0f65c4 100644
--- a/packages/frontend/src/pages/notifications.vue
+++ b/packages/frontend/src/pages/notifications.vue
@@ -6,10 +6,10 @@
<XNotifications class="notifications" :include-types="includeTypes" :unread-only="unreadOnly"/>
</div>
<div v-else-if="tab === 'mentions'">
- <XNotes :pagination="mentionsPagination"/>
+ <MkNotes :pagination="mentionsPagination"/>
</div>
<div v-else-if="tab === 'directNotes'">
- <XNotes :pagination="directNotesPagination"/>
+ <MkNotes :pagination="directNotesPagination"/>
</div>
</MkSpacer>
</MkStickyContainer>
@@ -19,7 +19,7 @@
import { computed } from 'vue';
import { notificationTypes } from 'misskey-js';
import XNotifications from '@/components/MkNotifications.vue';
-import XNotes from '@/components/MkNotes.vue';
+import MkNotes from '@/components/MkNotes.vue';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
index a84cb1e80e..fe230ad095 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
@@ -1,10 +1,10 @@
<template>
<!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')">
- <template #header><i class="fas fa-image"></i> {{ $ts._pages.blocks.image }}</template>
+ <template #header><i class="ti ti-photo"></i> {{ $ts._pages.blocks.image }}</template>
<template #func>
<button @click="choose()">
- <i class="fas fa-folder-open"></i>
+ <i class="ti ti-folder"></i>
</button>
</template>
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
index 6f11e2a08b..ee494b7574 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
@@ -1,7 +1,7 @@
<template>
<!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')">
- <template #header><i class="fas fa-align-left"></i> {{ $ts._pages.blocks.text }}</template>
+ <template #header><i class="ti ti-align-left"></i> {{ $ts._pages.blocks.text }}</template>
<section class="vckmsadr">
<textarea v-model="text"></textarea>
diff --git a/packages/frontend/src/pages/page-editor/page-editor.blocks.vue b/packages/frontend/src/pages/page-editor/page-editor.blocks.vue
index f99fcb202f..97bdcfe80f 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.blocks.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.blocks.vue
@@ -15,8 +15,6 @@ import XSection from './els/page-editor.el.section.vue';
import XText from './els/page-editor.el.text.vue';
import XImage from './els/page-editor.el.image.vue';
import XNote from './els/page-editor.el.note.vue';
-import * as os from '@/os';
-import { deepClone } from '@/scripts/clone';
export default defineComponent({
components: {
diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue
index 11575ae7f4..c4b37c91c6 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.vue
@@ -56,10 +56,9 @@
</template>
<script lang="ts" setup>
-import { defineAsyncComponent, computed, provide, watch } from 'vue';
+import { computed, provide, watch } from 'vue';
import { v4 as uuid } from 'uuid';
import XBlocks from './page-editor.blocks.vue';
-import MkTextarea from '@/components/MkTextarea.vue';
import MkButton from '@/components/MkButton.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkSwitch from '@/components/MkSwitch.vue';
diff --git a/packages/frontend/src/pages/pages.vue b/packages/frontend/src/pages/pages.vue
index af5f631caf..0427332ab2 100644
--- a/packages/frontend/src/pages/pages.vue
+++ b/packages/frontend/src/pages/pages.vue
@@ -25,7 +25,7 @@
</template>
<script lang="ts" setup>
-import { computed, inject } from 'vue';
+import { computed } from 'vue';
import MkPagePreview from '@/components/MkPagePreview.vue';
import MkPagination from '@/components/MkPagination.vue';
import MkButton from '@/components/MkButton.vue';
diff --git a/packages/frontend/src/pages/registry.keys.vue b/packages/frontend/src/pages/registry.keys.vue
index 2c2a1444c1..c687b89eab 100644
--- a/packages/frontend/src/pages/registry.keys.vue
+++ b/packages/frontend/src/pages/registry.keys.vue
@@ -28,7 +28,7 @@
</template>
<script lang="ts" setup>
-import { ref, watch } from 'vue';
+import { watch } from 'vue';
import JSON5 from 'json5';
import * as os from '@/os';
import { i18n } from '@/i18n';
diff --git a/packages/frontend/src/pages/registry.value.vue b/packages/frontend/src/pages/registry.value.vue
index bd4dbe7679..00e2ca5e03 100644
--- a/packages/frontend/src/pages/registry.value.vue
+++ b/packages/frontend/src/pages/registry.value.vue
@@ -40,13 +40,11 @@
</template>
<script lang="ts" setup>
-import { ref, watch } from 'vue';
+import { watch } from 'vue';
import JSON5 from 'json5';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
-import FormLink from '@/components/form/link.vue';
-import FormSection from '@/components/form/section.vue';
import MkButton from '@/components/MkButton.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
import MkTextarea from '@/components/MkTextarea.vue';
diff --git a/packages/frontend/src/pages/registry.vue b/packages/frontend/src/pages/registry.vue
index a2c65294fc..5a029cb0c7 100644
--- a/packages/frontend/src/pages/registry.vue
+++ b/packages/frontend/src/pages/registry.vue
@@ -15,7 +15,6 @@
</template>
<script lang="ts" setup>
-import { ref, watch } from 'vue';
import JSON5 from 'json5';
import * as os from '@/os';
import { i18n } from '@/i18n';
diff --git a/packages/frontend/src/pages/role.vue b/packages/frontend/src/pages/role.vue
new file mode 100644
index 0000000000..2e9d3d6169
--- /dev/null
+++ b/packages/frontend/src/pages/role.vue
@@ -0,0 +1,47 @@
+<template>
+<MkStickyContainer>
+ <template #header><MkPageHeader/></template>
+
+ <MkSpacer :content-max="1200">
+ <div class="_gaps_s">
+ <div v-if="role">{{ role.description }}</div>
+ <MkUserList :pagination="users" :extractor="(item) => item.user"/>
+ </div>
+ </MkSpacer>
+</MkStickyContainer>
+</template>
+
+<script lang="ts" setup>
+import { computed, watch } from 'vue';
+import * as os from '@/os';
+import MkUserList from '@/components/MkUserList.vue';
+import { definePageMetadata } from '@/scripts/page-metadata';
+
+const props = defineProps<{
+ role: string;
+}>();
+
+let role = $ref();
+
+watch(() => props.role, () => {
+ os.api('roles/show', {
+ roleId: props.role,
+ }).then(res => {
+ role = res;
+ });
+}, { immediate: true });
+
+const users = $computed(() => ({
+ endpoint: 'roles/users' as const,
+ limit: 30,
+ params: {
+ roleId: props.role,
+ },
+}));
+
+definePageMetadata(computed(() => ({
+ title: role?.name,
+ icon: 'ti ti-badge',
+})));
+</script>
+
diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue
index 6075dde326..fb78546cb1 100644
--- a/packages/frontend/src/pages/scratchpad.vue
+++ b/packages/frontend/src/pages/scratchpad.vue
@@ -44,7 +44,7 @@ import * as os from '@/os';
import { $i } from '@/account';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
-import { AsUiComponent, AsUiRoot, patch, registerAsUiLib, render } from '@/scripts/aiscript/ui';
+import { AsUiComponent, AsUiRoot, registerAsUiLib } from '@/scripts/aiscript/ui';
import MkAsUi from '@/components/MkAsUi.vue';
import { miLocalStorage } from '@/local-storage';
import { claimAchievement } from '@/scripts/achievements';
diff --git a/packages/frontend/src/pages/search.vue b/packages/frontend/src/pages/search.vue
index 7918f9f577..e52c97b350 100644
--- a/packages/frontend/src/pages/search.vue
+++ b/packages/frontend/src/pages/search.vue
@@ -2,14 +2,14 @@
<MkStickyContainer>
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :content-max="800">
- <XNotes ref="notes" :pagination="pagination"/>
+ <MkNotes ref="notes" :pagination="pagination"/>
</MkSpacer>
</MkStickyContainer>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
-import XNotes from '@/components/MkNotes.vue';
+import MkNotes from '@/components/MkNotes.vue';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
import * as os from '@/os';
diff --git a/packages/frontend/src/pages/settings/2fa.qrdialog.vue b/packages/frontend/src/pages/settings/2fa.qrdialog.vue
new file mode 100644
index 0000000000..1d836db5f5
--- /dev/null
+++ b/packages/frontend/src/pages/settings/2fa.qrdialog.vue
@@ -0,0 +1,82 @@
+<template>
+<MkModal
+ ref="dialogEl"
+ :prefer-type="'dialog'"
+ :z-priority="'low'"
+ @click="cancel"
+ @close="cancel"
+ @closed="emit('closed')"
+>
+ <div :class="$style.root" class="_gaps_m">
+ <I18n :src="i18n.ts._2fa.step1" tag="div">
+ <template #a>
+ <a href="https://authy.com/" rel="noopener" target="_blank" class="_link">Authy</a>
+ </template>
+ <template #b>
+ <a href="https://support.google.com/accounts/answer/1066447" rel="noopener" target="_blank" class="_link">Google Authenticator</a>
+ </template>
+ </I18n>
+ <div>
+ {{ i18n.ts._2fa.step2 }}<br>
+ {{ i18n.ts._2fa.step2Click }}
+ </div>
+ <a :href="twoFactorData.url"><img :class="$style.qr" :src="twoFactorData.qr"></a>
+ <MkKeyValue :copy="twoFactorData.url">
+ <template #key>{{ i18n.ts._2fa.step2Url }}</template>
+ <template #value>{{ twoFactorData.url }}</template>
+ </MkKeyValue>
+ <div class="_buttons">
+ <MkButton primary @click="ok">{{ i18n.ts.next }}</MkButton>
+ <MkButton @click="cancel">{{ i18n.ts.cancel }}</MkButton>
+ </div>
+ </div>
+</MkModal>
+</template>
+
+<script lang="ts" setup>
+import MkButton from '@/components/MkButton.vue';
+import MkModal from '@/components/MkModal.vue';
+import MkKeyValue from '@/components/MkKeyValue.vue';
+import { i18n } from '@/i18n';
+
+defineProps<{
+ twoFactorData: {
+ qr: string;
+ url: string;
+ };
+}>();
+
+const emit = defineEmits<{
+ (ev: 'ok'): void;
+ (ev: 'cancel'): void;
+ (ev: 'closed'): void;
+}>();
+
+const cancel = () => {
+ emit('cancel');
+ emit('closed');
+};
+
+const ok = () => {
+ emit('ok');
+ emit('closed');
+};
+</script>
+
+<style lang="scss" module>
+.root {
+ position: relative;
+ margin: auto;
+ padding: 32px;
+ min-width: 320px;
+ max-width: calc(100svw - 64px);
+ box-sizing: border-box;
+ background: var(--panel);
+ border-radius: var(--radius);
+}
+
+.qr {
+ width: 20em;
+ max-width: 100%;
+}
+</style>
diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue
index e6ef09668c..891934d706 100644
--- a/packages/frontend/src/pages/settings/2fa.vue
+++ b/packages/frontend/src/pages/settings/2fa.vue
@@ -1,216 +1,258 @@
<template>
-<div>
- <MkButton v-if="!twoFactorData && !$i.twoFactorEnabled" @click="register">{{ i18n.ts._2fa.registerDevice }}</MkButton>
- <template v-if="$i.twoFactorEnabled">
- <p>{{ i18n.ts._2fa.alreadyRegistered }}</p>
- <MkButton @click="unregister">{{ i18n.ts.unregister }}</MkButton>
+<FormSection :first="first">
+ <template #label>{{ i18n.ts['2fa'] }}</template>
- <template v-if="supportsCredentials">
- <hr class="totp-method-sep">
-
- <h2 class="heading">{{ i18n.ts.securityKey }}</h2>
- <p>{{ i18n.ts._2fa.securityKeyInfo }}</p>
- <div class="key-list">
- <div v-for="key in $i.securityKeysList" class="key">
- <h3>{{ key.name }}</h3>
- <div class="last-used">{{ i18n.ts.lastUsed }}<MkTime :time="key.lastUsed"/></div>
- <MkButton @click="unregisterKey(key)">{{ i18n.ts.unregister }}</MkButton>
- </div>
+ <div v-if="$i" class="_gaps_s">
+ <MkFolder>
+ <template #icon><i class="ti ti-shield-lock"></i></template>
+ <template #label>{{ i18n.ts.totp }}</template>
+ <template #caption>{{ i18n.ts.totpDescription }}</template>
+ <div v-if="$i.twoFactorEnabled" class="_gaps_s">
+ <div v-text="i18n.ts._2fa.alreadyRegistered"/>
+ <template v-if="$i.securityKeysList.length > 0">
+ <MkButton @click="renewTOTP">{{ i18n.ts._2fa.renewTOTP }}</MkButton>
+ <MkInfo>{{ i18n.ts._2fa.whyTOTPOnlyRenew }}</MkInfo>
+ </template>
+ <MkButton v-else @click="unregisterTOTP">{{ i18n.ts.unregister }}</MkButton>
</div>
- <MkSwitch v-if="$i.securityKeysList.length > 0" v-model="usePasswordLessLogin" @update:model-value="updatePasswordLessLogin">{{ i18n.ts.passwordLessLogin }}</MkSwitch>
+ <MkButton v-else-if="!twoFactorData && !$i.twoFactorEnabled" @click="registerTOTP">{{ i18n.ts._2fa.registerTOTP }}</MkButton>
+ </MkFolder>
+
+ <MkFolder>
+ <template #icon><i class="ti ti-key"></i></template>
+ <template #label>{{ i18n.ts.securityKeyAndPasskey }}</template>
+ <div class="_gaps_s">
+ <MkInfo>
+ {{ i18n.ts._2fa.securityKeyInfo }}<br>
+ <br>
+ {{ i18n.ts._2fa.chromePasskeyNotSupported }}
+ </MkInfo>
- <MkInfo v-if="registration && registration.error" warn>{{ i18n.ts.error }} {{ registration.error }}</MkInfo>
- <MkButton v-if="!registration || registration.error" @click="addSecurityKey">{{ i18n.ts._2fa.registerKey }}</MkButton>
+ <MkInfo v-if="!supportsCredentials" warn>
+ {{ i18n.ts._2fa.securityKeyNotSupported }}
+ </MkInfo>
- <ol v-if="registration && !registration.error">
- <li v-if="registration.stage >= 0">
- {{ i18n.ts.tapSecurityKey }}
- <MkLoading v-if="registration.saving && registration.stage == 0" :em="true"/>
- </li>
- <li v-if="registration.stage >= 1">
- <MkForm :disabled="registration.stage != 1 || registration.saving">
- <MkInput v-model="keyName" :max="30">
- <template #label>{{ i18n.ts.securityKeyName }}</template>
- </MkInput>
- <MkButton :disabled="keyName.length == 0" @click="registerKey">{{ i18n.ts.registerSecurityKey }}</MkButton>
- <MkLoading v-if="registration.saving && registration.stage == 1" :em="true"/>
- </MkForm>
- </li>
- </ol>
- </template>
- </template>
- <div v-if="twoFactorData && !$i.twoFactorEnabled">
- <ol style="margin: 0; padding: 0 0 0 1em;">
- <li>
- <I18n :src="i18n.ts._2fa.step1" tag="span">
- <template #a>
- <a href="https://authy.com/" rel="noopener" target="_blank" class="_link">Authy</a>
- </template>
- <template #b>
- <a href="https://support.google.com/accounts/answer/1066447" rel="noopener" target="_blank" class="_link">Google Authenticator</a>
- </template>
- </I18n>
- </li>
- <li>{{ i18n.ts._2fa.step2 }}<br><img :src="twoFactorData.qr"><p>{{ $ts._2fa.step2Url }}<br>{{ twoFactorData.url }}</p></li>
- <li>
- {{ i18n.ts._2fa.step3 }}<br>
- <MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" :spellcheck="false"><template #label>{{ i18n.ts.token }}</template></MkInput>
- <MkButton primary @click="submit">{{ i18n.ts.done }}</MkButton>
- </li>
- </ol>
- <MkInfo>{{ i18n.ts._2fa.step4 }}</MkInfo>
+ <MkInfo v-else-if="supportsCredentials && !$i.twoFactorEnabled" warn>
+ {{ i18n.ts._2fa.registerTOTPBeforeKey }}
+ </MkInfo>
+
+ <template v-else>
+ <MkButton primary @click="addSecurityKey">{{ i18n.ts._2fa.registerSecurityKey }}</MkButton>
+ <MkFolder v-for="key in $i.securityKeysList" :key="key.id">
+ <template #label>{{ key.name }}</template>
+ <template #suffix><I18n :src="i18n.ts.lastUsedAt"><template #t><MkTime :time="key.lastUsed"/></template></I18n></template>
+ <div class="_buttons">
+ <MkButton @click="renameKey(key)"><i class="ti ti-forms"></i> {{ i18n.ts.rename }}</MkButton>
+ <MkButton danger @click="unregisterKey(key)"><i class="ti ti-trash"></i> {{ i18n.ts.unregister }}</MkButton>
+ </div>
+ </MkFolder>
+ </template>
+ </div>
+ </MkFolder>
+
+ <MkSwitch :disabled="!$i.twoFactorEnabled || $i.securityKeysList.length === 0" :model-value="usePasswordLessLogin" @update:model-value="v => updatePasswordLessLogin(v)">
+ <template #label>{{ i18n.ts.passwordLessLogin }}</template>
+ <template #caption>{{ i18n.ts.passwordLessLoginDescription }}</template>
+ </MkSwitch>
</div>
-</div>
+</FormSection>
</template>
<script lang="ts" setup>
-import { ref } from 'vue';
+import { ref, defineAsyncComponent } from 'vue';
import { hostname } from '@/config';
import { byteify, hexify, stringify } from '@/scripts/2fa';
import MkButton from '@/components/MkButton.vue';
import MkInfo from '@/components/MkInfo.vue';
-import MkInput from '@/components/MkInput.vue';
import MkSwitch from '@/components/MkSwitch.vue';
+import FormSection from '@/components/form/section.vue';
+import MkFolder from '@/components/MkFolder.vue';
import * as os from '@/os';
import { $i } from '@/account';
import { i18n } from '@/i18n';
+// メモ: 各エンドポイントはmeUpdatedを発行するため、refreshAccountは不要
+
+withDefaults(defineProps<{
+ first?: boolean;
+}>(), {
+ first: false,
+});
+
const twoFactorData = ref<any>(null);
const supportsCredentials = ref(!!navigator.credentials);
-const usePasswordLessLogin = ref($i!.usePasswordLessLogin);
-const registration = ref<any>(null);
-const keyName = ref('');
-const token = ref(null);
+const usePasswordLessLogin = $computed(() => $i!.usePasswordLessLogin);
-function register() {
- os.inputText({
- title: i18n.ts.password,
+async function registerTOTP() {
+ const password = await os.inputText({
+ title: i18n.ts._2fa.registerTOTP,
+ text: i18n.ts._2fa.passwordToTOTP,
type: 'password',
- }).then(({ canceled, result: password }) => {
- if (canceled) return;
- os.api('i/2fa/register', {
- password: password,
- }).then(data => {
- twoFactorData.value = data;
- });
+ autocomplete: 'current-password',
+ });
+ if (password.canceled) return;
+
+ const twoFactorData = await os.apiWithDialog('i/2fa/register', {
+ password: password.result,
+ });
+
+ const qrdialog = await new Promise<boolean>(res => {
+ os.popup(defineAsyncComponent(() => import('./2fa.qrdialog.vue')), {
+ twoFactorData,
+ }, {
+ 'ok': () => res(true),
+ 'cancel': () => res(false),
+ }, 'closed');
+ });
+ if (!qrdialog) return;
+
+ const token = await os.inputNumber({
+ title: i18n.ts._2fa.step3Title,
+ text: i18n.ts._2fa.step3,
+ autocomplete: 'one-time-code',
+ });
+ if (token.canceled) return;
+
+ await os.apiWithDialog('i/2fa/done', {
+ token: token.result.toString(),
+ });
+
+ await os.alert({
+ type: 'success',
+ text: i18n.ts._2fa.step4,
});
}
-function unregister() {
+function unregisterTOTP() {
os.inputText({
title: i18n.ts.password,
type: 'password',
+ autocomplete: 'current-password',
}).then(({ canceled, result: password }) => {
if (canceled) return;
- os.api('i/2fa/unregister', {
+ os.apiWithDialog('i/2fa/unregister', {
password: password,
- }).then(() => {
- usePasswordLessLogin.value = false;
- updatePasswordLessLogin();
- }).then(() => {
- os.success();
- $i!.twoFactorEnabled = false;
+ }).catch(error => {
+ os.alert({
+ type: 'error',
+ text: error,
+ });
});
});
}
-function submit() {
- os.api('i/2fa/done', {
- token: token.value,
- }).then(() => {
- os.success();
- $i!.twoFactorEnabled = true;
- }).catch(err => {
- os.alert({
- type: 'error',
- text: err,
- });
+function renewTOTP() {
+ os.confirm({
+ type: 'question',
+ title: i18n.ts._2fa.renewTOTP,
+ text: i18n.ts._2fa.renewTOTPConfirm,
+ okText: i18n.ts._2fa.renewTOTPOk,
+ cancelText: i18n.ts._2fa.renewTOTPCancel,
+ }).then(({ canceled }) => {
+ if (canceled) return;
+ registerTOTP();
});
}
-function registerKey() {
- registration.value.saving = true;
- os.api('i/2fa/key-done', {
- password: registration.value.password,
- name: keyName.value,
- challengeId: registration.value.challengeId,
- // we convert each 16 bits to a string to serialise
- clientDataJSON: stringify(registration.value.credential.response.clientDataJSON),
- attestationObject: hexify(registration.value.credential.response.attestationObject),
- }).then(key => {
- registration.value = null;
- key.lastUsed = new Date();
- os.success();
+async function unregisterKey(key) {
+ const confirm = await os.confirm({
+ type: 'question',
+ title: i18n.ts._2fa.removeKey,
+ text: i18n.t('_2fa.removeKeyConfirm', { name: key.name }),
});
-}
+ if (confirm.canceled) return;
-function unregisterKey(key) {
- os.inputText({
+ const password = await os.inputText({
title: i18n.ts.password,
type: 'password',
- }).then(({ canceled, result: password }) => {
- if (canceled) return;
- return os.api('i/2fa/remove-key', {
- password,
- credentialId: key.id,
- }).then(() => {
- usePasswordLessLogin.value = false;
- updatePasswordLessLogin();
- }).then(() => {
- os.success();
- });
+ autocomplete: 'current-password',
+ });
+ if (password.canceled) return;
+
+ await os.apiWithDialog('i/2fa/remove-key', {
+ password: password.result,
+ credentialId: key.id,
});
+ os.success();
}
-function addSecurityKey() {
- os.inputText({
+async function renameKey(key) {
+ const name = await os.inputText({
+ title: i18n.ts.rename,
+ default: key.name,
+ type: 'text',
+ minLength: 1,
+ maxLength: 30,
+ });
+ if (name.canceled) return;
+
+ await os.apiWithDialog('i/2fa/update-key', {
+ name: name.result,
+ credentialId: key.id,
+ });
+}
+
+async function addSecurityKey() {
+ const password = await os.inputText({
title: i18n.ts.password,
type: 'password',
- }).then(({ canceled, result: password }) => {
- if (canceled) return;
- os.api('i/2fa/register-key', {
- password,
- }).then(reg => {
- registration.value = {
- password,
- challengeId: reg!.challengeId,
- stage: 0,
- publicKeyOptions: {
- challenge: byteify(reg!.challenge, 'base64'),
- rp: {
- id: hostname,
- name: 'Misskey',
- },
- user: {
- id: byteify($i!.id, 'ascii'),
- name: $i!.username,
- displayName: $i!.name,
- },
- pubKeyCredParams: [{ alg: -7, type: 'public-key' }],
- timeout: 60000,
- attestation: 'direct',
- },
- saving: true,
- };
- return navigator.credentials.create({
- publicKey: registration.value.publicKeyOptions,
- });
- }).then(credential => {
- registration.value.credential = credential;
- registration.value.saving = false;
- registration.value.stage = 1;
- }).catch(err => {
- console.warn('Error while registering?', err);
- registration.value.error = err.message;
- registration.value.stage = -1;
- });
+ autocomplete: 'current-password',
+ });
+ if (password.canceled) return;
+
+ const challenge: any = await os.apiWithDialog('i/2fa/register-key', {
+ password: password.result,
+ });
+
+ const name = await os.inputText({
+ title: i18n.ts._2fa.registerSecurityKey,
+ text: i18n.ts._2fa.securityKeyName,
+ type: 'text',
+ minLength: 1,
+ maxLength: 30,
+ });
+ if (name.canceled) return;
+
+ const webAuthnCreation = navigator.credentials.create({
+ publicKey: {
+ challenge: byteify(challenge.challenge, 'base64'),
+ rp: {
+ id: hostname,
+ name: 'Misskey',
+ },
+ user: {
+ id: byteify($i!.id, 'ascii'),
+ name: $i!.username,
+ displayName: $i!.name,
+ },
+ pubKeyCredParams: [{ alg: -7, type: 'public-key' }],
+ timeout: 60000,
+ attestation: 'direct',
+ },
+ }) as Promise<PublicKeyCredential & { response: AuthenticatorAttestationResponse; } | null>;
+
+ const credential = await os.promiseDialog(
+ webAuthnCreation,
+ null,
+ () => {}, // ユーザーのキャンセルはrejectなのでエラーダイアログを出さない
+ i18n.ts._2fa.tapSecurityKey,
+ );
+ if (!credential) return;
+
+ await os.apiWithDialog('i/2fa/key-done', {
+ password: password.result,
+ name: name.result,
+ challengeId: challenge.challengeId,
+ // we convert each 16 bits to a string to serialise
+ clientDataJSON: stringify(credential.response.clientDataJSON),
+ attestationObject: hexify(credential.response.attestationObject),
});
}
-async function updatePasswordLessLogin() {
- await os.api('i/2fa/password-less', {
- value: !!usePasswordLessLogin.value,
+async function updatePasswordLessLogin(value: boolean) {
+ await os.apiWithDialog('i/2fa/password-less', {
+ value,
});
}
</script>
diff --git a/packages/frontend/src/pages/settings/deck.vue b/packages/frontend/src/pages/settings/deck.vue
index 3f1f2820f0..bc0179b3aa 100644
--- a/packages/frontend/src/pages/settings/deck.vue
+++ b/packages/frontend/src/pages/settings/deck.vue
@@ -13,14 +13,10 @@
</template>
<script lang="ts" setup>
-import { computed, watch } from 'vue';
+import { computed } from 'vue';
import MkSwitch from '@/components/MkSwitch.vue';
-import FormLink from '@/components/form/link.vue';
import MkRadios from '@/components/MkRadios.vue';
-import MkInput from '@/components/MkInput.vue';
import { deckStore } from '@/ui/deck/deck-store';
-import * as os from '@/os';
-import { unisonReload } from '@/scripts/unison-reload';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue
index 4099a3c11d..a23bdfe69e 100644
--- a/packages/frontend/src/pages/settings/drive.vue
+++ b/packages/frontend/src/pages/settings/drive.vue
@@ -30,7 +30,7 @@
<FormLink @click="chooseUploadFolder()">
{{ i18n.ts.uploadFolder }}
<template #suffix>{{ uploadFolder ? uploadFolder.name : '-' }}</template>
- <template #suffixIcon><i class="fas fa-folder-open"></i></template>
+ <template #suffixIcon><i class="ti ti-folder"></i></template>
</FormLink>
<MkSwitch v-model="keepOriginalUploading">
<template #label>{{ i18n.ts.keepOriginalUploading }}</template>
diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue
index 57b07b1cc1..1734dcfe42 100644
--- a/packages/frontend/src/pages/settings/email.vue
+++ b/packages/frontend/src/pages/settings/email.vue
@@ -34,9 +34,6 @@
<MkSwitch v-model="emailNotification_receiveFollowRequest">
{{ i18n.ts._notification._types.receiveFollowRequest }}
</MkSwitch>
- <MkSwitch v-model="emailNotification_groupInvited">
- {{ i18n.ts._notification._types.groupInvited }}
- </MkSwitch>
</div>
</FormSection>
</div>
@@ -78,7 +75,6 @@ const emailNotification_reply = ref($i!.emailNotificationTypes.includes('reply')
const emailNotification_quote = ref($i!.emailNotificationTypes.includes('quote'));
const emailNotification_follow = ref($i!.emailNotificationTypes.includes('follow'));
const emailNotification_receiveFollowRequest = ref($i!.emailNotificationTypes.includes('receiveFollowRequest'));
-const emailNotification_groupInvited = ref($i!.emailNotificationTypes.includes('groupInvited'));
const saveNotificationSettings = () => {
os.api('i/update', {
@@ -88,12 +84,11 @@ const saveNotificationSettings = () => {
...[emailNotification_quote.value ? 'quote' : null],
...[emailNotification_follow.value ? 'follow' : null],
...[emailNotification_receiveFollowRequest.value ? 'receiveFollowRequest' : null],
- ...[emailNotification_groupInvited.value ? 'groupInvited' : null],
].filter(x => x != null),
});
};
-watch([emailNotification_mention, emailNotification_reply, emailNotification_quote, emailNotification_follow, emailNotification_receiveFollowRequest, emailNotification_groupInvited], () => {
+watch([emailNotification_mention, emailNotification_reply, emailNotification_quote, emailNotification_follow, emailNotification_receiveFollowRequest], () => {
saveNotificationSettings();
});
diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue
index 8631f3e341..e6752460a8 100644
--- a/packages/frontend/src/pages/settings/index.vue
+++ b/packages/frontend/src/pages/settings/index.vue
@@ -22,16 +22,15 @@
</template>
<script setup lang="ts">
-import { computed, defineAsyncComponent, inject, nextTick, onActivated, onMounted, onUnmounted, provide, ref, shallowRef, watch } from 'vue';
+import { computed, onActivated, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue';
import { i18n } from '@/i18n';
import MkInfo from '@/components/MkInfo.vue';
import MkSuperMenu from '@/components/MkSuperMenu.vue';
-import { scroll } from '@/scripts/scroll';
import { signout, $i } from '@/account';
import { unisonReload } from '@/scripts/unison-reload';
import { instance } from '@/instance';
import { useRouter } from '@/router';
-import { definePageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata';
+import { definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
import * as os from '@/os';
import { miLocalStorage } from '@/local-storage';
import { fetchCustomEmojis } from '@/custom-emojis';
diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue
index 48579aa069..a08308f0ce 100644
--- a/packages/frontend/src/pages/settings/mute-block.vue
+++ b/packages/frontend/src/pages/settings/mute-block.vue
@@ -34,7 +34,6 @@ import MkTab from '@/components/MkTab.vue';
import FormInfo from '@/components/MkInfo.vue';
import FormLink from '@/components/form/link.vue';
import { userPage } from '@/filters/user';
-import * as os from '@/os';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index db32ee862c..f64202fff2 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -5,7 +5,6 @@
<div class="_gaps_m">
<FormLink @click="readAllNotifications">{{ i18n.ts.markAsReadAllNotifications }}</FormLink>
<FormLink @click="readAllUnreadNotes">{{ i18n.ts.markAsReadAllUnreadNotes }}</FormLink>
- <FormLink @click="readAllMessagingMessages">{{ i18n.ts.markAsReadAllTalkMessages }}</FormLink>
</div>
</FormSection>
<FormSection>
@@ -29,7 +28,6 @@
<script lang="ts" setup>
import { defineAsyncComponent } from 'vue';
import { notificationTypes } from 'misskey-js';
-import MkButton from '@/components/MkButton.vue';
import FormLink from '@/components/form/link.vue';
import FormSection from '@/components/form/section.vue';
import MkSwitch from '@/components/MkSwitch.vue';
@@ -47,10 +45,6 @@ async function readAllUnreadNotes() {
await os.api('i/read-all-unread-notes');
}
-async function readAllMessagingMessages() {
- await os.api('i/read-all-messaging-messages');
-}
-
async function readAllNotifications() {
await os.api('notifications/mark-all-as-read');
}
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index 3647e90ce7..41563c441f 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -78,7 +78,6 @@ import MkSelect from '@/components/MkSelect.vue';
import FormSplit from '@/components/form/split.vue';
import MkFolder from '@/components/MkFolder.vue';
import FormSlot from '@/components/form/slot.vue';
-import { host } from '@/config';
import { selectFile } from '@/scripts/select-file';
import * as os from '@/os';
import { i18n } from '@/i18n';
@@ -125,11 +124,11 @@ function saveFields() {
function save() {
os.apiWithDialog('i/update', {
- name: profile.name || null,
- description: profile.description || null,
- location: profile.location || null,
- birthday: profile.birthday || null,
- lang: profile.lang || null,
+ name: profile.name ?? null,
+ description: profile.description ?? null,
+ location: profile.location ?? null,
+ birthday: profile.birthday ?? null,
+ lang: profile.lang ?? null,
isBot: !!profile.isBot,
isCat: !!profile.isCat,
showTimelineReplies: !!profile.showTimelineReplies,
diff --git a/packages/frontend/src/pages/settings/reaction.vue b/packages/frontend/src/pages/settings/reaction.vue
index c8b47b8299..ed913731d3 100644
--- a/packages/frontend/src/pages/settings/reaction.vue
+++ b/packages/frontend/src/pages/settings/reaction.vue
@@ -57,7 +57,6 @@
<script lang="ts" setup>
import { defineAsyncComponent, watch } from 'vue';
import Sortable from 'vuedraggable';
-import MkInput from '@/components/MkInput.vue';
import MkRadios from '@/components/MkRadios.vue';
import FromSlot from '@/components/form/slot.vue';
import MkButton from '@/components/MkButton.vue';
diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue
index b09c4ffd2f..0cc2df09c5 100644
--- a/packages/frontend/src/pages/settings/security.vue
+++ b/packages/frontend/src/pages/settings/security.vue
@@ -5,11 +5,8 @@
<MkButton primary @click="change()">{{ i18n.ts.changePassword }}</MkButton>
</FormSection>
- <FormSection>
- <template #label>{{ i18n.ts.twoStepAuthentication }}</template>
- <X2fa/>
- </FormSection>
-
+ <X2fa/>
+
<FormSection>
<template #label>{{ i18n.ts.signinHistory }}</template>
<MkPagination :pagination="pagination" disable-auto-load>
@@ -56,18 +53,21 @@ async function change() {
const { canceled: canceled1, result: currentPassword } = await os.inputText({
title: i18n.ts.currentPassword,
type: 'password',
+ autocomplete: 'current-password',
});
if (canceled1) return;
const { canceled: canceled2, result: newPassword } = await os.inputText({
title: i18n.ts.newPassword,
type: 'password',
+ autocomplete: 'new-password',
});
if (canceled2) return;
const { canceled: canceled3, result: newPassword2 } = await os.inputText({
title: i18n.ts.newPasswordRetype,
type: 'password',
+ autocomplete: 'new-password',
});
if (canceled3) return;
@@ -109,7 +109,7 @@ definePageMetadata({
<style lang="scss" scoped>
.timnmucd {
- padding: 16px;
+ padding: 12px;
&:first-child {
border-top-left-radius: 6px;
diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue
index f9e301aef9..006a2377d4 100644
--- a/packages/frontend/src/pages/settings/sounds.vue
+++ b/packages/frontend/src/pages/settings/sounds.vue
@@ -25,12 +25,9 @@ import { computed, ref } from 'vue';
import XSound from './sounds.sound.vue';
import MkRange from '@/components/MkRange.vue';
import MkButton from '@/components/MkButton.vue';
-import FormLink from '@/components/form/link.vue';
import FormSection from '@/components/form/section.vue';
import MkFolder from '@/components/MkFolder.vue';
-import * as os from '@/os';
import { ColdDeviceStorage } from '@/store';
-import { playFile } from '@/scripts/sound';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
@@ -43,7 +40,7 @@ const masterVolume = computed({
},
});
-const volumeIcon = computed(() => masterVolume.value === 0 ? 'fas fa-volume-mute' : 'fas fa-volume-up');
+const volumeIcon = computed(() => masterVolume.value === 0 ? 'ti ti-volume-3' : 'ti ti-volume');
const sounds = ref({
note: ColdDeviceStorage.get('sound_note'),
diff --git a/packages/frontend/src/pages/settings/statusbar.statusbar.vue b/packages/frontend/src/pages/settings/statusbar.statusbar.vue
index eee65e0e95..81ff873e9e 100644
--- a/packages/frontend/src/pages/settings/statusbar.statusbar.vue
+++ b/packages/frontend/src/pages/settings/statusbar.statusbar.vue
@@ -81,14 +81,13 @@
</template>
<script lang="ts" setup>
-import { computed, reactive, ref, watch } from 'vue';
+import { reactive, watch } from 'vue';
import MkSelect from '@/components/MkSelect.vue';
import MkInput from '@/components/MkInput.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkRadios from '@/components/MkRadios.vue';
import MkButton from '@/components/MkButton.vue';
import MkRange from '@/components/MkRange.vue';
-import * as os from '@/os';
import { defaultStore } from '@/store';
import { i18n } from '@/i18n';
import { deepClone } from '@/scripts/clone';
diff --git a/packages/frontend/src/pages/settings/statusbar.vue b/packages/frontend/src/pages/settings/statusbar.vue
index ab081964c2..cb46858c5a 100644
--- a/packages/frontend/src/pages/settings/statusbar.vue
+++ b/packages/frontend/src/pages/settings/statusbar.vue
@@ -10,15 +10,13 @@
</template>
<script lang="ts" setup>
-import { computed, onMounted, ref, watch } from 'vue';
+import { onMounted } from 'vue';
import { v4 as uuid } from 'uuid';
import XStatusbar from './statusbar.statusbar.vue';
-import MkRadios from '@/components/MkRadios.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os';
import { defaultStore } from '@/store';
-import { unisonReload } from '@/scripts/unison-reload';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
diff --git a/packages/frontend/src/pages/settings/webhook.vue b/packages/frontend/src/pages/settings/webhook.vue
index 01c31688cc..e10f65b0af 100644
--- a/packages/frontend/src/pages/settings/webhook.vue
+++ b/packages/frontend/src/pages/settings/webhook.vue
@@ -30,9 +30,6 @@ import { } from 'vue';
import MkPagination from '@/components/MkPagination.vue';
import FormSection from '@/components/form/section.vue';
import FormLink from '@/components/form/link.vue';
-import { userPage } from '@/filters/user';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
const pagination = {
diff --git a/packages/frontend/src/pages/tag.vue b/packages/frontend/src/pages/tag.vue
index 5d6d01d2ae..511052c424 100644
--- a/packages/frontend/src/pages/tag.vue
+++ b/packages/frontend/src/pages/tag.vue
@@ -2,14 +2,14 @@
<MkStickyContainer>
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :content-max="800">
- <XNotes class="" :pagination="pagination"/>
+ <MkNotes class="" :pagination="pagination"/>
</MkSpacer>
</MkStickyContainer>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
-import XNotes from '@/components/MkNotes.vue';
+import MkNotes from '@/components/MkNotes.vue';
import { definePageMetadata } from '@/scripts/page-metadata';
const props = defineProps<{
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index a071361150..d982a76d03 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -8,7 +8,7 @@
<div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
<div :class="$style.tl">
- <XTimeline
+ <MkTimeline
ref="tlComponent"
:key="src"
:src="src"
@@ -23,7 +23,8 @@
<script lang="ts" setup>
import { defineAsyncComponent, computed, watch, provide } from 'vue';
-import XTimeline from '@/components/MkTimeline.vue';
+import type { Tab } from '@/components/global/MkPageHeader.tabs.vue';
+import MkTimeline from '@/components/MkTimeline.vue';
import MkPostForm from '@/components/MkPostForm.vue';
import { scroll } from '@/scripts/scroll';
import * as os from '@/os';
@@ -32,7 +33,6 @@ import { i18n } from '@/i18n';
import { instance } from '@/instance';
import { $i } from '@/account';
import { definePageMetadata } from '@/scripts/page-metadata';
-import type { Tab } from '@/components/global/MkPageHeader.tabs.vue';
provide('shouldOmitHeaderTitle', true);
@@ -44,7 +44,7 @@ const keymap = {
't': focus,
};
-const tlComponent = $shallowRef<InstanceType<typeof XTimeline>>();
+const tlComponent = $shallowRef<InstanceType<typeof MkTimeline>>();
const rootEl = $shallowRef<HTMLElement>();
let queue = $ref(0);
@@ -83,7 +83,9 @@ async function chooseAntenna(ev: MouseEvent): Promise<void> {
}
async function chooseChannel(ev: MouseEvent): Promise<void> {
- const channels = await os.api('channels/followed');
+ const channels = await os.api('channels/followed', {
+ limit: 100,
+ });
const items = channels.map(channel => ({
type: 'link' as const,
text: channel.name,
diff --git a/packages/frontend/src/pages/user-info.vue b/packages/frontend/src/pages/user-info.vue
index 7ba8a3d16b..13a06286f6 100644
--- a/packages/frontend/src/pages/user-info.vue
+++ b/packages/frontend/src/pages/user-info.vue
@@ -112,7 +112,7 @@
<MkButton v-if="user.host == null && iAmModerator" primary rounded @click="assignRole"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton>
<div v-for="role in info.roles" :key="role.id" :class="$style.roleItem">
- <MkRolePreview :class="$style.role" :role="role"/>
+ <MkRolePreview :class="$style.role" :role="role" :for-moderation="true"/>
<button v-if="role.target === 'manual'" class="_button" :class="$style.roleUnassign" @click="unassignRole(role, $event)"><i class="ti ti-x"></i></button>
<button v-else class="_button" :class="$style.roleUnassign" disabled><i class="ti ti-ban"></i></button>
</div>
@@ -181,8 +181,6 @@ import MkSwitch from '@/components/MkSwitch.vue';
import FormLink from '@/components/form/link.vue';
import FormSection from '@/components/form/section.vue';
import MkButton from '@/components/MkButton.vue';
-import MkInput from '@/components/MkInput.vue';
-import FormSplit from '@/components/form/split.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
import MkSelect from '@/components/MkSelect.vue';
@@ -190,14 +188,11 @@ import FormSuspense from '@/components/form/suspense.vue';
import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
import MkInfo from '@/components/MkInfo.vue';
import * as os from '@/os';
-import number from '@/filters/number';
-import bytes from '@/filters/bytes';
import { url } from '@/config';
import { userPage, acct } from '@/filters/user';
import { definePageMetadata } from '@/scripts/page-metadata';
import { i18n } from '@/i18n';
import { iAmAdmin, iAmModerator } from '@/account';
-import { instance } from '@/instance';
import MkRolePreview from '@/components/MkRolePreview.vue';
const props = withDefaults(defineProps<{
diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue
index 3f47edfd44..acf7ea9b2c 100644
--- a/packages/frontend/src/pages/user-list-timeline.vue
+++ b/packages/frontend/src/pages/user-list-timeline.vue
@@ -4,7 +4,7 @@
<div ref="rootEl" class="eqqrhokj">
<div v-if="queue > 0" class="new"><button class="_buttonPrimary" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
<div class="tl">
- <XTimeline
+ <MkTimeline
ref="tlEl" :key="listId"
class="tl"
src="list"
@@ -18,8 +18,8 @@
</template>
<script lang="ts" setup>
-import { computed, watch, inject } from 'vue';
-import XTimeline from '@/components/MkTimeline.vue';
+import { computed, watch } from 'vue';
+import MkTimeline from '@/components/MkTimeline.vue';
import { scroll } from '@/scripts/scroll';
import * as os from '@/os';
import { useRouter } from '@/router';
@@ -34,7 +34,7 @@ const props = defineProps<{
let list = $ref(null);
let queue = $ref(0);
-let tlEl = $shallowRef<InstanceType<typeof XTimeline>>();
+let tlEl = $shallowRef<InstanceType<typeof MkTimeline>>();
let rootEl = $shallowRef<HTMLElement>();
watch(() => props.listId, async () => {
@@ -65,7 +65,7 @@ async function timetravel() {
}
const headerActions = $computed(() => list ? [{
- icon: 'fas fa-calendar-alt',
+ icon: 'ti ti-calendar-time',
text: i18n.ts.jumpToSpecifiedDate,
handler: timetravel,
}, {
diff --git a/packages/frontend/src/pages/user-tag.vue b/packages/frontend/src/pages/user-tag.vue
new file mode 100644
index 0000000000..fac7593e9c
--- /dev/null
+++ b/packages/frontend/src/pages/user-tag.vue
@@ -0,0 +1,38 @@
+<template>
+<MkStickyContainer>
+ <template #header><MkPageHeader/></template>
+
+ <MkSpacer :content-max="1200">
+ <div class="_gaps_s">
+ <MkUserList :pagination="tagUsers"/>
+ </div>
+ </MkSpacer>
+</MkStickyContainer>
+</template>
+
+<script lang="ts" setup>
+import { computed, watch } from 'vue';
+import * as os from '@/os';
+import MkUserList from '@/components/MkUserList.vue';
+import { definePageMetadata } from '@/scripts/page-metadata';
+
+const props = defineProps<{
+ tag: string;
+}>();
+
+const tagUsers = $computed(() => ({
+ endpoint: 'hashtags/users' as const,
+ limit: 30,
+ params: {
+ tag: props.tag,
+ origin: 'combined',
+ sort: '+follower',
+ },
+}));
+
+definePageMetadata(computed(() => ({
+ title: props.tag,
+ icon: 'ti ti-user-search',
+})));
+</script>
+
diff --git a/packages/frontend/src/pages/user/achievements.vue b/packages/frontend/src/pages/user/achievements.vue
index 615613b7fc..1b3a6e24b3 100644
--- a/packages/frontend/src/pages/user/achievements.vue
+++ b/packages/frontend/src/pages/user/achievements.vue
@@ -5,10 +5,9 @@
</template>
<script lang="ts" setup>
-import { onActivated, onDeactivated, onMounted, onUnmounted, ref } from 'vue';
+import { onActivated, onDeactivated, onMounted, onUnmounted } from 'vue';
import * as misskey from 'misskey-js';
import MkAchievements from '@/components/MkAchievements.vue';
-import { i18n } from '@/i18n';
import { claimAchievement } from '@/scripts/achievements';
import { $i } from '@/account';
diff --git a/packages/frontend/src/pages/user/activity.following.vue b/packages/frontend/src/pages/user/activity.following.vue
index 500995e392..54360024f3 100644
--- a/packages/frontend/src/pages/user/activity.following.vue
+++ b/packages/frontend/src/pages/user/activity.following.vue
@@ -9,17 +9,14 @@
</template>
<script lang="ts" setup>
-import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue';
+import { onMounted } from 'vue';
import { Chart, ChartDataset } from 'chart.js';
-import tinycolor from 'tinycolor2';
import * as misskey from 'misskey-js';
import gradient from 'chartjs-plugin-gradient';
-import { satisfies } from 'compare-versions';
import * as os from '@/os';
import { defaultStore } from '@/store';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
import { chartVLine } from '@/scripts/chart-vline';
-import { alpha } from '@/scripts/color';
import { initChart } from '@/scripts/init-chart';
import { chartLegend } from '@/scripts/chart-legend';
import MkChartLegend from '@/components/MkChartLegend.vue';
diff --git a/packages/frontend/src/pages/user/activity.heatmap.vue b/packages/frontend/src/pages/user/activity.heatmap.vue
index 202201afb5..2dcb754c9b 100644
--- a/packages/frontend/src/pages/user/activity.heatmap.vue
+++ b/packages/frontend/src/pages/user/activity.heatmap.vue
@@ -8,14 +8,12 @@
</template>
<script lang="ts" setup>
-import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
+import { onMounted, nextTick, watch } from 'vue';
import { Chart } from 'chart.js';
-import tinycolor from 'tinycolor2';
import * as misskey from 'misskey-js';
import * as os from '@/os';
import { defaultStore } from '@/store';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { chartVLine } from '@/scripts/chart-vline';
import { alpha } from '@/scripts/color';
import { initChart } from '@/scripts/init-chart';
diff --git a/packages/frontend/src/pages/user/activity.notes.vue b/packages/frontend/src/pages/user/activity.notes.vue
index 8763997f8e..7dd02ad6d4 100644
--- a/packages/frontend/src/pages/user/activity.notes.vue
+++ b/packages/frontend/src/pages/user/activity.notes.vue
@@ -9,17 +9,14 @@
</template>
<script lang="ts" setup>
-import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue';
+import { onMounted } from 'vue';
import { Chart, ChartDataset } from 'chart.js';
-import tinycolor from 'tinycolor2';
import * as misskey from 'misskey-js';
import gradient from 'chartjs-plugin-gradient';
-import { satisfies } from 'compare-versions';
import * as os from '@/os';
import { defaultStore } from '@/store';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
import { chartVLine } from '@/scripts/chart-vline';
-import { alpha } from '@/scripts/color';
import { initChart } from '@/scripts/init-chart';
import { chartLegend } from '@/scripts/chart-legend';
import MkChartLegend from '@/components/MkChartLegend.vue';
diff --git a/packages/frontend/src/pages/user/activity.pv.vue b/packages/frontend/src/pages/user/activity.pv.vue
index d23f89a31e..6a7506e388 100644
--- a/packages/frontend/src/pages/user/activity.pv.vue
+++ b/packages/frontend/src/pages/user/activity.pv.vue
@@ -9,16 +9,14 @@
</template>
<script lang="ts" setup>
-import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue';
+import { onMounted } from 'vue';
import { Chart, ChartDataset } from 'chart.js';
-import tinycolor from 'tinycolor2';
import * as misskey from 'misskey-js';
import gradient from 'chartjs-plugin-gradient';
import * as os from '@/os';
import { defaultStore } from '@/store';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
import { chartVLine } from '@/scripts/chart-vline';
-import { alpha } from '@/scripts/color';
import { initChart } from '@/scripts/init-chart';
import { chartLegend } from '@/scripts/chart-legend';
import MkChartLegend from '@/components/MkChartLegend.vue';
diff --git a/packages/frontend/src/pages/user/activity.vue b/packages/frontend/src/pages/user/activity.vue
index 6d7c7e7722..cd538ad61f 100644
--- a/packages/frontend/src/pages/user/activity.vue
+++ b/packages/frontend/src/pages/user/activity.vue
@@ -22,7 +22,6 @@
</template>
<script lang="ts" setup>
-import { computed } from 'vue';
import * as misskey from 'misskey-js';
import XHeatmap from './activity.heatmap.vue';
import XPv from './activity.pv.vue';
diff --git a/packages/frontend/src/pages/user/followers.vue b/packages/frontend/src/pages/user/followers.vue
index 8859928784..20573e67e9 100644
--- a/packages/frontend/src/pages/user/followers.vue
+++ b/packages/frontend/src/pages/user/followers.vue
@@ -14,7 +14,7 @@
</template>
<script lang="ts" setup>
-import { defineAsyncComponent, computed, inject, onMounted, onUnmounted, watch } from 'vue';
+import { computed, watch } from 'vue';
import * as Acct from 'misskey-js/built/acct';
import * as misskey from 'misskey-js';
import XFollowList from './follow-list.vue';
diff --git a/packages/frontend/src/pages/user/following.vue b/packages/frontend/src/pages/user/following.vue
index 51015905c6..3825f138cf 100644
--- a/packages/frontend/src/pages/user/following.vue
+++ b/packages/frontend/src/pages/user/following.vue
@@ -14,7 +14,7 @@
</template>
<script lang="ts" setup>
-import { defineAsyncComponent, computed, inject, onMounted, onUnmounted, watch } from 'vue';
+import { computed, watch } from 'vue';
import * as Acct from 'misskey-js/built/acct';
import * as misskey from 'misskey-js';
import XFollowList from './follow-list.vue';
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index 6d942d4391..441b19440c 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -100,7 +100,7 @@
<XPhotos :key="user.id" :user="user"/>
<XActivity :key="user.id" :user="user"/>
</template>
- <XNotes :no-gap="true" :pagination="pagination"/>
+ <MkNotes :class="$style.tl" :no-gap="true" :pagination="pagination"/>
</div>
</div>
<div v-if="!narrow" class="sub _gaps" style="container-type: inline-size;">
@@ -112,28 +112,25 @@
</template>
<script lang="ts" setup>
-import { defineAsyncComponent, computed, inject, onMounted, onUnmounted, watch } from 'vue';
+import { defineAsyncComponent, computed, onMounted, onUnmounted } from 'vue';
import calcAge from 's-age';
import * as misskey from 'misskey-js';
import XNote from '@/components/MkNote.vue';
import MkFollowButton from '@/components/MkFollowButton.vue';
-import MkContainer from '@/components/MkContainer.vue';
-import MkFoldableSection from '@/components/MkFoldableSection.vue';
import MkRemoteCaution from '@/components/MkRemoteCaution.vue';
-import MkTab from '@/components/MkTab.vue';
import MkOmit from '@/components/MkOmit.vue';
import MkInfo from '@/components/MkInfo.vue';
import { getScrollPosition } from '@/scripts/scroll';
import { getUserMenu } from '@/scripts/get-user-menu';
import number from '@/filters/number';
-import { userPage, acct as getAcct } from '@/filters/user';
+import { userPage } from '@/filters/user';
import * as os from '@/os';
import { useRouter } from '@/router';
import { i18n } from '@/i18n';
import { $i } from '@/account';
import { dateString } from '@/filters/date';
import { confetti } from '@/scripts/confetti';
-import XNotes from '@/components/MkNotes.vue';
+import MkNotes from '@/components/MkNotes.vue';
const XPhotos = defineAsyncComponent(() => import('./index.photos.vue'));
const XActivity = defineAsyncComponent(() => import('./index.activity.vue'));
@@ -522,3 +519,11 @@ onUnmounted(() => {
}
}
</style>
+
+<style lang="scss" module>
+.tl {
+ background: var(--bg);
+ border-radius: var(--radius);
+ overflow: clip;
+}
+</style>
diff --git a/packages/frontend/src/pages/user/index.timeline.vue b/packages/frontend/src/pages/user/index.timeline.vue
index 34e16c707d..d8fc253910 100644
--- a/packages/frontend/src/pages/user/index.timeline.vue
+++ b/packages/frontend/src/pages/user/index.timeline.vue
@@ -8,7 +8,7 @@
<option value="files">{{ i18n.ts.withFiles }}</option>
</MkTab>
</template>
- <XNotes :no-gap="true" :pagination="pagination" :class="$style.tl"/>
+ <MkNotes :no-gap="true" :pagination="pagination" :class="$style.tl"/>
</MkStickyContainer>
</MkSpacer>
</template>
@@ -16,9 +16,8 @@
<script lang="ts" setup>
import { ref, computed } from 'vue';
import * as misskey from 'misskey-js';
-import XNotes from '@/components/MkNotes.vue';
+import MkNotes from '@/components/MkNotes.vue';
import MkTab from '@/components/MkTab.vue';
-import * as os from '@/os';
import { i18n } from '@/i18n';
const props = defineProps<{
@@ -32,7 +31,7 @@ const pagination = {
limit: 10,
params: computed(() => ({
userId: props.user.id,
- includeReplies: include.value === 'replies',
+ includeReplies: include.value === 'replies' || include.value === 'files',
withFiles: include.value === 'files',
})),
};
diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue
index 29aef21859..03a226cc09 100644
--- a/packages/frontend/src/pages/user/index.vue
+++ b/packages/frontend/src/pages/user/index.vue
@@ -26,7 +26,6 @@ import * as Acct from 'misskey-js/built/acct';
import * as misskey from 'misskey-js';
import { acct as getAcct } from '@/filters/user';
import * as os from '@/os';
-import { useRouter } from '@/router';
import { definePageMetadata } from '@/scripts/page-metadata';
import { i18n } from '@/i18n';
import { $i } from '@/account';
diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue
index bfa54d39f2..f62a6461c5 100644
--- a/packages/frontend/src/pages/welcome.entrance.a.vue
+++ b/packages/frontend/src/pages/welcome.entrance.a.vue
@@ -1,18 +1,18 @@
<template>
<div v-if="meta" class="rsqzvsbo">
- <div class="top">
- <MkFeaturedPhotos class="bg"/>
- <XTimeline class="tl"/>
- <div class="shape1"></div>
- <div class="shape2"></div>
- <img src="/client-assets/misskey.svg" class="misskey"/>
- <div class="emojis">
- <MkEmoji :normal="true" :no-style="true" emoji="👍"/>
- <MkEmoji :normal="true" :no-style="true" emoji="❤"/>
- <MkEmoji :normal="true" :no-style="true" emoji="😆"/>
- <MkEmoji :normal="true" :no-style="true" emoji="🎉"/>
- <MkEmoji :normal="true" :no-style="true" emoji="🍮"/>
- </div>
+ <MkFeaturedPhotos class="bg"/>
+ <XTimeline class="tl"/>
+ <div class="shape1"></div>
+ <div class="shape2"></div>
+ <img src="/client-assets/misskey.svg" class="misskey"/>
+ <div class="emojis">
+ <MkEmoji :normal="true" :no-style="true" emoji="👍"/>
+ <MkEmoji :normal="true" :no-style="true" emoji="❤"/>
+ <MkEmoji :normal="true" :no-style="true" emoji="😆"/>
+ <MkEmoji :normal="true" :no-style="true" emoji="🎉"/>
+ <MkEmoji :normal="true" :no-style="true" emoji="🍮"/>
+ </div>
+ <div class="contents">
<div class="main">
<img :src="$instance.iconUrl || $instance.faviconUrl || '/favicon.ico'" alt="" class="icon"/>
<button class="_button _acrylic menu" @click="showMenu"><i class="ti ti-dots"></i></button>
@@ -26,66 +26,55 @@
<!-- eslint-disable-next-line vue/no-v-html -->
<div class="desc" v-html="meta.description || i18n.ts.headlineMisskey"></div>
</div>
- <div class="action">
- <MkButton inline rounded gradate data-cy-signup style="margin-right: 12px;" @click="signup()">{{ i18n.ts.signup }}</MkButton>
- <MkButton inline rounded data-cy-signin @click="signin()">{{ i18n.ts.login }}</MkButton>
+ <div class="action _gaps_s">
+ <MkButton full rounded gradate data-cy-signup style="margin-right: 12px;" @click="signup()">{{ i18n.ts.joinThisServer }}</MkButton>
+ <MkButton full rounded @click="exploreOtherServers()">{{ i18n.ts.exploreOtherServers }}</MkButton>
+ <MkButton full rounded data-cy-signin @click="signin()">{{ i18n.ts.login }}</MkButton>
</div>
</div>
</div>
- <div v-if="instances" class="federation">
- <MarqueeText :duration="40">
- <MkA v-for="instance in instances" :key="instance.id" :class="$style.federationInstance" :to="`/instance-info/${instance.host}`" behavior="window">
- <!--<MkInstanceCardMini :instance="instance"/>-->
- <img v-if="instance.iconUrl" class="icon" :src="instance.iconUrl" alt=""/>
- <span class="name _monospace">{{ instance.host }}</span>
- </MkA>
- </MarqueeText>
+ <div v-if="instance.policies.ltlAvailable" class="tl">
+ <div class="title">{{ i18n.ts.letsLookAtTimeline }}</div>
+ <div class="body">
+ <MkTimeline src="local"/>
+ </div>
</div>
</div>
+ <div v-if="instances && instances.length > 0" class="federation">
+ <MarqueeText :duration="40">
+ <MkA v-for="instance in instances" :key="instance.id" :class="$style.federationInstance" :to="`/instance-info/${instance.host}`" behavior="window">
+ <!--<MkInstanceCardMini :instance="instance"/>-->
+ <img v-if="instance.iconUrl" class="icon" :src="instance.iconUrl" alt=""/>
+ <span class="name _monospace">{{ instance.host }}</span>
+ </MkA>
+ </MarqueeText>
+ </div>
</div>
</template>
<script lang="ts" setup>
import { } from 'vue';
-import { toUnicode } from 'punycode/';
+import { Instance } from 'misskey-js/built/entities';
import XTimeline from './welcome.timeline.vue';
import MarqueeText from '@/components/MkMarquee.vue';
import XSigninDialog from '@/components/MkSigninDialog.vue';
import XSignupDialog from '@/components/MkSignupDialog.vue';
import MkButton from '@/components/MkButton.vue';
-import XNote from '@/components/MkNote.vue';
import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue';
-import { host, instanceName } from '@/config';
+import MkTimeline from '@/components/MkTimeline.vue';
+import { instanceName } from '@/config';
import * as os from '@/os';
-import number from '@/filters/number';
import { i18n } from '@/i18n';
+import { instance } from '@/instance';
-let meta = $ref();
-let stats = $ref();
-let tags = $ref();
-let onlineUsersCount = $ref();
-let instances = $ref();
+let meta = $ref<Instance>();
+let instances = $ref<any[]>();
os.api('meta', { detail: true }).then(_meta => {
meta = _meta;
});
-os.api('stats').then(_stats => {
- stats = _stats;
-});
-
-os.api('get-online-users-count').then(res => {
- onlineUsersCount = res.count;
-});
-
-os.api('hashtags/list', {
- sort: '+mentionedLocalUsers',
- limit: 8,
-}).then(_tags => {
- tags = _tags;
-});
-
-os.api('federation/instances', {
+os.apiGet('federation/instances', {
sort: '+pubSub',
limit: 20,
}).then(_instances => {
@@ -125,99 +114,103 @@ function showMenu(ev) {
},
}], ev.currentTarget ?? ev.target);
}
+
+function exploreOtherServers() {
+ // TODO: 言語をよしなに
+ window.open('https://join.misskey.page/ja-JP/instances', '_blank');
+}
</script>
<style lang="scss" scoped>
.rsqzvsbo {
- > .top {
- display: flex;
- text-align: center;
- min-height: 100vh;
- box-sizing: border-box;
- padding: 16px;
+ > .bg {
+ position: fixed;
+ top: 0;
+ right: 0;
+ width: 80vw; // 100%からshapeの幅を引いている
+ height: 100vh;
+ }
+
+ > .tl {
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ right: 64px;
+ margin: auto;
+ padding: 128px 0;
+ width: 500px;
+ height: calc(100% - 256px);
+ overflow: hidden;
+ -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
+ mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
- > .bg {
- position: absolute;
- top: 0;
- right: 0;
- width: 80%; // 100%からshapeの幅を引いている
- height: 100%;
+ @media (max-width: 1200px) {
+ display: none;
}
+ }
- > .tl {
- position: absolute;
- top: 0;
- bottom: 0;
- right: 64px;
- margin: auto;
- width: 500px;
- height: calc(100% - 128px);
- overflow: hidden;
- -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
- mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
+ > .shape1 {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ background: var(--accent);
+ clip-path: polygon(0% 0%, 45% 0%, 20% 100%, 0% 100%);
+ }
+ > .shape2 {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ background: var(--accent);
+ clip-path: polygon(0% 0%, 25% 0%, 35% 100%, 0% 100%);
+ opacity: 0.5;
+ }
- @media (max-width: 1200px) {
- display: none;
- }
- }
+ > .misskey {
+ position: fixed;
+ top: 42px;
+ left: 42px;
+ width: 140px;
- > .shape1 {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: var(--accent);
- clip-path: polygon(0% 0%, 45% 0%, 20% 100%, 0% 100%);
- }
- > .shape2 {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: var(--accent);
- clip-path: polygon(0% 0%, 25% 0%, 35% 100%, 0% 100%);
- opacity: 0.5;
+ @media (max-width: 450px) {
+ width: 130px;
}
+ }
- > .misskey {
- position: absolute;
- top: 42px;
- left: 42px;
- width: 140px;
+ > .emojis {
+ position: fixed;
+ bottom: 32px;
+ left: 35px;
- @media (max-width: 450px) {
- width: 130px;
- }
+ > * {
+ margin-right: 8px;
}
- > .emojis {
- position: absolute;
- bottom: 32px;
- left: 35px;
+ @media (max-width: 1200px) {
+ display: none;
+ }
+ }
- > * {
- margin-right: 8px;
- }
+ > .contents {
+ position: relative;
+ width: min(430px, calc(100% - 32px));
+ margin-left: 128px;
+ padding: 150px 0 100px 0;
- @media (max-width: 1200px) {
- display: none;
- }
+ @media (max-width: 1200px) {
+ margin: auto;
}
> .main {
position: relative;
- width: min(480px, 100%);
- margin: auto auto auto 128px;
background: var(--panel);
border-radius: var(--radius);
box-shadow: 0 12px 32px rgb(0 0 0 / 25%);
-
- @media (max-width: 1200px) {
- margin: auto;
- }
-
+ text-align: center;
+
> .icon {
width: 85px;
margin-top: -47px;
@@ -266,25 +259,44 @@ function showMenu(ev) {
}
}
- > .federation {
- position: absolute;
- bottom: 16px;
- left: 0;
- right: 0;
- margin: auto;
- background: var(--acrylicPanel);
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
- border-radius: 999px;
+ > .tl {
+ position: relative;
+ background: var(--panel);
+ border-radius: var(--radius);
overflow: clip;
- width: 800px;
- padding: 8px 0;
+ box-shadow: 0 12px 32px rgb(0 0 0 / 25%);
+ margin-top: 16px;
+
+ > .title {
+ padding: 12px 16px;
+ border-bottom: solid 1px var(--divider);
+ }
- @media (max-width: 900px) {
- display: none;
+ > .body {
+ height: 350px;
+ overflow: auto;
}
}
}
+
+ > .federation {
+ position: fixed;
+ bottom: 16px;
+ left: 0;
+ right: 0;
+ margin: auto;
+ background: var(--acrylicPanel);
+ -webkit-backdrop-filter: var(--blur, blur(15px));
+ backdrop-filter: var(--blur, blur(15px));
+ border-radius: 999px;
+ overflow: clip;
+ width: 800px;
+ padding: 8px 0;
+
+ @media (max-width: 900px) {
+ display: none;
+ }
+ }
}
</style>
diff --git a/packages/frontend/src/pages/welcome.timeline.vue b/packages/frontend/src/pages/welcome.timeline.vue
index ed3c5a4af7..c34d43dc1c 100644
--- a/packages/frontend/src/pages/welcome.timeline.vue
+++ b/packages/frontend/src/pages/welcome.timeline.vue
@@ -1,62 +1,54 @@
<template>
-<div class="civpbkhh">
- <div ref="scroll" class="scrollbox" v-bind:class="{ scroll: isScrolling }">
- <div v-for="note in notes" class="note">
- <div class="content _panel">
- <div class="body">
+<div :class="$style.root">
+ <div ref="scrollEl" :class="[$style.scrollbox, { [$style.scroll]: isScrolling }]">
+ <div v-for="note in notes" :key="note.id" :class="$style.note">
+ <div class="_panel" :class="$style.content">
+ <div :class="$style.body">
<MkA v-if="note.replyId" class="reply" :to="`/notes/${note.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA>
- <Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$i"/>
+ <Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$i" />
<MkA v-if="note.renoteId" class="rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA>
</div>
- <div v-if="note.files.length > 0" class="richcontent">
- <MkMediaList :media-list="note.files"/>
+ <div v-if="note.files.length > 0" :class="$style.richcontent">
+ <MkMediaList :media-list="note.files" />
</div>
<div v-if="note.poll">
- <MkPoll :note="note" :readOnly="true"/>
+ <MkPoll :note="note" :readOnly="true" />
</div>
</div>
- <MkReactionsViewer ref="reactionsViewer" :note="note"/>
+ <MkReactionsViewer ref="reactionsViewer" :note="note" />
</div>
</div>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
import MkMediaList from '@/components/MkMediaList.vue';
import MkPoll from '@/components/MkPoll.vue';
import * as os from '@/os';
+import { Note } from 'misskey-js/built/entities';
+import { onUpdated } from 'vue';
+import { getScrollContainer } from '@/scripts/scroll';
-export default defineComponent({
- components: {
- MkReactionsViewer,
- MkMediaList,
- MkPoll,
- },
+let notes = $ref<Note[]>([]);
+let isScrolling = $ref(false);
+let scrollEl = $ref<HTMLElement>();
- data() {
- return {
- notes: [],
- isScrolling: false,
- };
- },
-
- created() {
- os.api('notes/featured').then(notes => {
- this.notes = notes;
- });
- },
+os.apiGet('notes/featured').then(_notes => {
+ notes = _notes;
+});
- updated() {
- if (this.$refs.scroll.clientHeight > window.innerHeight) {
- this.isScrolling = true;
- }
- },
+onUpdated(() => {
+ if (!scrollEl) return;
+ const container = getScrollContainer(scrollEl);
+ const containerHeight = container ? container.clientHeight : window.innerHeight;
+ if (scrollEl.offsetHeight > containerHeight) {
+ isScrolling = true;
+ }
});
</script>
-<style lang="scss" scoped>
+<style lang="scss" module>
@keyframes scroll {
0% {
transform: translate3d(0, 0, 0);
@@ -72,28 +64,28 @@ export default defineComponent({
}
}
-.civpbkhh {
+.root {
text-align: right;
+}
- > .scrollbox {
- &.scroll {
- animation: scroll 45s linear infinite;
- }
+.scrollbox {
+ &.scroll {
+ animation: scroll 45s linear infinite;
+ }
+}
- > .note {
- margin: 16px 0 16px auto;
+.note {
+ margin: 16px 0 16px auto;
+}
- > .content {
- padding: 16px;
- margin: 0 0 0 auto;
- max-width: max-content;
- border-radius: 16px;
+.content {
+ padding: 16px;
+ margin: 0 0 0 auto;
+ max-width: max-content;
+ border-radius: 16px;
+}
- > .richcontent {
- min-width: 250px;
- }
- }
- }
- }
+.richcontent {
+ min-width: 250px;
}
</style>
diff --git a/packages/frontend/src/pizzax.ts b/packages/frontend/src/pizzax.ts
index 2ca89b7351..2616a8a1d5 100644
--- a/packages/frontend/src/pizzax.ts
+++ b/packages/frontend/src/pizzax.ts
@@ -48,8 +48,8 @@ export class Storage<T extends StateDef> {
// 簡易的にキューイングして占有ロックとする
private currentIdbJob: Promise<any> = Promise.resolve();
private addIdbSetJob<T>(job: () => Promise<T>) {
- const promise = this.currentIdbJob.then(job, e => {
- console.error('Pizzax failed to save data to idb!', e);
+ const promise = this.currentIdbJob.then(job, err => {
+ console.error('Pizzax failed to save data to idb!', err);
return job();
});
this.currentIdbJob = promise;
@@ -130,22 +130,22 @@ export class Storage<T extends StateDef> {
await defaultStore.ready;
api('i/registry/get-all', { scope: ['client', this.key] })
- .then(kvs => {
- const cache: Partial<T> = {};
- for (const [k, v] of Object.entries(this.def) as [keyof T, T[keyof T]['default']][]) {
- if (v.where === 'account') {
- if (Object.prototype.hasOwnProperty.call(kvs, k)) {
- this.reactiveState[k].value = this.state[k] = (kvs as Partial<T>)[k];
- cache[k] = (kvs as Partial<T>)[k];
- } else {
- this.reactiveState[k].value = this.state[k] = v.default;
+ .then(kvs => {
+ const cache: Partial<T> = {};
+ for (const [k, v] of Object.entries(this.def) as [keyof T, T[keyof T]['default']][]) {
+ if (v.where === 'account') {
+ if (Object.prototype.hasOwnProperty.call(kvs, k)) {
+ this.reactiveState[k].value = this.state[k] = (kvs as Partial<T>)[k];
+ cache[k] = (kvs as Partial<T>)[k];
+ } else {
+ this.reactiveState[k].value = this.state[k] = v.default;
+ }
}
}
- }
- return set(this.registryCacheKeyName, cache);
- })
- .then(() => resolve());
+ return set(this.registryCacheKeyName, cache);
+ })
+ .then(() => resolve());
}, 1);
} else {
resolve();
diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts
index 9004262689..9521e01910 100644
--- a/packages/frontend/src/router.ts
+++ b/packages/frontend/src/router.ts
@@ -3,7 +3,6 @@ import { Router } from '@/nirax';
import { $i, iAmModerator } from '@/account';
import MkLoading from '@/pages/_loading_.vue';
import MkError from '@/pages/_error_.vue';
-import { ui } from '@/config';
const page = (loader: AsyncComponentLoader<any>) => defineAsyncComponent({
loader: loader,
@@ -199,8 +198,11 @@ export const routes = [{
component: page(() => import('./pages/theme-editor.vue')),
loginRequired: true,
}, {
- path: '/explore/tags/:tag',
- component: page(() => import('./pages/explore.vue')),
+ path: '/roles/:role',
+ component: page(() => import('./pages/role.vue')),
+}, {
+ path: '/user-tags/:tag',
+ component: page(() => import('./pages/user-tag.vue')),
}, {
path: '/explore',
component: page(() => import('./pages/explore.vue')),
@@ -421,19 +423,6 @@ export const routes = [{
component: page(() => import('./pages/achievements.vue')),
loginRequired: true,
}, {
- name: 'messaging',
- path: '/my/messaging',
- component: page(() => import('./pages/messaging/index.vue')),
- loginRequired: true,
-}, {
- path: '/my/messaging/:userAcct',
- component: page(() => import('./pages/messaging/messaging-room.vue')),
- loginRequired: true,
-}, {
- path: '/my/messaging/group/:groupId',
- component: page(() => import('./pages/messaging/messaging-room.vue')),
- loginRequired: true,
-}, {
path: '/my/drive/folder/:folder',
component: page(() => import('./pages/drive.vue')),
loginRequired: true,
diff --git a/packages/frontend/src/scripts/aiscript/ui.ts b/packages/frontend/src/scripts/aiscript/ui.ts
index fb73c0b4b7..6b8041d78e 100644
--- a/packages/frontend/src/scripts/aiscript/ui.ts
+++ b/packages/frontend/src/scripts/aiscript/ui.ts
@@ -1,4 +1,4 @@
-import { Interpreter, Parser, utils, values } from '@syuilo/aiscript';
+import { utils, values } from '@syuilo/aiscript';
import { v4 as uuid } from 'uuid';
import { ref, Ref } from 'vue';
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index b5d2251d28..9da7447bfd 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -1,6 +1,5 @@
-import { defineAsyncComponent, Ref, inject } from 'vue';
+import { defineAsyncComponent, Ref } from 'vue';
import * as misskey from 'misskey-js';
-import { pleaseLogin } from './please-login';
import { claimAchievement } from './achievements';
import { $i } from '@/account';
import { i18n } from '@/i18n';
@@ -9,7 +8,6 @@ import * as os from '@/os';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import { url } from '@/config';
import { noteActions } from '@/store';
-import { notePage } from '@/filters/note';
import { miLocalStorage } from '@/local-storage';
export function getNoteMenu(props: {
@@ -202,7 +200,7 @@ export function getNoteMenu(props: {
props.translating.value = true;
const res = await os.api('notes/translate', {
noteId: appearNote.id,
- targetLang: miLocalStorage.getItem('lang') || navigator.language,
+ targetLang: miLocalStorage.getItem('lang') ?? navigator.language,
});
props.translating.value = false;
props.translation.value = res;
@@ -242,7 +240,7 @@ export function getNoteMenu(props: {
icon: 'ti ti-external-link',
text: i18n.ts.showOnRemote,
action: () => {
- window.open(appearNote.url || appearNote.uri, '_blank');
+ window.open(appearNote.url ?? appearNote.uri, '_blank');
},
} : undefined,
{
@@ -292,7 +290,7 @@ export function getNoteMenu(props: {
...($i.isModerator || $i.isAdmin ? [
null,
{
- icon: 'fas fa-bullhorn',
+ icon: 'ti ti-speakerphone',
text: i18n.ts.promote,
action: promote
}]
@@ -304,7 +302,7 @@ export function getNoteMenu(props: {
icon: 'ti ti-exclamation-circle',
text: i18n.ts.reportAbuse,
action: () => {
- const u = appearNote.url || appearNote.uri || `${url}/notes/${appearNote.id}`;
+ const u = appearNote.url ?? appearNote.uri ?? `${url}/notes/${appearNote.id}`;
os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
user: appearNote.user,
initialComment: `Note: ${u}\n-----\n`,
@@ -346,7 +344,7 @@ export function getNoteMenu(props: {
icon: 'ti ti-external-link',
text: i18n.ts.showOnRemote,
action: () => {
- window.open(appearNote.url || appearNote.uri, '_blank');
+ window.open(appearNote.url ?? appearNote.uri, '_blank');
},
} : undefined]
.filter(x => x !== undefined);
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index 941d9a0db9..557b257f62 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -1,4 +1,3 @@
-import * as Acct from 'misskey-js/built/acct';
import { defineAsyncComponent } from 'vue';
import { i18n } from '@/i18n';
import copyToClipboard from '@/scripts/copy-to-clipboard';
@@ -35,28 +34,6 @@ export function getUserMenu(user, router: Router = mainRouter) {
});
}
- async function inviteGroup() {
- const groups = await os.api('users/groups/owned');
- if (groups.length === 0) {
- os.alert({
- type: 'error',
- text: i18n.ts.youHaveNoGroups,
- });
- return;
- }
- const { canceled, result: groupId } = await os.select({
- title: i18n.ts.group,
- items: groups.map(group => ({
- value: group.id, text: group.name,
- })),
- });
- if (canceled) return;
- os.apiWithDialog('users/groups/invite', {
- groupId: groupId,
- userId: user.id,
- });
- }
-
async function toggleMute() {
if (user.isMuted) {
os.apiWithDialog('mute/delete', {
@@ -156,20 +133,11 @@ export function getUserMenu(user, router: Router = mainRouter) {
action: () => {
os.post({ specified: user });
},
- }, meId !== user.id ? {
- type: 'link',
- icon: 'ti ti-messages',
- text: i18n.ts.startMessaging,
- to: '/my/messaging/' + Acct.toString(user),
- } : undefined, null, {
+ }, null, {
icon: 'ti ti-list',
text: i18n.ts.addToList,
action: pushList,
- }, meId !== user.id ? {
- icon: 'ti ti-users',
- text: i18n.ts.inviteToGroup,
- action: inviteGroup,
- } : undefined] as any;
+ }] as any;
if ($i && meId !== user.id) {
menu = menu.concat([null, {
diff --git a/packages/frontend/src/scripts/get-user-name.ts b/packages/frontend/src/scripts/get-user-name.ts
index d499ea0203..4daf203e06 100644
--- a/packages/frontend/src/scripts/get-user-name.ts
+++ b/packages/frontend/src/scripts/get-user-name.ts
@@ -1,3 +1,3 @@
export default function(user: { name?: string | null, username: string }): string {
- return user.name || user.username;
+ return user.name === '' ? user.username : user.name ?? user.username;
}
diff --git a/packages/frontend/src/scripts/hpml/evaluator.ts b/packages/frontend/src/scripts/hpml/evaluator.ts
index d4090ea15c..7bddd3f62d 100644
--- a/packages/frontend/src/scripts/hpml/evaluator.ts
+++ b/packages/frontend/src/scripts/hpml/evaluator.ts
@@ -1,11 +1,10 @@
import autobind from 'autobind-decorator';
-import { markRaw, ref, Ref, unref } from 'vue';
+import { ref, Ref, unref } from 'vue';
import { collectPageVars } from '../collect-page-vars';
-import { initHpmlLib, initAiLib } from './lib';
+import { initHpmlLib } from './lib';
import { Expr, isLiteralValue, Variable } from './expr';
import { PageVar, envVarsDef, Fn, HpmlScope, HpmlError } from '.';
import { version } from '@/config';
-import * as os from '@/os';
/**
* Hpml evaluator
diff --git a/packages/frontend/src/scripts/hpml/index.ts b/packages/frontend/src/scripts/hpml/index.ts
index 9a55a5c286..587c6a36c8 100644
--- a/packages/frontend/src/scripts/hpml/index.ts
+++ b/packages/frontend/src/scripts/hpml/index.ts
@@ -15,12 +15,12 @@ export type Type = 'string' | 'number' | 'boolean' | 'stringArray' | null;
export const literalDefs: Record<string, { out: any; category: string; icon: any; }> = {
text: { out: 'string', category: 'value', icon: 'ti ti-quote' },
- multiLineText: { out: 'string', category: 'value', icon: 'fas fa-align-left' },
- textList: { out: 'stringArray', category: 'value', icon: 'fas fa-list' },
- number: { out: 'number', category: 'value', icon: 'fas fa-sort-numeric-up' },
- ref: { out: null, category: 'value', icon: 'fas fa-magic' },
- aiScriptVar: { out: null, category: 'value', icon: 'fas fa-magic' },
- fn: { out: 'function', category: 'value', icon: 'fas fa-square-root-alt' },
+ multiLineText: { out: 'string', category: 'value', icon: 'ti ti-align-left' },
+ textList: { out: 'stringArray', category: 'value', icon: 'ti ti-list' },
+ number: { out: 'number', category: 'value', icon: 'ti ti-list-numbers' },
+ ref: { out: null, category: 'value', icon: 'ti ti-wand' },
+ aiScriptVar: { out: null, category: 'value', icon: 'ti ti-wand' },
+ fn: { out: 'function', category: 'value', icon: 'ti ti-math-function' },
};
export const blockDefs = [
@@ -58,7 +58,7 @@ export class HpmlScope {
constructor(layerdStates: HpmlScope['layerdStates'], name?: HpmlScope['name']) {
this.layerdStates = layerdStates;
- this.name = name || 'anonymous';
+ this.name = name ?? 'anonymous';
}
@autobind
diff --git a/packages/frontend/src/scripts/hpml/lib.ts b/packages/frontend/src/scripts/hpml/lib.ts
index 02d663b31b..88db82dd27 100644
--- a/packages/frontend/src/scripts/hpml/lib.ts
+++ b/packages/frontend/src/scripts/hpml/lib.ts
@@ -1,4 +1,3 @@
-import tinycolor from 'tinycolor2';
import seedrandom from 'seedrandom';
import { Hpml } from './evaluator';
import { Expr } from './expr';
@@ -130,42 +129,42 @@ export function initAiLib(hpml: Hpml) {
export const funcDefs: Record<string, { in: any[]; out: any; category: string; icon: any; }> = {
if: { in: ['boolean', 0, 0], out: 0, category: 'flow', icon: 'ti ti-share' },
- for: { in: ['number', 'function'], out: null, category: 'flow', icon: 'fas fa-recycle' },
- not: { in: ['boolean'], out: 'boolean', category: 'logical', icon: 'fas fa-flag' },
- or: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: 'fas fa-flag' },
- and: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: 'fas fa-flag' },
+ for: { in: ['number', 'function'], out: null, category: 'flow', icon: 'ti ti-recycle' },
+ not: { in: ['boolean'], out: 'boolean', category: 'logical', icon: 'ti ti-flag' },
+ or: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: 'ti ti-flag' },
+ and: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: 'ti ti-flag' },
add: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'ti ti-plus' },
subtract: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'ti ti-minus' },
multiply: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'ti ti-x' },
- divide: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'fas fa-divide' },
- mod: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'fas fa-divide' },
- round: { in: ['number'], out: 'number', category: 'operation', icon: 'fas fa-calculator' },
- eq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: 'fas fa-equals' },
- notEq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: 'fas fa-not-equal' },
- gt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'fas fa-greater-than' },
- lt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'fas fa-less-than' },
- gtEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'fas fa-greater-than-equal' },
- ltEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'fas fa-less-than-equal' },
+ divide: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'ti ti-divide' },
+ mod: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'ti ti-divide' },
+ round: { in: ['number'], out: 'number', category: 'operation', icon: 'ti ti-calculator' },
+ eq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: 'ti ti-equal' },
+ notEq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: 'ti ti-equal-not' },
+ gt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'ti ti-math-greater' },
+ lt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'ti ti-math-lower' },
+ gtEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'ti ti-math-equal-greater' },
+ ltEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'ti ti-math-equal-lower' },
strLen: { in: ['string'], out: 'number', category: 'text', icon: 'ti ti-quote' },
strPick: { in: ['string', 'number'], out: 'string', category: 'text', icon: 'ti ti-quote' },
strReplace: { in: ['string', 'string', 'string'], out: 'string', category: 'text', icon: 'ti ti-quote' },
strReverse: { in: ['string'], out: 'string', category: 'text', icon: 'ti ti-quote' },
join: { in: ['stringArray', 'string'], out: 'string', category: 'text', icon: 'ti ti-quote' },
- stringToNumber: { in: ['string'], out: 'number', category: 'convert', icon: 'fas fa-exchange-alt' },
- numberToString: { in: ['number'], out: 'string', category: 'convert', icon: 'fas fa-exchange-alt' },
- splitStrByLine: { in: ['string'], out: 'stringArray', category: 'convert', icon: 'fas fa-exchange-alt' },
- pick: { in: [null, 'number'], out: null, category: 'list', icon: 'fas fa-indent' },
- listLen: { in: [null], out: 'number', category: 'list', icon: 'fas fa-indent' },
- rannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: 'fas fa-dice' },
- dailyRannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: 'fas fa-dice' },
- seedRannum: { in: [null, 'number', 'number'], out: 'number', category: 'random', icon: 'fas fa-dice' },
- random: { in: ['number'], out: 'boolean', category: 'random', icon: 'fas fa-dice' },
- dailyRandom: { in: ['number'], out: 'boolean', category: 'random', icon: 'fas fa-dice' },
- seedRandom: { in: [null, 'number'], out: 'boolean', category: 'random', icon: 'fas fa-dice' },
- randomPick: { in: [0], out: 0, category: 'random', icon: 'fas fa-dice' },
- dailyRandomPick: { in: [0], out: 0, category: 'random', icon: 'fas fa-dice' },
- seedRandomPick: { in: [null, 0], out: 0, category: 'random', icon: 'fas fa-dice' },
- DRPWPM: { in: ['stringArray'], out: 'string', category: 'random', icon: 'fas fa-dice' }, // dailyRandomPickWithProbabilityMapping
+ stringToNumber: { in: ['string'], out: 'number', category: 'convert', icon: 'ti ti-arrows-right-left' },
+ numberToString: { in: ['number'], out: 'string', category: 'convert', icon: 'ti ti-arrows-right-left' },
+ splitStrByLine: { in: ['string'], out: 'stringArray', category: 'convert', icon: 'ti ti-arrows-right-left' },
+ pick: { in: [null, 'number'], out: null, category: 'list', icon: 'ti ti-indent-increase' },
+ listLen: { in: [null], out: 'number', category: 'list', icon: 'ti ti-indent-increase' },
+ rannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: 'ti ti-dice' },
+ dailyRannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: 'ti ti-dice' },
+ seedRannum: { in: [null, 'number', 'number'], out: 'number', category: 'random', icon: 'ti ti-dice' },
+ random: { in: ['number'], out: 'boolean', category: 'random', icon: 'ti ti-dice' },
+ dailyRandom: { in: ['number'], out: 'boolean', category: 'random', icon: 'ti ti-dice' },
+ seedRandom: { in: [null, 'number'], out: 'boolean', category: 'random', icon: 'ti ti-dice' },
+ randomPick: { in: [0], out: 0, category: 'random', icon: 'ti ti-dice' },
+ dailyRandomPick: { in: [0], out: 0, category: 'random', icon: 'ti ti-dice' },
+ seedRandomPick: { in: [null, 0], out: 0, category: 'random', icon: 'ti ti-dice' },
+ DRPWPM: { in: ['stringArray'], out: 'string', category: 'random', icon: 'ti ti-dice' }, // dailyRandomPickWithProbabilityMapping
};
export function initHpmlLib(expr: Expr, scope: HpmlScope, randomSeed: string, visitor?: any) {
diff --git a/packages/frontend/src/scripts/hpml/type-checker.ts b/packages/frontend/src/scripts/hpml/type-checker.ts
index 24c9ed8bcb..692826fc90 100644
--- a/packages/frontend/src/scripts/hpml/type-checker.ts
+++ b/packages/frontend/src/scripts/hpml/type-checker.ts
@@ -63,7 +63,7 @@ export class HpmlTypeChecker {
@autobind
public getExpectedType(v: Expr, slot: number): Type {
- const def = funcDefs[v.type || ''];
+ const def = funcDefs[v.type ?? ''];
if (def == null) {
throw new Error('Unknown type: ' + v.type);
}
@@ -107,7 +107,7 @@ export class HpmlTypeChecker {
return pageVar.type;
}
- const envVar = envVarsDef[v.value || ''];
+ const envVar = envVarsDef[v.value ?? ''];
if (envVar !== undefined) {
return envVar;
}
diff --git a/packages/frontend/src/scripts/popup-position.ts b/packages/frontend/src/scripts/popup-position.ts
index e84eebf103..cb45002202 100644
--- a/packages/frontend/src/scripts/popup-position.ts
+++ b/packages/frontend/src/scripts/popup-position.ts
@@ -1,4 +1,3 @@
-import { Ref } from 'vue';
export function calcPopupPosition(el: HTMLElement, props: {
anchorElement: HTMLElement | null;
diff --git a/packages/frontend/src/scripts/scroll.ts b/packages/frontend/src/scripts/scroll.ts
index e3d9dc00c2..a002f02b5a 100644
--- a/packages/frontend/src/scripts/scroll.ts
+++ b/packages/frontend/src/scripts/scroll.ts
@@ -10,7 +10,7 @@ export function getScrollContainer(el: HTMLElement | null): HTMLElement | null {
}
}
-export function getStickyTop(el: HTMLElement, container: HTMLElement | null = null, top: number = 0) {
+export function getStickyTop(el: HTMLElement, container: HTMLElement | null = null, top = 0) {
if (!el.parentElement) return top;
const data = el.dataset.stickyContainerHeaderHeight;
const newTop = data ? Number(data) + top : top;
@@ -23,14 +23,14 @@ export function getScrollPosition(el: HTMLElement | null): number {
return container == null ? window.scrollY : container.scrollTop;
}
-export function onScrollTop(el: HTMLElement, cb: () => unknown, tolerance: number = 1, once: boolean = false) {
+export function onScrollTop(el: HTMLElement, cb: () => unknown, tolerance = 1, once = false) {
// とりあえず評価してみる
if (isTopVisible(el)) {
cb();
if (once) return null;
}
- const container = getScrollContainer(el) || window;
+ const container = getScrollContainer(el) ?? window;
const onScroll = ev => {
if (!document.body.contains(el)) return;
@@ -45,7 +45,7 @@ export function onScrollTop(el: HTMLElement, cb: () => unknown, tolerance: numbe
return removeListener;
}
-export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance: number = 1, once: boolean = false) {
+export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance = 1, once = false) {
const container = getScrollContainer(el);
// とりあえず評価してみる
@@ -54,7 +54,7 @@ export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance: nu
if (once) return null;
}
- const containerOrWindow = container || window;
+ const containerOrWindow = container ?? window;
const onScroll = ev => {
if (!document.body.contains(el)) return;
if (isBottomVisible(el, 1, container)) {
@@ -104,12 +104,12 @@ export function scrollToBottom(
} else {
window.scroll({
top: (el.scrollHeight - window.innerHeight + getStickyTop(el, container) + (window.innerWidth <= 500 ? 96 : 0)) || 0,
- ...options
+ ...options,
});
}
}
-export function isTopVisible(el: HTMLElement, tolerance: number = 1): boolean {
+export function isTopVisible(el: HTMLElement, tolerance = 1): boolean {
const scrollTop = getScrollPosition(el);
return scrollTop <= tolerance;
}
@@ -124,6 +124,6 @@ export function getBodyScrollHeight() {
return Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
- document.body.clientHeight, document.documentElement.clientHeight
+ document.body.clientHeight, document.documentElement.clientHeight,
);
}
diff --git a/packages/frontend/src/scripts/search.ts b/packages/frontend/src/scripts/search.ts
index 64914d3d65..69f1586b77 100644
--- a/packages/frontend/src/scripts/search.ts
+++ b/packages/frontend/src/scripts/search.ts
@@ -35,7 +35,7 @@ export async function search() {
// TODO
//v.$root.$emit('warp', date);
os.alert({
- icon: 'fas fa-history',
+ icon: 'ti ti-history',
iconOnly: true, autoClose: true,
});
return;
diff --git a/packages/frontend/src/scripts/use-leave-guard.ts b/packages/frontend/src/scripts/use-leave-guard.ts
index a93b84d1fe..146b012471 100644
--- a/packages/frontend/src/scripts/use-leave-guard.ts
+++ b/packages/frontend/src/scripts/use-leave-guard.ts
@@ -1,6 +1,4 @@
-import { inject, onUnmounted, Ref } from 'vue';
-import { i18n } from '@/i18n';
-import * as os from '@/os';
+import { Ref } from 'vue';
export function useLeaveGuard(enabled: Ref<boolean>) {
/* TODO
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 46e55900cd..54c159ed6b 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -1,6 +1,5 @@
import { markRaw, ref } from 'vue';
import { Storage } from './pizzax';
-import { Theme } from './scripts/theme';
interface PostFormAction {
title: string,
diff --git a/packages/frontend/src/theme-store.ts b/packages/frontend/src/theme-store.ts
index aa1244665b..580c7da007 100644
--- a/packages/frontend/src/theme-store.ts
+++ b/packages/frontend/src/theme-store.ts
@@ -1,13 +1,13 @@
-import { api } from '@/os';
-import { $i } from '@/account';
import { Theme } from './scripts/theme';
import { miLocalStorage } from './local-storage';
+import { api } from '@/os';
+import { $i } from '@/account';
const lsCacheKey = $i ? `themes:${$i.id}` as const : null;
export function getThemes(): Theme[] {
if ($i == null) return [];
- return JSON.parse(miLocalStorage.getItem(lsCacheKey!) || '[]');
+ return JSON.parse(miLocalStorage.getItem(lsCacheKey!) ?? '[]');
}
export async function fetchThemes(): Promise<void> {
diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue
index 342b3f2dbf..976345f9ee 100644
--- a/packages/frontend/src/ui/_common_/common.vue
+++ b/packages/frontend/src/ui/_common_/common.vue
@@ -30,11 +30,11 @@
</template>
<script lang="ts" setup>
-import { defineAsyncComponent, nextTick } from 'vue';
+import { defineAsyncComponent } from 'vue';
import * as misskey from 'misskey-js';
import { swInject } from './sw-inject';
import XNotification from './notification.vue';
-import { popup, popups, pendingApiRequestsCount } from '@/os';
+import { popups, pendingApiRequestsCount } from '@/os';
import { uploads } from '@/scripts/upload';
import * as sound from '@/scripts/sound';
import { $i } from '@/account';
diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
index 357db5599f..935aceea7c 100644
--- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
+++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
@@ -43,7 +43,7 @@
</template>
<script lang="ts" setup>
-import { computed, defineAsyncComponent, ref, toRef, watch } from 'vue';
+import { computed, defineAsyncComponent, toRef } from 'vue';
import { openInstanceMenu } from './common';
import * as os from '@/os';
import { navbarItemDef } from '@/navbar';
diff --git a/packages/frontend/src/ui/_common_/statusbar-federation.vue b/packages/frontend/src/ui/_common_/statusbar-federation.vue
index 70d683d755..fe95460ba4 100644
--- a/packages/frontend/src/ui/_common_/statusbar-federation.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-federation.vue
@@ -20,13 +20,11 @@
</template>
<script lang="ts" setup>
-import { computed, defineAsyncComponent, ref, toRef, watch } from 'vue';
+import { ref } from 'vue';
import * as misskey from 'misskey-js';
import MarqueeText from '@/components/MkMarquee.vue';
import * as os from '@/os';
import { useInterval } from '@/scripts/use-interval';
-import { getNoteSummary } from '@/scripts/get-note-summary';
-import { notePage } from '@/filters/note';
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy';
const props = defineProps<{
diff --git a/packages/frontend/src/ui/_common_/statusbar-rss.vue b/packages/frontend/src/ui/_common_/statusbar-rss.vue
index e59ace8876..44b6b278ea 100644
--- a/packages/frontend/src/ui/_common_/statusbar-rss.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-rss.vue
@@ -16,9 +16,8 @@
</template>
<script lang="ts" setup>
-import { computed, defineAsyncComponent, ref, toRef, watch } from 'vue';
+import { ref } from 'vue';
import MarqueeText from '@/components/MkMarquee.vue';
-import * as os from '@/os';
import { useInterval } from '@/scripts/use-interval';
import { shuffle } from '@/scripts/shuffle';
diff --git a/packages/frontend/src/ui/_common_/statusbar-user-list.vue b/packages/frontend/src/ui/_common_/statusbar-user-list.vue
index 6fec81de36..16df69d968 100644
--- a/packages/frontend/src/ui/_common_/statusbar-user-list.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-user-list.vue
@@ -20,7 +20,7 @@
</template>
<script lang="ts" setup>
-import { computed, defineAsyncComponent, ref, toRef, watch } from 'vue';
+import { ref, watch } from 'vue';
import * as misskey from 'misskey-js';
import MarqueeText from '@/components/MkMarquee.vue';
import * as os from '@/os';
diff --git a/packages/frontend/src/ui/_common_/statusbars.vue b/packages/frontend/src/ui/_common_/statusbars.vue
index 114ca5be8c..f84695c15f 100644
--- a/packages/frontend/src/ui/_common_/statusbars.vue
+++ b/packages/frontend/src/ui/_common_/statusbars.vue
@@ -18,8 +18,7 @@
</template>
<script lang="ts" setup>
-import { computed, defineAsyncComponent, ref, toRef, watch } from 'vue';
-import * as os from '@/os';
+import { defineAsyncComponent } from 'vue';
import { defaultStore } from '@/store';
const XRss = defineAsyncComponent(() => import('./statusbar-rss.vue'));
const XFederation = defineAsyncComponent(() => import('./statusbar-federation.vue'));
diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue
index 02dafcc3b6..a359463d9b 100644
--- a/packages/frontend/src/ui/classic.vue
+++ b/packages/frontend/src/ui/classic.vue
@@ -41,14 +41,14 @@
</template>
<script lang="ts" setup>
-import { defineAsyncComponent, markRaw, ComputedRef, ref, onMounted, provide } from 'vue';
+import { defineAsyncComponent, ComputedRef, onMounted, provide } from 'vue';
import XSidebar from './classic.sidebar.vue';
import XCommon from './_common_/common.vue';
import { instanceName } from '@/config';
import { StickySidebar } from '@/scripts/sticky-sidebar';
import * as os from '@/os';
import { mainRouter } from '@/router';
-import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata';
+import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
import { defaultStore } from '@/store';
import { i18n } from '@/i18n';
import { miLocalStorage } from '@/local-storage';
@@ -105,7 +105,7 @@ function onContextmenu(ev: MouseEvent) {
type: 'label',
text: path,
}, {
- icon: fullView ? 'fas fa-compress' : 'fas fa-expand',
+ icon: fullView ? 'ti ti-minimize' : 'ti ti-maximize',
text: fullView ? i18n.ts.quitFullView : i18n.ts.fullView,
action: () => {
fullView = !fullView;
@@ -124,8 +124,8 @@ function onAiClick(ev) {
}
if (window.innerWidth < 1024) {
- const currentUI = miLocalStorage.getItem('ui')
- miLocalStorage.setItem('ui_temp', currentUI || 'default');
+ const currentUI = miLocalStorage.getItem('ui');
+ miLocalStorage.setItem('ui_temp', currentUI ?? 'default');
miLocalStorage.setItem('ui', 'default');
location.reload();
}
diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index b09721dec9..4e93359591 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -4,7 +4,7 @@
<div :class="$style.main">
<XStatusBars/>
- <div ref="columnsEl" :class="[$style.columns, deckStore.reactiveState.columnAlign.value]" @contextmenu.self.prevent="onContextmenu">
+ <div ref="columnsEl" :class="[$style.columns, deckStore.reactiveState.columnAlign.value, { [$style.snapScroll]: snapScroll }]" @contextmenu.self.prevent="onContextmenu">
<template v-for="ids in layout">
<!-- sectionを利用しているのは、deck.vue側でcolumnに対してfirst-of-typeを効かせるため -->
<section
@@ -83,7 +83,7 @@
</template>
<script lang="ts" setup>
-import { computed, defineAsyncComponent, onMounted, provide, ref, watch } from 'vue';
+import { computed, defineAsyncComponent, ref, watch } from 'vue';
import { v4 as uuid } from 'uuid';
import XCommon from './_common_/common.vue';
import { deckStore, addColumn as addColumnToStore, loadDeck, getProfiles, deleteProfile as deleteProfile_ } from './deck/deck-store';
@@ -115,6 +115,7 @@ window.addEventListener('resize', () => {
isMobile.value = window.innerWidth <= 500;
});
+const snapScroll = isMobile;
const drawerMenuShowing = ref(false);
const route = 'TODO';
@@ -297,9 +298,14 @@ async function deleteProfile() {
margin-right: auto;
}
}
+
+ &.snapScroll {
+ scroll-snap-type: x mandatory;
+ }
}
.column {
+ scroll-snap-align: start;
flex-shrink: 0;
border-right: solid var(--deckDividerThickness) var(--deckDivider);
diff --git a/packages/frontend/src/ui/deck/antenna-column.vue b/packages/frontend/src/ui/deck/antenna-column.vue
index 4c69c8e8e8..76a8b6e760 100644
--- a/packages/frontend/src/ui/deck/antenna-column.vue
+++ b/packages/frontend/src/ui/deck/antenna-column.vue
@@ -4,7 +4,7 @@
<i class="ti ti-antenna"></i><span style="margin-left: 8px;">{{ column.name }}</span>
</template>
- <XTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId" @after="() => emit('loaded')"/>
+ <MkTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId" @after="() => emit('loaded')"/>
</XColumn>
</template>
@@ -12,7 +12,7 @@
import { onMounted } from 'vue';
import XColumn from './column.vue';
import { updateColumn, Column } from './deck-store';
-import XTimeline from '@/components/MkTimeline.vue';
+import MkTimeline from '@/components/MkTimeline.vue';
import * as os from '@/os';
import { i18n } from '@/i18n';
@@ -26,7 +26,7 @@ const emit = defineEmits<{
(ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
-let timeline = $shallowRef<InstanceType<typeof XTimeline>>();
+let timeline = $shallowRef<InstanceType<typeof MkTimeline>>();
onMounted(() => {
if (props.column.antennaId == null) {
diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue
index 5a84237c80..4c6b41e42e 100644
--- a/packages/frontend/src/ui/deck/channel-column.vue
+++ b/packages/frontend/src/ui/deck/channel-column.vue
@@ -8,7 +8,7 @@
<div style="padding: 8px; text-align: center;">
<MkButton primary gradate rounded inline @click="post"><i class="ti ti-pencil"></i></MkButton>
</div>
- <XTimeline ref="timeline" src="channel" :channel="column.channelId" @after="() => emit('loaded')"/>
+ <MkTimeline ref="timeline" src="channel" :channel="column.channelId" @after="() => emit('loaded')"/>
</template>
</XColumn>
</template>
@@ -17,7 +17,7 @@
import { } from 'vue';
import XColumn from './column.vue';
import { updateColumn, Column } from './deck-store';
-import XTimeline from '@/components/MkTimeline.vue';
+import MkTimeline from '@/components/MkTimeline.vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os';
import { i18n } from '@/i18n';
@@ -32,14 +32,16 @@ const emit = defineEmits<{
(ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
-let timeline = $shallowRef<InstanceType<typeof XTimeline>>();
+let timeline = $shallowRef<InstanceType<typeof MkTimeline>>();
if (props.column.channelId == null) {
setChannel();
}
async function setChannel() {
- const channels = await os.api('channels/followed');
+ const channels = await os.api('channels/followed', {
+ limit: 100,
+ });
const { canceled, result: channel } = await os.select({
title: i18n.ts.selectChannel,
items: channels.map(x => ({
diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue
index 339421a13e..38ee37de27 100644
--- a/packages/frontend/src/ui/deck/column.vue
+++ b/packages/frontend/src/ui/deck/column.vue
@@ -29,8 +29,8 @@
</template>
<script lang="ts" setup>
-import { onBeforeUnmount, onMounted, provide, Ref, watch } from 'vue';
-import { updateColumn, swapLeftColumn, swapRightColumn, swapUpColumn, swapDownColumn, stackLeftColumn, popRightColumn, removeColumn, swapColumn, Column, deckStore } from './deck-store';
+import { onBeforeUnmount, onMounted, provide, watch } from 'vue';
+import { updateColumn, swapLeftColumn, swapRightColumn, swapUpColumn, swapDownColumn, stackLeftColumn, popRightColumn, removeColumn, swapColumn, Column } from './deck-store';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { MenuItem } from '@/types/menu';
diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts
index 80c202a2ef..1420ad8b30 100644
--- a/packages/frontend/src/ui/deck/deck-store.ts
+++ b/packages/frontend/src/ui/deck/deck-store.ts
@@ -2,7 +2,6 @@ import { throttle } from 'throttle-debounce';
import { markRaw } from 'vue';
import { notificationTypes } from 'misskey-js';
import { Storage } from '../../pizzax';
-import { i18n } from '@/i18n';
import { api } from '@/os';
import { deepClone } from '@/scripts/clone';
diff --git a/packages/frontend/src/ui/deck/direct-column.vue b/packages/frontend/src/ui/deck/direct-column.vue
index 75b018cacd..15b76c4d92 100644
--- a/packages/frontend/src/ui/deck/direct-column.vue
+++ b/packages/frontend/src/ui/deck/direct-column.vue
@@ -2,15 +2,15 @@
<XColumn :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
<template #header><i class="ti ti-mail" style="margin-right: 8px;"></i>{{ column.name }}</template>
- <XNotes :pagination="pagination"/>
+ <MkNotes :pagination="pagination"/>
</XColumn>
</template>
<script lang="ts" setup>
import { } from 'vue';
import XColumn from './column.vue';
-import XNotes from '@/components/MkNotes.vue';
import { Column } from './deck-store';
+import MkNotes from '@/components/MkNotes.vue';
defineProps<{
column: Column;
diff --git a/packages/frontend/src/ui/deck/list-column.vue b/packages/frontend/src/ui/deck/list-column.vue
index 58633e5672..352c1d246a 100644
--- a/packages/frontend/src/ui/deck/list-column.vue
+++ b/packages/frontend/src/ui/deck/list-column.vue
@@ -4,7 +4,7 @@
<i class="ti ti-list"></i><span style="margin-left: 8px;">{{ column.name }}</span>
</template>
- <XTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" @after="() => emit('loaded')"/>
+ <MkTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" @after="() => emit('loaded')"/>
</XColumn>
</template>
@@ -12,7 +12,7 @@
import { } from 'vue';
import XColumn from './column.vue';
import { updateColumn, Column } from './deck-store';
-import XTimeline from '@/components/MkTimeline.vue';
+import MkTimeline from '@/components/MkTimeline.vue';
import * as os from '@/os';
import { i18n } from '@/i18n';
@@ -26,7 +26,7 @@ const emit = defineEmits<{
(ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
-let timeline = $shallowRef<InstanceType<typeof XTimeline>>();
+let timeline = $shallowRef<InstanceType<typeof MkTimeline>>();
if (props.column.listId == null) {
setList();
diff --git a/packages/frontend/src/ui/deck/main-column.vue b/packages/frontend/src/ui/deck/main-column.vue
index 0c66172397..f3826a8d31 100644
--- a/packages/frontend/src/ui/deck/main-column.vue
+++ b/packages/frontend/src/ui/deck/main-column.vue
@@ -18,7 +18,7 @@ import { deckStore, Column } from '@/ui/deck/deck-store';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { mainRouter } from '@/router';
-import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata';
+import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
defineProps<{
column: Column;
diff --git a/packages/frontend/src/ui/deck/mentions-column.vue b/packages/frontend/src/ui/deck/mentions-column.vue
index 16962956a0..852d7a8f7e 100644
--- a/packages/frontend/src/ui/deck/mentions-column.vue
+++ b/packages/frontend/src/ui/deck/mentions-column.vue
@@ -2,15 +2,15 @@
<XColumn :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
<template #header><i class="ti ti-at" style="margin-right: 8px;"></i>{{ column.name }}</template>
- <XNotes :pagination="pagination"/>
+ <MkNotes :pagination="pagination"/>
</XColumn>
</template>
<script lang="ts" setup>
import { } from 'vue';
import XColumn from './column.vue';
-import XNotes from '@/components/MkNotes.vue';
import { Column } from './deck-store';
+import MkNotes from '@/components/MkNotes.vue';
defineProps<{
column: Column;
diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue
index 8bde7e4ce3..a947e27e57 100644
--- a/packages/frontend/src/ui/deck/tl-column.vue
+++ b/packages/frontend/src/ui/deck/tl-column.vue
@@ -15,7 +15,7 @@
</p>
<p :class="$style.disabledDescription">{{ $t('disabled-timeline.description') }}</p>
</div>
- <XTimeline v-else-if="column.tl" ref="timeline" :key="column.tl" :src="column.tl" @after="() => emit('loaded')"/>
+ <MkTimeline v-else-if="column.tl" ref="timeline" :key="column.tl" :src="column.tl" @after="() => emit('loaded')"/>
</XColumn>
</template>
@@ -23,10 +23,9 @@
import { onMounted } from 'vue';
import XColumn from './column.vue';
import { removeColumn, updateColumn, Column } from './deck-store';
-import XTimeline from '@/components/MkTimeline.vue';
+import MkTimeline from '@/components/MkTimeline.vue';
import * as os from '@/os';
import { $i } from '@/account';
-import { instance } from '@/instance';
import { i18n } from '@/i18n';
const props = defineProps<{
diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue
index 65a1ce0fce..beae799f5c 100644
--- a/packages/frontend/src/ui/universal.vue
+++ b/packages/frontend/src/ui/universal.vue
@@ -84,7 +84,7 @@
</template>
<script lang="ts" setup>
-import { defineAsyncComponent, provide, onMounted, computed, ref, watch, ComputedRef } from 'vue';
+import { defineAsyncComponent, provide, onMounted, computed, ref, ComputedRef } from 'vue';
import XCommon from './_common_/common.vue';
import { instanceName } from '@/config';
import { StickySidebar } from '@/scripts/sticky-sidebar';
@@ -94,9 +94,8 @@ import { defaultStore } from '@/store';
import { navbarItemDef } from '@/navbar';
import { i18n } from '@/i18n';
import { $i } from '@/account';
-import { Router } from '@/nirax';
import { mainRouter } from '@/router';
-import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata';
+import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
import { deviceKind } from '@/scripts/device-kind';
import { miLocalStorage } from '@/local-storage';
const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue'));
diff --git a/packages/frontend/src/ui/visitor.vue b/packages/frontend/src/ui/visitor.vue
index 797e2aa6c3..6c96440ebd 100644
--- a/packages/frontend/src/ui/visitor.vue
+++ b/packages/frontend/src/ui/visitor.vue
@@ -4,7 +4,7 @@
</template>
<script lang="ts">
-import { defineComponent, defineAsyncComponent } from 'vue';
+import { defineComponent } from 'vue';
//import DesignA from './visitor/a.vue';
import DesignB from './visitor/b.vue';
import XCommon from './_common_/common.vue';
diff --git a/packages/frontend/src/ui/visitor/b.vue b/packages/frontend/src/ui/visitor/b.vue
index 058a9482fa..163f038b43 100644
--- a/packages/frontend/src/ui/visitor/b.vue
+++ b/packages/frontend/src/ui/visitor/b.vue
@@ -11,7 +11,10 @@
<div class="contents">
<XHeader v-if="!root" class="header"/>
- <main style="container-type: inline-size;">
+ <main v-if="!root" style="container-type: inline-size;">
+ <RouterView/>
+ </main>
+ <main v-else>
<RouterView/>
</main>
<div v-if="!root" class="powered-by">
@@ -58,13 +61,11 @@ import { host, instanceName } from '@/config';
import { search } from '@/scripts/search';
import * as os from '@/os';
import { instance } from '@/instance';
-import MkPagination from '@/components/MkPagination.vue';
import XSigninDialog from '@/components/MkSigninDialog.vue';
import XSignupDialog from '@/components/MkSignupDialog.vue';
-import MkButton from '@/components/MkButton.vue';
import { ColdDeviceStorage, defaultStore } from '@/store';
import { mainRouter } from '@/router';
-import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata';
+import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
const DESKTOP_THRESHOLD = 1100;
@@ -83,7 +84,7 @@ const announcements = {
limit: 10,
};
-const isTimelineAvailable = instance.policies.ltlAvailable || instance.policies.gtlAvailable;
+const isTimelineAvailable = $ref(instance.policies?.ltlAvailable || instance.policies?.gtlAvailable);
let showMenu = $ref(false);
let isDesktop = $ref(window.innerWidth >= DESKTOP_THRESHOLD);
diff --git a/packages/frontend/src/ui/visitor/kanban.vue b/packages/frontend/src/ui/visitor/kanban.vue
index 51e47f277d..05ded834ee 100644
--- a/packages/frontend/src/ui/visitor/kanban.vue
+++ b/packages/frontend/src/ui/visitor/kanban.vue
@@ -38,7 +38,7 @@
</template>
<script lang="ts">
-import { defineComponent, defineAsyncComponent } from 'vue';
+import { defineComponent } from 'vue';
import { host, instanceName } from '@/config';
import * as os from '@/os';
import MkPagination from '@/components/MkPagination.vue';
diff --git a/packages/frontend/src/ui/zen.vue b/packages/frontend/src/ui/zen.vue
index d8fda1f7c3..628390e3f7 100644
--- a/packages/frontend/src/ui/zen.vue
+++ b/packages/frontend/src/ui/zen.vue
@@ -10,7 +10,7 @@
import { provide, ComputedRef } from 'vue';
import XCommon from './_common_/common.vue';
import { mainRouter } from '@/router';
-import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata';
+import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
import { instanceName } from '@/config';
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
diff --git a/packages/frontend/src/widgets/WidgetActivity.vue b/packages/frontend/src/widgets/WidgetActivity.vue
index da1cab6f88..7acf2140cf 100644
--- a/packages/frontend/src/widgets/WidgetActivity.vue
+++ b/packages/frontend/src/widgets/WidgetActivity.vue
@@ -15,8 +15,8 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, reactive, ref, watch } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { ref } from 'vue';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import XCalendar from './WidgetActivity.calendar.vue';
import XChart from './WidgetActivity.chart.vue';
import { GetFormResultType } from '@/scripts/form';
diff --git a/packages/frontend/src/widgets/WidgetAichan.vue b/packages/frontend/src/widgets/WidgetAichan.vue
index 1bb3089d16..cb055a56f6 100644
--- a/packages/frontend/src/widgets/WidgetAichan.vue
+++ b/packages/frontend/src/widgets/WidgetAichan.vue
@@ -5,8 +5,8 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, reactive, ref, shallowRef } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { onMounted, onUnmounted, shallowRef } from 'vue';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
const name = 'ai';
diff --git a/packages/frontend/src/widgets/WidgetAiscript.vue b/packages/frontend/src/widgets/WidgetAiscript.vue
index 9e489227ff..33218a110b 100644
--- a/packages/frontend/src/widgets/WidgetAiscript.vue
+++ b/packages/frontend/src/widgets/WidgetAiscript.vue
@@ -14,9 +14,9 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, ref, watch } from 'vue';
+import { ref } from 'vue';
import { Interpreter, Parser, utils } from '@syuilo/aiscript';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
import * as os from '@/os';
import MkContainer from '@/components/MkContainer.vue';
diff --git a/packages/frontend/src/widgets/WidgetAiscriptApp.vue b/packages/frontend/src/widgets/WidgetAiscriptApp.vue
index 9a2b60eb05..455a6e6ea5 100644
--- a/packages/frontend/src/widgets/WidgetAiscriptApp.vue
+++ b/packages/frontend/src/widgets/WidgetAiscriptApp.vue
@@ -8,16 +8,16 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, Ref, ref, watch } from 'vue';
-import { Interpreter, Parser, utils, values } from '@syuilo/aiscript';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { onMounted, Ref, ref, watch } from 'vue';
+import { Interpreter, Parser } from '@syuilo/aiscript';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
import * as os from '@/os';
import { createAiScriptEnv } from '@/scripts/aiscript/api';
import { $i } from '@/account';
import MkAsUi from '@/components/MkAsUi.vue';
import MkContainer from '@/components/MkContainer.vue';
-import { AsUiComponent, AsUiRoot, patch, registerAsUiLib, render } from '@/scripts/aiscript/ui';
+import { AsUiComponent, AsUiRoot, registerAsUiLib } from '@/scripts/aiscript/ui';
const name = 'aiscriptApp';
diff --git a/packages/frontend/src/widgets/WidgetButton.vue b/packages/frontend/src/widgets/WidgetButton.vue
index 6c2c366aa2..462f1e5a5d 100644
--- a/packages/frontend/src/widgets/WidgetButton.vue
+++ b/packages/frontend/src/widgets/WidgetButton.vue
@@ -7,9 +7,8 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, ref, watch } from 'vue';
-import { Interpreter, Parser, utils } from '@syuilo/aiscript';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { Interpreter, Parser } from '@syuilo/aiscript';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
import * as os from '@/os';
import { createAiScriptEnv } from '@/scripts/aiscript/api';
diff --git a/packages/frontend/src/widgets/WidgetCalendar.vue b/packages/frontend/src/widgets/WidgetCalendar.vue
index fe31e10215..083d8588af 100644
--- a/packages/frontend/src/widgets/WidgetCalendar.vue
+++ b/packages/frontend/src/widgets/WidgetCalendar.vue
@@ -33,8 +33,8 @@
</template>
<script lang="ts" setup>
-import { onUnmounted, ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { ref } from 'vue';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
import { i18n } from '@/i18n';
import { useInterval } from '@/scripts/use-interval';
diff --git a/packages/frontend/src/widgets/WidgetClicker.vue b/packages/frontend/src/widgets/WidgetClicker.vue
index 66c3fbd9d2..981788a3c5 100644
--- a/packages/frontend/src/widgets/WidgetClicker.vue
+++ b/packages/frontend/src/widgets/WidgetClicker.vue
@@ -7,10 +7,8 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, Ref, ref, watch } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
-import { $i } from '@/account';
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 7f9c96b93b..ecbb03b570 100644
--- a/packages/frontend/src/widgets/WidgetClock.vue
+++ b/packages/frontend/src/widgets/WidgetClock.vue
@@ -19,7 +19,7 @@
<script lang="ts" setup>
import { } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
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 0f9c46f1c7..1780a1c8d2 100644
--- a/packages/frontend/src/widgets/WidgetDigitalClock.vue
+++ b/packages/frontend/src/widgets/WidgetDigitalClock.vue
@@ -9,8 +9,7 @@
</template>
<script lang="ts" setup>
-import { onUnmounted, ref, watch } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
import { timezones } from '@/scripts/timezones';
import MkDigitalClock from '@/components/MkDigitalClock.vue';
diff --git a/packages/frontend/src/widgets/WidgetFederation.vue b/packages/frontend/src/widgets/WidgetFederation.vue
index 806c5d1801..a8095acf65 100644
--- a/packages/frontend/src/widgets/WidgetFederation.vue
+++ b/packages/frontend/src/widgets/WidgetFederation.vue
@@ -20,8 +20,8 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { ref } from 'vue';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
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 1068c5ac4b..b157807655 100644
--- a/packages/frontend/src/widgets/WidgetInstanceCloud.vue
+++ b/packages/frontend/src/widgets/WidgetInstanceCloud.vue
@@ -14,7 +14,7 @@
<script lang="ts" setup>
import { } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
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 990802d847..3a3b071b7d 100644
--- a/packages/frontend/src/widgets/WidgetInstanceInfo.vue
+++ b/packages/frontend/src/widgets/WidgetInstanceInfo.vue
@@ -15,8 +15,7 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, Ref, ref, watch } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
import { host } from '@/config';
diff --git a/packages/frontend/src/widgets/WidgetJobQueue.vue b/packages/frontend/src/widgets/WidgetJobQueue.vue
index e19cbb185a..69912e21f7 100644
--- a/packages/frontend/src/widgets/WidgetJobQueue.vue
+++ b/packages/frontend/src/widgets/WidgetJobQueue.vue
@@ -46,13 +46,12 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, reactive, ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { onUnmounted, reactive } from 'vue';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
import { stream } from '@/stream';
import number from '@/filters/number';
import * as sound from '@/scripts/sound';
-import * as os from '@/os';
import { deepClone } from '@/scripts/clone';
const name = 'jobQueue';
diff --git a/packages/frontend/src/widgets/WidgetMemo.vue b/packages/frontend/src/widgets/WidgetMemo.vue
index 689dbe0e6f..149d20af47 100644
--- a/packages/frontend/src/widgets/WidgetMemo.vue
+++ b/packages/frontend/src/widgets/WidgetMemo.vue
@@ -11,10 +11,9 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, reactive, ref, watch } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { ref, watch } from 'vue';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
-import * as os from '@/os';
import MkContainer from '@/components/MkContainer.vue';
import { defaultStore } from '@/store';
import { i18n } from '@/i18n';
diff --git a/packages/frontend/src/widgets/WidgetNotifications.vue b/packages/frontend/src/widgets/WidgetNotifications.vue
index e4ae37950c..63400b09d0 100644
--- a/packages/frontend/src/widgets/WidgetNotifications.vue
+++ b/packages/frontend/src/widgets/WidgetNotifications.vue
@@ -12,7 +12,7 @@
<script lang="ts" setup>
import { defineAsyncComponent } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
import MkContainer from '@/components/MkContainer.vue';
import XNotifications from '@/components/MkNotifications.vue';
diff --git a/packages/frontend/src/widgets/WidgetOnlineUsers.vue b/packages/frontend/src/widgets/WidgetOnlineUsers.vue
index 705be1c8a7..a096cc8fe8 100644
--- a/packages/frontend/src/widgets/WidgetOnlineUsers.vue
+++ b/packages/frontend/src/widgets/WidgetOnlineUsers.vue
@@ -7,8 +7,8 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { ref } from 'vue';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
import * as os from '@/os';
import { useInterval } from '@/scripts/use-interval';
diff --git a/packages/frontend/src/widgets/WidgetPhotos.vue b/packages/frontend/src/widgets/WidgetPhotos.vue
index 2d6f0a5ec7..8746ababbb 100644
--- a/packages/frontend/src/widgets/WidgetPhotos.vue
+++ b/packages/frontend/src/widgets/WidgetPhotos.vue
@@ -17,8 +17,8 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, reactive, ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { onUnmounted, ref } from 'vue';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
import { stream } from '@/stream';
import { getStaticImageUrl } from '@/scripts/media-proxy';
diff --git a/packages/frontend/src/widgets/WidgetPostForm.vue b/packages/frontend/src/widgets/WidgetPostForm.vue
index f8bebcbf96..9953bca65f 100644
--- a/packages/frontend/src/widgets/WidgetPostForm.vue
+++ b/packages/frontend/src/widgets/WidgetPostForm.vue
@@ -4,7 +4,7 @@
<script lang="ts" setup>
import { } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
import MkPostForm from '@/components/MkPostForm.vue';
diff --git a/packages/frontend/src/widgets/WidgetProfile.vue b/packages/frontend/src/widgets/WidgetProfile.vue
index 7e8ac429e5..819663a366 100644
--- a/packages/frontend/src/widgets/WidgetProfile.vue
+++ b/packages/frontend/src/widgets/WidgetProfile.vue
@@ -17,8 +17,7 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, Ref, ref, watch } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
import { $i } from '@/account';
import { userPage } from '@/filters/user';
diff --git a/packages/frontend/src/widgets/WidgetRss.vue b/packages/frontend/src/widgets/WidgetRss.vue
index e212003f34..965bb89153 100644
--- a/packages/frontend/src/widgets/WidgetRss.vue
+++ b/packages/frontend/src/widgets/WidgetRss.vue
@@ -19,7 +19,7 @@
<script lang="ts" setup>
import { ref, watch, computed } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
import MkContainer from '@/components/MkContainer.vue';
import { url as base } from '@/config';
diff --git a/packages/frontend/src/widgets/WidgetRssTicker.vue b/packages/frontend/src/widgets/WidgetRssTicker.vue
index a7488f4ca2..b0408f0d7f 100644
--- a/packages/frontend/src/widgets/WidgetRssTicker.vue
+++ b/packages/frontend/src/widgets/WidgetRssTicker.vue
@@ -23,7 +23,7 @@
<script lang="ts" setup>
import { ref, watch, computed } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import MarqueeText from '@/components/MkMarquee.vue';
import { GetFormResultType } from '@/scripts/form';
import MkContainer from '@/components/MkContainer.vue';
diff --git a/packages/frontend/src/widgets/WidgetSlideshow.vue b/packages/frontend/src/widgets/WidgetSlideshow.vue
index c2ad23b311..ffb77b281a 100644
--- a/packages/frontend/src/widgets/WidgetSlideshow.vue
+++ b/packages/frontend/src/widgets/WidgetSlideshow.vue
@@ -12,8 +12,8 @@
</template>
<script lang="ts" setup>
-import { nextTick, onMounted, onUnmounted, reactive, ref, shallowRef } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { onMounted, ref, shallowRef } from 'vue';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
import * as os from '@/os';
import { useInterval } from '@/scripts/use-interval';
diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue
index e6abf561b9..d6be6532a6 100644
--- a/packages/frontend/src/widgets/WidgetTimeline.vue
+++ b/packages/frontend/src/widgets/WidgetTimeline.vue
@@ -16,19 +16,18 @@
</template>
<div>
- <XTimeline :key="widgetProps.src === 'list' ? `list:${widgetProps.list.id}` : widgetProps.src === 'antenna' ? `antenna:${widgetProps.antenna.id}` : widgetProps.src" :src="widgetProps.src" :list="widgetProps.list ? widgetProps.list.id : null" :antenna="widgetProps.antenna ? widgetProps.antenna.id : null"/>
+ <MkTimeline :key="widgetProps.src === 'list' ? `list:${widgetProps.list.id}` : widgetProps.src === 'antenna' ? `antenna:${widgetProps.antenna.id}` : widgetProps.src" :src="widgetProps.src" :list="widgetProps.list ? widgetProps.list.id : null" :antenna="widgetProps.antenna ? widgetProps.antenna.id : null"/>
</div>
</MkContainer>
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, reactive, ref, watch } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { ref } from 'vue';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
import * as os from '@/os';
import MkContainer from '@/components/MkContainer.vue';
-import XTimeline from '@/components/MkTimeline.vue';
-import { $i } from '@/account';
+import MkTimeline from '@/components/MkTimeline.vue';
import { i18n } from '@/i18n';
const name = 'timeline';
diff --git a/packages/frontend/src/widgets/WidgetTrends.vue b/packages/frontend/src/widgets/WidgetTrends.vue
index 76651c1d0f..1423ae076c 100644
--- a/packages/frontend/src/widgets/WidgetTrends.vue
+++ b/packages/frontend/src/widgets/WidgetTrends.vue
@@ -19,8 +19,8 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { ref } from 'vue';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
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 cf85ac782c..22162d2b2c 100644
--- a/packages/frontend/src/widgets/WidgetUnixClock.vue
+++ b/packages/frontend/src/widgets/WidgetUnixClock.vue
@@ -12,7 +12,7 @@
<script lang="ts" setup>
import { onUnmounted, ref, watch } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
const name = 'unixClock';
diff --git a/packages/frontend/src/widgets/WidgetUserList.vue b/packages/frontend/src/widgets/WidgetUserList.vue
index cc49c92550..b8811d2fed 100644
--- a/packages/frontend/src/widgets/WidgetUserList.vue
+++ b/packages/frontend/src/widgets/WidgetUserList.vue
@@ -19,8 +19,7 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget';
import { GetFormResultType } from '@/scripts/form';
import MkContainer from '@/components/MkContainer.vue';
import * as os from '@/os';
diff --git a/packages/frontend/src/widgets/server-metric/index.vue b/packages/frontend/src/widgets/server-metric/index.vue
index f79858db26..72c88d9a00 100644
--- a/packages/frontend/src/widgets/server-metric/index.vue
+++ b/packages/frontend/src/widgets/server-metric/index.vue
@@ -15,8 +15,8 @@
</template>
<script lang="ts" setup>
-import { onMounted, onUnmounted, ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from '../widget';
+import { onUnmounted, ref } from 'vue';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from '../widget';
import XCpuMemory from './cpu-mem.vue';
import XNet from './net.vue';
import XCpu from './cpu.vue';
diff --git a/packages/frontend/src/widgets/server-metric/mem.vue b/packages/frontend/src/widgets/server-metric/mem.vue
index 6018eb4265..d6a5d021ad 100644
--- a/packages/frontend/src/widgets/server-metric/mem.vue
+++ b/packages/frontend/src/widgets/server-metric/mem.vue
@@ -2,7 +2,7 @@
<div class="zlxnikvl">
<XPie class="pie" :value="usage"/>
<div>
- <p><i class="fas fa-memory"></i>RAM</p>
+ <p><i class="ti ti-section"></i>RAM</p>
<p>Total: {{ bytes(total, 1) }}</p>
<p>Used: {{ bytes(used, 1) }}</p>
<p>Free: {{ bytes(free, 1) }}</p>
diff --git a/packages/frontend/vite.json5.ts b/packages/frontend/vite.json5.ts
index 0a37fbff44..87b67c2142 100644
--- a/packages/frontend/vite.json5.ts
+++ b/packages/frontend/vite.json5.ts
@@ -5,6 +5,13 @@ import { Plugin } from 'rollup';
import { createFilter, dataToEsm } from '@rollup/pluginutils';
import { RollupJsonOptions } from '@rollup/plugin-json';
+// json5 extends SyntaxError with additional fields (without subclassing)
+// https://github.com/json5/json5/blob/de344f0619bda1465a6e25c76f1c0c3dda8108d9/lib/parse.js#L1111-L1112
+interface Json5SyntaxError extends SyntaxError {
+ lineNumber: number;
+ columnNumber: number;
+}
+
export default function json5(options: RollupJsonOptions = {}): Plugin {
const filter = createFilter(options.include, options.exclude);
const indent = 'indent' in options ? options.indent : '\t';
@@ -28,9 +35,12 @@ export default function json5(options: RollupJsonOptions = {}): Plugin {
map: { mappings: '' },
};
} catch (err) {
- const message = 'Could not parse JSON file';
- const position = parseInt(/[\d]/.exec(err.message)[0], 10);
- this.warn({ message, id, position });
+ if (!(err instanceof SyntaxError)) {
+ throw err;
+ }
+ const message = 'Could not parse JSON5 file';
+ const { lineNumber, columnNumber } = err as Json5SyntaxError;
+ this.warn({ message, id, loc: { line: lineNumber, column: columnNumber } });
return null;
}
},