Merge branch 'develop' into watermark

This commit is contained in:
syuilo 2025-06-01 08:11:31 +09:00
commit 3691b69488
10 changed files with 40 additions and 15 deletions

View File

@ -1,3 +1,15 @@
## Unreleased
### General
-
### Client
- Fix: リアクションの一部の絵文字が重複して表示されることがある問題を修正
### Server
-
## 2025.5.1 ## 2025.5.1
### Note ### Note

View File

@ -1330,6 +1330,7 @@ restore: "Restaurar "
syncBetweenDevices: "Sincronització entre dispositius" syncBetweenDevices: "Sincronització entre dispositius"
preferenceSyncConflictTitle: "Els valors de la configuració ja existeixen al dispositiu" preferenceSyncConflictTitle: "Els valors de la configuració ja existeixen al dispositiu"
preferenceSyncConflictText: "Un element de la configuració amb sincronització activada desa els seus valors al servidor, però s'ha trobat un valor a la configuració desat al servidor per aquest element de la configuració. Quin valor us sobreescriure?" preferenceSyncConflictText: "Un element de la configuració amb sincronització activada desa els seus valors al servidor, però s'ha trobat un valor a la configuració desat al servidor per aquest element de la configuració. Quin valor us sobreescriure?"
preferenceSyncConflictChoiceMerge: "Integració "
preferenceSyncConflictChoiceServer: "Valors de configuració del servidor" preferenceSyncConflictChoiceServer: "Valors de configuració del servidor"
preferenceSyncConflictChoiceDevice: "Punts d'ajustos del dispositiu " preferenceSyncConflictChoiceDevice: "Punts d'ajustos del dispositiu "
preferenceSyncConflictChoiceCancel: "Cancel·lar l'activació de la sincronització " preferenceSyncConflictChoiceCancel: "Cancel·lar l'activació de la sincronització "

View File

@ -1330,6 +1330,7 @@ restore: "Restore"
syncBetweenDevices: "Sync between devices" syncBetweenDevices: "Sync between devices"
preferenceSyncConflictTitle: "The configured value exists on the server." preferenceSyncConflictTitle: "The configured value exists on the server."
preferenceSyncConflictText: "The sync enabled settings will save their values to the server. However, there are existing values on the server. Which set of values would you like to overwrite?" preferenceSyncConflictText: "The sync enabled settings will save their values to the server. However, there are existing values on the server. Which set of values would you like to overwrite?"
preferenceSyncConflictChoiceMerge: "Merge"
preferenceSyncConflictChoiceServer: "Configured value on server" preferenceSyncConflictChoiceServer: "Configured value on server"
preferenceSyncConflictChoiceDevice: "Configured value on device" preferenceSyncConflictChoiceDevice: "Configured value on device"
preferenceSyncConflictChoiceCancel: "Cancel enabling sync" preferenceSyncConflictChoiceCancel: "Cancel enabling sync"

View File

@ -327,6 +327,7 @@ dark: "深色"
lightThemes: "淺色佈景主題" lightThemes: "淺色佈景主題"
darkThemes: "深色佈景主題" darkThemes: "深色佈景主題"
syncDeviceDarkMode: "與裝置的深色模式同步" syncDeviceDarkMode: "與裝置的深色模式同步"
switchDarkModeManuallyWhenSyncEnabledConfirm: "「{x}」已開啟。要關閉同步並手動切換模式嗎?\n"
drive: "雲端硬碟" drive: "雲端硬碟"
fileName: "檔案名稱" fileName: "檔案名稱"
selectFile: "選擇檔案" selectFile: "選擇檔案"
@ -1329,6 +1330,7 @@ restore: "還原"
syncBetweenDevices: "裝置之間的同步化" syncBetweenDevices: "裝置之間的同步化"
preferenceSyncConflictTitle: "伺服器上存在設定值" preferenceSyncConflictTitle: "伺服器上存在設定值"
preferenceSyncConflictText: "已啟用同步的設定項目會將設定值儲存至伺服器,並已找到該設定項目在伺服器上儲存的設定值。請選擇要使用哪個設定值進行覆寫。" preferenceSyncConflictText: "已啟用同步的設定項目會將設定值儲存至伺服器,並已找到該設定項目在伺服器上儲存的設定值。請選擇要使用哪個設定值進行覆寫。"
preferenceSyncConflictChoiceMerge: "合併至"
preferenceSyncConflictChoiceServer: "伺服器設定值" preferenceSyncConflictChoiceServer: "伺服器設定值"
preferenceSyncConflictChoiceDevice: "裝置的設定值" preferenceSyncConflictChoiceDevice: "裝置的設定值"
preferenceSyncConflictChoiceCancel: "取消啟用同步" preferenceSyncConflictChoiceCancel: "取消啟用同步"

View File

@ -1,6 +1,6 @@
{ {
"name": "misskey", "name": "misskey",
"version": "2025.5.1-beta.6", "version": "2025.5.1",
"codename": "nasubi", "codename": "nasubi",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -194,9 +194,9 @@ export function useNoteCapture(props: {
parentNote: Misskey.entities.Note | null; parentNote: Misskey.entities.Note | null;
mock?: boolean; mock?: boolean;
}): { }): {
$note: Reactive<ReactiveNoteData>; $note: Reactive<ReactiveNoteData>;
subscribe: () => void; subscribe: () => void;
} { } {
const { note, parentNote, mock } = props; const { note, parentNote, mock } = props;
const $note = reactive<ReactiveNoteData>({ const $note = reactive<ReactiveNoteData>({
@ -225,8 +225,8 @@ export function useNoteCapture(props: {
let latestPollVotedKey: string | null = null; let latestPollVotedKey: string | null = null;
function onReacted(ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }): void { function onReacted(ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }): void {
const normalizedName = ctx.reaction.replace(/^:(\w+):$/, ':$1@.:'); let normalizedName = ctx.reaction.replace(/^:(\w+):$/, ':$1@.:');
normalizedName = normalizedName.match('\u200d') ? normalizedName : normalizedName.replace(/\ufe0f/g, '');
if (reactionUserMap.has(ctx.userId) && reactionUserMap.get(ctx.userId) === normalizedName) return; if (reactionUserMap.has(ctx.userId) && reactionUserMap.get(ctx.userId) === normalizedName) return;
reactionUserMap.set(ctx.userId, normalizedName); reactionUserMap.set(ctx.userId, normalizedName);
@ -245,7 +245,8 @@ export function useNoteCapture(props: {
} }
function onUnreacted(ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }): void { function onUnreacted(ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }): void {
const normalizedName = ctx.reaction.replace(/^:(\w+):$/, ':$1@.:'); let normalizedName = ctx.reaction.replace(/^:(\w+):$/, ':$1@.:');
normalizedName = normalizedName.match('\u200d') ? normalizedName : normalizedName.replace(/\ufe0f/g, '');
// 確実に一度リアクションされて取り消されている場合のみ処理をとめるAPIで初回読み込み→Streamでアップデート等の場合、reactionUserMapに情報がないため // 確実に一度リアクションされて取り消されている場合のみ処理をとめるAPIで初回読み込み→Streamでアップデート等の場合、reactionUserMapに情報がないため
if (reactionUserMap.has(ctx.userId) && reactionUserMap.get(ctx.userId) === noReaction) return; if (reactionUserMap.has(ctx.userId) && reactionUserMap.get(ctx.userId) === noReaction) return;

View File

@ -604,7 +604,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInfo> <MkInfo>
<div class="_gaps_s"> <div class="_gaps_s">
<div>{{ i18n.ts._clientPerformanceIssueTip.title }}</div> <div>{{ i18n.ts._clientPerformanceIssueTip.title }}:</div>
<div> <div>
<div><b>{{ i18n.ts._clientPerformanceIssueTip.makeSureDisabledAdBlocker }}</b></div> <div><b>{{ i18n.ts._clientPerformanceIssueTip.makeSureDisabledAdBlocker }}</b></div>
<div>{{ i18n.ts._clientPerformanceIssueTip.makeSureDisabledAdBlocker_description }}</div> <div>{{ i18n.ts._clientPerformanceIssueTip.makeSureDisabledAdBlocker_description }}</div>

View File

@ -6,12 +6,12 @@
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { hemisphere } from '@@/js/intl-const.js'; import { hemisphere } from '@@/js/intl-const.js';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { definePreferences } from './manager.js';
import type { Theme } from '@/theme.js'; import type { Theme } from '@/theme.js';
import type { SoundType } from '@/utility/sound.js'; import type { SoundType } from '@/utility/sound.js';
import type { Plugin } from '@/plugin.js'; import type { Plugin } from '@/plugin.js';
import type { DeviceKind } from '@/utility/device-kind.js'; import type { DeviceKind } from '@/utility/device-kind.js';
import type { DeckProfile } from '@/deck.js'; import type { DeckProfile } from '@/deck.js';
import type { PreferencesDefinition } from './manager.js';
import type { WatermarkPreset } from '@/utility/watermark.js'; import type { WatermarkPreset } from '@/utility/watermark.js';
import { DEFAULT_DEVICE_KIND } from '@/utility/device-kind.js'; import { DEFAULT_DEVICE_KIND } from '@/utility/device-kind.js';
import { deepEqual } from '@/utility/deep-equal.js'; import { deepEqual } from '@/utility/deep-equal.js';
@ -34,7 +34,7 @@ export type SoundStore = {
// NOTE: デフォルト値は他の設定の状態に依存してはならない(依存していた場合、ユーザーがその設定項目単体で「初期値にリセット」した場合不具合の原因になる) // NOTE: デフォルト値は他の設定の状態に依存してはならない(依存していた場合、ユーザーがその設定項目単体で「初期値にリセット」した場合不具合の原因になる)
export const PREF_DEF = { export const PREF_DEF = definePreferences({
accounts: { accounts: {
default: [] as [host: string, user: { default: [] as [host: string, user: {
id: string; id: string;
@ -89,7 +89,7 @@ export const PREF_DEF = {
emojis: string[]; emojis: string[];
}[], }[],
mergeStrategy: (a, b) => { mergeStrategy: (a, b) => {
const mergedItems = [] as (typeof a)[]; const mergedItems = [] as typeof a;
for (const x of a.concat(b)) { for (const x of a.concat(b)) {
const sameIdItem = mergedItems.find(y => y.id === x.id); const sameIdItem = mergedItems.find(y => y.id === x.id);
if (sameIdItem != null) { if (sameIdItem != null) {
@ -120,7 +120,7 @@ export const PREF_DEF = {
themes: { themes: {
default: [] as Theme[], default: [] as Theme[],
mergeStrategy: (a, b) => { mergeStrategy: (a, b) => {
const mergedItems = [] as (typeof a)[]; const mergedItems = [] as typeof a;
for (const x of a.concat(b)) { for (const x of a.concat(b)) {
const sameIdItem = mergedItems.find(y => y.id === x.id); const sameIdItem = mergedItems.find(y => y.id === x.id);
if (sameIdItem != null) { if (sameIdItem != null) {
@ -398,7 +398,7 @@ export const PREF_DEF = {
accountDependent: true, accountDependent: true,
default: [] as WatermarkPreset[], default: [] as WatermarkPreset[],
mergeStrategy: (a, b) => { mergeStrategy: (a, b) => {
const mergedItems = [] as (typeof a)[]; const mergedItems = [] as typeof a;
for (const x of a.concat(b)) { for (const x of a.concat(b)) {
const sameIdItem = mergedItems.find(y => y.id === x.id); const sameIdItem = mergedItems.find(y => y.id === x.id);
if (sameIdItem != null) { if (sameIdItem != null) {
@ -492,4 +492,4 @@ export const PREF_DEF = {
'experimental.enableFolderPageView': { 'experimental.enableFolderPageView': {
default: false, default: false,
}, },
} satisfies PreferencesDefinition; });

View File

@ -96,6 +96,14 @@ type PreferencesDefinitionRecord<Default, T = Default extends (...args: any) =>
export type PreferencesDefinition = Record<string, PreferencesDefinitionRecord<any>>; export type PreferencesDefinition = Record<string, PreferencesDefinitionRecord<any>>;
export function definePreferences<T extends Record<string, unknown>>(x: {
[K in keyof T]: PreferencesDefinitionRecord<T[K]>
}): {
[K in keyof T]: PreferencesDefinitionRecord<T[K]>
} {
return x;
}
export function getInitialPrefValue<K extends keyof PREF>(k: K): ValueOf<K> { export function getInitialPrefValue<K extends keyof PREF>(k: K): ValueOf<K> {
if (typeof PREF_DEF[k].default === 'function') { // factory if (typeof PREF_DEF[k].default === 'function') { // factory
return PREF_DEF[k].default(); return PREF_DEF[k].default();

View File

@ -1,7 +1,7 @@
{ {
"type": "module", "type": "module",
"name": "misskey-js", "name": "misskey-js",
"version": "2025.5.1-beta.6", "version": "2025.5.1",
"description": "Misskey SDK for JavaScript", "description": "Misskey SDK for JavaScript",
"license": "MIT", "license": "MIT",
"main": "./built/index.js", "main": "./built/index.js",