Merge branch 'develop' into enhance-migration
This commit is contained in:
commit
c879cddfd2
|
@ -13,6 +13,9 @@
|
||||||
-->
|
-->
|
||||||
## 13.x.x (unreleased)
|
## 13.x.x (unreleased)
|
||||||
|
|
||||||
|
### NOTE
|
||||||
|
- Node.js 18.6.0以上が必要になりました
|
||||||
|
|
||||||
### General
|
### General
|
||||||
- アカウントの引っ越し(フォロワー引き継ぎ)に対応
|
- アカウントの引っ越し(フォロワー引き継ぎ)に対応
|
||||||
* 一度引っ越したアカウントは利用に制限がかかります
|
* 一度引っ越したアカウントは利用に制限がかかります
|
||||||
|
@ -23,6 +26,7 @@
|
||||||
(デスクトップ表示ではusernameの右側のボタンからも追加可能)
|
(デスクトップ表示ではusernameの右側のボタンからも追加可能)
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
|
- 通知の表示をカスタマイズできるように
|
||||||
- コントロールパネルのカスタム絵文字ページおよびaboutのカスタム絵文字の検索インプットで、`:emojiname1::emojiname2:`のように検索して絵文字を検索できるように
|
- コントロールパネルのカスタム絵文字ページおよびaboutのカスタム絵文字の検索インプットで、`:emojiname1::emojiname2:`のように検索して絵文字を検索できるように
|
||||||
* 絵文字ピッカーから入力可能になります
|
* 絵文字ピッカーから入力可能になります
|
||||||
- データセーバーモードを追加
|
- データセーバーモードを追加
|
||||||
|
@ -33,6 +37,7 @@
|
||||||
### Server
|
### Server
|
||||||
- Fix: エクスポートデータの拡張子がunknownになる問題を修正
|
- Fix: エクスポートデータの拡張子がunknownになる問題を修正
|
||||||
- Fix: Content-Dispositionのパースでエラーが発生した場合にダウンロードが完了しない問題を修正
|
- Fix: Content-Dispositionのパースでエラーが発生した場合にダウンロードが完了しない問題を修正
|
||||||
|
- Fix: API: i/update avatarIdとbannerIdにnullを渡した時、画像がリセットされない問題を修正
|
||||||
|
|
||||||
## 13.11.3
|
## 13.11.3
|
||||||
|
|
||||||
|
|
|
@ -1001,6 +1001,15 @@ accountMoved: "このユーザーは新しいアカウントに引っ越しま
|
||||||
forceShowAds: "常に広告を表示する"
|
forceShowAds: "常に広告を表示する"
|
||||||
addMemo: "メモを追加"
|
addMemo: "メモを追加"
|
||||||
editMemo: "メモを編集"
|
editMemo: "メモを編集"
|
||||||
|
notificationDisplay: "通知の表示"
|
||||||
|
leftTop: "左上"
|
||||||
|
rightTop: "右上"
|
||||||
|
leftBottom: "左下"
|
||||||
|
rightBottom: "右下"
|
||||||
|
stackAxis: "スタック方向"
|
||||||
|
vertical: "縦"
|
||||||
|
horizontal: "横"
|
||||||
|
position: "位置"
|
||||||
|
|
||||||
_accountMigration:
|
_accountMigration:
|
||||||
moveTo: "このアカウントを新しいアカウントに引っ越す"
|
moveTo: "このアカウントを新しいアカウントに引っ越す"
|
||||||
|
|
|
@ -221,6 +221,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
updates.avatarId = avatar.id;
|
updates.avatarId = avatar.id;
|
||||||
updates.avatarUrl = this.driveFileEntityService.getPublicUrl(avatar, 'avatar');
|
updates.avatarUrl = this.driveFileEntityService.getPublicUrl(avatar, 'avatar');
|
||||||
updates.avatarBlurhash = avatar.blurhash;
|
updates.avatarBlurhash = avatar.blurhash;
|
||||||
|
} else if (ps.avatarId === null) {
|
||||||
|
updates.avatarId = null;
|
||||||
|
updates.avatarUrl = null;
|
||||||
|
updates.avatarBlurhash = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.bannerId) {
|
if (ps.bannerId) {
|
||||||
|
@ -232,6 +236,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
updates.bannerId = banner.id;
|
updates.bannerId = banner.id;
|
||||||
updates.bannerUrl = this.driveFileEntityService.getPublicUrl(banner);
|
updates.bannerUrl = this.driveFileEntityService.getPublicUrl(banner);
|
||||||
updates.bannerBlurhash = banner.blurhash;
|
updates.bannerBlurhash = banner.blurhash;
|
||||||
|
} else if (ps.bannerId === null) {
|
||||||
|
updates.bannerId = null;
|
||||||
|
updates.bannerUrl = null;
|
||||||
|
updates.bannerBlurhash = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.pinnedPageId) {
|
if (ps.pinnedPageId) {
|
||||||
|
|
|
@ -508,7 +508,6 @@ describe('ユーザー', () => {
|
||||||
};
|
};
|
||||||
assert.deepStrictEqual(response, expected, inspect(parameters));
|
assert.deepStrictEqual(response, expected, inspect(parameters));
|
||||||
|
|
||||||
if (1) return; // BUG 521eb95 以降アバターのリセットができない。
|
|
||||||
const parameters2 = { avatarId: null };
|
const parameters2 = { avatarId: null };
|
||||||
const response2 = await successfulApiCall({ endpoint: 'i/update', parameters: parameters2, user: alice });
|
const response2 = await successfulApiCall({ endpoint: 'i/update', parameters: parameters2, user: alice });
|
||||||
const expected2 = {
|
const expected2 = {
|
||||||
|
@ -534,7 +533,6 @@ describe('ユーザー', () => {
|
||||||
};
|
};
|
||||||
assert.deepStrictEqual(response, expected, inspect(parameters));
|
assert.deepStrictEqual(response, expected, inspect(parameters));
|
||||||
|
|
||||||
if (1) return; // BUG 521eb95 以降バナーのリセットができない。
|
|
||||||
const parameters2 = { bannerId: null };
|
const parameters2 = { bannerId: null };
|
||||||
const response2 = await successfulApiCall({ endpoint: 'i/update', parameters: parameters2, user: alice });
|
const response2 = await successfulApiCall({ endpoint: 'i/update', parameters: parameters2, user: alice });
|
||||||
const expected2 = {
|
const expected2 = {
|
||||||
|
|
|
@ -38,6 +38,7 @@ fs.readFile(
|
||||||
path.resolve(__dirname, '../../..', arg)
|
path.resolve(__dirname, '../../..', arg)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.map((path) => path.replace(/(?:(?<=\.stories)\.(?:impl|meta)|\.msw)(?=\.ts$)/g, ''))
|
||||||
.map((path) => (path.startsWith('.') ? path : `./${path}`))
|
.map((path) => (path.startsWith('.') ? path : `./${path}`))
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -247,6 +247,10 @@ watch($$(text), () => {
|
||||||
checkMissingMention();
|
checkMissingMention();
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
|
watch($$(visibility), () => {
|
||||||
|
checkMissingMention();
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
watch($$(visibleUsers), () => {
|
watch($$(visibleUsers), () => {
|
||||||
checkMissingMention();
|
checkMissingMention();
|
||||||
}, {
|
}, {
|
||||||
|
|
|
@ -92,6 +92,26 @@
|
||||||
</div>
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
||||||
|
<FormSection>
|
||||||
|
<template #label>{{ i18n.ts.notificationDisplay }}</template>
|
||||||
|
|
||||||
|
<div class="_gaps_m">
|
||||||
|
<MkRadios v-model="notificationPosition">
|
||||||
|
<template #label>{{ i18n.ts.position }}</template>
|
||||||
|
<option value="leftTop"><i class="ti ti-align-box-left-top"></i> {{ i18n.ts.leftTop }}</option>
|
||||||
|
<option value="rightTop"><i class="ti ti-align-box-right-top"></i> {{ i18n.ts.rightTop }}</option>
|
||||||
|
<option value="leftBottom"><i class="ti ti-align-box-left-bottom"></i> {{ i18n.ts.leftBottom }}</option>
|
||||||
|
<option value="rightBottom"><i class="ti ti-align-box-right-bottom"></i> {{ i18n.ts.rightBottom }}</option>
|
||||||
|
</MkRadios>
|
||||||
|
|
||||||
|
<MkRadios v-model="notificationStackAxis">
|
||||||
|
<template #label>{{ i18n.ts.stackAxis }}</template>
|
||||||
|
<option value="vertical"><i class="ti ti-carousel-vertical"></i> {{ i18n.ts.vertical }}</option>
|
||||||
|
<option value="horizontal"><i class="ti ti-carousel-horizontal"></i> {{ i18n.ts.horizontal }}</option>
|
||||||
|
</MkRadios>
|
||||||
|
</div>
|
||||||
|
</FormSection>
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<MkSwitch v-model="aiChanMode">{{ i18n.ts.aiChanMode }}</MkSwitch>
|
<MkSwitch v-model="aiChanMode">{{ i18n.ts.aiChanMode }}</MkSwitch>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
@ -181,6 +201,8 @@ const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('
|
||||||
const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars'));
|
const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars'));
|
||||||
const aiChanMode = computed(defaultStore.makeGetterSetter('aiChanMode'));
|
const aiChanMode = computed(defaultStore.makeGetterSetter('aiChanMode'));
|
||||||
const mediaListWithOneImageAppearance = computed(defaultStore.makeGetterSetter('mediaListWithOneImageAppearance'));
|
const mediaListWithOneImageAppearance = computed(defaultStore.makeGetterSetter('mediaListWithOneImageAppearance'));
|
||||||
|
const notificationPosition = computed(defaultStore.makeGetterSetter('notificationPosition'));
|
||||||
|
const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis'));
|
||||||
|
|
||||||
watch(lang, () => {
|
watch(lang, () => {
|
||||||
miLocalStorage.setItem('lang', lang.value as string);
|
miLocalStorage.setItem('lang', lang.value as string);
|
||||||
|
|
|
@ -314,6 +314,14 @@ export const defaultStore = markRaw(new Storage('base', {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: 'expand' as 'expand' | '16_9' | '1_1' | '2_3',
|
default: 'expand' as 'expand' | '16_9' | '1_1' | '2_3',
|
||||||
},
|
},
|
||||||
|
notificationPosition: {
|
||||||
|
where: 'device',
|
||||||
|
default: 'rightBottom' as 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom',
|
||||||
|
},
|
||||||
|
notificationStackAxis: {
|
||||||
|
where: 'device',
|
||||||
|
default: 'horizontal' as 'vertical' | 'horizontal',
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// TODO: 他のタブと永続化されたstateを同期
|
// TODO: 他のタブと永続化されたstateを同期
|
||||||
|
|
|
@ -10,14 +10,16 @@
|
||||||
<XUpload v-if="uploads.length > 0"/>
|
<XUpload v-if="uploads.length > 0"/>
|
||||||
|
|
||||||
<TransitionGroup
|
<TransitionGroup
|
||||||
tag="div" :class="$style.notifications"
|
tag="div" :class="[$style.notifications, $style[`notificationsPosition-${defaultStore.state.notificationPosition}`], $style[`notificationsStackAxis-${defaultStore.state.notificationStackAxis}`]]"
|
||||||
:move-class="defaultStore.state.animation ? $style.transition_notification_move : ''"
|
:move-class="defaultStore.state.animation ? $style.transition_notification_move : ''"
|
||||||
:enter-active-class="defaultStore.state.animation ? $style.transition_notification_enterActive : ''"
|
:enter-active-class="defaultStore.state.animation ? $style.transition_notification_enterActive : ''"
|
||||||
:leave-active-class="defaultStore.state.animation ? $style.transition_notification_leaveActive : ''"
|
:leave-active-class="defaultStore.state.animation ? $style.transition_notification_leaveActive : ''"
|
||||||
:enter-from-class="defaultStore.state.animation ? $style.transition_notification_enterFrom : ''"
|
:enter-from-class="defaultStore.state.animation ? $style.transition_notification_enterFrom : ''"
|
||||||
:leave-to-class="defaultStore.state.animation ? $style.transition_notification_leaveTo : ''"
|
:leave-to-class="defaultStore.state.animation ? $style.transition_notification_leaveTo : ''"
|
||||||
>
|
>
|
||||||
<XNotification v-for="notification in notifications" :key="notification.id" :notification="notification" :class="$style.notification"/>
|
<div v-for="notification in notifications" :key="notification.id" :class="$style.notification">
|
||||||
|
<XNotification :notification="notification"/>
|
||||||
|
</div>
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
|
|
||||||
<XStreamIndicator/>
|
<XStreamIndicator/>
|
||||||
|
@ -30,7 +32,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent } from 'vue';
|
import { defineAsyncComponent, ref } from 'vue';
|
||||||
import * as misskey from 'misskey-js';
|
import * as misskey from 'misskey-js';
|
||||||
import { swInject } from './sw-inject';
|
import { swInject } from './sw-inject';
|
||||||
import XNotification from './notification.vue';
|
import XNotification from './notification.vue';
|
||||||
|
@ -85,7 +87,10 @@ if ($i) {
|
||||||
.transition_notification_leaveActive {
|
.transition_notification_leaveActive {
|
||||||
transition: opacity 0.3s, transform 0.3s !important;
|
transition: opacity 0.3s, transform 0.3s !important;
|
||||||
}
|
}
|
||||||
.transition_notification_enterFrom,
|
.transition_notification_enterFrom {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(250px);
|
||||||
|
}
|
||||||
.transition_notification_leaveTo {
|
.transition_notification_leaveTo {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(-250px);
|
transform: translateX(-250px);
|
||||||
|
@ -94,36 +99,91 @@ if ($i) {
|
||||||
.notifications {
|
.notifications {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 3900000;
|
z-index: 3900000;
|
||||||
left: 0;
|
padding: 0 var(--margin);
|
||||||
width: 250px;
|
|
||||||
top: 32px;
|
|
||||||
padding: 0 32px;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
container-type: inline-size;
|
display: flex;
|
||||||
|
|
||||||
|
&.notificationsPosition-leftTop {
|
||||||
|
top: var(--margin);
|
||||||
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.notificationsPosition-rightTop {
|
||||||
|
top: var(--margin);
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.notificationsPosition-leftBottom {
|
||||||
|
bottom: calc(var(--minBottomSpacing) + var(--margin));
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.notificationsPosition-rightBottom {
|
||||||
|
bottom: calc(var(--minBottomSpacing) + var(--margin));
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.notificationsStackAxis-vertical {
|
||||||
|
width: 250px;
|
||||||
|
|
||||||
|
&.notificationsPosition-leftTop,
|
||||||
|
&.notificationsPosition-rightTop {
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
.notification {
|
.notification {
|
||||||
& + .notification {
|
& + .notification {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 500px) {
|
|
||||||
.notifications {
|
|
||||||
top: initial;
|
|
||||||
bottom: calc(var(--minBottomSpacing) + var(--margin));
|
|
||||||
padding: 0 var(--margin);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column-reverse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.notificationsPosition-leftBottom,
|
||||||
|
&.notificationsPosition-rightBottom {
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
|
||||||
.notification {
|
.notification {
|
||||||
& + .notification {
|
& + .notification {
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.notificationsStackAxis-horizontal {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&.notificationsPosition-leftTop,
|
||||||
|
&.notificationsPosition-leftBottom {
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
& + .notification {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.notificationsPosition-rightTop,
|
||||||
|
&.notificationsPosition-rightBottom {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
& + .notification {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
width: 250px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
container-type: inline-size;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
Loading…
Reference in New Issue