From c548ec9906947c72743e611254a6557e8e8d057c Mon Sep 17 00:00:00 2001
From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Wed, 5 Feb 2025 19:01:44 +0900
Subject: refactor(frontend): verbatimModuleSyntaxを有効化 (#15323)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* wip
* wip
* wip
* wip
* revert unnecessary changes
* wip
* refactor(frontend): enforce verbatimModuleSyntax
* fix
* refactor(frontend-shared): enforce verbatimModuleSyntax
* wip
* refactor(frontend-embed): enforce verbatimModuleSyntax
* enforce consistent-type-imports
* fix lint config
* attemt to fix ci
* fix lint
* fix
* fix
* fix
---
packages/frontend-shared/eslint.config.js | 1 +
packages/frontend-shared/tsconfig.json | 1 +
2 files changed, 2 insertions(+)
(limited to 'packages/frontend-shared')
diff --git a/packages/frontend-shared/eslint.config.js b/packages/frontend-shared/eslint.config.js
index cd4641a270..a3ab438336 100644
--- a/packages/frontend-shared/eslint.config.js
+++ b/packages/frontend-shared/eslint.config.js
@@ -52,6 +52,7 @@ export default [
'@typescript-eslint/no-empty-interface': ['error', {
allowSingleExtends: true,
}],
+ 'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
// window の禁止理由: グローバルスコープと衝突し、予期せぬ結果を招くため
// e の禁止理由: error や event など、複数のキーワードの頭文字であり分かりにくいため
'id-denylist': ['error', 'window', 'e'],
diff --git a/packages/frontend-shared/tsconfig.json b/packages/frontend-shared/tsconfig.json
index 09a8ff76aa..12f00eb503 100644
--- a/packages/frontend-shared/tsconfig.json
+++ b/packages/frontend-shared/tsconfig.json
@@ -16,6 +16,7 @@
"experimentalDecorators": true,
"noImplicitReturns": true,
"esModuleInterop": true,
+ "verbatimModuleSyntax": true,
"baseUrl": ".",
"paths": {
"@/*": ["./*"],
--
cgit v1.2.3-freya
From cfb61289a99a2f6c4e0d8214a11046a93db1180d Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Wed, 5 Feb 2025 20:17:48 +0900
Subject: refactor(frontend): remove X theme properties (#15376)
* refactor(frontend): remove X theme properties
* Update MkAutocomplete.vue
* Update WidgetCalendar.vue
---
packages/frontend-shared/themes/_dark.json5 | 8 --------
packages/frontend-shared/themes/_light.json5 | 8 --------
packages/frontend-shared/themes/d-astro.json5 | 8 --------
packages/frontend-shared/themes/d-u0.json5 | 8 --------
packages/frontend-shared/themes/l-u0.json5 | 8 --------
packages/frontend-shared/themes/l-vivid.json5 | 8 --------
packages/frontend/src/components/MkAutocomplete.vue | 6 +++---
packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue | 4 ++--
packages/frontend/src/components/MkEmojiPicker.vue | 6 +++---
packages/frontend/src/components/MkPostForm.vue | 6 +++---
packages/frontend/src/components/MkUserSelectDialog.vue | 4 ++--
packages/frontend/src/pages/admin/server-rules.vue | 2 +-
packages/frontend/src/pages/page-editor/page-editor.container.vue | 4 ++--
packages/frontend/src/widgets/WidgetCalendar.vue | 2 +-
14 files changed, 17 insertions(+), 65 deletions(-)
(limited to 'packages/frontend-shared')
diff --git a/packages/frontend-shared/themes/_dark.json5 b/packages/frontend-shared/themes/_dark.json5
index 5271785e62..f2d8a7aed8 100644
--- a/packages/frontend-shared/themes/_dark.json5
+++ b/packages/frontend-shared/themes/_dark.json5
@@ -79,14 +79,6 @@
codeBoolean: '#c59eff',
deckBg: '#000',
htmlThemeColor: '@bg',
- X3: 'rgba(255, 255, 255, 0.05)',
- X4: 'rgba(255, 255, 255, 0.1)',
- X5: 'rgba(255, 255, 255, 0.05)',
- X6: 'rgba(255, 255, 255, 0.15)',
- X7: 'rgba(255, 255, 255, 0.05)',
- X11: 'rgba(0, 0, 0, 0.3)',
- X12: 'rgba(255, 255, 255, 0.1)',
- X13: 'rgba(255, 255, 255, 0.15)',
},
codeHighlighter: {
diff --git a/packages/frontend-shared/themes/_light.json5 b/packages/frontend-shared/themes/_light.json5
index be331ce58f..22893bf4b3 100644
--- a/packages/frontend-shared/themes/_light.json5
+++ b/packages/frontend-shared/themes/_light.json5
@@ -79,14 +79,6 @@
codeBoolean: '#62b70c',
deckBg: ':darken<3<@bg',
htmlThemeColor: '@bg',
- X3: 'rgba(0, 0, 0, 0.05)',
- X4: 'rgba(0, 0, 0, 0.1)',
- X5: 'rgba(0, 0, 0, 0.05)',
- X6: 'rgba(0, 0, 0, 0.25)',
- X7: 'rgba(0, 0, 0, 0.05)',
- X11: 'rgba(0, 0, 0, 0.1)',
- X12: 'rgba(0, 0, 0, 0.1)',
- X13: 'rgba(0, 0, 0, 0.15)',
},
codeHighlighter: {
diff --git a/packages/frontend-shared/themes/d-astro.json5 b/packages/frontend-shared/themes/d-astro.json5
index 4422526a33..e8864df336 100644
--- a/packages/frontend-shared/themes/d-astro.json5
+++ b/packages/frontend-shared/themes/d-astro.json5
@@ -54,13 +54,5 @@
wallpaperOverlay: 'rgba(0, 0, 0, 0.5)',
panelHeaderDivider: 'rgba(0, 0, 0, 0)',
scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)',
- X3: 'rgba(255, 255, 255, 0.05)',
- X4: 'rgba(255, 255, 255, 0.1)',
- X5: 'rgba(255, 255, 255, 0.05)',
- X6: 'rgba(255, 255, 255, 0.15)',
- X7: 'rgba(255, 255, 255, 0.05)',
- X11: 'rgba(0, 0, 0, 0.3)',
- X12: 'rgba(255, 255, 255, 0.1)',
- X13: 'rgba(255, 255, 255, 0.15)',
},
}
diff --git a/packages/frontend-shared/themes/d-u0.json5 b/packages/frontend-shared/themes/d-u0.json5
index fb707c74c3..0223b1fb5c 100644
--- a/packages/frontend-shared/themes/d-u0.json5
+++ b/packages/frontend-shared/themes/d-u0.json5
@@ -3,17 +3,9 @@
base: 'dark',
name: 'Mi U0 Dark',
props: {
- X3: 'rgba(255, 255, 255, 0.05)',
- X4: 'rgba(255, 255, 255, 0.1)',
- X5: 'rgba(255, 255, 255, 0.05)',
- X6: 'rgba(255, 255, 255, 0.15)',
- X7: 'rgba(255, 255, 255, 0.05)',
bg: '#172426',
fg: '#dadada',
X10: ':alpha<0.4<@accent',
- X11: 'rgba(0, 0, 0, 0.3)',
- X12: 'rgba(255, 255, 255, 0.1)',
- X13: 'rgba(255, 255, 255, 0.15)',
X14: ':alpha<0.5<@navBg',
X15: ':alpha<0<@panel',
X16: ':alpha<0.7<@panel',
diff --git a/packages/frontend-shared/themes/l-u0.json5 b/packages/frontend-shared/themes/l-u0.json5
index 7062e7fe5b..f6023af819 100644
--- a/packages/frontend-shared/themes/l-u0.json5
+++ b/packages/frontend-shared/themes/l-u0.json5
@@ -3,17 +3,9 @@
base: 'light',
name: 'Mi U0 Light',
props: {
- X3: 'rgba(255, 255, 255, 0.05)',
- X4: 'rgba(255, 255, 255, 0.1)',
- X5: 'rgba(255, 255, 255, 0.05)',
- X6: 'rgba(255, 255, 255, 0.15)',
- X7: 'rgba(255, 255, 255, 0.05)',
bg: '#e7e7eb',
fg: '#5f5f5f',
X10: ':alpha<0.4<@accent',
- X11: 'rgba(0, 0, 0, 0.3)',
- X12: 'rgba(255, 255, 255, 0.1)',
- X13: 'rgba(255, 255, 255, 0.15)',
X14: ':alpha<0.5<@navBg',
X15: ':alpha<0<@panel',
X16: ':alpha<0.7<@panel',
diff --git a/packages/frontend-shared/themes/l-vivid.json5 b/packages/frontend-shared/themes/l-vivid.json5
index 39768d4ac6..058c9c32e5 100644
--- a/packages/frontend-shared/themes/l-vivid.json5
+++ b/packages/frontend-shared/themes/l-vivid.json5
@@ -57,13 +57,5 @@
fgTransparentWeak: ':alpha<0.75<@fg',
panelHeaderDivider: '@divider',
scrollbarHandleHover: 'rgba(0, 0, 0, 0.4)',
- X3: 'rgba(0, 0, 0, 0.05)',
- X4: 'rgba(0, 0, 0, 0.1)',
- X5: 'rgba(0, 0, 0, 0.05)',
- X6: 'rgba(0, 0, 0, 0.25)',
- X7: 'rgba(0, 0, 0, 0.05)',
- X11: 'rgba(0, 0, 0, 0.1)',
- X12: 'rgba(0, 0, 0, 0.1)',
- X13: 'rgba(0, 0, 0, 0.15)',
},
}
diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue
index e7372da6f8..685dcb86e4 100644
--- a/packages/frontend/src/components/MkAutocomplete.vue
+++ b/packages/frontend/src/components/MkAutocomplete.vue
@@ -47,8 +47,9 @@ SPDX-License-Identifier: AGPL-3.0-only
import { markRaw, ref, shallowRef, computed, onUpdated, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
import sanitizeHtml from 'sanitize-html';
import { emojilist, getEmojiName } from '@@/js/emojilist.js';
-import contains from '@/scripts/contains.js';
import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@@/js/emoji-base.js';
+import { MFM_TAGS, MFM_PARAMS } from '@@/js/const.js';
+import contains from '@/scripts/contains.js';
import { acct } from '@/filters/user.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
@@ -56,7 +57,6 @@ import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js';
import { miLocalStorage } from '@/local-storage.js';
import { customEmojis } from '@/custom-emojis.js';
-import { MFM_TAGS, MFM_PARAMS } from '@@/js/const.js';
import { searchEmoji } from '@/scripts/search-emoji.js';
import type { EmojiDef } from '@/scripts/search-emoji.js';
@@ -408,7 +408,7 @@ onBeforeUnmount(() => {
text-overflow: ellipsis;
&:hover {
- background: var(--MI_THEME-X3);
+ background: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05));
}
&[data-selected='true'] {
diff --git a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
index e6ab17417d..86d6269c69 100644
--- a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
+++ b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
@@ -85,7 +85,7 @@ function cancel() {
.emojiImgWrapper {
max-width: 100%;
height: 40cqh;
- background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, var(--MI_THEME-X5) 8px, var(--MI_THEME-X5) 14px);
+ background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05)) 8px, light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05)) 14px);
border-radius: var(--MI-radius);
margin: auto;
overflow-y: hidden;
@@ -101,7 +101,7 @@ function cancel() {
display: inline-block;
word-break: break-all;
padding: 3px 10px;
- background-color: var(--MI_THEME-X5);
+ background-color: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05));
border: solid 1px var(--MI_THEME-divider);
border-radius: var(--MI-radius);
}
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 7ee0d6a6ac..5da161dae8 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -582,7 +582,7 @@ defineExpose({
&:disabled {
cursor: not-allowed;
- background: linear-gradient(-45deg, transparent 0% 48%, var(--MI_THEME-X6) 48% 52%, transparent 52% 100%);
+ background: linear-gradient(-45deg, transparent 0% 48%, light-dark(rgba(0, 0, 0, 0.25), rgba(255, 255, 255, 0.15)) 48% 52%, transparent 52% 100%);
opacity: 1;
> .emoji {
@@ -617,7 +617,7 @@ defineExpose({
&:disabled {
cursor: not-allowed;
- background: linear-gradient(-45deg, transparent 0% 48%, var(--MI_THEME-X6) 48% 52%, transparent 52% 100%);
+ background: linear-gradient(-45deg, transparent 0% 48%, light-dark(rgba(0, 0, 0, 0.25), rgba(255, 255, 255, 0.15)) 48% 52%, transparent 52% 100%);
opacity: 1;
> .emoji {
@@ -738,7 +738,7 @@ defineExpose({
&:disabled {
cursor: not-allowed;
- background: linear-gradient(-45deg, transparent 0% 48%, var(--MI_THEME-X6) 48% 52%, transparent 52% 100%);
+ background: linear-gradient(-45deg, transparent 0% 48%, light-dark(rgba(0, 0, 0, 0.25), rgba(255, 255, 255, 0.15)) 48% 52%, transparent 52% 100%);
opacity: 1;
> .emoji {
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 500f97b2ed..4f2792ed40 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -1166,7 +1166,7 @@ defineExpose({
border-radius: 6px;
&:hover {
- background: var(--MI_THEME-X5);
+ background: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05));
}
&:disabled {
@@ -1238,7 +1238,7 @@ html[data-color-scheme=light] .preview {
margin-right: 14px;
padding: 8px 0 8px 8px;
border-radius: 8px;
- background: var(--MI_THEME-X4);
+ background: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1));
}
.hasNotSpecifiedMentions {
@@ -1349,7 +1349,7 @@ html[data-color-scheme=light] .preview {
border-radius: 6px;
&:hover {
- background: var(--MI_THEME-X5);
+ background: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05));
}
&.footerButtonActive {
diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue
index 63af652cbc..1e93d9dbea 100644
--- a/packages/frontend/src/components/MkUserSelectDialog.vue
+++ b/packages/frontend/src/components/MkUserSelectDialog.vue
@@ -63,6 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@@ -47,6 +50,10 @@ withDefaults(defineProps<{
min-height: calc(var(--fukidashi-radius) * 2);
padding-top: calc(var(--fukidashi-radius) * .13);
+ &.accented {
+ --fukidashi-bg: var(--MI_THEME-accent);
+ }
+
&.shadow {
filter: drop-shadow(0 4px 32px var(--MI_THEME-shadow));
}
@@ -77,7 +84,7 @@ withDefaults(defineProps<{
.content {
position: relative;
- padding: 8px 12px;
+ padding: 10px 14px;
}
.tail {
diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue
index ae15776041..4a1100c324 100644
--- a/packages/frontend/src/components/MkMediaList.vue
+++ b/packages/frontend/src/components/MkMediaList.vue
@@ -227,7 +227,6 @@ defineExpose({
.container {
position: relative;
width: 100%;
- margin-top: 4px;
}
.medias {
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index a84bd9b256..f2f36308ca 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -11,6 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
[$style.center]: align === 'center',
[$style.big]: big,
[$style.asDrawer]: asDrawer,
+ [$style.widthSpecified]: width != null,
}"
@focusin.passive.stop="() => {}"
>
@@ -29,15 +30,19 @@ SPDX-License-Identifier: AGPL-3.0-only
>
+
{{ item.text }}
+
+
+
-
{{ item.text }}
+
+
{{ item.text }}
+
{{ item.caption }}
+
+
-
{{ item.text }}
+
+
{{ item.text }}
+
{{ item.caption }}
+
+
+
+
+
+
+
+
{{ i18n.ts.none }}
@@ -438,6 +473,12 @@ onBeforeUnmount(() => {
}
}
+ &:not(.widthSpecified) {
+ > .menu {
+ max-width: 400px;
+ }
+ }
+
&.big:not(.asDrawer) {
> .menu {
min-width: 230px;
@@ -607,10 +648,19 @@ onBeforeUnmount(() => {
.item_content_text {
max-width: calc(100vw - 4rem);
+}
+
+.item_content_text_title {
text-overflow: ellipsis;
overflow: hidden;
}
+.item_content_text_caption {
+ text-wrap: auto;
+ font-size: 85%;
+ opacity: 0.7;
+}
+
.switchButton {
margin-left: -2px;
--height: 1.35em;
diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue
index ab8bda403b..a729619180 100644
--- a/packages/frontend/src/components/MkPagination.vue
+++ b/packages/frontend/src/components/MkPagination.vue
@@ -24,16 +24,16 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
-
+
+
+
{{ i18n.ts.loadMore }}
-
-
+
+
{{ i18n.ts.loadMore }}
@@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { computed, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, useTemplateRef, watch } from 'vue';
import * as Misskey from 'misskey-js';
import { useDocumentVisibility } from '@@/js/use-document-visibility.js';
-import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@@/js/scroll.js';
+import { onScrollTop, isHeadVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isTailVisible } from '@@/js/scroll.js';
import type { ComputedRef } from 'vue';
import type { MisskeyEntity } from '@/types/date-separated-list.js';
import { misskeyApi } from '@/utility/misskey-api.js';
@@ -74,8 +74,6 @@ export type Paging
reversed?: boolean;
offsetMode?: boolean;
-
- pageEl?: HTMLElement;
};
type MisskeyEntityMap = Map;
@@ -141,8 +139,7 @@ const {
enableInfiniteScroll,
} = prefer.r;
-const contentEl = computed(() => props.pagination.pageEl ?? rootEl.value);
-const scrollableElement = computed(() => contentEl.value ? getScrollContainer(contentEl.value) : window.document.body);
+const scrollableElement = computed(() => rootEl.value ? getScrollContainer(rootEl.value) : window.document.body);
const visibility = useDocumentVisibility();
@@ -173,13 +170,13 @@ watch(rootEl, () => {
});
});
-watch([backed, contentEl], () => {
+watch([backed, rootEl], () => {
if (!backed.value) {
- if (!contentEl.value) return;
+ if (!rootEl.value) return;
scrollRemove.value = props.pagination.reversed
- ? onScrollBottom(contentEl.value, executeQueue, TOLERANCE)
- : onScrollTop(contentEl.value, (topVisible) => { if (topVisible) executeQueue(); }, TOLERANCE);
+ ? onScrollBottom(rootEl.value, executeQueue, TOLERANCE)
+ : onScrollTop(rootEl.value, (topVisible) => { if (topVisible) executeQueue(); }, TOLERANCE);
} else {
if (scrollRemove.value) scrollRemove.value();
scrollRemove.value = null;
@@ -349,7 +346,7 @@ const appearFetchMoreAhead = async (): Promise => {
fetchMoreAppearTimeout();
};
-const isTop = (): boolean => isBackTop.value || (props.pagination.reversed ? isBottomVisible : isTopVisible)(contentEl.value!, TOLERANCE);
+const isHead = (): boolean => isBackTop.value || (props.pagination.reversed ? isTailVisible : isHeadVisible)(rootEl.value!, TOLERANCE);
watch(visibility, () => {
if (visibility.value === 'hidden') {
@@ -364,7 +361,7 @@ watch(visibility, () => {
timerForSetPause = null;
} else {
isPausingUpdate = false;
- if (isTop()) {
+ if (isHead()) {
executeQueue();
}
}
@@ -376,16 +373,18 @@ watch(visibility, () => {
* ストリーミングから降ってきたアイテムはこれで追加する
* @param item アイテム
*/
-const prepend = (item: MisskeyEntity): void => {
+function prepend(item: MisskeyEntity): void {
if (items.value.size === 0) {
items.value.set(item.id, item);
fetching.value = false;
return;
}
- if (isTop() && !isPausingUpdate) unshiftItems([item]);
+ console.log(isHead(), isPausingUpdate);
+
+ if (isHead() && !isPausingUpdate) unshiftItems([item]);
else prependQueue(item);
-};
+}
/**
* 新着アイテムをitemsの先頭に追加し、displayLimitを適用する
@@ -447,7 +446,7 @@ onDeactivated(() => {
});
function toBottom() {
- scrollToBottom(contentEl.value!);
+ scrollToBottom(rootEl.value!);
}
onBeforeMount(() => {
diff --git a/packages/frontend/src/components/MkPolkadots.vue b/packages/frontend/src/components/MkPolkadots.vue
new file mode 100644
index 0000000000..285c4d0b79
--- /dev/null
+++ b/packages/frontend/src/components/MkPolkadots.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue
index f20aee0ce3..20dab6f028 100644
--- a/packages/frontend/src/components/MkUrlPreview.vue
+++ b/packages/frontend/src/components/MkUrlPreview.vue
@@ -246,6 +246,7 @@ onUnmounted(() => {
box-shadow: 0 0 0 1px var(--MI_THEME-divider);
border-radius: 8px;
overflow: clip;
+ text-align: left;
&:hover {
text-decoration: none;
diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts
index 099339fbee..f6d6bbf0fb 100644
--- a/packages/frontend/src/local-storage.ts
+++ b/packages/frontend/src/local-storage.ts
@@ -28,7 +28,7 @@ export type Keys = (
'theme' |
'themeId' |
'customCss' |
- 'message_drafts' |
+ 'chatMessageDrafts' |
'scratchpad' |
'debug' |
'preferences' |
diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts
index d478ece641..894df83721 100644
--- a/packages/frontend/src/navbar.ts
+++ b/packages/frontend/src/navbar.ts
@@ -4,6 +4,7 @@
*/
import { computed, reactive } from 'vue';
+import { ui } from '@@/js/config.js';
import { clearCache } from './utility/clear-cache.js';
import { $i } from '@/i.js';
import { miLocalStorage } from '@/local-storage.js';
@@ -11,7 +12,6 @@ import { openInstanceMenu, openToolsMenu } from '@/ui/_common_/common.js';
import { lookup } from '@/utility/lookup.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
-import { ui } from '@@/js/config.js';
import { unisonReload } from '@/utility/unison-reload.js';
export const navbarItemDef = reactive({
@@ -110,6 +110,12 @@ export const navbarItemDef = reactive({
icon: 'ti ti-device-tv',
to: '/channels',
},
+ chat: {
+ title: i18n.ts.chat,
+ icon: 'ti ti-message',
+ to: '/chat',
+ indicated: computed(() => $i != null && $i.hasUnreadChatMessages),
+ },
achievements: {
title: i18n.ts.achievements,
icon: 'ti ti-medal',
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index 4e9f4edb70..d1e823215a 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -160,6 +160,26 @@ SPDX-License-Identifier: AGPL-3.0-only
+
+ {{ i18n.ts._role._options.canChat }}
+
+ {{ i18n.ts._role.useBaseValue }}
+ {{ role.policies.canChat.value ? i18n.ts.yes : i18n.ts.no }}
+
+
+
+
+ {{ i18n.ts._role.useBaseValue }}
+
+
+ {{ i18n.ts.enable }}
+
+
+ {{ i18n.ts._role.priority }}
+
+
+
+
{{ i18n.ts._role._options.mentionMax }}
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index 0428352350..df4efd1271 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -51,6 +51,14 @@ SPDX-License-Identifier: AGPL-3.0-only
+
+ {{ i18n.ts._role._options.canChat }}
+ {{ policies.canChat ? i18n.ts.yes : i18n.ts.no }}
+
+ {{ i18n.ts.enable }}
+
+
+
{{ i18n.ts._role._options.mentionMax }}
{{ policies.mentionLimit }}
diff --git a/packages/frontend/src/pages/chat/XMessage.vue b/packages/frontend/src/pages/chat/XMessage.vue
new file mode 100644
index 0000000000..1e7f8e20ea
--- /dev/null
+++ b/packages/frontend/src/pages/chat/XMessage.vue
@@ -0,0 +1,245 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ i18n.ts.deleted }}
+
+
+
+
+
+
+ {{ message.toRoom.name }}
+ @{{ message.toUser.username }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/pages/chat/XRoom.vue b/packages/frontend/src/pages/chat/XRoom.vue
new file mode 100644
index 0000000000..b063a0cdd1
--- /dev/null
+++ b/packages/frontend/src/pages/chat/XRoom.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+ {{ room.description }}
+
+
+
+
+
+
diff --git a/packages/frontend/src/pages/chat/home.home.vue b/packages/frontend/src/pages/chat/home.home.vue
new file mode 100644
index 0000000000..1d0605136c
--- /dev/null
+++ b/packages/frontend/src/pages/chat/home.home.vue
@@ -0,0 +1,252 @@
+
+
+
+
+
{{ i18n.ts.startChat }}
+
+
+
+
+
+
+
+
{{ i18n.ts.search }}
+
+
+ {{ i18n.ts.searchResult }}
+
+
+
+
+
+ {{ i18n.ts._chat.history }}
+
+
+
+
+
+
+ {{ item.message.toRoom.name }}
+
+
+
+
{{ i18n.ts.you }}:{{ item.message.text }}
+
+
+
+
+
{{ i18n.ts._chat.noHistory }}
+
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/pages/chat/home.invitations.vue b/packages/frontend/src/pages/chat/home.invitations.vue
new file mode 100644
index 0000000000..4c3c0b282e
--- /dev/null
+++ b/packages/frontend/src/pages/chat/home.invitations.vue
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+ {{ invitation.room.name }}
+
+
+
+ {{ i18n.ts._chat.join }}
+ {{ i18n.ts._chat.ignore }}
+
+
+
+
+
+
+
+
+
{{ invitation.room.description === '' ? i18n.ts.noDescription : invitation.room.description }}
+
+
+
+
+
+
{{ i18n.ts._chat.noInvitations }}
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/pages/chat/home.joiningRooms.vue b/packages/frontend/src/pages/chat/home.joiningRooms.vue
new file mode 100644
index 0000000000..63e4d2adf8
--- /dev/null
+++ b/packages/frontend/src/pages/chat/home.joiningRooms.vue
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
{{ i18n.ts._chat.noRooms }}
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/pages/chat/home.ownedRooms.vue b/packages/frontend/src/pages/chat/home.ownedRooms.vue
new file mode 100644
index 0000000000..b0449fb373
--- /dev/null
+++ b/packages/frontend/src/pages/chat/home.ownedRooms.vue
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
{{ i18n.ts._chat.noRooms }}
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/pages/chat/home.vue b/packages/frontend/src/pages/chat/home.vue
new file mode 100644
index 0000000000..c2b272a42d
--- /dev/null
+++ b/packages/frontend/src/pages/chat/home.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/pages/chat/message.vue b/packages/frontend/src/pages/chat/message.vue
new file mode 100644
index 0000000000..be8be7e5d1
--- /dev/null
+++ b/packages/frontend/src/pages/chat/message.vue
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/pages/chat/room.form.vue b/packages/frontend/src/pages/chat/room.form.vue
new file mode 100644
index 0000000000..aba9d6061f
--- /dev/null
+++ b/packages/frontend/src/pages/chat/room.form.vue
@@ -0,0 +1,333 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/pages/chat/room.info.vue b/packages/frontend/src/pages/chat/room.info.vue
new file mode 100644
index 0000000000..7d38d07b3a
--- /dev/null
+++ b/packages/frontend/src/pages/chat/room.info.vue
@@ -0,0 +1,87 @@
+
+
+
+
+
+ {{ i18n.ts.name }}
+
+
+
+ {{ i18n.ts.description }}
+
+
+ {{ i18n.ts.save }}
+
+
+
+
+ {{ i18n.ts._chat.muteThisRoom }}
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/pages/chat/room.members.vue b/packages/frontend/src/pages/chat/room.members.vue
new file mode 100644
index 0000000000..d20216a81c
--- /dev/null
+++ b/packages/frontend/src/pages/chat/room.members.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
{{ i18n.ts._chat.inviteUser }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/pages/chat/room.search.vue b/packages/frontend/src/pages/chat/room.search.vue
new file mode 100644
index 0000000000..de5e7156ca
--- /dev/null
+++ b/packages/frontend/src/pages/chat/room.search.vue
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
{{ i18n.ts.search }}
+
+
+ {{ i18n.ts.searchResult }}
+
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/pages/chat/room.vue b/packages/frontend/src/pages/chat/room.vue
new file mode 100644
index 0000000000..15e9f43db2
--- /dev/null
+++ b/packages/frontend/src/pages/chat/room.vue
@@ -0,0 +1,426 @@
+
+
+
+
+
+
+
+
+
+
+
+
{{ i18n.ts._chat.noMessagesYet }}
+
+ {{ i18n.ts._chat.thisUserAllowsChatOnlyFromFollowers }}
+ {{ i18n.ts._chat.thisUserAllowsChatOnlyFromFollowing }}
+ {{ i18n.ts._chat.thisUserAllowsChatOnlyFromMutualFollowing }}
+ {{ i18n.ts._chat.thisUserNotAllowedChatAnyone }}
+
+
+ {{ i18n.ts._chat.inviteUserToChat }}
+
+
+
+
+
+
+ {{ i18n.ts.loadMore }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index 530b63b701..93a41e9ddd 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -38,7 +38,6 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.markAsReadAllNotifications }}
- {{ i18n.ts.markAsReadAllUnreadNotes }}
@@ -93,10 +92,6 @@ const pushRegistrationInServer = computed(() => allowButton.value?.pushRegistrat
const sendReadMessage = computed(() => pushRegistrationInServer.value?.sendReadMessage || false);
const userLists = await misskeyApi('users/lists/list');
-async function readAllUnreadNotes() {
- await os.apiWithDialog('i/read-all-unread-notes');
-}
-
async function readAllNotifications() {
await os.apiWithDialog('notifications/mark-all-as-read');
}
diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue
index f6eb203095..2f8a697d74 100644
--- a/packages/frontend/src/pages/settings/privacy.vue
+++ b/packages/frontend/src/pages/settings/privacy.vue
@@ -78,6 +78,20 @@ SPDX-License-Identifier: AGPL-3.0-only
+
+
+
+ {{ i18n.ts._chat.chatAllowedUsers }}
+
+
+
+
+
+ {{ i18n.ts._chat.chatAllowedUsers_note }}
+
+
+
+
{{ i18n.ts.lockdown }}{{ i18n.ts.beta }}
@@ -208,6 +222,7 @@ const hideOnlineStatus = ref($i.hideOnlineStatus);
const publicReactions = ref($i.publicReactions);
const followingVisibility = ref($i.followingVisibility);
const followersVisibility = ref($i.followersVisibility);
+const chatScope = ref($i.chatScope);
const makeNotesFollowersOnlyBefore_type = computed(() => {
if (makeNotesFollowersOnlyBefore.value == null) {
@@ -260,6 +275,7 @@ function save() {
publicReactions: !!publicReactions.value,
followingVisibility: followingVisibility.value,
followersVisibility: followersVisibility.value,
+ chatScope: chatScope.value,
});
}
diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue
index 9e5c82a266..4461ee1ab1 100644
--- a/packages/frontend/src/pages/settings/sounds.vue
+++ b/packages/frontend/src/pages/settings/sounds.vue
@@ -85,6 +85,7 @@ const sounds = ref>>({
noteMy: prefer.r['sound.on.noteMy'],
notification: prefer.r['sound.on.notification'],
reaction: prefer.r['sound.on.reaction'],
+ chatMessage: prefer.r['sound.on.chatMessage'],
});
function getSoundTypeName(f: SoundType): string {
diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts
index c3c37553d7..127ebeef0c 100644
--- a/packages/frontend/src/preferences/def.ts
+++ b/packages/frontend/src/preferences/def.ts
@@ -136,6 +136,7 @@ export const PREF_DEF = {
'clips',
'drive',
'followRequests',
+ 'chat',
'-',
'explore',
'announcements',
@@ -331,6 +332,7 @@ export const PREF_DEF = {
plugins: {
default: [] as Plugin[],
},
+
'sound.masterVolume': {
default: 0.3,
},
@@ -352,6 +354,10 @@ export const PREF_DEF = {
'sound.on.reaction': {
default: { type: 'syuilo/bubble2', volume: 1 } as SoundStore,
},
+ 'sound.on.chatMessage': {
+ default: { type: 'syuilo/waon', volume: 1 } as SoundStore,
+ },
+
'deck.alwaysShowMainColumn': {
default: true,
},
@@ -364,6 +370,7 @@ export const PREF_DEF = {
'deck.columnAlign': {
default: 'left' as 'left' | 'right' | 'center',
},
+
'game.dropAndFusion': {
default: {
bgmVolume: 0.25,
diff --git a/packages/frontend/src/router.definition.ts b/packages/frontend/src/router.definition.ts
index 3b60ee68e3..0585a31fd1 100644
--- a/packages/frontend/src/router.definition.ts
+++ b/packages/frontend/src/router.definition.ts
@@ -40,6 +40,22 @@ export const ROUTE_DEF = [{
}, {
path: '/clips/:clipId',
component: page(() => import('@/pages/clip.vue')),
+}, {
+ path: '/chat',
+ component: page(() => import('@/pages/chat/home.vue')),
+ loginRequired: true,
+}, {
+ path: '/chat/user/:userId',
+ component: page(() => import('@/pages/chat/room.vue')),
+ loginRequired: true,
+}, {
+ path: '/chat/room/:roomId',
+ component: page(() => import('@/pages/chat/room.vue')),
+ loginRequired: true,
+}, {
+ path: '/chat/messages/:messageId',
+ component: page(() => import('@/pages/chat/message.vue')),
+ loginRequired: true,
}, {
path: '/instance-info/:host',
component: page(() => import('@/pages/instance-info.vue')),
diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss
index 8c5617b72e..f122f47c06 100644
--- a/packages/frontend/src/style.scss
+++ b/packages/frontend/src/style.scss
@@ -113,10 +113,6 @@ a {
outline-offset: 2px;
}
- &:hover {
- text-decoration: underline;
- }
-
&[target="_blank"] {
-webkit-touch-callout: default;
}
@@ -335,13 +331,13 @@ rt {
._gaps_m {
display: flex;
flex-direction: column;
- gap: 1.5em;
+ gap: 21px;
}
._gaps_s {
display: flex;
flex-direction: column;
- gap: 0.75em;
+ gap: 10px;
}
._gaps {
diff --git a/packages/frontend/src/types/menu.ts b/packages/frontend/src/types/menu.ts
index 5d1fc1fe72..820759ce61 100644
--- a/packages/frontend/src/types/menu.ts
+++ b/packages/frontend/src/types/menu.ts
@@ -15,16 +15,16 @@ export type MenuAction = (ev: MouseEvent) => void;
export type MenuDivider = { type: 'divider' };
export type MenuNull = undefined;
-export type MenuLabel = { type: 'label', text: string };
-export type MenuLink = { type: 'link', to: string, text: string, icon?: string, indicate?: boolean, avatar?: Misskey.entities.User };
-export type MenuA = { type: 'a', href: string, target?: string, download?: string, text: string, icon?: string, indicate?: boolean };
+export type MenuLabel = { type: 'label', text: string, caption?: string };
+export type MenuLink = { type: 'link', to: string, text: string, caption?: string, icon?: string, indicate?: boolean, avatar?: Misskey.entities.User };
+export type MenuA = { type: 'a', href: string, target?: string, download?: string, text: string, caption?: string, icon?: string, indicate?: boolean };
export type MenuUser = { type: 'user', user: Misskey.entities.User, active?: boolean, indicate?: boolean, action: MenuAction };
-export type MenuSwitch = { type: 'switch', ref: Ref, text: string, icon?: string, disabled?: boolean | Ref };
-export type MenuButton = { type?: 'button', text: string, icon?: string, indicate?: boolean, danger?: boolean, active?: boolean | ComputedRef, avatar?: Misskey.entities.User; action: MenuAction };
-export type MenuRadio = { type: 'radio', text: string, icon?: string, ref: Ref, options: MenuRadioOptionsDef, disabled?: boolean | Ref };
-export type MenuRadioOption = { type: 'radioOption', text: string, action: MenuAction; active?: boolean | ComputedRef };
+export type MenuSwitch = { type: 'switch', ref: Ref, text: string, caption?: string, icon?: string, disabled?: boolean | Ref };
+export type MenuButton = { type?: 'button', text: string, caption?: string, icon?: string, indicate?: boolean, danger?: boolean, active?: boolean | ComputedRef, avatar?: Misskey.entities.User; action: MenuAction };
+export type MenuRadio = { type: 'radio', text: string, caption?: string, icon?: string, ref: Ref, options: MenuRadioOptionsDef, disabled?: boolean | Ref };
+export type MenuRadioOption = { type: 'radioOption', text: string, caption?: string, action: MenuAction; active?: boolean | ComputedRef };
export type MenuComponent = { type: 'component', component: T, props?: ComponentProps };
-export type MenuParent = { type: 'parent', text: string, icon?: string, children: MenuItem[] | (() => Promise