mirror of
https://github.com/misskey-dev/misskey.git
synced 2024-12-23 00:29:22 +09:00
Merge branch 'develop' into fix-stl-note-fetch
This commit is contained in:
commit
0c65b8058a
17
CHANGELOG.md
17
CHANGELOG.md
@ -11,7 +11,22 @@
|
||||
-
|
||||
|
||||
-->
|
||||
## 202x.x.x (unreleased)
|
||||
|
||||
## 2024.3.1 (unreleased)
|
||||
|
||||
### General
|
||||
-
|
||||
|
||||
### Client
|
||||
- Fix: 絵文字関係の不具合を修正 (#13485)
|
||||
- 履歴に残っている or ピン留めされた絵文字がコントロールパネルより削除されていた際にリアクションデッキが表示できなくなる
|
||||
- Unicode絵文字が履歴に残っている or ピン留めされているとリアクションデッキが表示できなくなる
|
||||
- Fix: カスタム絵文字の画像読み込みに失敗した際はテキストではなくダミー画像を表示 #13487
|
||||
|
||||
### Server
|
||||
-
|
||||
|
||||
## 2024.3.0
|
||||
|
||||
### General
|
||||
- Enhance: 投稿者のロールに応じて、一つのノートに含むことのできるメンションとダイレクト投稿の宛先の人数に上限を設定できるように
|
||||
|
@ -380,8 +380,11 @@ hcaptcha: "hCaptcha"
|
||||
enableHcaptcha: "Activer hCaptcha"
|
||||
hcaptchaSiteKey: "Clé du site"
|
||||
hcaptchaSecretKey: "Clé secrète"
|
||||
mcaptcha: "mCaptcha"
|
||||
enableMcaptcha: "Activer mCaptcha"
|
||||
mcaptchaSiteKey: "Clé du site"
|
||||
mcaptchaSecretKey: "Clé secrète"
|
||||
mcaptchaInstanceUrl: "URL de l'instance de mCaptcha"
|
||||
recaptcha: "reCAPTCHA"
|
||||
enableRecaptcha: "Activer reCAPTCHA"
|
||||
recaptchaSiteKey: "Clé du site"
|
||||
@ -523,7 +526,7 @@ hideThisNote: "Masquer cette note"
|
||||
showFeaturedNotesInTimeline: "Afficher les notes des Tendances dans le fil d'actualité"
|
||||
objectStorage: "Stockage d'objets"
|
||||
useObjectStorage: "Utiliser le stockage d'objets"
|
||||
objectStorageBaseUrl: "Base URL"
|
||||
objectStorageBaseUrl: "URL de base"
|
||||
objectStorageBaseUrlDesc: "Préfixe d’URL utilisé pour construire l’URL vers le référencement d’objet (média). Spécifiez son URL si vous utilisez un CDN ou un proxy, sinon spécifiez l’adresse accessible au public selon le guide de service que vous allez utiliser. P.ex. 'https://<bucket>.s3.amazonaws.com' pour AWS S3 et 'https://storage.googleapis.com/<bucket>' pour GCS."
|
||||
objectStorageBucket: "Bucket"
|
||||
objectStorageBucketDesc: "Veuillez spécifier le nom du compartiment utilisé sur le service configuré."
|
||||
@ -628,6 +631,7 @@ medium: "Moyen"
|
||||
small: "Petit"
|
||||
generateAccessToken: "Générer un jeton d'accès"
|
||||
permission: "Autorisations "
|
||||
adminPermission: "Droits de l'administrateur"
|
||||
enableAll: "Tout activer"
|
||||
disableAll: "Tout désactiver"
|
||||
tokenRequested: "Autoriser l'accès au compte"
|
||||
@ -1031,12 +1035,18 @@ nonSensitiveOnlyForLocalLikeOnlyForRemote: "Non sensibles seulement (mentions j'
|
||||
rolesAssignedToMe: "Rôles attribués à moi"
|
||||
resetPasswordConfirm: "Souhaitez-vous réinitialiser votre mot de passe ?"
|
||||
sensitiveWords: "Mots sensibles"
|
||||
sensitiveWordsDescription2: "Séparer par une espace pour créer une expression AND ; entourer de barres obliques pour créer une expression régulière."
|
||||
prohibitedWords: "Mots interdits"
|
||||
prohibitedWordsDescription2: "Séparer par une espace pour créer une expression AND ; entourer de barres obliques pour créer une expression régulière."
|
||||
hiddenTags: "Hashtags cachés"
|
||||
hiddenTagsDescription: "Les hashtags définis ne s'afficheront pas dans les tendances. Vous pouvez définir plusieurs hashtags en faisant un saut de ligne."
|
||||
notesSearchNotAvailable: "La recherche de notes n'est pas disponible."
|
||||
license: "Licence"
|
||||
unfavoriteConfirm: "Vraiment supprimer des favoris ?"
|
||||
myClips: "Mes clips"
|
||||
drivecleaner: "Nettoyeur du Disque"
|
||||
retryAllQueuesNow: "Réessayer tous les fils d'attente immédiatement"
|
||||
retryAllQueuesConfirmTitle: "Vraiment réessayer ?"
|
||||
retryAllQueuesConfirmText: "Cela peut augmenter temporairement la charge du serveur."
|
||||
enableChartsForRemoteUser: "Générer les graphiques pour les utilisateurs distants"
|
||||
enableChartsForFederatedInstances: "Générer les graphiques pour les instances distantes"
|
||||
@ -1046,6 +1056,8 @@ limitWidthOfReaction: "Limiter la largeur maximale des réactions et les affiche
|
||||
noteIdOrUrl: "Identifiant de la note ou URL"
|
||||
video: "Vidéo"
|
||||
videos: "Vidéos"
|
||||
audio: "Audio"
|
||||
audioFiles: "Fichiers audio"
|
||||
dataSaver: "Économiseur de données"
|
||||
accountMigration: "Migration de compte"
|
||||
accountMoved: "Cet·te utilisateur·rice a migré son compte vers :"
|
||||
@ -1084,7 +1096,10 @@ specifyUser: "Spécifier l'utilisateur·rice"
|
||||
failedToPreviewUrl: "Aperçu d'URL échoué"
|
||||
update: "Mettre à jour"
|
||||
rolesThatCanBeUsedThisEmojiAsReaction: "Rôles qui peuvent utiliser cet émoji comme réaction"
|
||||
rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "Si aucun rôle n'est spécifié, tout le monde peut utiliser cet émoji comme réaction."
|
||||
rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "Il faut un rôle public."
|
||||
cancelReactionConfirm: "Supprimez la réaction ?"
|
||||
changeReactionConfirm: "Changer la réaction ?"
|
||||
later: "Plus tard"
|
||||
goToMisskey: "Retour vers Misskey"
|
||||
additionalEmojiDictionary: "Dictionnaires d'émojis additionnels"
|
||||
@ -1110,11 +1125,13 @@ used: "Utilisé"
|
||||
expired: "Expiré"
|
||||
doYouAgree: "Êtes-vous d’accord ?"
|
||||
beSureToReadThisAsItIsImportant: "Assurez-vous de le lire ; c'est important."
|
||||
iHaveReadXCarefullyAndAgree: "J'ai lu le contenu de « {x} » et donne mon accord."
|
||||
dialog: "Dialogue"
|
||||
icon: "Avatar"
|
||||
forYou: "Pour vous"
|
||||
currentAnnouncements: "Annonces actuelles"
|
||||
pastAnnouncements: "Annonces passées"
|
||||
youHaveUnreadAnnouncements: "Il y a des annonces non lues."
|
||||
replies: "Réponses"
|
||||
renotes: "Renotes"
|
||||
loadReplies: "Inclure les réponses"
|
||||
@ -1129,6 +1146,7 @@ showRenotes: "Afficher les renotes"
|
||||
edited: "Modifié"
|
||||
notificationRecieveConfig: "Paramètres des notifications"
|
||||
mutualFollow: "Abonnement mutuel"
|
||||
fileAttachedOnly: "Avec fichiers joints seulement"
|
||||
showRepliesToOthersInTimeline: "Afficher les réponses aux autres dans le fil"
|
||||
hideRepliesToOthersInTimeline: "Masquer les réponses aux autres dans le fil"
|
||||
showRepliesToOthersInTimelineAll: "Afficher les réponses de toutes les personnes que vous suivez dans le fil"
|
||||
@ -1137,6 +1155,11 @@ confirmShowRepliesAll: "Cette opération est irréversible. Voulez-vous vraiment
|
||||
confirmHideRepliesAll: "Cette opération est irréversible. Voulez-vous vraiment masquer les réponses de toutes les personnes que vous suivez dans le fil ?"
|
||||
externalServices: "Services externes"
|
||||
sourceCode: "Code source"
|
||||
sourceCodeIsNotYetProvided: "Le code source n'est pas encore disponible. Veuillez signaler ce problème aux administrateurs."
|
||||
repositoryUrl: "URL du dépôt"
|
||||
repositoryUrlDescription: "Entrez l'URL du dépôt où se trouve le code source ici. Si vous utilisez Misskey tel quel (sans changer le code source), entrez https://github.com/misskey-dev/misskey"
|
||||
feedback: "Commentaires"
|
||||
feedbackUrl: "URL pour les commentaires"
|
||||
impressum: "Impressum"
|
||||
impressumUrl: "URL de l'impressum"
|
||||
impressumDescription: "Dans certains pays comme l'Allemagne, il est obligatoire d'afficher les informations sur l'opérateur d'un site (un impressum)."
|
||||
@ -1164,11 +1187,32 @@ remainingN: "Restants : {n}"
|
||||
overwriteContentConfirm: "Voulez-vous remplacer le contenu actuel ?"
|
||||
seasonalScreenEffect: "Effet d'écran saisonnier"
|
||||
decorate: "Décorer"
|
||||
addMfmFunction: "Insérer MFM"
|
||||
enableQuickAddMfmFunction: "Afficher le sélecteur de MFM avancé"
|
||||
bubbleGame: "Jeu de bulles"
|
||||
sfx: "Effets sonores"
|
||||
soundWillBePlayed: "Le son sera joué"
|
||||
showReplay: "Voir le replay"
|
||||
replay: "Rediffusion"
|
||||
replaying: "En cours de rediffusion"
|
||||
endReplay: "Arrêter la rediffusion"
|
||||
copyReplayData: "Copier les données de la rediffusion"
|
||||
ranking: "Classement"
|
||||
lastNDays: "Derniers {n} jours"
|
||||
backToTitle: "Retourner au titre"
|
||||
hemisphere: "Votre région"
|
||||
enableHorizontalSwipe: "Glisser pour changer d'onglet"
|
||||
loading: "Chargement en cours"
|
||||
surrender: "Annuler"
|
||||
gameRetry: "Réessayer"
|
||||
_bubbleGame:
|
||||
howToPlay: "Comment jouer"
|
||||
hold: "Réserver"
|
||||
_score:
|
||||
score: "Score"
|
||||
scoreYen: "Montant gagné"
|
||||
highScore: "Meilleur score"
|
||||
yen: "{yen} yens"
|
||||
_announcement:
|
||||
forExistingUsers: "Pour les utilisateurs existants seulement"
|
||||
readConfirmTitle: "Marquer comme lu ?"
|
||||
|
@ -1655,6 +1655,7 @@ _role:
|
||||
gtlAvailable: "瀏覽全域時間軸"
|
||||
ltlAvailable: "瀏覽本地時間軸"
|
||||
canPublicNote: "允許公開貼文"
|
||||
mentionMax: "貼文內的最大提及數"
|
||||
canInvite: "發行伺服器邀請碼"
|
||||
inviteLimit: "可建立邀請碼的數量"
|
||||
inviteLimitCycle: "邀請碼的發放間隔"
|
||||
@ -2299,6 +2300,7 @@ _notification:
|
||||
reactedBySomeUsers: "{n}人做出了反應"
|
||||
renotedBySomeUsers: "{n}人做了轉發"
|
||||
followedBySomeUsers: "被{n}人追隨了"
|
||||
flushNotification: "重置通知歷史紀錄"
|
||||
_types:
|
||||
all: "全部 "
|
||||
note: "使用者的最新貼文"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "2024.2.0",
|
||||
"version": "2024.3.0",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -77,7 +77,7 @@ const emojiDb = computed(() => {
|
||||
unicodeEmojiDB.push({
|
||||
emoji: emoji,
|
||||
name: k,
|
||||
aliasOf: getEmojiName(emoji)!,
|
||||
aliasOf: getEmojiName(emoji),
|
||||
url: char2path(emoji),
|
||||
});
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ const shown = ref(!!props.initialShown);
|
||||
function computeButtonTitle(ev: MouseEvent): void {
|
||||
const elm = ev.target as HTMLElement;
|
||||
const emoji = elm.dataset.emoji as string;
|
||||
elm.title = getEmojiName(emoji) ?? emoji;
|
||||
elm.title = getEmojiName(emoji);
|
||||
}
|
||||
|
||||
function nestedChosen(emoji: any, ev: MouseEvent) {
|
||||
|
@ -353,7 +353,7 @@ watch(q, () => {
|
||||
searchResultUnicode.value = Array.from(searchUnicode());
|
||||
});
|
||||
|
||||
function canReact(emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef): boolean {
|
||||
function canReact(emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef | string): boolean {
|
||||
return !props.targetNote || checkReactionPermissions($i!, props.targetNote, emoji);
|
||||
}
|
||||
|
||||
@ -378,11 +378,14 @@ function getKey(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef):
|
||||
return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`;
|
||||
}
|
||||
|
||||
function getDef(emoji: string) {
|
||||
function getDef(emoji: string): string | Misskey.entities.EmojiSimple | UnicodeEmojiDef {
|
||||
if (emoji.includes(':')) {
|
||||
return customEmojisMap.get(emoji.replace(/:/g, ''))!;
|
||||
// カスタム絵文字が存在する場合はその情報を持つオブジェクトを返し、
|
||||
// サーバの管理画面から削除された等で情報が見つからない場合は名前の文字列をそのまま返しておく(undefinedを返すとエラーになるため)
|
||||
const name = emoji.replaceAll(':', '');
|
||||
return customEmojisMap.get(name) ?? emoji;
|
||||
} else {
|
||||
return getUnicodeEmoji(emoji)!;
|
||||
return getUnicodeEmoji(emoji);
|
||||
}
|
||||
}
|
||||
|
||||
@ -390,7 +393,7 @@ function getDef(emoji: string) {
|
||||
function computeButtonTitle(ev: MouseEvent): void {
|
||||
const elm = ev.target as HTMLElement;
|
||||
const emoji = elm.dataset.emoji as string;
|
||||
elm.title = getEmojiName(emoji) ?? emoji;
|
||||
elm.title = getEmojiName(emoji);
|
||||
}
|
||||
|
||||
function chosen(emoji: any, ev?: MouseEvent) {
|
||||
|
@ -44,7 +44,7 @@ function getReactionName(reaction: string): string {
|
||||
if (trimLocal.startsWith(':')) {
|
||||
return trimLocal;
|
||||
}
|
||||
return getEmojiName(reaction) ?? reaction;
|
||||
return getEmojiName(reaction);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -48,3 +48,10 @@ export const Missing = {
|
||||
name: Default.args.name,
|
||||
},
|
||||
} satisfies StoryObj<typeof MkCustomEmoji>;
|
||||
export const Error = {
|
||||
...Default,
|
||||
args: {
|
||||
url: 'https://example.com/404',
|
||||
name: Default.args.name,
|
||||
},
|
||||
} satisfies StoryObj<typeof MkCustomEmoji>;
|
||||
|
@ -4,7 +4,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<span v-if="errored">:{{ customEmojiName }}:</span>
|
||||
<img
|
||||
v-if="errored"
|
||||
:class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]"
|
||||
src="/client-assets/dummy.png"
|
||||
:title="alt"
|
||||
/>
|
||||
<img
|
||||
v-else
|
||||
:class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]"
|
||||
|
@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject } from 'vue';
|
||||
import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base.js';
|
||||
import { char2fluentEmojiFilePath, char2twemojiFilePath } from '@/scripts/emoji-base.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
import { colorizeEmoji, getEmojiName } from '@/scripts/emojilist.js';
|
||||
import * as os from '@/os.js';
|
||||
@ -34,8 +34,7 @@ const colorizedNativeEmoji = computed(() => colorizeEmoji(props.emoji));
|
||||
|
||||
// Searching from an array with 2000 items for every emoji felt like too energy-consuming, so I decided to do it lazily on pointerenter
|
||||
function computeTitle(event: PointerEvent): void {
|
||||
const title = getEmojiName(props.emoji as string) ?? props.emoji as string;
|
||||
(event.target as HTMLElement).title = title;
|
||||
(event.target as HTMLElement).title = getEmojiName(props.emoji);
|
||||
}
|
||||
|
||||
function onClick(ev: MouseEvent) {
|
||||
|
@ -1,12 +1,12 @@
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { UnicodeEmojiDef } from './emojilist.js';
|
||||
|
||||
export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef): boolean {
|
||||
if ('char' in emoji) return true; // UnicodeEmojiDefなら常にリアクション可能
|
||||
export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef | string): boolean {
|
||||
if (typeof emoji === 'string') return true; // UnicodeEmojiDefにも無い絵文字であれば文字列で来る。Unicode絵文字であることには変わりないので常にリアクション可能とする;
|
||||
if ('char' in emoji) return true; // UnicodeEmojiDefなら常にリアクション可能
|
||||
|
||||
emoji = emoji as Misskey.entities.EmojiSimple;
|
||||
const roleIdsThatCanBeUsedThisEmojiAsReaction = emoji.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [];
|
||||
return !(emoji.localOnly && note.user.host !== me.host)
|
||||
const roleIdsThatCanBeUsedThisEmojiAsReaction = emoji.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [];
|
||||
return !(emoji.localOnly && note.user.host !== me.host)
|
||||
&& !(emoji.isSensitive && (note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote'))
|
||||
&& (roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0 || me.roles.some(role => roleIdsThatCanBeUsedThisEmojiAsReaction.includes(role.id)));
|
||||
}
|
||||
|
@ -39,21 +39,29 @@ for (let i = 0; i < emojilist.length; i++) {
|
||||
|
||||
export const emojiCharByCategory = _charGroupByCategory;
|
||||
|
||||
export function getUnicodeEmoji(char: string): UnicodeEmojiDef | null {
|
||||
export function getUnicodeEmoji(char: string): UnicodeEmojiDef | string {
|
||||
// Colorize it because emojilist.json assumes that
|
||||
return unicodeEmojisMap.get(colorizeEmoji(char)) ?? null;
|
||||
return unicodeEmojisMap.get(colorizeEmoji(char))
|
||||
// カラースタイル絵文字がjsonに無い場合はテキストスタイル絵文字にフォールバックする
|
||||
?? unicodeEmojisMap.get(char)
|
||||
// それでも見つからない場合はそのまま返す(絵文字情報がjsonに無い場合、このフォールバックが無いとレンダリングに失敗する)
|
||||
?? char;
|
||||
}
|
||||
|
||||
export function getEmojiName(char: string): string | null {
|
||||
export function getEmojiName(char: string): string {
|
||||
// Colorize it because emojilist.json assumes that
|
||||
const idx = _indexByChar.get(colorizeEmoji(char));
|
||||
if (idx == null) {
|
||||
return null;
|
||||
const idx = _indexByChar.get(colorizeEmoji(char)) ?? _indexByChar.get(char);
|
||||
if (idx === undefined) {
|
||||
// 絵文字情報がjsonに無い場合は名前の取得が出来ないのでそのまま返すしか無い
|
||||
return char;
|
||||
} else {
|
||||
return emojilist[idx].name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* テキストスタイル絵文字(U+260Eなどの1文字で表現される絵文字)をカラースタイル絵文字に変換します(VS16:U+FE0Fを付与)。
|
||||
*/
|
||||
export function colorizeEmoji(char: string) {
|
||||
return char.length === 1 ? `${char}\uFE0F` : char;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "misskey-js",
|
||||
"version": "2024.2.0",
|
||||
"version": "2024.3.0",
|
||||
"description": "Misskey SDK for JavaScript",
|
||||
"types": "./built/dts/index.d.ts",
|
||||
"exports": {
|
||||
|
Loading…
Reference in New Issue
Block a user