Compare commits

...

7 Commits

Author SHA1 Message Date
おさむのひと 21cad3bc7d
Merge branch 'develop' into fix/deck_column_target_change 2023-11-06 20:43:42 +09:00
おさむのひと 828749be64
fix #12266 (#12267)
ポップアップの表示後、MkNoteとMkNoteDetailedでそれぞれが持つfocusメソッドを呼び出していたのをやめた

Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com>
2023-11-06 19:26:17 +09:00
syuilo bfca457510 enhance(frontend): improve aiscript plugin error handling 2023-11-06 11:21:43 +09:00
syuilo f72228f428 2023.11.0 2023-11-05 18:17:50 +09:00
syuilo bb76ee2c0e enhance(frontend): 投稿内のunicode絵文字もメニューを出せるように 2023-11-05 18:01:51 +09:00
syuilo 2c836ba71f enhance(build): フォールバック効かすためにlocaleの空文字は項目ごと消す 2023-11-05 18:00:41 +09:00
syuilo 8f49c5cd48
New Crowdin updates (#12244)
* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)
2023-11-05 17:53:08 +09:00
12 changed files with 108 additions and 22 deletions

View File

@ -12,7 +12,7 @@
-->
## 2023.11.0 (unreleased)
## 2023.11.0
### Note
- iOS 16.4未満を使用している場合はiOS 16.4以上にアップデートをお願いします
@ -51,7 +51,7 @@
- Enhance: プラグインで`Plugin:register_note_view_interruptor`を用いてnoteの代わりにnullを返却することでートを非表示にできるようになりました
- Enhance: AiScript関数`Mk:nyaize()`が追加されました
- Enhance: 情報→ツール はナビゲーションバーにツールとして独立した項目になりました
- Enhance: ノート内のカスタム絵文字をクリックすることで、コピーおよびリアクションができるように
- Enhance: ノート内の絵文字をクリックすることで、コピーおよびリアクションができるように
- Enhance: その他細かなブラッシュアップ
- Fix: 投稿フォームでのユーザー変更がプレビューに反映されない問題を修正
- Fix: ユーザーページの ノート > ファイル付き タブにリプライが表示されてしまう

View File

@ -1088,7 +1088,26 @@ _initialAccountSetting:
profileSetting: "Paramètres du profil"
privacySetting: "Paramètres de confidentialité"
initialAccountSettingCompleted: "Configuration du profil terminée avec succès !"
startTutorial: "Démarrer le tutoriel"
skipAreYouSure: "Désirez-vous ignorer la configuration du profil ?"
_initialTutorial:
title: "Tutoriel"
wellDone: "Bien joué !"
skipAreYouSure: "Quitter le tutoriel ?"
_landing:
title: "Bienvenue dans le tutoriel"
description: "Ici, vous pouvez apprendre l'utilisation de base de Misskey et ses fonctionnalités."
_note:
title: "Qu'est-ce que les notes ?"
description: "Les messages sur Misskey sont appelés des « notes » . Les notes sont classées par ordre chronologique sur le fil et sont mises à jour en temps réel."
reply: "Vous pouvez répondre aux messages. Vous pouvez également répondre aux réponses et poursuivre la conversation comme un fil de discussion."
renote: "Vous pouvez partager cette note sur votre propre fil. Vous pouvez aussi ajouter du texte en citant."
reaction: "Vous pouvez ajouter des réactions. Les détails sont expliqués à la page suivante."
menu: "Vous pouvez afficher les détails de la note, copier le lien et effectuer d'autres actions."
_reaction:
title: "Qu'est-ce que les réactions ?"
description: "Vous pouvez ajouter des « réactions » aux notes. Les réactions vous permettent d'exprimer à l'aise des nuances qui ne peuvent pas être exprimées par des mentions j'aime."
letsTryReacting: "Des réactions peuvent être ajoutées en cliquant sur le bouton « + » de la note. Essayez d'ajouter une réaction à cet exemple de note !"
_serverSettings:
iconUrl: "URL de licône"
fanoutTimelineDescription: "Si activée, la performance de la récupération de la chronologie augmentera considérablement et la charge sur la base de données sera réduite. En revanche, l'utilisation de la mémoire de Redis augmentera. Considérez désactiver cette option si le serveur est bas en mémoire ou instable."

View File

@ -53,6 +53,19 @@ const clean = (text) => text.replace(new RegExp(String.fromCodePoint(0x08), 'g')
const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(new URL(`${c}.yml`, import.meta.url), 'utf-8'))) || {}, a), {});
// 空文字列が入ることがあり、フォールバックが動作しなくなるのでプロパティごと消す
const removeEmpty = (obj) => {
for (const [k, v] of Object.entries(obj)) {
if (v === '') {
delete obj[k];
} else if (typeof v === 'object') {
removeEmpty(v);
}
}
return obj;
};
removeEmpty(locales);
export default Object.entries(locales)
.reduce((a, [k ,v]) => (a[k] = (() => {
const [lang] = k.split('-');
@ -63,7 +76,7 @@ export default Object.entries(locales)
default: return merge(
locales['ja-JP'],
locales['en-US'],
locales[`${lang}-${primaries[lang]}`] || {},
locales[`${lang}-${primaries[lang]}`] ?? {},
v
);
}

View File

@ -1157,6 +1157,7 @@ disableStreamingTimeline: "Disabilitare gli aggiornamenti della TL in tempo real
useGroupedNotifications: "Mostra le notifiche raggruppate"
signupPendingError: "Si è verificato un problema durante la verifica del tuo indirizzo email. Potrebbe essere scaduto il collegamento temporaneo."
cwNotationRequired: "Devi indicare perché il contenuto è indicato come esplicito."
doReaction: "Reagisci"
_announcement:
forExistingUsers: "Solo ai profili attuali"
forExistingUsersDescription: "L'annuncio sarà visibile solo ai profili esistenti in questo momento. Se disabilitato, sarà visibile anche ai profili che verranno creati dopo la pubblicazione di questo annuncio."

View File

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

View File

@ -58,7 +58,7 @@ export async function mainBoot() {
});
for (const plugin of ColdDeviceStorage.get('plugins').filter(p => p.active)) {
import('../plugin').then(async ({ install }) => {
import('@/plugin.js').then(async ({ install }) => {
// Workaround for https://bugs.webkit.org/show_bug.cgi?id=242740
await new Promise(r => setTimeout(r, 0));
install(plugin);

View File

@ -202,11 +202,17 @@ let note = $ref(deepClone(props.note));
// plugin
if (noteViewInterruptors.length > 0) {
onMounted(async () => {
let result:Misskey.entities.Note | null = deepClone(note);
let result: Misskey.entities.Note | null = deepClone(note);
for (const interruptor of noteViewInterruptors) {
result = await interruptor.handler(result);
if (result === null) return isDeleted.value = true;
try {
result = await interruptor.handler(result);
if (result === null) {
isDeleted.value = true;
return;
}
} catch (err) {
console.error(err);
}
}
note = result;
});
@ -298,7 +304,7 @@ function renote(viaKeyboard = false) {
const { menu } = getRenoteMenu({ note: note, renoteButton, mock: props.mock });
os.popupMenu(menu, renoteButton.value, {
viaKeyboard,
}).then(focus);
});
}
function reply(viaKeyboard = false): void {

View File

@ -239,11 +239,17 @@ let note = $ref(deepClone(props.note));
// plugin
if (noteViewInterruptors.length > 0) {
onMounted(async () => {
let result:Misskey.entities.Note | null = deepClone(note);
let result: Misskey.entities.Note | null = deepClone(note);
for (const interruptor of noteViewInterruptors) {
result = await interruptor.handler(result);
if (result === null) return isDeleted.value = true;
try {
result = await interruptor.handler(result);
if (result === null) {
isDeleted.value = true;
return;
}
} catch (err) {
console.error(err);
}
}
note = result;
});
@ -344,7 +350,7 @@ function renote(viaKeyboard = false) {
const { menu } = getRenoteMenu({ note: note, renoteButton });
os.popupMenu(menu, renoteButton.value, {
viaKeyboard,
}).then(focus);
});
}
function reply(viaKeyboard = false): void {

View File

@ -750,7 +750,11 @@ async function post(ev?: MouseEvent) {
// plugin
if (notePostInterruptors.length > 0) {
for (const interruptor of notePostInterruptors) {
postData = await interruptor.handler(deepClone(postData));
try {
postData = await interruptor.handler(deepClone(postData));
} catch (err) {
console.error(err);
}
}
}

View File

@ -4,21 +4,28 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<img v-if="!useOsNativeEmojis" :class="$style.root" :src="url" :alt="props.emoji" decoding="async" @pointerenter="computeTitle"/>
<span v-else-if="useOsNativeEmojis" :alt="props.emoji" @pointerenter="computeTitle">{{ props.emoji }}</span>
<img v-if="!useOsNativeEmojis" :class="$style.root" :src="url" :alt="props.emoji" decoding="async" @pointerenter="computeTitle" @click="onClick"/>
<span v-else-if="useOsNativeEmojis" :alt="props.emoji" @pointerenter="computeTitle" @click="onClick">{{ props.emoji }}</span>
<span v-else>{{ emoji }}</span>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import { computed, inject } from 'vue';
import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base.js';
import { defaultStore } from '@/store.js';
import { getEmojiName } from '@/scripts/emojilist.js';
import * as os from '@/os.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { i18n } from '@/i18n.js';
const props = defineProps<{
emoji: string;
menu?: boolean;
menuReaction?: boolean;
}>();
const react = inject<((name: string) => void) | null>('react', null);
const char2path = defaultStore.state.emojiStyle === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath;
const useOsNativeEmojis = computed(() => defaultStore.state.emojiStyle === 'native');
@ -31,6 +38,28 @@ function computeTitle(event: PointerEvent): void {
const title = getEmojiName(props.emoji as string) ?? props.emoji as string;
(event.target as HTMLElement).title = title;
}
function onClick(ev: MouseEvent) {
if (props.menu) {
os.popupMenu([{
type: 'label',
text: props.emoji,
}, {
text: i18n.ts.copy,
icon: 'ti ti-copy',
action: () => {
copyToClipboard(props.emoji);
os.success();
},
}, ...(props.menuReaction && react ? [{
text: i18n.ts.doReaction,
icon: 'ti ti-plus',
action: () => {
react(props.emoji);
},
}] : [])], ev.currentTarget ?? ev.target);
}
}
</script>
<style lang="scss" module>

View File

@ -354,6 +354,8 @@ export default function(props: MfmProps) {
return [h(MkEmoji, {
key: Math.random(),
emoji: token.props.emoji,
menu: props.enableEmojiMenu,
menuReaction: props.enableEmojiMenuReaction,
})];
}

View File

@ -11,10 +11,9 @@ import { Plugin, noteActions, notePostInterruptors, noteViewInterruptors, postFo
const parser = new Parser();
const pluginContexts = new Map<string, Interpreter>();
export function install(plugin: Plugin): void {
export async function install(plugin: Plugin): Promise<void> {
// 後方互換性のため
if (plugin.src == null) return;
console.info('Plugin installed:', plugin.name, 'v' + plugin.version);
const aiscript = new Interpreter(createPluginEnv({
plugin: plugin,
@ -42,7 +41,14 @@ export function install(plugin: Plugin): void {
initPlugin({ plugin, aiscript });
aiscript.exec(parser.parse(plugin.src));
try {
await aiscript.exec(parser.parse(plugin.src));
} catch (err) {
console.error('Plugin install failed:', plugin.name, 'v' + plugin.version);
return;
}
console.info('Plugin installed:', plugin.name, 'v' + plugin.version);
}
function createPluginEnv(opts: { plugin: Plugin; storageKey: string }): Record<string, values.Value> {