Merge branch 'develop' into feat-1714
This commit is contained in:
commit
24c7fb38a0
165 changed files with 6316 additions and 4265 deletions
19
packages/frontend/src/scripts/check-permissions.ts
Normal file
19
packages/frontend/src/scripts/check-permissions.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { instance } from '@/instance.js';
|
||||
import { $i } from '@/account.js';
|
||||
|
||||
export const notesSearchAvailable = (
|
||||
// FIXME: instance.policies would be null in Vitest
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
($i == null && instance.policies != null && instance.policies.canSearchNotes) ||
|
||||
($i != null && $i.policies.canSearchNotes) ||
|
||||
false
|
||||
) as boolean;
|
||||
|
||||
export const canSearchNonLocalNotes = (
|
||||
instance.noteSearchableScope === 'global'
|
||||
);
|
||||
|
|
@ -41,6 +41,15 @@ function describe(file: Misskey.entities.DriveFile) {
|
|||
});
|
||||
}
|
||||
|
||||
function move(file: Misskey.entities.DriveFile) {
|
||||
os.selectDriveFolder(false).then(folder => {
|
||||
misskeyApi('drive/files/update', {
|
||||
fileId: file.id,
|
||||
folderId: folder[0] ? folder[0].id : null,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function toggleSensitive(file: Misskey.entities.DriveFile) {
|
||||
misskeyApi('drive/files/update', {
|
||||
fileId: file.id,
|
||||
|
|
@ -88,6 +97,10 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss
|
|||
text: i18n.ts.rename,
|
||||
icon: 'ti ti-forms',
|
||||
action: () => rename(file),
|
||||
}, {
|
||||
text: i18n.ts.move,
|
||||
icon: 'ti ti-folder-symlink',
|
||||
action: () => move(file),
|
||||
}, {
|
||||
text: file.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive,
|
||||
icon: file.isSensitive ? 'ti ti-eye' : 'ti ti-eye-exclamation',
|
||||
|
|
|
|||
|
|
@ -13,10 +13,12 @@ import * as os from '@/os.js';
|
|||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||
import { defaultStore, userActions } from '@/store.js';
|
||||
import { $i, iAmModerator } from '@/account.js';
|
||||
import { notesSearchAvailable, canSearchNonLocalNotes } from '@/scripts/check-permissions.js';
|
||||
import { IRouter } from '@/nirax.js';
|
||||
import { antennasCache, rolesCache, userListsCache } from '@/cache.js';
|
||||
import { mainRouter } from '@/router/main.js';
|
||||
import { copyEmbedCode } from '@/scripts/get-embed-code.js';
|
||||
import { MenuItem } from '@/types/menu.js';
|
||||
|
||||
export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter = mainRouter) {
|
||||
const meId = $i ? $i.id : null;
|
||||
|
|
@ -82,15 +84,6 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter
|
|||
});
|
||||
}
|
||||
|
||||
async function toggleWithReplies() {
|
||||
os.apiWithDialog('following/update', {
|
||||
userId: user.id,
|
||||
withReplies: !user.withReplies,
|
||||
}).then(() => {
|
||||
user.withReplies = !user.withReplies;
|
||||
});
|
||||
}
|
||||
|
||||
async function toggleNotify() {
|
||||
os.apiWithDialog('following/update', {
|
||||
userId: user.id,
|
||||
|
|
@ -155,13 +148,20 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter
|
|||
});
|
||||
}
|
||||
|
||||
let menu = [{
|
||||
let menu: MenuItem[] = [{
|
||||
icon: 'ti ti-at',
|
||||
text: i18n.ts.copyUsername,
|
||||
action: () => {
|
||||
copyToClipboard(`@${user.username}@${user.host ?? host}`);
|
||||
},
|
||||
}, ...(iAmModerator ? [{
|
||||
}, ...( notesSearchAvailable && (user.host == null || canSearchNonLocalNotes) ? [{
|
||||
icon: 'ti ti-search',
|
||||
text: i18n.ts.searchThisUsersNotes,
|
||||
action: () => {
|
||||
router.push(`/search?username=${encodeURIComponent(user.username)}${user.host != null ? '&host=' + encodeURIComponent(user.host) : ''}`);
|
||||
},
|
||||
}] : [])
|
||||
, ...(iAmModerator ? [{
|
||||
icon: 'ti ti-user-exclamation',
|
||||
text: i18n.ts.moderation,
|
||||
action: () => {
|
||||
|
|
@ -317,15 +317,25 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter
|
|||
|
||||
// フォローしたとしても user.isFollowing はリアルタイム更新されないので不便なため
|
||||
//if (user.isFollowing) {
|
||||
const withRepliesRef = ref(user.withReplies);
|
||||
menu = menu.concat([{
|
||||
icon: user.withReplies ? 'ti ti-messages-off' : 'ti ti-messages',
|
||||
text: user.withReplies ? i18n.ts.hideRepliesToOthersInTimeline : i18n.ts.showRepliesToOthersInTimeline,
|
||||
action: toggleWithReplies,
|
||||
type: 'switch',
|
||||
icon: 'ti ti-messages',
|
||||
text: i18n.ts.showRepliesToOthersInTimeline,
|
||||
ref: withRepliesRef,
|
||||
}, {
|
||||
icon: user.notify === 'none' ? 'ti ti-bell' : 'ti ti-bell-off',
|
||||
text: user.notify === 'none' ? i18n.ts.notifyNotes : i18n.ts.unnotifyNotes,
|
||||
action: toggleNotify,
|
||||
}]);
|
||||
watch(withRepliesRef, (withReplies) => {
|
||||
misskeyApi('following/update', {
|
||||
userId: user.id,
|
||||
withReplies,
|
||||
}).then(() => {
|
||||
user.withReplies = withReplies;
|
||||
});
|
||||
});
|
||||
//}
|
||||
|
||||
menu = menu.concat([{ type: 'divider' }, {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export async function lookup(router?: Router) {
|
|||
title: i18n.ts.lookup,
|
||||
});
|
||||
const query = temp ? temp.trim() : '';
|
||||
if (canceled) return;
|
||||
if (canceled || query.length <= 1) return;
|
||||
|
||||
if (query.startsWith('@') && !query.includes(' ')) {
|
||||
_router.push(`/${query}`);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
import { deepClone } from './clone.js';
|
||||
import type { Cloneable } from './clone.js';
|
||||
|
||||
type DeepPartial<T> = {
|
||||
export type DeepPartial<T> = {
|
||||
[P in keyof T]?: T[P] extends Record<string | number | symbol, unknown> ? DeepPartial<T[P]> : T[P];
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -124,14 +124,16 @@ export async function loadAudio(url: string, options?: { useCache?: boolean; })
|
|||
*/
|
||||
export function playMisskeySfx(operationType: OperationType) {
|
||||
const sound = defaultStore.state[`sound_${operationType}`];
|
||||
if (sound.type == null || !canPlay || ('userActivation' in navigator && !navigator.userActivation.hasBeenActive)) return;
|
||||
|
||||
canPlay = false;
|
||||
playMisskeySfxFile(sound).finally(() => {
|
||||
// ごく短時間に音が重複しないように
|
||||
setTimeout(() => {
|
||||
canPlay = true;
|
||||
}, 25);
|
||||
playMisskeySfxFile(sound).then((succeed) => {
|
||||
if (!succeed && sound.type === '_driveFile_') {
|
||||
// ドライブファイルが存在しない場合はデフォルトのサウンドを再生する
|
||||
const soundName = defaultStore.def[`sound_${operationType}`].default.type as Exclude<SoundType, '_driveFile_'>;
|
||||
if (_DEV_) console.log(`Failed to play sound: ${sound.fileUrl}, so play default sound: ${soundName}`);
|
||||
playMisskeySfxFileInternal({
|
||||
type: soundName,
|
||||
volume: sound.volume,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -139,19 +141,39 @@ export function playMisskeySfx(operationType: OperationType) {
|
|||
* サウンド設定形式で指定された音声を再生する
|
||||
* @param soundStore サウンド設定
|
||||
*/
|
||||
export async function playMisskeySfxFile(soundStore: SoundStore) {
|
||||
export async function playMisskeySfxFile(soundStore: SoundStore): Promise<boolean> {
|
||||
// 連続して再生しない
|
||||
if (!canPlay) return false;
|
||||
// ユーザーアクティベーションが必要な場合はそれがない場合は再生しない
|
||||
if ('userActivation' in navigator && !navigator.userActivation.hasBeenActive) return false;
|
||||
// サウンドがない場合は再生しない
|
||||
if (soundStore.type === null || soundStore.type === '_driveFile_' && !soundStore.fileUrl) return false;
|
||||
|
||||
canPlay = false;
|
||||
return await playMisskeySfxFileInternal(soundStore).finally(() => {
|
||||
// ごく短時間に音が重複しないように
|
||||
setTimeout(() => {
|
||||
canPlay = true;
|
||||
}, 25);
|
||||
});
|
||||
}
|
||||
|
||||
async function playMisskeySfxFileInternal(soundStore: SoundStore): Promise<boolean> {
|
||||
if (soundStore.type === null || (soundStore.type === '_driveFile_' && !soundStore.fileUrl)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
const masterVolume = defaultStore.state.sound_masterVolume;
|
||||
if (isMute() || masterVolume === 0 || soundStore.volume === 0) {
|
||||
return;
|
||||
return true; // ミュート時は成功として扱う
|
||||
}
|
||||
const url = soundStore.type === '_driveFile_' ? soundStore.fileUrl : `/client-assets/sounds/${soundStore.type}.mp3`;
|
||||
const buffer = await loadAudio(url);
|
||||
if (!buffer) return;
|
||||
const buffer = await loadAudio(url).catch(() => {
|
||||
return undefined;
|
||||
});
|
||||
if (!buffer) return false;
|
||||
const volume = soundStore.volume * masterVolume;
|
||||
createSourceNode(buffer, { volume }).soundSource.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function playUrl(url: string, opts: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue