Merge remote-tracking branch 'misskey/release/2024.5.0' into future
This commit is contained in:
commit
3372e0ffe1
181 changed files with 4057 additions and 891 deletions
|
|
@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
v-for="announcement in $i.unreadAnnouncements.filter(x => x.display === 'banner')"
|
||||
:key="announcement.id"
|
||||
:class="$style.item"
|
||||
to="/announcements"
|
||||
:to="`/announcements/${announcement.id}`"
|
||||
>
|
||||
<span :class="$style.icon">
|
||||
<i v-if="announcement.icon === 'info'" class="ph-info ph-bold ph-lg"></i>
|
||||
|
|
|
|||
|
|
@ -9,18 +9,22 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<i class="ph-flying-saucer ph-bold ph-lg"></i><span style="margin-left: 8px;">{{ column.name }}</span>
|
||||
</template>
|
||||
|
||||
<MkTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId"/>
|
||||
<MkTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId" @note="onNote"/>
|
||||
</XColumn>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, shallowRef } from 'vue';
|
||||
import { onMounted, ref, shallowRef, watch } from 'vue';
|
||||
import XColumn from './column.vue';
|
||||
import { updateColumn, Column } from './deck-store.js';
|
||||
import MkTimeline from '@/components/MkTimeline.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { MenuItem } from '@/types/menu.js';
|
||||
import { SoundStore } from '@/store.js';
|
||||
import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
|
||||
import * as sound from '@/scripts/sound.js';
|
||||
|
||||
const props = defineProps<{
|
||||
column: Column;
|
||||
|
|
@ -28,6 +32,7 @@ const props = defineProps<{
|
|||
}>();
|
||||
|
||||
const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
|
||||
const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
|
||||
|
||||
onMounted(() => {
|
||||
if (props.column.antennaId == null) {
|
||||
|
|
@ -35,6 +40,10 @@ onMounted(() => {
|
|||
}
|
||||
});
|
||||
|
||||
watch(soundSetting, v => {
|
||||
updateColumn(props.column.id, { soundSetting: v });
|
||||
});
|
||||
|
||||
async function setAntenna() {
|
||||
const antennas = await misskeyApi('antennas/list');
|
||||
const { canceled, result: antenna } = await os.select({
|
||||
|
|
@ -54,7 +63,11 @@ function editAntenna() {
|
|||
os.pageWindow('my/antennas/' + props.column.antennaId);
|
||||
}
|
||||
|
||||
const menu = [
|
||||
function onNote() {
|
||||
sound.playMisskeySfxFile(soundSetting.value);
|
||||
}
|
||||
|
||||
const menu: MenuItem[] = [
|
||||
{
|
||||
icon: 'ph-pencil-simple ph-bold ph-lg',
|
||||
text: i18n.ts.selectAntenna,
|
||||
|
|
@ -65,6 +78,11 @@ const menu = [
|
|||
text: i18n.ts.editAntenna,
|
||||
action: editAntenna,
|
||||
},
|
||||
{
|
||||
icon: 'ti ti-bell',
|
||||
text: i18n.ts._deck.newNoteNotificationSettings,
|
||||
action: () => soundSettingsButton(soundSetting),
|
||||
},
|
||||
];
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -13,21 +13,26 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<div style="padding: 8px; text-align: center;">
|
||||
<MkButton primary gradate rounded inline small @click="post"><i class="ph-pencil-simple ph-bold ph-lg"></i></MkButton>
|
||||
</div>
|
||||
<MkTimeline ref="timeline" src="channel" :channel="column.channelId" :key="column.channelId + column.withRenotes + column.onlyFiles" :withRenotes="withRenotes" :onlyFiles="onlyFiles"/>
|
||||
<MkTimeline ref="timeline" src="channel" :channel="column.channelId" :key="column.channelId + column.withRenotes + column.onlyFiles" :withRenotes="withRenotes" :onlyFiles="onlyFiles" @note="onNote"/>
|
||||
</template>
|
||||
</XColumn>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch, ref, shallowRef } from 'vue';
|
||||
import { ref, shallowRef, watch } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import XColumn from './column.vue';
|
||||
import { updateColumn, Column } from './deck-store.js';
|
||||
import MkTimeline from '@/components/MkTimeline.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { favoritedChannelsCache } from '@/cache.js';
|
||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { MenuItem } from '@/types/menu.js';
|
||||
import { SoundStore } from '@/store.js';
|
||||
import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
|
||||
import * as sound from '@/scripts/sound.js';
|
||||
|
||||
const props = defineProps<{
|
||||
column: Column;
|
||||
|
|
@ -51,25 +56,29 @@ watch(onlyFiles, v => {
|
|||
});
|
||||
});
|
||||
|
||||
const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
|
||||
|
||||
if (props.column.channelId == null) {
|
||||
setChannel();
|
||||
}
|
||||
|
||||
watch(soundSetting, v => {
|
||||
updateColumn(props.column.id, { soundSetting: v });
|
||||
});
|
||||
|
||||
async function setChannel() {
|
||||
const channels = await misskeyApi('channels/my-favorites', {
|
||||
limit: 100,
|
||||
});
|
||||
const { canceled, result: channel } = await os.select({
|
||||
const channels = await favoritedChannelsCache.fetch();
|
||||
const { canceled, result: chosenChannel } = await os.select({
|
||||
title: i18n.ts.selectChannel,
|
||||
items: channels.map(x => ({
|
||||
value: x, text: x.name,
|
||||
})),
|
||||
default: props.column.channelId,
|
||||
});
|
||||
if (canceled) return;
|
||||
if (canceled || chosenChannel == null) return;
|
||||
updateColumn(props.column.id, {
|
||||
channelId: channel.id,
|
||||
name: channel.name,
|
||||
channelId: chosenChannel.id,
|
||||
name: chosenChannel.name,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -85,7 +94,11 @@ async function post() {
|
|||
});
|
||||
}
|
||||
|
||||
const menu = [{
|
||||
function onNote() {
|
||||
sound.playMisskeySfxFile(soundSetting.value);
|
||||
}
|
||||
|
||||
const menu: MenuItem[] = [{
|
||||
icon: 'ph-pencil-simple ph-bold ph-lg',
|
||||
text: i18n.ts.selectChannel,
|
||||
action: setChannel,
|
||||
|
|
@ -97,5 +110,9 @@ const menu = [{
|
|||
type: 'switch',
|
||||
text: i18n.ts.fileAttachedOnly,
|
||||
ref: onlyFiles,
|
||||
}, {
|
||||
icon: 'ti ti-bell',
|
||||
text: i18n.ts._deck.newNoteNotificationSettings,
|
||||
action: () => soundSettingsButton(soundSetting),
|
||||
}];
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { notificationTypes } from 'misskey-js';
|
|||
import { Storage } from '@/pizzax.js';
|
||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||
import { deepClone } from '@/scripts/clone.js';
|
||||
import { SoundStore } from '@/store.js';
|
||||
|
||||
type ColumnWidget = {
|
||||
name: string;
|
||||
|
|
@ -33,6 +34,7 @@ export type Column = {
|
|||
withRenotes?: boolean;
|
||||
withReplies?: boolean;
|
||||
onlyFiles?: boolean;
|
||||
soundSetting: SoundStore;
|
||||
};
|
||||
|
||||
export const deckStore = markRaw(new Storage('deck', {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<i class="ph-list ph-bold ph-lg"></i><span style="margin-left: 8px;">{{ column.name }}</span>
|
||||
</template>
|
||||
|
||||
<MkTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" :key="column.listId + column.withRenotes + column.onlyFiles" :withRenotes="withRenotes" :onlyFiles="onlyFiles"/>
|
||||
<MkTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" :key="column.listId + column.withRenotes + column.onlyFiles" :withRenotes="withRenotes" :onlyFiles="onlyFiles" @note="onNote"/>
|
||||
</XColumn>
|
||||
</template>
|
||||
|
||||
|
|
@ -21,6 +21,10 @@ import MkTimeline from '@/components/MkTimeline.vue';
|
|||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { MenuItem } from '@/types/menu.js';
|
||||
import { SoundStore } from '@/store.js';
|
||||
import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
|
||||
import * as sound from '@/scripts/sound.js';
|
||||
|
||||
const props = defineProps<{
|
||||
column: Column;
|
||||
|
|
@ -30,6 +34,7 @@ const props = defineProps<{
|
|||
const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
|
||||
const withRenotes = ref(props.column.withRenotes ?? true);
|
||||
const onlyFiles = ref(props.column.onlyFiles ?? false);
|
||||
const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
|
||||
|
||||
if (props.column.listId == null) {
|
||||
setList();
|
||||
|
|
@ -47,6 +52,10 @@ watch(onlyFiles, v => {
|
|||
});
|
||||
});
|
||||
|
||||
watch(soundSetting, v => {
|
||||
updateColumn(props.column.id, { soundSetting: v });
|
||||
});
|
||||
|
||||
async function setList() {
|
||||
const lists = await misskeyApi('users/lists/list');
|
||||
const { canceled, result: list } = await os.select({
|
||||
|
|
@ -66,7 +75,11 @@ function editList() {
|
|||
os.pageWindow('my/lists/' + props.column.listId);
|
||||
}
|
||||
|
||||
const menu = [
|
||||
function onNote() {
|
||||
sound.playMisskeySfxFile(soundSetting.value);
|
||||
}
|
||||
|
||||
const menu: MenuItem[] = [
|
||||
{
|
||||
icon: 'ph-pencil-simple ph-bold ph-lg',
|
||||
text: i18n.ts.selectList,
|
||||
|
|
@ -87,5 +100,10 @@ const menu = [
|
|||
text: i18n.ts.fileAttachedOnly,
|
||||
ref: onlyFiles,
|
||||
},
|
||||
{
|
||||
icon: 'ti ti-bell',
|
||||
text: i18n.ts._deck.newNoteNotificationSettings,
|
||||
action: () => soundSettingsButton(soundSetting),
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -9,18 +9,22 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<i class="ph-seal-check ph-bold ph-lg"></i><span style="margin-left: 8px;">{{ column.name }}</span>
|
||||
</template>
|
||||
|
||||
<MkTimeline v-if="column.roleId" ref="timeline" src="role" :role="column.roleId"/>
|
||||
<MkTimeline v-if="column.roleId" ref="timeline" src="role" :role="column.roleId" @note="onNote"/>
|
||||
</XColumn>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, shallowRef } from 'vue';
|
||||
import { onMounted, ref, shallowRef, watch } from 'vue';
|
||||
import XColumn from './column.vue';
|
||||
import { updateColumn, Column } from './deck-store.js';
|
||||
import MkTimeline from '@/components/MkTimeline.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { MenuItem } from '@/types/menu.js';
|
||||
import { SoundStore } from '@/store.js';
|
||||
import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
|
||||
import * as sound from '@/scripts/sound.js';
|
||||
|
||||
const props = defineProps<{
|
||||
column: Column;
|
||||
|
|
@ -28,6 +32,7 @@ const props = defineProps<{
|
|||
}>();
|
||||
|
||||
const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
|
||||
const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
|
||||
|
||||
onMounted(() => {
|
||||
if (props.column.roleId == null) {
|
||||
|
|
@ -35,6 +40,10 @@ onMounted(() => {
|
|||
}
|
||||
});
|
||||
|
||||
watch(soundSetting, v => {
|
||||
updateColumn(props.column.id, { soundSetting: v });
|
||||
});
|
||||
|
||||
async function setRole() {
|
||||
const roles = (await misskeyApi('roles/list')).filter(x => x.isExplorable);
|
||||
const { canceled, result: role } = await os.select({
|
||||
|
|
@ -50,10 +59,18 @@ async function setRole() {
|
|||
});
|
||||
}
|
||||
|
||||
const menu = [{
|
||||
function onNote() {
|
||||
sound.playMisskeySfxFile(soundSetting.value);
|
||||
}
|
||||
|
||||
const menu: MenuItem[] = [{
|
||||
icon: 'ph-pencil-simple ph-bold ph-lg',
|
||||
text: i18n.ts.role,
|
||||
action: setRole,
|
||||
}, {
|
||||
icon: 'ti ti-bell',
|
||||
text: i18n.ts._deck.newNoteNotificationSettings,
|
||||
action: () => soundSettingsButton(soundSetting),
|
||||
}];
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
:withRenotes="withRenotes"
|
||||
:withReplies="withReplies"
|
||||
:onlyFiles="onlyFiles"
|
||||
@note="onNote"
|
||||
/>
|
||||
</XColumn>
|
||||
</template>
|
||||
|
|
@ -42,6 +43,10 @@ import * as os from '@/os.js';
|
|||
import { $i } from '@/account.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { instance } from '@/instance.js';
|
||||
import { MenuItem } from '@/types/menu.js';
|
||||
import { SoundStore } from '@/store.js';
|
||||
import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
|
||||
import * as sound from '@/scripts/sound.js';
|
||||
|
||||
const props = defineProps<{
|
||||
column: Column;
|
||||
|
|
@ -54,6 +59,7 @@ const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
|
|||
const isLocalTimelineAvailable = (($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable));
|
||||
const isGlobalTimelineAvailable = (($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable));
|
||||
const isBubbleTimelineAvailable = ($i == null && instance.policies.btlAvailable) || ($i != null && $i.policies.btlAvailable);
|
||||
const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
|
||||
const withRenotes = ref(props.column.withRenotes ?? true);
|
||||
const withReplies = ref(props.column.withReplies ?? false);
|
||||
const onlyFiles = ref(props.column.onlyFiles ?? false);
|
||||
|
|
@ -76,6 +82,10 @@ watch(onlyFiles, v => {
|
|||
});
|
||||
});
|
||||
|
||||
watch(soundSetting, v => {
|
||||
updateColumn(props.column.id, { soundSetting: v });
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (props.column.tl == null) {
|
||||
setType();
|
||||
|
|
@ -113,10 +123,18 @@ async function setType() {
|
|||
});
|
||||
}
|
||||
|
||||
const menu = [{
|
||||
function onNote() {
|
||||
sound.playMisskeySfxFile(soundSetting.value);
|
||||
}
|
||||
|
||||
const menu: MenuItem[] = [{
|
||||
icon: 'ph-pencil-simple ph-bold ph-lg',
|
||||
text: i18n.ts.timeline,
|
||||
action: setType,
|
||||
}, {
|
||||
icon: 'ti ti-bell',
|
||||
text: i18n.ts._deck.newNoteNotificationSettings,
|
||||
action: () => soundSettingsButton(soundSetting),
|
||||
}, {
|
||||
type: 'switch',
|
||||
text: i18n.ts.showRenotes,
|
||||
|
|
|
|||
107
packages/frontend/src/ui/deck/tl-note-notification.ts
Normal file
107
packages/frontend/src/ui/deck/tl-note-notification.ts
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { Ref } from 'vue';
|
||||
import { SoundStore } from '@/store.js';
|
||||
import { getSoundDuration, playMisskeySfxFile, soundsTypes, SoundType } from '@/scripts/sound.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import * as os from '@/os.js';
|
||||
|
||||
export async function soundSettingsButton(soundSetting: Ref<SoundStore>): Promise<void> {
|
||||
function getSoundTypeName(f: SoundType): string {
|
||||
switch (f) {
|
||||
case null:
|
||||
return i18n.ts.none;
|
||||
case '_driveFile_':
|
||||
return i18n.ts._soundSettings.driveFile;
|
||||
default:
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
const { canceled, result } = await os.form(i18n.ts.sound, {
|
||||
type: {
|
||||
type: 'enum',
|
||||
label: i18n.ts.sound,
|
||||
default: soundSetting.value.type ?? 'none',
|
||||
enum: soundsTypes.map(f => ({
|
||||
value: f ?? 'none', label: getSoundTypeName(f),
|
||||
})),
|
||||
},
|
||||
soundFile: {
|
||||
type: 'drive-file',
|
||||
label: i18n.ts.file,
|
||||
defaultFileId: soundSetting.value.type === '_driveFile_' ? soundSetting.value.fileId : null,
|
||||
hidden: v => v.type !== '_driveFile_',
|
||||
validate: async (file: Misskey.entities.DriveFile) => {
|
||||
if (!file.type.startsWith('audio')) {
|
||||
os.alert({
|
||||
type: 'warning',
|
||||
title: i18n.ts._soundSettings.driveFileTypeWarn,
|
||||
text: i18n.ts._soundSettings.driveFileTypeWarnDescription,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const duration = await getSoundDuration(file.url);
|
||||
if (duration >= 2000) {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'warning',
|
||||
title: i18n.ts._soundSettings.driveFileDurationWarn,
|
||||
text: i18n.ts._soundSettings.driveFileDurationWarnDescription,
|
||||
okText: i18n.ts.continue,
|
||||
cancelText: i18n.ts.cancel,
|
||||
});
|
||||
if (canceled) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
volume: {
|
||||
type: 'range',
|
||||
label: i18n.ts.volume,
|
||||
default: soundSetting.value.volume ?? 1,
|
||||
textConverter: (v) => `${Math.floor(v * 100)}%`,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.05,
|
||||
},
|
||||
listen: {
|
||||
type: 'button',
|
||||
content: i18n.ts.listen,
|
||||
action: (_, v) => {
|
||||
const sound = buildSoundStore(v);
|
||||
if (!sound) return;
|
||||
playMisskeySfxFile(sound);
|
||||
},
|
||||
},
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
const res = buildSoundStore(result);
|
||||
if (res) soundSetting.value = res;
|
||||
|
||||
function buildSoundStore(result: any): SoundStore | null {
|
||||
const type = (result.type === 'none' ? null : result.type) as SoundType;
|
||||
const volume = result.volume as number;
|
||||
const fileId = result.soundFile?.id ?? (soundSetting.value.type === '_driveFile_' ? soundSetting.value.fileId : undefined);
|
||||
const fileUrl = result.soundFile?.url ?? (soundSetting.value.type === '_driveFile_' ? soundSetting.value.fileUrl : undefined);
|
||||
|
||||
if (type === '_driveFile_') {
|
||||
if (!fileUrl || !fileId) {
|
||||
os.alert({
|
||||
type: 'warning',
|
||||
text: i18n.ts._soundSettings.driveFileWarn,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
return { type, volume, fileId, fileUrl };
|
||||
} else {
|
||||
return { type, volume };
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue