mirror of
https://git.joinsharkey.org/Sharkey/Sharkey.git
synced 2024-11-23 03:03:08 +02:00
merge: feat: Add language metadata to notes (!401)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/401 Closes #253
This commit is contained in:
commit
50a93e453e
37 changed files with 580 additions and 68 deletions
|
@ -106,7 +106,7 @@ redis:
|
||||||
# ┌───────────────────────────┐
|
# ┌───────────────────────────┐
|
||||||
#───┘ MeiliSearch configuration └─────────────────────────────
|
#───┘ MeiliSearch configuration └─────────────────────────────
|
||||||
|
|
||||||
# You can set scope to local (default value) or global
|
# You can set scope to local (default value) or global
|
||||||
# (include notes from remote).
|
# (include notes from remote).
|
||||||
|
|
||||||
#meilisearch:
|
#meilisearch:
|
||||||
|
@ -204,7 +204,7 @@ signToActivityPubGet: true
|
||||||
checkActivityPubGetSignature: false
|
checkActivityPubGetSignature: false
|
||||||
|
|
||||||
# For security reasons, uploading attachments from the intranet is prohibited,
|
# For security reasons, uploading attachments from the intranet is prohibited,
|
||||||
# but exceptions can be made from the following settings. Default value is "undefined".
|
# but exceptions can be made from the following settings. Default value is "undefined".
|
||||||
# Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
|
# Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
|
||||||
#allowedPrivateNetworks: [
|
#allowedPrivateNetworks: [
|
||||||
# '127.0.0.1/32'
|
# '127.0.0.1/32'
|
||||||
|
@ -212,5 +212,9 @@ checkActivityPubGetSignature: false
|
||||||
|
|
||||||
#customMOTD: ['Hello World', 'The sharks rule all', 'Shonks']
|
#customMOTD: ['Hello World', 'The sharks rule all', 'Shonks']
|
||||||
|
|
||||||
|
# Prefer these languages for remote notes with language-specific content
|
||||||
|
# Must be valid language codes according to BCP 47
|
||||||
|
#langPref: ['en', 'ja']
|
||||||
|
|
||||||
# Upload or download file size limits (bytes)
|
# Upload or download file size limits (bytes)
|
||||||
#maxFileSize: 262144000
|
#maxFileSize: 262144000
|
||||||
|
|
|
@ -163,7 +163,7 @@ redis:
|
||||||
# ┌───────────────────────────┐
|
# ┌───────────────────────────┐
|
||||||
#───┘ MeiliSearch configuration └─────────────────────────────
|
#───┘ MeiliSearch configuration └─────────────────────────────
|
||||||
|
|
||||||
# You can set scope to local (default value) or global
|
# You can set scope to local (default value) or global
|
||||||
# (include notes from remote).
|
# (include notes from remote).
|
||||||
|
|
||||||
#meilisearch:
|
#meilisearch:
|
||||||
|
@ -261,7 +261,7 @@ signToActivityPubGet: true
|
||||||
checkActivityPubGetSignature: false
|
checkActivityPubGetSignature: false
|
||||||
|
|
||||||
# For security reasons, uploading attachments from the intranet is prohibited,
|
# For security reasons, uploading attachments from the intranet is prohibited,
|
||||||
# but exceptions can be made from the following settings. Default value is "undefined".
|
# but exceptions can be made from the following settings. Default value is "undefined".
|
||||||
# Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
|
# Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
|
||||||
#allowedPrivateNetworks: [
|
#allowedPrivateNetworks: [
|
||||||
# '127.0.0.1/32'
|
# '127.0.0.1/32'
|
||||||
|
@ -269,5 +269,9 @@ checkActivityPubGetSignature: false
|
||||||
|
|
||||||
#customMOTD: ['Hello World', 'The sharks rule all', 'Shonks']
|
#customMOTD: ['Hello World', 'The sharks rule all', 'Shonks']
|
||||||
|
|
||||||
|
# Prefer these languages for remote notes with language-specific content
|
||||||
|
# Must be valid language codes according to BCP 47
|
||||||
|
#langPref: ['en', 'ja']
|
||||||
|
|
||||||
# Upload or download file size limits (bytes)
|
# Upload or download file size limits (bytes)
|
||||||
#maxFileSize: 262144000
|
#maxFileSize: 262144000
|
||||||
|
|
|
@ -284,6 +284,10 @@ checkActivityPubGetSignature: false
|
||||||
|
|
||||||
#customMOTD: ['Hello World', 'The sharks rule all', 'Shonks']
|
#customMOTD: ['Hello World', 'The sharks rule all', 'Shonks']
|
||||||
|
|
||||||
|
# Prefer these languages for remote notes with language-specific content
|
||||||
|
# Must be valid language codes according to BCP 47
|
||||||
|
#langPref: ['en', 'ja']
|
||||||
|
|
||||||
# Upload or download file size limits (bytes)
|
# Upload or download file size limits (bytes)
|
||||||
#maxFileSize: 262144000
|
#maxFileSize: 262144000
|
||||||
|
|
||||||
|
|
|
@ -1270,6 +1270,7 @@ hemisphere: "Where are you located"
|
||||||
withSensitive: "Include notes with sensitive files"
|
withSensitive: "Include notes with sensitive files"
|
||||||
userSaysSomethingSensitive: "Post by {name} contains sensitive content"
|
userSaysSomethingSensitive: "Post by {name} contains sensitive content"
|
||||||
enableHorizontalSwipe: "Swipe to switch tabs"
|
enableHorizontalSwipe: "Swipe to switch tabs"
|
||||||
|
noLanguage: "No language"
|
||||||
loading: "Loading"
|
loading: "Loading"
|
||||||
surrender: "Cancel"
|
surrender: "Cancel"
|
||||||
gameRetry: "Retry"
|
gameRetry: "Retry"
|
||||||
|
|
4
locales/index.d.ts
vendored
4
locales/index.d.ts
vendored
|
@ -5093,6 +5093,10 @@ export interface Locale extends ILocale {
|
||||||
* スワイプしてタブを切り替える
|
* スワイプしてタブを切り替える
|
||||||
*/
|
*/
|
||||||
"enableHorizontalSwipe": string;
|
"enableHorizontalSwipe": string;
|
||||||
|
/**
|
||||||
|
* 言語なし
|
||||||
|
*/
|
||||||
|
"noLanguage": string;
|
||||||
/**
|
/**
|
||||||
* 読み込み中
|
* 読み込み中
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1269,6 +1269,7 @@ hemisphere: "お住まいの地域"
|
||||||
withSensitive: "センシティブなファイルを含むノートを表示"
|
withSensitive: "センシティブなファイルを含むノートを表示"
|
||||||
userSaysSomethingSensitive: "{name}のセンシティブなファイルを含む投稿"
|
userSaysSomethingSensitive: "{name}のセンシティブなファイルを含む投稿"
|
||||||
enableHorizontalSwipe: "スワイプしてタブを切り替える"
|
enableHorizontalSwipe: "スワイプしてタブを切り替える"
|
||||||
|
noLanguage: "言語なし"
|
||||||
loading: "読み込み中"
|
loading: "読み込み中"
|
||||||
surrender: "やめる"
|
surrender: "やめる"
|
||||||
gameRetry: "リトライ"
|
gameRetry: "リトライ"
|
||||||
|
|
13
packages/backend/migration/1706852162173-add-post-lang.js
Normal file
13
packages/backend/migration/1706852162173-add-post-lang.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
export class AddPostLang1706852162173 {
|
||||||
|
name = 'AddPostLang1706852162173'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "note" ADD "lang" character varying(10)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_edit" ADD "lang" character varying(10)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "lang"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_edit" DROP COLUMN "lang"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -91,6 +91,8 @@ type Source = {
|
||||||
|
|
||||||
customMOTD?: string[];
|
customMOTD?: string[];
|
||||||
|
|
||||||
|
langPref?: string[];
|
||||||
|
|
||||||
signToActivityPubGet?: boolean;
|
signToActivityPubGet?: boolean;
|
||||||
checkActivityPubGetSignature?: boolean;
|
checkActivityPubGetSignature?: boolean;
|
||||||
|
|
||||||
|
@ -153,6 +155,7 @@ export type Config = {
|
||||||
customMOTD: string[] | undefined;
|
customMOTD: string[] | undefined;
|
||||||
signToActivityPubGet: boolean;
|
signToActivityPubGet: boolean;
|
||||||
checkActivityPubGetSignature: boolean | undefined;
|
checkActivityPubGetSignature: boolean | undefined;
|
||||||
|
langPref: string[];
|
||||||
|
|
||||||
version: string;
|
version: string;
|
||||||
publishTarballInsteadOfProvideRepositoryUrl: boolean;
|
publishTarballInsteadOfProvideRepositoryUrl: boolean;
|
||||||
|
@ -269,6 +272,7 @@ export function loadConfig(): Config {
|
||||||
inboxJobMaxAttempts: config.inboxJobMaxAttempts,
|
inboxJobMaxAttempts: config.inboxJobMaxAttempts,
|
||||||
proxyRemoteFiles: config.proxyRemoteFiles,
|
proxyRemoteFiles: config.proxyRemoteFiles,
|
||||||
customMOTD: config.customMOTD,
|
customMOTD: config.customMOTD,
|
||||||
|
langPref: config.langPref ?? ['en', 'ja'],
|
||||||
signToActivityPubGet: config.signToActivityPubGet ?? true,
|
signToActivityPubGet: config.signToActivityPubGet ?? true,
|
||||||
checkActivityPubGetSignature: config.checkActivityPubGetSignature,
|
checkActivityPubGetSignature: config.checkActivityPubGetSignature,
|
||||||
mediaProxy: externalMediaProxy ?? internalMediaProxy,
|
mediaProxy: externalMediaProxy ?? internalMediaProxy,
|
||||||
|
|
|
@ -133,6 +133,7 @@ type Option = {
|
||||||
createdAt?: Date | null;
|
createdAt?: Date | null;
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
|
lang?: string | null;
|
||||||
reply?: MiNote | null;
|
reply?: MiNote | null;
|
||||||
renote?: MiNote | null;
|
renote?: MiNote | null;
|
||||||
files?: MiDriveFile[] | null;
|
files?: MiDriveFile[] | null;
|
||||||
|
@ -603,6 +604,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||||
: null,
|
: null,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
text: data.text,
|
text: data.text,
|
||||||
|
lang: data.lang,
|
||||||
hasPoll: data.poll != null,
|
hasPoll: data.poll != null,
|
||||||
cw: data.cw ?? null,
|
cw: data.cw ?? null,
|
||||||
tags: tags.map(tag => normalizeForSearch(tag)),
|
tags: tags.map(tag => normalizeForSearch(tag)),
|
||||||
|
|
|
@ -123,6 +123,7 @@ type Option = {
|
||||||
createdAt?: Date | null;
|
createdAt?: Date | null;
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
|
lang?: string | null;
|
||||||
reply?: MiNote | null;
|
reply?: MiNote | null;
|
||||||
renote?: MiNote | null;
|
renote?: MiNote | null;
|
||||||
files?: MiDriveFile[] | null;
|
files?: MiDriveFile[] | null;
|
||||||
|
@ -429,6 +430,9 @@ export class NoteEditService implements OnApplicationShutdown {
|
||||||
if (oldnote.hasPoll !== !!data.poll) {
|
if (oldnote.hasPoll !== !!data.poll) {
|
||||||
update.hasPoll = !!data.poll;
|
update.hasPoll = !!data.poll;
|
||||||
}
|
}
|
||||||
|
if (data.lang !== oldnote.lang) {
|
||||||
|
update.lang = data.lang;
|
||||||
|
}
|
||||||
|
|
||||||
const poll = await this.pollsRepository.findOneBy({ noteId: oldnote.id });
|
const poll = await this.pollsRepository.findOneBy({ noteId: oldnote.id });
|
||||||
|
|
||||||
|
@ -443,6 +447,7 @@ export class NoteEditService implements OnApplicationShutdown {
|
||||||
oldText: oldnote.text || undefined,
|
oldText: oldnote.text || undefined,
|
||||||
newText: update.text || undefined,
|
newText: update.text || undefined,
|
||||||
cw: update.cw || undefined,
|
cw: update.cw || undefined,
|
||||||
|
lang: update.lang || undefined,
|
||||||
fileIds: undefined,
|
fileIds: undefined,
|
||||||
oldDate: exists ? oldnote.updatedAt as Date : this.idService.parse(oldnote.id).date,
|
oldDate: exists ? oldnote.updatedAt as Date : this.idService.parse(oldnote.id).date,
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
@ -462,6 +467,7 @@ export class NoteEditService implements OnApplicationShutdown {
|
||||||
: null,
|
: null,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
text: data.text,
|
text: data.text,
|
||||||
|
lang: data.lang,
|
||||||
hasPoll: data.poll != null,
|
hasPoll: data.poll != null,
|
||||||
cw: data.cw ?? null,
|
cw: data.cw ?? null,
|
||||||
tags: tags.map(tag => normalizeForSearch(tag)),
|
tags: tags.map(tag => normalizeForSearch(tag)),
|
||||||
|
|
|
@ -285,7 +285,7 @@ export class ApRendererService {
|
||||||
if (instance && instance.softwareName === 'pleroma') isMastodon = true;
|
if (instance && instance.softwareName === 'pleroma') isMastodon = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const object: ILike = {
|
const object: ILike = {
|
||||||
type: 'Like',
|
type: 'Like',
|
||||||
id: `${this.config.url}/likes/${noteReaction.id}`,
|
id: `${this.config.url}/likes/${noteReaction.id}`,
|
||||||
|
@ -451,9 +451,15 @@ export class ApRendererService {
|
||||||
_misskey_content: text,
|
_misskey_content: text,
|
||||||
source: {
|
source: {
|
||||||
content: text,
|
content: text,
|
||||||
|
contentMap: note.lang ? {
|
||||||
|
[note.lang]: text,
|
||||||
|
} : undefined,
|
||||||
mediaType: 'text/x.misskeymarkdown',
|
mediaType: 'text/x.misskeymarkdown',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
contentMap: note.lang && content ? {
|
||||||
|
[note.lang]: content,
|
||||||
|
} : undefined,
|
||||||
_misskey_quote: quote,
|
_misskey_quote: quote,
|
||||||
quoteUrl: quote,
|
quoteUrl: quote,
|
||||||
quoteUri: quote,
|
quoteUri: quote,
|
||||||
|
@ -743,9 +749,15 @@ export class ApRendererService {
|
||||||
_misskey_content: text,
|
_misskey_content: text,
|
||||||
source: {
|
source: {
|
||||||
content: text,
|
content: text,
|
||||||
|
contentMap: note.lang ? {
|
||||||
|
[note.lang]: text,
|
||||||
|
} : undefined,
|
||||||
mediaType: 'text/x.misskeymarkdown',
|
mediaType: 'text/x.misskeymarkdown',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
contentMap: note.lang && content ? {
|
||||||
|
[note.lang]: content,
|
||||||
|
} : undefined,
|
||||||
_misskey_quote: quote,
|
_misskey_quote: quote,
|
||||||
quoteUrl: quote,
|
quoteUrl: quote,
|
||||||
quoteUri: quote,
|
quoteUri: quote,
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { StatusError } from '@/misc/status-error.js';
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { checkHttps } from '@/misc/check-https.js';
|
import { checkHttps } from '@/misc/check-https.js';
|
||||||
|
import { langs } from '@/misc/langmap.js';
|
||||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||||
import { isNotNull } from '@/misc/is-not-null.js';
|
import { isNotNull } from '@/misc/is-not-null.js';
|
||||||
import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js';
|
import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js';
|
||||||
|
@ -39,7 +40,7 @@ import { ApMentionService } from './ApMentionService.js';
|
||||||
import { ApQuestionService } from './ApQuestionService.js';
|
import { ApQuestionService } from './ApQuestionService.js';
|
||||||
import { ApImageService } from './ApImageService.js';
|
import { ApImageService } from './ApImageService.js';
|
||||||
import type { Resolver } from '../ApResolverService.js';
|
import type { Resolver } from '../ApResolverService.js';
|
||||||
import type { IObject, IPost } from '../type.js';
|
import type { IObject, IPost, Obj } from '../type.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApNoteService {
|
export class ApNoteService {
|
||||||
|
@ -173,12 +174,17 @@ export class ApNoteService {
|
||||||
|
|
||||||
// テキストのパース
|
// テキストのパース
|
||||||
let text: string | null = null;
|
let text: string | null = null;
|
||||||
if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') {
|
let lang: string | null = null;
|
||||||
text = note.source.content;
|
if (note.source?.mediaType === 'text/x.misskeymarkdown' && (typeof note.source.content === 'string' || note.source.contentMap)) {
|
||||||
|
const guessed = this.guessLang(note.source);
|
||||||
|
text = guessed.text;
|
||||||
|
lang = guessed.lang;
|
||||||
} else if (typeof note._misskey_content !== 'undefined') {
|
} else if (typeof note._misskey_content !== 'undefined') {
|
||||||
text = note._misskey_content;
|
text = note._misskey_content;
|
||||||
} else if (typeof note.content === 'string') {
|
} else if (typeof note.content === 'string' || note.contentMap) {
|
||||||
text = this.apMfmService.htmlToMfm(note.content, note.tag);
|
const guessed = this.guessLang(note);
|
||||||
|
lang = guessed.lang;
|
||||||
|
if (guessed.text) text = this.apMfmService.htmlToMfm(guessed.text, note.tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined);
|
const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined);
|
||||||
|
@ -310,6 +316,7 @@ export class ApNoteService {
|
||||||
name: note.name,
|
name: note.name,
|
||||||
cw,
|
cw,
|
||||||
text,
|
text,
|
||||||
|
lang,
|
||||||
localOnly: false,
|
localOnly: false,
|
||||||
visibility,
|
visibility,
|
||||||
visibleUsers,
|
visibleUsers,
|
||||||
|
@ -400,12 +407,17 @@ export class ApNoteService {
|
||||||
|
|
||||||
// テキストのパース
|
// テキストのパース
|
||||||
let text: string | null = null;
|
let text: string | null = null;
|
||||||
if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') {
|
let lang: string | null = null;
|
||||||
text = note.source.content;
|
if (note.source?.mediaType === 'text/x.misskeymarkdown' && (typeof note.source.content === 'string' || note.source.contentMap)) {
|
||||||
|
const guessed = this.guessLang(note.source);
|
||||||
|
text = guessed.text;
|
||||||
|
lang = guessed.lang;
|
||||||
} else if (typeof note._misskey_content !== 'undefined') {
|
} else if (typeof note._misskey_content !== 'undefined') {
|
||||||
text = note._misskey_content;
|
text = note._misskey_content;
|
||||||
} else if (typeof note.content === 'string') {
|
} else if (typeof note.content === 'string' || note.contentMap) {
|
||||||
text = this.apMfmService.htmlToMfm(note.content, note.tag);
|
const guessed = this.guessLang(note);
|
||||||
|
lang = guessed.lang;
|
||||||
|
if (guessed.text) text = this.apMfmService.htmlToMfm(guessed.text, note.tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined);
|
const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined);
|
||||||
|
@ -537,6 +549,7 @@ export class ApNoteService {
|
||||||
name: note.name,
|
name: note.name,
|
||||||
cw,
|
cw,
|
||||||
text,
|
text,
|
||||||
|
lang,
|
||||||
localOnly: false,
|
localOnly: false,
|
||||||
visibility,
|
visibility,
|
||||||
visibleUsers,
|
visibleUsers,
|
||||||
|
@ -654,4 +667,40 @@ export class ApNoteService {
|
||||||
}).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
|
}).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private guessLang(source: { contentMap?: Obj | null, content?: string | null }): { lang: string | null, text: string | null } {
|
||||||
|
// do we have a map?
|
||||||
|
if (source.contentMap) {
|
||||||
|
const entries = Object.entries(source.contentMap);
|
||||||
|
|
||||||
|
// only one entry: take that
|
||||||
|
if (entries.length === 1) {
|
||||||
|
return { lang: entries[0][0], text: entries[0][1] };
|
||||||
|
}
|
||||||
|
|
||||||
|
// did the sender indicate a preferred language?
|
||||||
|
if (source.content) {
|
||||||
|
for (const e of entries) {
|
||||||
|
if (e[1] === source.content) {
|
||||||
|
return { lang: e[0], text: e[1] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// can we find one of *our* preferred languages?
|
||||||
|
for (const prefLang of this.config.langPref) {
|
||||||
|
if (source.contentMap[prefLang]) {
|
||||||
|
return { lang: prefLang, text: source.contentMap[prefLang] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bah, just pick one
|
||||||
|
return { lang: entries[0][0], text: entries[0][1] };
|
||||||
|
}
|
||||||
|
|
||||||
|
// no map, so we don't know the language, just take whatever
|
||||||
|
// content we got
|
||||||
|
return { lang: null, text: source.content ?? null };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ export interface IObject {
|
||||||
inReplyTo?: any;
|
inReplyTo?: any;
|
||||||
replies?: ICollection;
|
replies?: ICollection;
|
||||||
content?: string | null;
|
content?: string | null;
|
||||||
|
contentMap?: Obj | null;
|
||||||
startTime?: Date;
|
startTime?: Date;
|
||||||
endTime?: Date;
|
endTime?: Date;
|
||||||
icon?: any;
|
icon?: any;
|
||||||
|
@ -114,6 +115,7 @@ export interface IPost extends IObject {
|
||||||
type: 'Note' | 'Question' | 'Article' | 'Audio' | 'Document' | 'Image' | 'Page' | 'Video' | 'Event';
|
type: 'Note' | 'Question' | 'Article' | 'Audio' | 'Document' | 'Image' | 'Page' | 'Video' | 'Event';
|
||||||
source?: {
|
source?: {
|
||||||
content: string;
|
content: string;
|
||||||
|
contentMap?: Obj | null;
|
||||||
mediaType: string;
|
mediaType: string;
|
||||||
};
|
};
|
||||||
_misskey_quote?: string;
|
_misskey_quote?: string;
|
||||||
|
@ -128,6 +130,7 @@ export interface IQuestion extends IObject {
|
||||||
actor: string;
|
actor: string;
|
||||||
source?: {
|
source?: {
|
||||||
content: string;
|
content: string;
|
||||||
|
contentMap?: Obj | null;
|
||||||
mediaType: string;
|
mediaType: string;
|
||||||
};
|
};
|
||||||
_misskey_quote?: string;
|
_misskey_quote?: string;
|
||||||
|
|
|
@ -17,12 +17,12 @@ import { bindThis } from '@/decorators.js';
|
||||||
import { isNotNull } from '@/misc/is-not-null.js';
|
import { isNotNull } from '@/misc/is-not-null.js';
|
||||||
import { DebounceLoader } from '@/misc/loader.js';
|
import { DebounceLoader } from '@/misc/loader.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
|
import type { Config } from '@/config.js';
|
||||||
import type { OnModuleInit } from '@nestjs/common';
|
import type { OnModuleInit } from '@nestjs/common';
|
||||||
import type { CustomEmojiService } from '../CustomEmojiService.js';
|
import type { CustomEmojiService } from '../CustomEmojiService.js';
|
||||||
import type { ReactionService } from '../ReactionService.js';
|
import type { ReactionService } from '../ReactionService.js';
|
||||||
import type { UserEntityService } from './UserEntityService.js';
|
import type { UserEntityService } from './UserEntityService.js';
|
||||||
import type { DriveFileEntityService } from './DriveFileEntityService.js';
|
import type { DriveFileEntityService } from './DriveFileEntityService.js';
|
||||||
import type { Config } from '@/config.js';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class NoteEntityService implements OnModuleInit {
|
export class NoteEntityService implements OnModuleInit {
|
||||||
|
@ -120,7 +120,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
followerId: meId,
|
followerId: meId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
hide = !isFollowing;
|
hide = !isFollowing;
|
||||||
} else {
|
} else {
|
||||||
// フォロワーかどうか
|
// フォロワーかどうか
|
||||||
|
@ -373,6 +373,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
uri: note.uri ?? undefined,
|
uri: note.uri ?? undefined,
|
||||||
url: note.url ?? undefined,
|
url: note.url ?? undefined,
|
||||||
poll: note.hasPoll ? this.populatePoll(note, meId) : undefined,
|
poll: note.hasPoll ? this.populatePoll(note, meId) : undefined,
|
||||||
|
lang: note.lang,
|
||||||
...(meId && Object.keys(note.reactions).length > 0 ? {
|
...(meId && Object.keys(note.reactions).length > 0 ? {
|
||||||
myReaction: this.populateMyReaction(note, meId, options?._hint_),
|
myReaction: this.populateMyReaction(note, meId, options?._hint_),
|
||||||
} : {}),
|
} : {}),
|
||||||
|
|
|
@ -35,6 +35,9 @@ export const langmap = {
|
||||||
'ar-SA': {
|
'ar-SA': {
|
||||||
nativeName: 'العربية (السعودية)',
|
nativeName: 'العربية (السعودية)',
|
||||||
},
|
},
|
||||||
|
'ay': {
|
||||||
|
nativeName: 'Aymar aru',
|
||||||
|
},
|
||||||
'ay-BO': {
|
'ay-BO': {
|
||||||
nativeName: 'Aymar aru',
|
nativeName: 'Aymar aru',
|
||||||
},
|
},
|
||||||
|
@ -44,6 +47,9 @@ export const langmap = {
|
||||||
'az-AZ': {
|
'az-AZ': {
|
||||||
nativeName: 'Azərbaycan dili',
|
nativeName: 'Azərbaycan dili',
|
||||||
},
|
},
|
||||||
|
'be': {
|
||||||
|
nativeName: 'Беларуская',
|
||||||
|
},
|
||||||
'be-BY': {
|
'be-BY': {
|
||||||
nativeName: 'Беларуская',
|
nativeName: 'Беларуская',
|
||||||
},
|
},
|
||||||
|
@ -65,6 +71,9 @@ export const langmap = {
|
||||||
'br': {
|
'br': {
|
||||||
nativeName: 'Brezhoneg',
|
nativeName: 'Brezhoneg',
|
||||||
},
|
},
|
||||||
|
'bs': {
|
||||||
|
nativeName: 'Bosanski',
|
||||||
|
},
|
||||||
'bs-BA': {
|
'bs-BA': {
|
||||||
nativeName: 'Bosanski',
|
nativeName: 'Bosanski',
|
||||||
},
|
},
|
||||||
|
@ -77,7 +86,7 @@ export const langmap = {
|
||||||
'cak': {
|
'cak': {
|
||||||
nativeName: 'Maya Kaqchikel',
|
nativeName: 'Maya Kaqchikel',
|
||||||
},
|
},
|
||||||
'ck-US': {
|
'chr': {
|
||||||
nativeName: 'ᏣᎳᎩ (tsalagi)',
|
nativeName: 'ᏣᎳᎩ (tsalagi)',
|
||||||
},
|
},
|
||||||
'cs': {
|
'cs': {
|
||||||
|
@ -152,9 +161,6 @@ export const langmap = {
|
||||||
'en-ZA': {
|
'en-ZA': {
|
||||||
nativeName: 'English (South Africa)',
|
nativeName: 'English (South Africa)',
|
||||||
},
|
},
|
||||||
'en@pirate': {
|
|
||||||
nativeName: 'English (Pirate)',
|
|
||||||
},
|
|
||||||
'eo': {
|
'eo': {
|
||||||
nativeName: 'Esperanto',
|
nativeName: 'Esperanto',
|
||||||
},
|
},
|
||||||
|
@ -248,6 +254,9 @@ export const langmap = {
|
||||||
'fr-CH': {
|
'fr-CH': {
|
||||||
nativeName: 'Français (Suisse)',
|
nativeName: 'Français (Suisse)',
|
||||||
},
|
},
|
||||||
|
'fy': {
|
||||||
|
nativeName: 'Frysk',
|
||||||
|
},
|
||||||
'fy-NL': {
|
'fy-NL': {
|
||||||
nativeName: 'Frysk',
|
nativeName: 'Frysk',
|
||||||
},
|
},
|
||||||
|
@ -266,16 +275,22 @@ export const langmap = {
|
||||||
'gl-ES': {
|
'gl-ES': {
|
||||||
nativeName: 'Galego',
|
nativeName: 'Galego',
|
||||||
},
|
},
|
||||||
|
'gn': {
|
||||||
|
nativeName: 'Avañe\'ẽ',
|
||||||
|
},
|
||||||
'gn-PY': {
|
'gn-PY': {
|
||||||
nativeName: 'Avañe\'ẽ',
|
nativeName: 'Avañe\'ẽ',
|
||||||
},
|
},
|
||||||
|
'gu': {
|
||||||
|
nativeName: 'ગુજરાતી',
|
||||||
|
},
|
||||||
'gu-IN': {
|
'gu-IN': {
|
||||||
nativeName: 'ગુજરાતી',
|
nativeName: 'ગુજરાતી',
|
||||||
},
|
},
|
||||||
'gv': {
|
'gv': {
|
||||||
nativeName: 'Gaelg',
|
nativeName: 'Gaelg',
|
||||||
},
|
},
|
||||||
'gx-GR': {
|
'grc': {
|
||||||
nativeName: 'Ἑλληνική ἀρχαία',
|
nativeName: 'Ἑλληνική ἀρχαία',
|
||||||
},
|
},
|
||||||
'he': {
|
'he': {
|
||||||
|
@ -338,12 +353,21 @@ export const langmap = {
|
||||||
'ja-JP': {
|
'ja-JP': {
|
||||||
nativeName: '日本語 (日本)',
|
nativeName: '日本語 (日本)',
|
||||||
},
|
},
|
||||||
|
'jv': {
|
||||||
|
nativeName: 'Basa Jawa',
|
||||||
|
},
|
||||||
'jv-ID': {
|
'jv-ID': {
|
||||||
nativeName: 'Basa Jawa',
|
nativeName: 'Basa Jawa',
|
||||||
},
|
},
|
||||||
|
'ka': {
|
||||||
|
nativeName: 'ქართული',
|
||||||
|
},
|
||||||
'ka-GE': {
|
'ka-GE': {
|
||||||
nativeName: 'ქართული',
|
nativeName: 'ქართული',
|
||||||
},
|
},
|
||||||
|
'kk': {
|
||||||
|
nativeName: 'Қазақша',
|
||||||
|
},
|
||||||
'kk-KZ': {
|
'kk-KZ': {
|
||||||
nativeName: 'Қазақша',
|
nativeName: 'Қазақша',
|
||||||
},
|
},
|
||||||
|
@ -371,6 +395,9 @@ export const langmap = {
|
||||||
'ko-KR': {
|
'ko-KR': {
|
||||||
nativeName: '한국어 (한국)',
|
nativeName: '한국어 (한국)',
|
||||||
},
|
},
|
||||||
|
'ku': {
|
||||||
|
nativeName: 'Kurdî',
|
||||||
|
},
|
||||||
'ku-TR': {
|
'ku-TR': {
|
||||||
nativeName: 'Kurdî',
|
nativeName: 'Kurdî',
|
||||||
},
|
},
|
||||||
|
@ -386,6 +413,9 @@ export const langmap = {
|
||||||
'lb': {
|
'lb': {
|
||||||
nativeName: 'Lëtzebuergesch',
|
nativeName: 'Lëtzebuergesch',
|
||||||
},
|
},
|
||||||
|
'li': {
|
||||||
|
nativeName: 'Lèmbörgs',
|
||||||
|
},
|
||||||
'li-NL': {
|
'li-NL': {
|
||||||
nativeName: 'Lèmbörgs',
|
nativeName: 'Lèmbörgs',
|
||||||
},
|
},
|
||||||
|
@ -404,6 +434,9 @@ export const langmap = {
|
||||||
'mai': {
|
'mai': {
|
||||||
nativeName: 'मैथिली, মৈথিলী',
|
nativeName: 'मैथिली, মৈথিলী',
|
||||||
},
|
},
|
||||||
|
'mg': {
|
||||||
|
nativeName: 'Malagasy',
|
||||||
|
},
|
||||||
'mg-MG': {
|
'mg-MG': {
|
||||||
nativeName: 'Malagasy',
|
nativeName: 'Malagasy',
|
||||||
},
|
},
|
||||||
|
@ -419,6 +452,9 @@ export const langmap = {
|
||||||
'ml-IN': {
|
'ml-IN': {
|
||||||
nativeName: 'മലയാളം',
|
nativeName: 'മലയാളം',
|
||||||
},
|
},
|
||||||
|
'mn': {
|
||||||
|
nativeName: 'Монгол',
|
||||||
|
},
|
||||||
'mn-MN': {
|
'mn-MN': {
|
||||||
nativeName: 'Монгол',
|
nativeName: 'Монгол',
|
||||||
},
|
},
|
||||||
|
@ -443,6 +479,9 @@ export const langmap = {
|
||||||
'my': {
|
'my': {
|
||||||
nativeName: 'ဗမာစကာ',
|
nativeName: 'ဗမာစကာ',
|
||||||
},
|
},
|
||||||
|
'nan': {
|
||||||
|
nativeName: '閩南語',
|
||||||
|
},
|
||||||
'no': {
|
'no': {
|
||||||
nativeName: 'Norsk',
|
nativeName: 'Norsk',
|
||||||
},
|
},
|
||||||
|
@ -467,12 +506,18 @@ export const langmap = {
|
||||||
'nl-NL': {
|
'nl-NL': {
|
||||||
nativeName: 'Nederlands (Nederland)',
|
nativeName: 'Nederlands (Nederland)',
|
||||||
},
|
},
|
||||||
|
'nn': {
|
||||||
|
nativeName: 'Norsk (nynorsk)',
|
||||||
|
},
|
||||||
'nn-NO': {
|
'nn-NO': {
|
||||||
nativeName: 'Norsk (nynorsk)',
|
nativeName: 'Norsk (nynorsk)',
|
||||||
},
|
},
|
||||||
'oc': {
|
'oc': {
|
||||||
nativeName: 'Occitan',
|
nativeName: 'Occitan',
|
||||||
},
|
},
|
||||||
|
'or': {
|
||||||
|
nativeName: 'ଓଡ଼ିଆ',
|
||||||
|
},
|
||||||
'or-IN': {
|
'or-IN': {
|
||||||
nativeName: 'ଓଡ଼ିଆ',
|
nativeName: 'ଓଡ଼ିଆ',
|
||||||
},
|
},
|
||||||
|
@ -488,6 +533,9 @@ export const langmap = {
|
||||||
'pl-PL': {
|
'pl-PL': {
|
||||||
nativeName: 'Polski',
|
nativeName: 'Polski',
|
||||||
},
|
},
|
||||||
|
'ps': {
|
||||||
|
nativeName: 'پښتو',
|
||||||
|
},
|
||||||
'ps-AF': {
|
'ps-AF': {
|
||||||
nativeName: 'پښتو',
|
nativeName: 'پښتو',
|
||||||
},
|
},
|
||||||
|
@ -500,9 +548,15 @@ export const langmap = {
|
||||||
'pt-PT': {
|
'pt-PT': {
|
||||||
nativeName: 'Português (Portugal)',
|
nativeName: 'Português (Portugal)',
|
||||||
},
|
},
|
||||||
|
'qu': {
|
||||||
|
nativeName: 'Qhichwa',
|
||||||
|
},
|
||||||
'qu-PE': {
|
'qu-PE': {
|
||||||
nativeName: 'Qhichwa',
|
nativeName: 'Qhichwa',
|
||||||
},
|
},
|
||||||
|
'rm': {
|
||||||
|
nativeName: 'Rumantsch',
|
||||||
|
},
|
||||||
'rm-CH': {
|
'rm-CH': {
|
||||||
nativeName: 'Rumantsch',
|
nativeName: 'Rumantsch',
|
||||||
},
|
},
|
||||||
|
@ -518,15 +572,24 @@ export const langmap = {
|
||||||
'ru-RU': {
|
'ru-RU': {
|
||||||
nativeName: 'Русский',
|
nativeName: 'Русский',
|
||||||
},
|
},
|
||||||
|
'sa': {
|
||||||
|
nativeName: 'संस्कृतम्',
|
||||||
|
},
|
||||||
'sa-IN': {
|
'sa-IN': {
|
||||||
nativeName: 'संस्कृतम्',
|
nativeName: 'संस्कृतम्',
|
||||||
},
|
},
|
||||||
|
'se': {
|
||||||
|
nativeName: 'Davvisámegiella',
|
||||||
|
},
|
||||||
'se-NO': {
|
'se-NO': {
|
||||||
nativeName: 'Davvisámegiella',
|
nativeName: 'Davvisámegiella',
|
||||||
},
|
},
|
||||||
'sh': {
|
'sh': {
|
||||||
nativeName: 'српскохрватски',
|
nativeName: 'српскохрватски',
|
||||||
},
|
},
|
||||||
|
'si': {
|
||||||
|
nativeName: 'සිංහල',
|
||||||
|
},
|
||||||
'si-LK': {
|
'si-LK': {
|
||||||
nativeName: 'සිංහල',
|
nativeName: 'සිංහල',
|
||||||
},
|
},
|
||||||
|
@ -542,6 +605,9 @@ export const langmap = {
|
||||||
'sl-SI': {
|
'sl-SI': {
|
||||||
nativeName: 'Slovenščina',
|
nativeName: 'Slovenščina',
|
||||||
},
|
},
|
||||||
|
'so': {
|
||||||
|
nativeName: 'Soomaaliga',
|
||||||
|
},
|
||||||
'so-SO': {
|
'so-SO': {
|
||||||
nativeName: 'Soomaaliga',
|
nativeName: 'Soomaaliga',
|
||||||
},
|
},
|
||||||
|
@ -602,12 +668,18 @@ export const langmap = {
|
||||||
'tlh': {
|
'tlh': {
|
||||||
nativeName: 'tlhIngan-Hol',
|
nativeName: 'tlhIngan-Hol',
|
||||||
},
|
},
|
||||||
|
'tok': {
|
||||||
|
nativeName: 'Toki Pona',
|
||||||
|
},
|
||||||
'tr': {
|
'tr': {
|
||||||
nativeName: 'Türkçe',
|
nativeName: 'Türkçe',
|
||||||
},
|
},
|
||||||
'tr-TR': {
|
'tr-TR': {
|
||||||
nativeName: 'Türkçe',
|
nativeName: 'Türkçe',
|
||||||
},
|
},
|
||||||
|
'tt': {
|
||||||
|
nativeName: 'татарча',
|
||||||
|
},
|
||||||
'tt-RU': {
|
'tt-RU': {
|
||||||
nativeName: 'татарча',
|
nativeName: 'татарча',
|
||||||
},
|
},
|
||||||
|
@ -635,6 +707,9 @@ export const langmap = {
|
||||||
'vi-VN': {
|
'vi-VN': {
|
||||||
nativeName: 'Tiếng Việt',
|
nativeName: 'Tiếng Việt',
|
||||||
},
|
},
|
||||||
|
'xh': {
|
||||||
|
nativeName: 'isiXhosa',
|
||||||
|
},
|
||||||
'xh-ZA': {
|
'xh-ZA': {
|
||||||
nativeName: 'isiXhosa',
|
nativeName: 'isiXhosa',
|
||||||
},
|
},
|
||||||
|
@ -644,6 +719,9 @@ export const langmap = {
|
||||||
'yi-DE': {
|
'yi-DE': {
|
||||||
nativeName: 'ייִדיש (German)',
|
nativeName: 'ייִדיש (German)',
|
||||||
},
|
},
|
||||||
|
'yue': {
|
||||||
|
nativeName: '粵語',
|
||||||
|
},
|
||||||
'zh': {
|
'zh': {
|
||||||
nativeName: '中文',
|
nativeName: '中文',
|
||||||
},
|
},
|
||||||
|
@ -665,7 +743,16 @@ export const langmap = {
|
||||||
'zh-TW': {
|
'zh-TW': {
|
||||||
nativeName: '中文(台灣)',
|
nativeName: '中文(台灣)',
|
||||||
},
|
},
|
||||||
|
'zu': {
|
||||||
|
nativeName: 'isiZulu',
|
||||||
|
},
|
||||||
'zu-ZA': {
|
'zu-ZA': {
|
||||||
nativeName: 'isiZulu',
|
nativeName: 'isiZulu',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const langs: string[] = [
|
||||||
|
...(Object.keys(langmap).filter(tag => tag.indexOf('-') < 0)),
|
||||||
|
'zh-Hans',
|
||||||
|
'zh-Hant',
|
||||||
|
];
|
||||||
|
|
|
@ -61,6 +61,12 @@ export class MiNote {
|
||||||
})
|
})
|
||||||
public text: string | null;
|
public text: string | null;
|
||||||
|
|
||||||
|
@Column('varchar', {
|
||||||
|
length: 10,
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public lang: string | null;
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 256, nullable: true,
|
length: 256, nullable: true,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Entity, JoinColumn, Column, ManyToOne, PrimaryColumn, Index } from "typeorm";
|
import { Entity, JoinColumn, Column, ManyToOne, PrimaryColumn, Index } from 'typeorm';
|
||||||
import { id } from './util/id.js';
|
import { id } from './util/id.js';
|
||||||
import { MiNote } from './Note.js';
|
import { MiNote } from './Note.js';
|
||||||
import type { MiDriveFile } from './DriveFile.js';
|
import type { MiDriveFile } from './DriveFile.js';
|
||||||
|
@ -11,46 +11,52 @@ export class NoteEdit {
|
||||||
@Index()
|
@Index()
|
||||||
@Column({
|
@Column({
|
||||||
...id(),
|
...id(),
|
||||||
comment: "The ID of note.",
|
comment: 'The ID of note.',
|
||||||
})
|
})
|
||||||
public noteId: MiNote["id"];
|
public noteId: MiNote['id'];
|
||||||
|
|
||||||
@ManyToOne((type) => MiNote, {
|
@ManyToOne((type) => MiNote, {
|
||||||
onDelete: "CASCADE",
|
onDelete: 'CASCADE',
|
||||||
})
|
})
|
||||||
@JoinColumn()
|
@JoinColumn()
|
||||||
public note: MiNote | null;
|
public note: MiNote | null;
|
||||||
|
|
||||||
@Column("text", {
|
@Column('text', {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
public oldText: string | null;
|
public oldText: string | null;
|
||||||
|
|
||||||
@Column("text", {
|
@Column('text', {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
public newText: string | null;
|
public newText: string | null;
|
||||||
|
|
||||||
@Column("varchar", {
|
@Column('varchar', {
|
||||||
length: 512,
|
length: 512,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
public cw: string | null;
|
public cw: string | null;
|
||||||
|
|
||||||
|
@Column('varchar', {
|
||||||
|
length: 10,
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public lang: string | null;
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
...id(),
|
...id(),
|
||||||
array: true,
|
array: true,
|
||||||
default: "{}",
|
default: '{}',
|
||||||
})
|
})
|
||||||
public fileIds: MiDriveFile["id"][];
|
public fileIds: MiDriveFile['id'][];
|
||||||
|
|
||||||
@Column("timestamp with time zone", {
|
@Column('timestamp with time zone', {
|
||||||
comment: "The updated date of the Note.",
|
comment: 'The updated date of the Note.',
|
||||||
})
|
})
|
||||||
public updatedAt: Date;
|
public updatedAt: Date;
|
||||||
|
|
||||||
@Column("timestamp with time zone", {
|
@Column('timestamp with time zone', {
|
||||||
comment: "The old date from before the edit",
|
comment: 'The old date from before the edit',
|
||||||
})
|
})
|
||||||
public oldDate: Date;
|
public oldDate: Date;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
import { langs } from '@/misc/langmap.js';
|
||||||
|
|
||||||
export const packedNoteSchema = {
|
export const packedNoteSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
@ -26,6 +27,11 @@ export const packedNoteSchema = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
},
|
},
|
||||||
|
lang: {
|
||||||
|
type: 'string',
|
||||||
|
enum: langs,
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
cw: {
|
cw: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: true, nullable: true,
|
optional: true, nullable: true,
|
||||||
|
|
|
@ -382,7 +382,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
updates.backgroundUrl = null;
|
updates.backgroundUrl = null;
|
||||||
updates.backgroundBlurhash = null;
|
updates.backgroundBlurhash = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.avatarDecorations) {
|
if (ps.avatarDecorations) {
|
||||||
const decorations = await this.avatarDecorationService.getAll(true);
|
const decorations = await this.avatarDecorationService.getAll(true);
|
||||||
const [myRoles, myPolicies] = await Promise.all([this.roleService.getUserRoles(user.id), this.roleService.getUserPolicies(user.id)]);
|
const [myRoles, myPolicies] = await Promise.all([this.roleService.getUserRoles(user.id), this.roleService.getUserPolicies(user.id)]);
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { isPureRenote } from '@/misc/is-pure-renote.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||||
|
import { langs } from '@/misc/langmap.js';
|
||||||
import { ApiError } from '../../error.js';
|
import { ApiError } from '../../error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
@ -148,6 +149,7 @@ export const paramDef = {
|
||||||
visibleUserIds: { type: 'array', uniqueItems: true, items: {
|
visibleUserIds: { type: 'array', uniqueItems: true, items: {
|
||||||
type: 'string', format: 'misskey:id',
|
type: 'string', format: 'misskey:id',
|
||||||
} },
|
} },
|
||||||
|
lang: { type: 'string', enum: langs, nullable: true, maxLength: 10 },
|
||||||
cw: { type: 'string', nullable: true, minLength: 1, maxLength: 500 },
|
cw: { type: 'string', nullable: true, minLength: 1, maxLength: 500 },
|
||||||
localOnly: { type: 'boolean', default: false },
|
localOnly: { type: 'boolean', default: false },
|
||||||
reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null },
|
reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null },
|
||||||
|
@ -384,6 +386,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null,
|
expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null,
|
||||||
} : undefined,
|
} : undefined,
|
||||||
text: ps.text ?? undefined,
|
text: ps.text ?? undefined,
|
||||||
|
lang: ps.lang,
|
||||||
reply,
|
reply,
|
||||||
renote,
|
renote,
|
||||||
cw: ps.cw,
|
cw: ps.cw,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { NoteEditService } from '@/core/NoteEditService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { isPureRenote } from '@/misc/is-pure-renote.js';
|
import { isPureRenote } from '@/misc/is-pure-renote.js';
|
||||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||||
|
import { langs } from '@/misc/langmap.js';
|
||||||
import { ApiError } from '../../error.js';
|
import { ApiError } from '../../error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
@ -197,6 +198,7 @@ export const paramDef = {
|
||||||
format: 'misskey:id',
|
format: 'misskey:id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
lang: { type: 'string', enum: langs, nullable: true, maxLength: 10 },
|
||||||
cw: { type: 'string', nullable: true, minLength: 1, maxLength: 500 },
|
cw: { type: 'string', nullable: true, minLength: 1, maxLength: 500 },
|
||||||
localOnly: { type: 'boolean', default: false },
|
localOnly: { type: 'boolean', default: false },
|
||||||
reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null },
|
reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null },
|
||||||
|
@ -436,6 +438,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null,
|
expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null,
|
||||||
} : undefined,
|
} : undefined,
|
||||||
text: ps.text ?? undefined,
|
text: ps.text ?? undefined,
|
||||||
|
lang: ps.lang,
|
||||||
reply,
|
reply,
|
||||||
renote,
|
renote,
|
||||||
cw: ps.cw,
|
cw: ps.cw,
|
||||||
|
|
|
@ -54,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/>
|
<MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/>
|
||||||
<div style="container-type: inline-size;">
|
<div style="container-type: inline-size;">
|
||||||
<p v-if="appearNote.cw != null" :class="$style.cw">
|
<p v-if="appearNote.cw != null" :class="$style.cw">
|
||||||
<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
|
<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :lang="appearNote.lang" :author="appearNote.user" :nyaize="'respect'"/>
|
||||||
<MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll" style="margin: 4px 0;" @click.stop/>
|
<MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll" style="margin: 4px 0;" @click.stop/>
|
||||||
</p>
|
</p>
|
||||||
<div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]">
|
<div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]">
|
||||||
|
@ -65,6 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
v-if="appearNote.text"
|
v-if="appearNote.text"
|
||||||
:parsedNodes="parsed"
|
:parsedNodes="parsed"
|
||||||
:text="appearNote.text"
|
:text="appearNote.text"
|
||||||
|
:lang="appearNote.lang"
|
||||||
:author="appearNote.user"
|
:author="appearNote.user"
|
||||||
:nyaize="'respect'"
|
:nyaize="'respect'"
|
||||||
:emojiUrls="appearNote.emojis"
|
:emojiUrls="appearNote.emojis"
|
||||||
|
@ -76,7 +77,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkLoading v-if="translating" mini/>
|
<MkLoading v-if="translating" mini/>
|
||||||
<div v-else-if="translation">
|
<div v-else-if="translation">
|
||||||
<b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b>
|
<b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b>
|
||||||
<Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/>
|
<Mfm :text="translation.text" :lang="nativeLang" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
||||||
|
@ -217,6 +218,7 @@ import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
|
||||||
import { shouldCollapsed } from '@/scripts/collapsed.js';
|
import { shouldCollapsed } from '@/scripts/collapsed.js';
|
||||||
import { useRouter } from '@/router/supplier.js';
|
import { useRouter } from '@/router/supplier.js';
|
||||||
import { boostMenuItems, type Visibility } from '@/scripts/boost-quote.js';
|
import { boostMenuItems, type Visibility } from '@/scripts/boost-quote.js';
|
||||||
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note;
|
||||||
|
@ -305,11 +307,12 @@ const renoteCollapsed = ref(
|
||||||
defaultStore.state.collapseRenotes && isRenote && (
|
defaultStore.state.collapseRenotes && isRenote && (
|
||||||
($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || // `||` must be `||`! See https://github.com/misskey-dev/misskey/issues/13131
|
($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || // `||` must be `||`! See https://github.com/misskey-dev/misskey/issues/13131
|
||||||
(appearNote.value.myReaction != null)
|
(appearNote.value.myReaction != null)
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
|
const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
|
||||||
const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null);
|
const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null);
|
||||||
const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false);
|
const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false);
|
||||||
|
const nativeLang = ref(miLocalStorage.getItem('lang') ?? window.navigator.language);
|
||||||
|
|
||||||
/* Overload FunctionにLintが対応していないのでコメントアウト
|
/* Overload FunctionにLintが対応していないのでコメントアウト
|
||||||
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: true): boolean;
|
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: true): boolean;
|
||||||
|
@ -416,7 +419,7 @@ function boostVisibility() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renote(visibility: Visibility, localOnly: boolean = false) {
|
function renote(visibility: Visibility, localOnly = false) {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
showMovedDialog();
|
showMovedDialog();
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</header>
|
</header>
|
||||||
<div :class="$style.noteContent">
|
<div :class="$style.noteContent">
|
||||||
<p v-if="appearNote.cw != null" :class="$style.cw">
|
<p v-if="appearNote.cw != null" :class="$style.cw">
|
||||||
<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
|
<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :lang="appearNote.lang" :author="appearNote.user" :nyaize="'respect'"/>
|
||||||
<MkCwButton v-model="showContent" :text="appearNote.text" :files="appearNote.files" :poll="appearNote.poll"/>
|
<MkCwButton v-model="showContent" :text="appearNote.text" :files="appearNote.files" :poll="appearNote.poll"/>
|
||||||
</p>
|
</p>
|
||||||
<div v-show="appearNote.cw == null || showContent">
|
<div v-show="appearNote.cw == null || showContent">
|
||||||
|
@ -78,6 +78,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
v-if="appearNote.text"
|
v-if="appearNote.text"
|
||||||
:parsedNodes="parsed"
|
:parsedNodes="parsed"
|
||||||
:text="appearNote.text"
|
:text="appearNote.text"
|
||||||
|
:lang="appearNote.lang"
|
||||||
:author="appearNote.user"
|
:author="appearNote.user"
|
||||||
:nyaize="'respect'"
|
:nyaize="'respect'"
|
||||||
:emojiUrls="appearNote.emojis"
|
:emojiUrls="appearNote.emojis"
|
||||||
|
@ -90,7 +91,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkLoading v-if="translating" mini/>
|
<MkLoading v-if="translating" mini/>
|
||||||
<div v-else-if="translation">
|
<div v-else-if="translation">
|
||||||
<b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b>
|
<b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b>
|
||||||
<Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/>
|
<Mfm :text="translation.text" :lang="nativeLang" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
||||||
|
@ -258,6 +259,7 @@ import MkPagination, { type Paging } from '@/components/MkPagination.vue';
|
||||||
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { boostMenuItems, type Visibility } from '@/scripts/boost-quote.js';
|
import { boostMenuItems, type Visibility } from '@/scripts/boost-quote.js';
|
||||||
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note;
|
||||||
|
@ -321,6 +323,7 @@ const replies = ref<Misskey.entities.Note[]>([]);
|
||||||
const quotes = ref<Misskey.entities.Note[]>([]);
|
const quotes = ref<Misskey.entities.Note[]>([]);
|
||||||
const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i?.id));
|
const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i?.id));
|
||||||
const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
|
const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
|
||||||
|
const nativeLang = ref(miLocalStorage.getItem('lang') ?? window.navigator.language);
|
||||||
|
|
||||||
watch(() => props.expandAllCws, (expandAllCws) => {
|
watch(() => props.expandAllCws, (expandAllCws) => {
|
||||||
if (expandAllCws !== showContent.value) showContent.value = expandAllCws;
|
if (expandAllCws !== showContent.value) showContent.value = expandAllCws;
|
||||||
|
@ -438,7 +441,7 @@ function boostVisibility() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renote(visibility: Visibility, localOnly: boolean = false) {
|
function renote(visibility: Visibility, localOnly = false) {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
showMovedDialog();
|
showMovedDialog();
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkNoteHeader :class="$style.header" :note="note" :mini="true"/>
|
<MkNoteHeader :class="$style.header" :note="note" :mini="true"/>
|
||||||
<div :class="$style.content">
|
<div :class="$style.content">
|
||||||
<p v-if="note.cw != null" :class="$style.cw">
|
<p v-if="note.cw != null" :class="$style.cw">
|
||||||
<Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'respect'"/>
|
<Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :lang="note.lang" :author="note.user" :nyaize="'respect'"/>
|
||||||
<MkCwButton v-model="showContent" :text="note.text" :files="note.files" :poll="note.poll"/>
|
<MkCwButton v-model="showContent" :text="note.text" :files="note.files" :poll="note.poll"/>
|
||||||
</p>
|
</p>
|
||||||
<div v-show="note.cw == null || showContent">
|
<div v-show="note.cw == null || showContent">
|
||||||
|
@ -281,7 +281,7 @@ function boostVisibility() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renote(visibility: Visibility, localOnly: boolean = false) {
|
function renote(visibility: Visibility, localOnly = false) {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
showMovedDialog();
|
showMovedDialog();
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<span :class="$style.headerRightButtonText">{{ channel.name }}</span>
|
<span :class="$style.headerRightButtonText">{{ channel.name }}</span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
<button v-click-anime v-tooltip="i18n.ts.language" :class="['_button', $style.headerRightItem]" @click="setLanguage">
|
||||||
|
<span><i class="ph-translate ph-bold ph-lg"></i></span>
|
||||||
|
<span v-if="language" :class="$style.headerRightButtonText">{{ language }}</span>
|
||||||
|
</button>
|
||||||
<button v-click-anime v-tooltip="i18n.ts._visibility.disableFederation" class="_button" :class="[$style.headerRightItem, { [$style.danger]: localOnly }]" :disabled="channel != null || visibility === 'specified'" @click="toggleLocalOnly">
|
<button v-click-anime v-tooltip="i18n.ts._visibility.disableFederation" class="_button" :class="[$style.headerRightItem, { [$style.danger]: localOnly }]" :disabled="channel != null || visibility === 'specified'" @click="toggleLocalOnly">
|
||||||
<span v-if="!localOnly"><i class="ph-rocket-launch ph-bold ph-lg"></i></span>
|
<span v-if="!localOnly"><i class="ph-rocket-launch ph-bold ph-lg"></i></span>
|
||||||
<span v-else><i class="ph-rocket ph-bold ph-lg"></i></span>
|
<span v-else><i class="ph-rocket ph-bold ph-lg"></i></span>
|
||||||
|
@ -65,16 +69,16 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MkInfo v-if="hasNotSpecifiedMentions" warn :class="$style.hasNotSpecifiedMentions">{{ i18n.ts.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ i18n.ts.add }}</button></MkInfo>
|
<MkInfo v-if="hasNotSpecifiedMentions" warn :class="$style.hasNotSpecifiedMentions">{{ i18n.ts.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ i18n.ts.add }}</button></MkInfo>
|
||||||
<input v-show="useCw" ref="cwInputEl" v-model="cw" :class="$style.cw" :placeholder="i18n.ts.annotation" @keydown="onKeydown">
|
<input v-show="useCw" ref="cwInputEl" v-model="cw" :class="$style.cw" :placeholder="i18n.ts.annotation" :lang="language ?? undefined" @keydown="onKeydown">
|
||||||
<div :class="[$style.textOuter, { [$style.withCw]: useCw }]">
|
<div :class="[$style.textOuter, { [$style.withCw]: useCw }]">
|
||||||
<div v-if="channel" :class="$style.colorBar" :style="{ background: channel.color }"></div>
|
<div v-if="channel" :class="$style.colorBar" :style="{ background: channel.color }"></div>
|
||||||
<textarea ref="textareaEl" v-model="text" :class="[$style.text]" :disabled="posting || posted" :readonly="textAreaReadOnly" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/>
|
<textarea ref="textareaEl" v-model="text" :class="[$style.text]" :disabled="posting || posted" :readonly="textAreaReadOnly" :placeholder="placeholder" :lang="language ?? undefined" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/>
|
||||||
<div v-if="maxTextLength - textLength < 100" :class="['_acrylic', $style.textCount, { [$style.textOver]: textLength > maxTextLength }]">{{ maxTextLength - textLength }}</div>
|
<div v-if="maxTextLength - textLength < 100" :class="['_acrylic', $style.textCount, { [$style.textOver]: textLength > maxTextLength }]">{{ maxTextLength - textLength }}</div>
|
||||||
</div>
|
</div>
|
||||||
<input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" :placeholder="i18n.ts.hashtags" list="hashtags">
|
<input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" :placeholder="i18n.ts.hashtags" list="hashtags">
|
||||||
<XPostFormAttaches v-model="files" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName" @replaceFile="replaceFile"/>
|
<XPostFormAttaches v-model="files" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName" @replaceFile="replaceFile"/>
|
||||||
<MkPollEditor v-if="poll" v-model="poll" @destroyed="poll = null"/>
|
<MkPollEditor v-if="poll" v-model="poll" :lang="language ?? undefined" @destroyed="poll = null"/>
|
||||||
<MkNotePreview v-if="showPreview" :class="$style.preview" :text="text" :files="files" :poll="poll ?? undefined" :useCw="useCw" :cw="cw" :user="postAccount ?? $i"/>
|
<MkNotePreview v-if="showPreview" :class="$style.preview" :text="text" :lang="language ?? undefined" :files="files" :poll="poll ?? undefined" :useCw="useCw" :cw="cw" :user="postAccount ?? $i"/>
|
||||||
<div v-if="showingOptions" style="padding: 8px 16px;">
|
<div v-if="showingOptions" style="padding: 8px 16px;">
|
||||||
</div>
|
</div>
|
||||||
<footer :class="$style.footer">
|
<footer :class="$style.footer">
|
||||||
|
@ -130,6 +134,8 @@ import { miLocalStorage } from '@/local-storage.js';
|
||||||
import { claimAchievement } from '@/scripts/achievements.js';
|
import { claimAchievement } from '@/scripts/achievements.js';
|
||||||
import { emojiPicker } from '@/scripts/emoji-picker.js';
|
import { emojiPicker } from '@/scripts/emoji-picker.js';
|
||||||
import { mfmFunctionPicker } from '@/scripts/mfm-function-picker.js';
|
import { mfmFunctionPicker } from '@/scripts/mfm-function-picker.js';
|
||||||
|
import { langmap, langs } from '@/scripts/langmap.js';
|
||||||
|
import { MenuItem } from '@/types/menu.js';
|
||||||
|
|
||||||
const $i = signinRequired();
|
const $i = signinRequired();
|
||||||
|
|
||||||
|
@ -144,6 +150,7 @@ const props = withDefaults(defineProps<{
|
||||||
initialText?: string;
|
initialText?: string;
|
||||||
initialCw?: string;
|
initialCw?: string;
|
||||||
initialVisibility?: (typeof Misskey.noteVisibilities)[number];
|
initialVisibility?: (typeof Misskey.noteVisibilities)[number];
|
||||||
|
initialLanguage?: (typeof Misskey.languages)[number];
|
||||||
initialFiles?: Misskey.entities.DriveFile[];
|
initialFiles?: Misskey.entities.DriveFile[];
|
||||||
initialLocalOnly?: boolean;
|
initialLocalOnly?: boolean;
|
||||||
initialVisibleUsers?: Misskey.entities.UserDetailed[];
|
initialVisibleUsers?: Misskey.entities.UserDetailed[];
|
||||||
|
@ -202,6 +209,7 @@ const recentHashtags = ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'
|
||||||
const imeText = ref('');
|
const imeText = ref('');
|
||||||
const showingOptions = ref(false);
|
const showingOptions = ref(false);
|
||||||
const textAreaReadOnly = ref(false);
|
const textAreaReadOnly = ref(false);
|
||||||
|
const language = ref<string | null>(props.initialLanguage ?? defaultStore.state.recentlyUsedPostLanguages[0] ?? attemptNormalizeLang(localStorage.getItem('lang')));
|
||||||
|
|
||||||
const draftKey = computed((): string => {
|
const draftKey = computed((): string => {
|
||||||
let key = props.channel ? `channel:${props.channel.id}` : '';
|
let key = props.channel ? `channel:${props.channel.id}` : '';
|
||||||
|
@ -362,6 +370,7 @@ function watchForDraft() {
|
||||||
watch(files, () => saveDraft(), { deep: true });
|
watch(files, () => saveDraft(), { deep: true });
|
||||||
watch(visibility, () => saveDraft());
|
watch(visibility, () => saveDraft());
|
||||||
watch(localOnly, () => saveDraft());
|
watch(localOnly, () => saveDraft());
|
||||||
|
watch(language, () => saveDraft());
|
||||||
}
|
}
|
||||||
|
|
||||||
function MFMWindow() {
|
function MFMWindow() {
|
||||||
|
@ -536,6 +545,64 @@ async function toggleReactionAcceptance() {
|
||||||
reactionAcceptance.value = select.result;
|
reactionAcceptance.value = select.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function attemptNormalizeLang(lang: string | null) {
|
||||||
|
if (lang == null) return null;
|
||||||
|
if (!langs[lang]) lang = lang.split('-')[0];
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLanguage(ev: MouseEvent) {
|
||||||
|
const actions: Array<MenuItem> = [];
|
||||||
|
|
||||||
|
if (language.value != null) actions.push({
|
||||||
|
text: langmap[language.value].nativeName,
|
||||||
|
danger: false,
|
||||||
|
active: true,
|
||||||
|
action: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show recently used language first
|
||||||
|
let recentlyUsedLanguagesExist = false;
|
||||||
|
for (const lang of defaultStore.state.recentlyUsedPostLanguages) {
|
||||||
|
if (lang === language.value) continue;
|
||||||
|
if (!langs.includes(lang)) continue;
|
||||||
|
actions.push({
|
||||||
|
text: langmap[lang].nativeName,
|
||||||
|
danger: false,
|
||||||
|
active: false,
|
||||||
|
action: () => {
|
||||||
|
language.value = lang;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
recentlyUsedLanguagesExist = true;
|
||||||
|
}
|
||||||
|
if (recentlyUsedLanguagesExist) actions.push({ type: 'divider' });
|
||||||
|
|
||||||
|
actions.push({
|
||||||
|
text: i18n.ts.noLanguage,
|
||||||
|
danger: false,
|
||||||
|
active: false,
|
||||||
|
action: () => {
|
||||||
|
language.value = null;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const lang of langs) {
|
||||||
|
if (lang === language.value) continue;
|
||||||
|
if (defaultStore.state.recentlyUsedPostLanguages.includes(lang)) continue;
|
||||||
|
actions.push({
|
||||||
|
text: langmap[lang].nativeName,
|
||||||
|
danger: false,
|
||||||
|
active: false,
|
||||||
|
action: () => {
|
||||||
|
language.value = lang;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
os.popupMenu(actions, ev.currentTarget ?? ev.target);
|
||||||
|
}
|
||||||
|
|
||||||
function pushVisibleUser(user: Misskey.entities.UserDetailed) {
|
function pushVisibleUser(user: Misskey.entities.UserDetailed) {
|
||||||
if (!visibleUsers.value.some(u => u.username === user.username && u.host === user.host)) {
|
if (!visibleUsers.value.some(u => u.username === user.username && u.host === user.host)) {
|
||||||
visibleUsers.value.push(user);
|
visibleUsers.value.push(user);
|
||||||
|
@ -677,6 +744,7 @@ function saveDraft() {
|
||||||
cw: cw.value,
|
cw: cw.value,
|
||||||
visibility: visibility.value,
|
visibility: visibility.value,
|
||||||
localOnly: localOnly.value,
|
localOnly: localOnly.value,
|
||||||
|
lang: language.value,
|
||||||
files: files.value,
|
files: files.value,
|
||||||
poll: poll.value,
|
poll: poll.value,
|
||||||
},
|
},
|
||||||
|
@ -745,7 +813,7 @@ async function post(ev?: MouseEvent) {
|
||||||
visibility.value = 'home';
|
visibility.value = 'home';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defaultStore.state.warnMissingAltText) {
|
if (defaultStore.state.warnMissingAltText) {
|
||||||
const filesData = toRaw(files.value);
|
const filesData = toRaw(files.value);
|
||||||
|
|
||||||
|
@ -765,7 +833,7 @@ async function post(ev?: MouseEvent) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
if (result === 'cancel') return;
|
if (result === 'cancel') return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -777,6 +845,7 @@ async function post(ev?: MouseEvent) {
|
||||||
channelId: props.channel ? props.channel.id : undefined,
|
channelId: props.channel ? props.channel.id : undefined,
|
||||||
poll: poll.value,
|
poll: poll.value,
|
||||||
cw: useCw.value ? cw.value ?? '' : null,
|
cw: useCw.value ? cw.value ?? '' : null,
|
||||||
|
lang: language.value ?? null,
|
||||||
localOnly: localOnly.value,
|
localOnly: localOnly.value,
|
||||||
visibility: visibility.value,
|
visibility: visibility.value,
|
||||||
visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined,
|
visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined,
|
||||||
|
@ -884,6 +953,17 @@ async function post(ev?: MouseEvent) {
|
||||||
text: err.message + '\n' + (err as any).id,
|
text: err.message + '\n' + (err as any).id,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// update recentlyUsedLanguages
|
||||||
|
if (language.value != null) {
|
||||||
|
const maxLength = 6;
|
||||||
|
const filteredRecentlyUsed = defaultStore.state.recentlyUsedPostLanguages.filter((lang) => {
|
||||||
|
return (lang !== language.value && langs.includes(lang));
|
||||||
|
});
|
||||||
|
const recentlyUsedLangs = [language.value].concat(filteredRecentlyUsed).slice(0, maxLength);
|
||||||
|
|
||||||
|
defaultStore.set('recentlyUsedPostLanguages', recentlyUsedLangs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
|
@ -986,6 +1066,7 @@ onMounted(() => {
|
||||||
useCw.value = draft.data.useCw;
|
useCw.value = draft.data.useCw;
|
||||||
visibility.value = draft.data.visibility;
|
visibility.value = draft.data.visibility;
|
||||||
localOnly.value = draft.data.localOnly;
|
localOnly.value = draft.data.localOnly;
|
||||||
|
language.value = draft.data.lang;
|
||||||
files.value = (draft.data.files || []).filter(draftFile => draftFile);
|
files.value = (draft.data.files || []).filter(draftFile => draftFile);
|
||||||
|
|
||||||
if (draft.data.poll) {
|
if (draft.data.poll) {
|
||||||
|
@ -1011,6 +1092,7 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
visibility.value = init.visibility;
|
visibility.value = init.visibility;
|
||||||
localOnly.value = init.localOnly ?? false;
|
localOnly.value = init.localOnly ?? false;
|
||||||
|
language.value = init.lang;
|
||||||
quoteId.value = init.renote ? init.renote.id : null;
|
quoteId.value = init.renote ? init.renote.id : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ const props = defineProps<{
|
||||||
initialText?: string;
|
initialText?: string;
|
||||||
initialCw?: string;
|
initialCw?: string;
|
||||||
initialVisibility?: (typeof Misskey.noteVisibilities)[number];
|
initialVisibility?: (typeof Misskey.noteVisibilities)[number];
|
||||||
|
initialLanguage?: (typeof Misskey.languages)[number];
|
||||||
initialFiles?: Misskey.entities.DriveFile[];
|
initialFiles?: Misskey.entities.DriveFile[];
|
||||||
initialLocalOnly?: boolean;
|
initialLocalOnly?: boolean;
|
||||||
initialVisibleUsers?: Misskey.entities.UserDetailed[];
|
initialVisibleUsers?: Misskey.entities.UserDetailed[];
|
||||||
|
@ -31,7 +32,7 @@ const props = defineProps<{
|
||||||
instant?: boolean;
|
instant?: boolean;
|
||||||
fixed?: boolean;
|
fixed?: boolean;
|
||||||
autofocus?: boolean;
|
autofocus?: boolean;
|
||||||
editId?: Misskey.entities.Note["id"];
|
editId?: Misskey.entities.Note['id'];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|
|
@ -9,14 +9,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<span v-if="note.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
|
<span v-if="note.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
|
||||||
<span v-if="note.deletedAt" style="opacity: 0.5">({{ i18n.ts.deletedNote }})</span>
|
<span v-if="note.deletedAt" style="opacity: 0.5">({{ i18n.ts.deletedNote }})</span>
|
||||||
<MkA v-if="note.replyId" :class="$style.reply" :to="`/notes/${note.replyId}`" @click.stop><i class="ph-arrow-bend-left-up ph-bold ph-lg"></i></MkA>
|
<MkA v-if="note.replyId" :class="$style.reply" :to="`/notes/${note.replyId}`" @click.stop><i class="ph-arrow-bend-left-up ph-bold ph-lg"></i></MkA>
|
||||||
<Mfm v-if="note.text" :text="note.text" :author="note.user" :nyaize="'respect'" :isAnim="allowAnim" :emojiUrls="note.emojis"/>
|
<Mfm v-if="note.text" :text="note.text" :lang="note.lang" :author="note.user" :nyaize="'respect'" :isAnim="allowAnim" :emojiUrls="note.emojis"/>
|
||||||
<MkButton v-if="!allowAnim && animated && !hideFiles" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
<MkButton v-if="!allowAnim && animated && !hideFiles" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
||||||
<MkButton v-else-if="!defaultStore.state.animatedMfm && allowAnim && animated && !hideFiles" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton>
|
<MkButton v-else-if="!defaultStore.state.animatedMfm && allowAnim && animated && !hideFiles" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton>
|
||||||
<div v-if="note.text && translating || note.text && translation" :class="$style.translation">
|
<div v-if="note.text && translating || note.text && translation" :class="$style.translation">
|
||||||
<MkLoading v-if="translating" mini/>
|
<MkLoading v-if="translating" mini/>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b>
|
<b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b>
|
||||||
<Mfm :text="translation.text" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/>
|
<Mfm :text="translation.text" :lang="nativeLang" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MkA v-if="note.renoteId" :class="$style.rp" :to="`/notes/${note.renoteId}`" @click.stop>RN: ...</MkA>
|
<MkA v-if="note.renoteId" :class="$style.rp" :to="`/notes/${note.renoteId}`" @click.stop>RN: ...</MkA>
|
||||||
|
@ -51,6 +51,7 @@ import { defaultStore } from '@/store.js';
|
||||||
import { useRouter } from '@/router/supplier.js';
|
import { useRouter } from '@/router/supplier.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { checkAnimationFromMfm } from '@/scripts/check-animated-mfm.js';
|
import { checkAnimationFromMfm } from '@/scripts/check-animated-mfm.js';
|
||||||
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note;
|
||||||
|
@ -72,6 +73,7 @@ function noteclick(id: string) {
|
||||||
const parsed = computed(() => props.note.text ? mfm.parse(props.note.text) : null);
|
const parsed = computed(() => props.note.text ? mfm.parse(props.note.text) : null);
|
||||||
const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null);
|
const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null);
|
||||||
let allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false);
|
let allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false);
|
||||||
|
const nativeLang = ref(miLocalStorage.getItem('lang') ?? window.navigator.language);
|
||||||
|
|
||||||
const isLong = defaultStore.state.expandLongNote && !props.hideFiles ? false : shouldCollapsed(props.note, []);
|
const isLong = defaultStore.state.expandLongNote && !props.hideFiles ? false : shouldCollapsed(props.note, []);
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div :class="[{ [$style.clickToOpen]: defaultStore.state.clickToOpen }]" @click.stop="defaultStore.state.clickToOpen ? noteclick(appearNote.id) : undefined">
|
<div :class="[{ [$style.clickToOpen]: defaultStore.state.clickToOpen }]" @click.stop="defaultStore.state.clickToOpen ? noteclick(appearNote.id) : undefined">
|
||||||
<div style="container-type: inline-size;">
|
<div style="container-type: inline-size;">
|
||||||
<p v-if="appearNote.cw != null" :class="$style.cw">
|
<p v-if="appearNote.cw != null" :class="$style.cw">
|
||||||
<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
|
<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :lang="appearNote.lang" :author="appearNote.user" :nyaize="'respect'"/>
|
||||||
<MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll" style="margin: 4px 0;" @click.stop/>
|
<MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll" style="margin: 4px 0;" @click.stop/>
|
||||||
</p>
|
</p>
|
||||||
<div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]">
|
<div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]">
|
||||||
|
@ -67,6 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
v-if="appearNote.text"
|
v-if="appearNote.text"
|
||||||
:parsedNodes="parsed"
|
:parsedNodes="parsed"
|
||||||
:text="appearNote.text"
|
:text="appearNote.text"
|
||||||
|
:lang="appearNote.lang"
|
||||||
:author="appearNote.user"
|
:author="appearNote.user"
|
||||||
:nyaize="'respect'"
|
:nyaize="'respect'"
|
||||||
:emojiUrls="appearNote.emojis"
|
:emojiUrls="appearNote.emojis"
|
||||||
|
@ -78,7 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkLoading v-if="translating" mini/>
|
<MkLoading v-if="translating" mini/>
|
||||||
<div v-else-if="translation">
|
<div v-else-if="translation">
|
||||||
<b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b>
|
<b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b>
|
||||||
<Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/>
|
<Mfm :text="translation.text" :lang="nativeLang" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
||||||
|
@ -218,6 +219,7 @@ import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
|
||||||
import { shouldCollapsed } from '@/scripts/collapsed.js';
|
import { shouldCollapsed } from '@/scripts/collapsed.js';
|
||||||
import { useRouter } from '@/router/supplier.js';
|
import { useRouter } from '@/router/supplier.js';
|
||||||
import { boostMenuItems, type Visibility } from '@/scripts/boost-quote.js';
|
import { boostMenuItems, type Visibility } from '@/scripts/boost-quote.js';
|
||||||
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note;
|
||||||
|
@ -306,11 +308,12 @@ const renoteCollapsed = ref(
|
||||||
defaultStore.state.collapseRenotes && isRenote && (
|
defaultStore.state.collapseRenotes && isRenote && (
|
||||||
($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || // `||` must be `||`! See https://github.com/misskey-dev/misskey/issues/13131
|
($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || // `||` must be `||`! See https://github.com/misskey-dev/misskey/issues/13131
|
||||||
(appearNote.value.myReaction != null)
|
(appearNote.value.myReaction != null)
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
|
const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
|
||||||
const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null);
|
const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null);
|
||||||
const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false);
|
const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false);
|
||||||
|
const nativeLang = ref(miLocalStorage.getItem('lang') ?? window.navigator.language);
|
||||||
|
|
||||||
/* Overload FunctionにLintが対応していないのでコメントアウト
|
/* Overload FunctionにLintが対応していないのでコメントアウト
|
||||||
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: true): boolean;
|
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: true): boolean;
|
||||||
|
@ -417,7 +420,7 @@ function boostVisibility() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renote(visibility: Visibility, localOnly: boolean = false) {
|
function renote(visibility: Visibility, localOnly = false) {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
showMovedDialog();
|
showMovedDialog();
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</header>
|
</header>
|
||||||
<div :class="$style.noteContent">
|
<div :class="$style.noteContent">
|
||||||
<p v-if="appearNote.cw != null" :class="$style.cw">
|
<p v-if="appearNote.cw != null" :class="$style.cw">
|
||||||
<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
|
<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :lang="appearNote.lang" :author="appearNote.user" :nyaize="'respect'"/>
|
||||||
<MkCwButton v-model="showContent" :text="appearNote.text" :files="appearNote.files" :poll="appearNote.poll"/>
|
<MkCwButton v-model="showContent" :text="appearNote.text" :files="appearNote.files" :poll="appearNote.poll"/>
|
||||||
</p>
|
</p>
|
||||||
<div v-show="appearNote.cw == null || showContent">
|
<div v-show="appearNote.cw == null || showContent">
|
||||||
|
@ -86,6 +86,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
v-if="appearNote.text"
|
v-if="appearNote.text"
|
||||||
:parsedNodes="parsed"
|
:parsedNodes="parsed"
|
||||||
:text="appearNote.text"
|
:text="appearNote.text"
|
||||||
|
:lang="appearNote.lang"
|
||||||
:author="appearNote.user"
|
:author="appearNote.user"
|
||||||
:nyaize="'respect'"
|
:nyaize="'respect'"
|
||||||
:emojiUrls="appearNote.emojis"
|
:emojiUrls="appearNote.emojis"
|
||||||
|
@ -98,7 +99,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkLoading v-if="translating" mini/>
|
<MkLoading v-if="translating" mini/>
|
||||||
<div v-else-if="translation">
|
<div v-else-if="translation">
|
||||||
<b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b>
|
<b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b>
|
||||||
<Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/>
|
<Mfm :text="translation.text" :lang="nativeLang" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
|
||||||
|
@ -266,6 +267,7 @@ import MkPagination, { type Paging } from '@/components/MkPagination.vue';
|
||||||
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { boostMenuItems, type Visibility } from '@/scripts/boost-quote.js';
|
import { boostMenuItems, type Visibility } from '@/scripts/boost-quote.js';
|
||||||
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note;
|
||||||
|
@ -330,6 +332,7 @@ const replies = ref<Misskey.entities.Note[]>([]);
|
||||||
const quotes = ref<Misskey.entities.Note[]>([]);
|
const quotes = ref<Misskey.entities.Note[]>([]);
|
||||||
const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i?.id));
|
const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i?.id));
|
||||||
const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
|
const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
|
||||||
|
const nativeLang = ref(miLocalStorage.getItem('lang') ?? window.navigator.language);
|
||||||
|
|
||||||
watch(() => props.expandAllCws, (expandAllCws) => {
|
watch(() => props.expandAllCws, (expandAllCws) => {
|
||||||
if (expandAllCws !== showContent.value) showContent.value = expandAllCws;
|
if (expandAllCws !== showContent.value) showContent.value = expandAllCws;
|
||||||
|
@ -447,7 +450,7 @@ function boostVisibility() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renote(visibility: Visibility, localOnly: boolean = false) {
|
function renote(visibility: Visibility, localOnly = false) {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
showMovedDialog();
|
showMovedDialog();
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<SkNoteHeader :class="$style.header" :note="note" :classic="true" :mini="true"/>
|
<SkNoteHeader :class="$style.header" :note="note" :classic="true" :mini="true"/>
|
||||||
<div :class="$style.content">
|
<div :class="$style.content">
|
||||||
<p v-if="note.cw != null" :class="$style.cw">
|
<p v-if="note.cw != null" :class="$style.cw">
|
||||||
<Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'respect'"/>
|
<Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :lang="note.lang" :author="note.user" :nyaize="'respect'"/>
|
||||||
<MkCwButton v-model="showContent" :text="note.text" :files="note.files" :poll="note.poll"/>
|
<MkCwButton v-model="showContent" :text="note.text" :files="note.files" :poll="note.poll"/>
|
||||||
</p>
|
</p>
|
||||||
<div v-show="note.cw == null || showContent">
|
<div v-show="note.cw == null || showContent">
|
||||||
|
@ -295,7 +295,7 @@ function boostVisibility() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renote(visibility: Visibility, localOnly: boolean = false) {
|
function renote(visibility: Visibility, localOnly = false) {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
showMovedDialog();
|
showMovedDialog();
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ type MfmProps = {
|
||||||
enableEmojiMenu?: boolean;
|
enableEmojiMenu?: boolean;
|
||||||
enableEmojiMenuReaction?: boolean;
|
enableEmojiMenuReaction?: boolean;
|
||||||
isAnim?: boolean;
|
isAnim?: boolean;
|
||||||
|
lang?: Misskey.entities.Note['lang'];
|
||||||
};
|
};
|
||||||
|
|
||||||
type MfmEvents = {
|
type MfmEvents = {
|
||||||
|
@ -475,5 +476,6 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
|
||||||
return h('span', {
|
return h('span', {
|
||||||
// https://codeday.me/jp/qa/20190424/690106.html
|
// https://codeday.me/jp/qa/20190424/690106.html
|
||||||
style: props.nowrap ? 'white-space: pre; word-wrap: normal; overflow: hidden; text-overflow: ellipsis;' : 'white-space: pre-wrap;',
|
style: props.nowrap ? 'white-space: pre; word-wrap: normal; overflow: hidden; text-overflow: ellipsis;' : 'white-space: pre-wrap;',
|
||||||
|
lang: props.lang ?? undefined,
|
||||||
}, genEl(rootAst, props.rootScale ?? 1));
|
}, genEl(rootAst, props.rootScale ?? 1));
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,9 @@ export const langmap = {
|
||||||
'ar-SA': {
|
'ar-SA': {
|
||||||
nativeName: 'العربية (السعودية)',
|
nativeName: 'العربية (السعودية)',
|
||||||
},
|
},
|
||||||
|
'ay': {
|
||||||
|
nativeName: 'Aymar aru',
|
||||||
|
},
|
||||||
'ay-BO': {
|
'ay-BO': {
|
||||||
nativeName: 'Aymar aru',
|
nativeName: 'Aymar aru',
|
||||||
},
|
},
|
||||||
|
@ -44,6 +47,9 @@ export const langmap = {
|
||||||
'az-AZ': {
|
'az-AZ': {
|
||||||
nativeName: 'Azərbaycan dili',
|
nativeName: 'Azərbaycan dili',
|
||||||
},
|
},
|
||||||
|
'be': {
|
||||||
|
nativeName: 'Беларуская',
|
||||||
|
},
|
||||||
'be-BY': {
|
'be-BY': {
|
||||||
nativeName: 'Беларуская',
|
nativeName: 'Беларуская',
|
||||||
},
|
},
|
||||||
|
@ -65,6 +71,9 @@ export const langmap = {
|
||||||
'br': {
|
'br': {
|
||||||
nativeName: 'Brezhoneg',
|
nativeName: 'Brezhoneg',
|
||||||
},
|
},
|
||||||
|
'bs': {
|
||||||
|
nativeName: 'Bosanski',
|
||||||
|
},
|
||||||
'bs-BA': {
|
'bs-BA': {
|
||||||
nativeName: 'Bosanski',
|
nativeName: 'Bosanski',
|
||||||
},
|
},
|
||||||
|
@ -77,7 +86,7 @@ export const langmap = {
|
||||||
'cak': {
|
'cak': {
|
||||||
nativeName: 'Maya Kaqchikel',
|
nativeName: 'Maya Kaqchikel',
|
||||||
},
|
},
|
||||||
'ck-US': {
|
'chr': {
|
||||||
nativeName: 'ᏣᎳᎩ (tsalagi)',
|
nativeName: 'ᏣᎳᎩ (tsalagi)',
|
||||||
},
|
},
|
||||||
'cs': {
|
'cs': {
|
||||||
|
@ -152,9 +161,6 @@ export const langmap = {
|
||||||
'en-ZA': {
|
'en-ZA': {
|
||||||
nativeName: 'English (South Africa)',
|
nativeName: 'English (South Africa)',
|
||||||
},
|
},
|
||||||
'en@pirate': {
|
|
||||||
nativeName: 'English (Pirate)',
|
|
||||||
},
|
|
||||||
'eo': {
|
'eo': {
|
||||||
nativeName: 'Esperanto',
|
nativeName: 'Esperanto',
|
||||||
},
|
},
|
||||||
|
@ -248,6 +254,9 @@ export const langmap = {
|
||||||
'fr-CH': {
|
'fr-CH': {
|
||||||
nativeName: 'Français (Suisse)',
|
nativeName: 'Français (Suisse)',
|
||||||
},
|
},
|
||||||
|
'fy': {
|
||||||
|
nativeName: 'Frysk',
|
||||||
|
},
|
||||||
'fy-NL': {
|
'fy-NL': {
|
||||||
nativeName: 'Frysk',
|
nativeName: 'Frysk',
|
||||||
},
|
},
|
||||||
|
@ -266,16 +275,22 @@ export const langmap = {
|
||||||
'gl-ES': {
|
'gl-ES': {
|
||||||
nativeName: 'Galego',
|
nativeName: 'Galego',
|
||||||
},
|
},
|
||||||
|
'gn': {
|
||||||
|
nativeName: 'Avañe\'ẽ',
|
||||||
|
},
|
||||||
'gn-PY': {
|
'gn-PY': {
|
||||||
nativeName: 'Avañe\'ẽ',
|
nativeName: 'Avañe\'ẽ',
|
||||||
},
|
},
|
||||||
|
'gu': {
|
||||||
|
nativeName: 'ગુજરાતી',
|
||||||
|
},
|
||||||
'gu-IN': {
|
'gu-IN': {
|
||||||
nativeName: 'ગુજરાતી',
|
nativeName: 'ગુજરાતી',
|
||||||
},
|
},
|
||||||
'gv': {
|
'gv': {
|
||||||
nativeName: 'Gaelg',
|
nativeName: 'Gaelg',
|
||||||
},
|
},
|
||||||
'gx-GR': {
|
'grc': {
|
||||||
nativeName: 'Ἑλληνική ἀρχαία',
|
nativeName: 'Ἑλληνική ἀρχαία',
|
||||||
},
|
},
|
||||||
'he': {
|
'he': {
|
||||||
|
@ -338,12 +353,21 @@ export const langmap = {
|
||||||
'ja-JP': {
|
'ja-JP': {
|
||||||
nativeName: '日本語 (日本)',
|
nativeName: '日本語 (日本)',
|
||||||
},
|
},
|
||||||
|
'jv': {
|
||||||
|
nativeName: 'Basa Jawa',
|
||||||
|
},
|
||||||
'jv-ID': {
|
'jv-ID': {
|
||||||
nativeName: 'Basa Jawa',
|
nativeName: 'Basa Jawa',
|
||||||
},
|
},
|
||||||
|
'ka': {
|
||||||
|
nativeName: 'ქართული',
|
||||||
|
},
|
||||||
'ka-GE': {
|
'ka-GE': {
|
||||||
nativeName: 'ქართული',
|
nativeName: 'ქართული',
|
||||||
},
|
},
|
||||||
|
'kk': {
|
||||||
|
nativeName: 'Қазақша',
|
||||||
|
},
|
||||||
'kk-KZ': {
|
'kk-KZ': {
|
||||||
nativeName: 'Қазақша',
|
nativeName: 'Қазақша',
|
||||||
},
|
},
|
||||||
|
@ -371,6 +395,9 @@ export const langmap = {
|
||||||
'ko-KR': {
|
'ko-KR': {
|
||||||
nativeName: '한국어 (한국)',
|
nativeName: '한국어 (한국)',
|
||||||
},
|
},
|
||||||
|
'ku': {
|
||||||
|
nativeName: 'Kurdî',
|
||||||
|
},
|
||||||
'ku-TR': {
|
'ku-TR': {
|
||||||
nativeName: 'Kurdî',
|
nativeName: 'Kurdî',
|
||||||
},
|
},
|
||||||
|
@ -386,6 +413,9 @@ export const langmap = {
|
||||||
'lb': {
|
'lb': {
|
||||||
nativeName: 'Lëtzebuergesch',
|
nativeName: 'Lëtzebuergesch',
|
||||||
},
|
},
|
||||||
|
'li': {
|
||||||
|
nativeName: 'Lèmbörgs',
|
||||||
|
},
|
||||||
'li-NL': {
|
'li-NL': {
|
||||||
nativeName: 'Lèmbörgs',
|
nativeName: 'Lèmbörgs',
|
||||||
},
|
},
|
||||||
|
@ -404,6 +434,9 @@ export const langmap = {
|
||||||
'mai': {
|
'mai': {
|
||||||
nativeName: 'मैथिली, মৈথিলী',
|
nativeName: 'मैथिली, মৈথিলী',
|
||||||
},
|
},
|
||||||
|
'mg': {
|
||||||
|
nativeName: 'Malagasy',
|
||||||
|
},
|
||||||
'mg-MG': {
|
'mg-MG': {
|
||||||
nativeName: 'Malagasy',
|
nativeName: 'Malagasy',
|
||||||
},
|
},
|
||||||
|
@ -419,6 +452,9 @@ export const langmap = {
|
||||||
'ml-IN': {
|
'ml-IN': {
|
||||||
nativeName: 'മലയാളം',
|
nativeName: 'മലയാളം',
|
||||||
},
|
},
|
||||||
|
'mn': {
|
||||||
|
nativeName: 'Монгол',
|
||||||
|
},
|
||||||
'mn-MN': {
|
'mn-MN': {
|
||||||
nativeName: 'Монгол',
|
nativeName: 'Монгол',
|
||||||
},
|
},
|
||||||
|
@ -443,6 +479,9 @@ export const langmap = {
|
||||||
'my': {
|
'my': {
|
||||||
nativeName: 'ဗမာစကာ',
|
nativeName: 'ဗမာစကာ',
|
||||||
},
|
},
|
||||||
|
'nan': {
|
||||||
|
nativeName: '閩南語',
|
||||||
|
},
|
||||||
'no': {
|
'no': {
|
||||||
nativeName: 'Norsk',
|
nativeName: 'Norsk',
|
||||||
},
|
},
|
||||||
|
@ -467,12 +506,18 @@ export const langmap = {
|
||||||
'nl-NL': {
|
'nl-NL': {
|
||||||
nativeName: 'Nederlands (Nederland)',
|
nativeName: 'Nederlands (Nederland)',
|
||||||
},
|
},
|
||||||
|
'nn': {
|
||||||
|
nativeName: 'Norsk (nynorsk)',
|
||||||
|
},
|
||||||
'nn-NO': {
|
'nn-NO': {
|
||||||
nativeName: 'Norsk (nynorsk)',
|
nativeName: 'Norsk (nynorsk)',
|
||||||
},
|
},
|
||||||
'oc': {
|
'oc': {
|
||||||
nativeName: 'Occitan',
|
nativeName: 'Occitan',
|
||||||
},
|
},
|
||||||
|
'or': {
|
||||||
|
nativeName: 'ଓଡ଼ିଆ',
|
||||||
|
},
|
||||||
'or-IN': {
|
'or-IN': {
|
||||||
nativeName: 'ଓଡ଼ିଆ',
|
nativeName: 'ଓଡ଼ିଆ',
|
||||||
},
|
},
|
||||||
|
@ -488,6 +533,9 @@ export const langmap = {
|
||||||
'pl-PL': {
|
'pl-PL': {
|
||||||
nativeName: 'Polski',
|
nativeName: 'Polski',
|
||||||
},
|
},
|
||||||
|
'ps': {
|
||||||
|
nativeName: 'پښتو',
|
||||||
|
},
|
||||||
'ps-AF': {
|
'ps-AF': {
|
||||||
nativeName: 'پښتو',
|
nativeName: 'پښتو',
|
||||||
},
|
},
|
||||||
|
@ -500,9 +548,15 @@ export const langmap = {
|
||||||
'pt-PT': {
|
'pt-PT': {
|
||||||
nativeName: 'Português (Portugal)',
|
nativeName: 'Português (Portugal)',
|
||||||
},
|
},
|
||||||
|
'qu': {
|
||||||
|
nativeName: 'Qhichwa',
|
||||||
|
},
|
||||||
'qu-PE': {
|
'qu-PE': {
|
||||||
nativeName: 'Qhichwa',
|
nativeName: 'Qhichwa',
|
||||||
},
|
},
|
||||||
|
'rm': {
|
||||||
|
nativeName: 'Rumantsch',
|
||||||
|
},
|
||||||
'rm-CH': {
|
'rm-CH': {
|
||||||
nativeName: 'Rumantsch',
|
nativeName: 'Rumantsch',
|
||||||
},
|
},
|
||||||
|
@ -518,15 +572,24 @@ export const langmap = {
|
||||||
'ru-RU': {
|
'ru-RU': {
|
||||||
nativeName: 'Русский',
|
nativeName: 'Русский',
|
||||||
},
|
},
|
||||||
|
'sa': {
|
||||||
|
nativeName: 'संस्कृतम्',
|
||||||
|
},
|
||||||
'sa-IN': {
|
'sa-IN': {
|
||||||
nativeName: 'संस्कृतम्',
|
nativeName: 'संस्कृतम्',
|
||||||
},
|
},
|
||||||
|
'se': {
|
||||||
|
nativeName: 'Davvisámegiella',
|
||||||
|
},
|
||||||
'se-NO': {
|
'se-NO': {
|
||||||
nativeName: 'Davvisámegiella',
|
nativeName: 'Davvisámegiella',
|
||||||
},
|
},
|
||||||
'sh': {
|
'sh': {
|
||||||
nativeName: 'српскохрватски',
|
nativeName: 'српскохрватски',
|
||||||
},
|
},
|
||||||
|
'si': {
|
||||||
|
nativeName: 'සිංහල',
|
||||||
|
},
|
||||||
'si-LK': {
|
'si-LK': {
|
||||||
nativeName: 'සිංහල',
|
nativeName: 'සිංහල',
|
||||||
},
|
},
|
||||||
|
@ -542,6 +605,9 @@ export const langmap = {
|
||||||
'sl-SI': {
|
'sl-SI': {
|
||||||
nativeName: 'Slovenščina',
|
nativeName: 'Slovenščina',
|
||||||
},
|
},
|
||||||
|
'so': {
|
||||||
|
nativeName: 'Soomaaliga',
|
||||||
|
},
|
||||||
'so-SO': {
|
'so-SO': {
|
||||||
nativeName: 'Soomaaliga',
|
nativeName: 'Soomaaliga',
|
||||||
},
|
},
|
||||||
|
@ -602,12 +668,18 @@ export const langmap = {
|
||||||
'tlh': {
|
'tlh': {
|
||||||
nativeName: 'tlhIngan-Hol',
|
nativeName: 'tlhIngan-Hol',
|
||||||
},
|
},
|
||||||
|
'tok': {
|
||||||
|
nativeName: 'Toki Pona',
|
||||||
|
},
|
||||||
'tr': {
|
'tr': {
|
||||||
nativeName: 'Türkçe',
|
nativeName: 'Türkçe',
|
||||||
},
|
},
|
||||||
'tr-TR': {
|
'tr-TR': {
|
||||||
nativeName: 'Türkçe',
|
nativeName: 'Türkçe',
|
||||||
},
|
},
|
||||||
|
'tt': {
|
||||||
|
nativeName: 'татарча',
|
||||||
|
},
|
||||||
'tt-RU': {
|
'tt-RU': {
|
||||||
nativeName: 'татарча',
|
nativeName: 'татарча',
|
||||||
},
|
},
|
||||||
|
@ -635,6 +707,9 @@ export const langmap = {
|
||||||
'vi-VN': {
|
'vi-VN': {
|
||||||
nativeName: 'Tiếng Việt',
|
nativeName: 'Tiếng Việt',
|
||||||
},
|
},
|
||||||
|
'xh': {
|
||||||
|
nativeName: 'isiXhosa',
|
||||||
|
},
|
||||||
'xh-ZA': {
|
'xh-ZA': {
|
||||||
nativeName: 'isiXhosa',
|
nativeName: 'isiXhosa',
|
||||||
},
|
},
|
||||||
|
@ -644,6 +719,9 @@ export const langmap = {
|
||||||
'yi-DE': {
|
'yi-DE': {
|
||||||
nativeName: 'ייִדיש (German)',
|
nativeName: 'ייִדיש (German)',
|
||||||
},
|
},
|
||||||
|
'yue': {
|
||||||
|
nativeName: '粵語',
|
||||||
|
},
|
||||||
'zh': {
|
'zh': {
|
||||||
nativeName: '中文',
|
nativeName: '中文',
|
||||||
},
|
},
|
||||||
|
@ -665,7 +743,16 @@ export const langmap = {
|
||||||
'zh-TW': {
|
'zh-TW': {
|
||||||
nativeName: '中文(台灣)',
|
nativeName: '中文(台灣)',
|
||||||
},
|
},
|
||||||
|
'zu': {
|
||||||
|
nativeName: 'isiZulu',
|
||||||
|
},
|
||||||
'zu-ZA': {
|
'zu-ZA': {
|
||||||
nativeName: 'isiZulu',
|
nativeName: 'isiZulu',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const langs: string[] = [
|
||||||
|
...(Object.keys(langmap).filter(tag => tag.indexOf('-') < 0)),
|
||||||
|
'zh-Hans',
|
||||||
|
'zh-Hant',
|
||||||
|
];
|
||||||
|
|
|
@ -529,6 +529,10 @@ export const defaultStore = markRaw(new Storage('base', {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: { type: 'syuilo/bubble2', volume: 1 } as SoundStore,
|
default: { type: 'syuilo/bubble2', volume: 1 } as SoundStore,
|
||||||
},
|
},
|
||||||
|
recentlyUsedPostLanguages: {
|
||||||
|
where: 'account',
|
||||||
|
default: [] as string[],
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// TODO: 他のタブと永続化されたstateを同期
|
// TODO: 他のタブと永続化されたstateを同期
|
||||||
|
|
|
@ -2278,6 +2278,9 @@ type IWebhooksShowResponse = operations['i/webhooks/show']['responses']['200']['
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type IWebhooksUpdateRequest = operations['i/webhooks/update']['requestBody']['content']['application/json'];
|
type IWebhooksUpdateRequest = operations['i/webhooks/update']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
export const languages: readonly ["ach", "ady", "af", "ak", "ar", "az", "bg", "bn", "br", "ca", "cak", "cs", "cy", "da", "de", "dsb", "el", "en", "eo", "es", "et", "eu", "fa", "ff", "fi", "fo", "fr", "ga", "gd", "gl", "gv", "he", "hi", "hr", "hsb", "ht", "hu", "hy", "id", "is", "it", "ja", "km", "kl", "kab", "kn", "ko", "kw", "la", "lb", "lt", "lv", "mai", "mk", "ml", "mr", "ms", "mt", "my", "no", "nb", "ne", "nl", "oc", "pa", "pl", "pt", "ro", "ru", "sh", "sk", "sl", "sq", "sr", "su", "sv", "sw", "ta", "te", "tg", "th", "fil", "tlh", "tr", "uk", "ur", "uz", "vi", "yi", "zh"];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type MeDetailed = components['schemas']['MeDetailed'];
|
type MeDetailed = components['schemas']['MeDetailed'];
|
||||||
|
|
||||||
|
|
|
@ -4061,6 +4061,8 @@ export type components = {
|
||||||
/** Format: date-time */
|
/** Format: date-time */
|
||||||
deletedAt?: string | null;
|
deletedAt?: string | null;
|
||||||
text: string | null;
|
text: string | null;
|
||||||
|
/** @enum {string|null} */
|
||||||
|
lang: 'ach' | 'ady' | 'af' | 'ak' | 'ar' | 'ay' | 'az' | 'be' | 'bg' | 'bn' | 'br' | 'bs' | 'ca' | 'cak' | 'chr' | 'cs' | 'cy' | 'da' | 'de' | 'dsb' | 'el' | 'en' | 'eo' | 'es' | 'et' | 'eu' | 'fa' | 'ff' | 'fi' | 'fo' | 'fr' | 'fy' | 'ga' | 'gd' | 'gl' | 'gn' | 'gu' | 'gv' | 'grc' | 'he' | 'hi' | 'hr' | 'hsb' | 'ht' | 'hu' | 'hy' | 'id' | 'is' | 'it' | 'ja' | 'jv' | 'ka' | 'kk' | 'km' | 'kl' | 'kab' | 'kn' | 'ko' | 'ku' | 'kw' | 'la' | 'lb' | 'li' | 'lt' | 'lv' | 'mai' | 'mg' | 'mk' | 'ml' | 'mn' | 'mr' | 'ms' | 'mt' | 'my' | 'nan' | 'no' | 'nb' | 'ne' | 'nl' | 'nn' | 'oc' | 'or' | 'pa' | 'pl' | 'ps' | 'pt' | 'qu' | 'rm' | 'ro' | 'ru' | 'sa' | 'se' | 'sh' | 'si' | 'sk' | 'sl' | 'so' | 'sq' | 'sr' | 'su' | 'sv' | 'sw' | 'ta' | 'te' | 'tg' | 'th' | 'fil' | 'tlh' | 'tok' | 'tr' | 'tt' | 'uk' | 'ur' | 'uz' | 'vi' | 'xh' | 'yi' | 'yue' | 'zh' | 'zu' | 'zh-Hans' | 'zh-Hant';
|
||||||
cw?: string | null;
|
cw?: string | null;
|
||||||
/** Format: id */
|
/** Format: id */
|
||||||
userId: string;
|
userId: string;
|
||||||
|
@ -19347,7 +19349,7 @@ export type operations = {
|
||||||
birthday?: string | null;
|
birthday?: string | null;
|
||||||
listenbrainz?: string | null;
|
listenbrainz?: string | null;
|
||||||
/** @enum {string|null} */
|
/** @enum {string|null} */
|
||||||
lang?: null | 'ach' | 'ady' | 'af' | 'af-NA' | 'af-ZA' | 'ak' | 'ar' | 'ar-AR' | 'ar-MA' | 'ar-SA' | 'ay-BO' | 'az' | 'az-AZ' | 'be-BY' | 'bg' | 'bg-BG' | 'bn' | 'bn-IN' | 'bn-BD' | 'br' | 'bs-BA' | 'ca' | 'ca-ES' | 'cak' | 'ck-US' | 'cs' | 'cs-CZ' | 'cy' | 'cy-GB' | 'da' | 'da-DK' | 'de' | 'de-AT' | 'de-DE' | 'de-CH' | 'dsb' | 'el' | 'el-GR' | 'en' | 'en-GB' | 'en-AU' | 'en-CA' | 'en-IE' | 'en-IN' | 'en-PI' | 'en-SG' | 'en-UD' | 'en-US' | 'en-ZA' | 'en@pirate' | 'eo' | 'eo-EO' | 'es' | 'es-AR' | 'es-419' | 'es-CL' | 'es-CO' | 'es-EC' | 'es-ES' | 'es-LA' | 'es-NI' | 'es-MX' | 'es-US' | 'es-VE' | 'et' | 'et-EE' | 'eu' | 'eu-ES' | 'fa' | 'fa-IR' | 'fb-LT' | 'ff' | 'fi' | 'fi-FI' | 'fo' | 'fo-FO' | 'fr' | 'fr-CA' | 'fr-FR' | 'fr-BE' | 'fr-CH' | 'fy-NL' | 'ga' | 'ga-IE' | 'gd' | 'gl' | 'gl-ES' | 'gn-PY' | 'gu-IN' | 'gv' | 'gx-GR' | 'he' | 'he-IL' | 'hi' | 'hi-IN' | 'hr' | 'hr-HR' | 'hsb' | 'ht' | 'hu' | 'hu-HU' | 'hy' | 'hy-AM' | 'id' | 'id-ID' | 'is' | 'is-IS' | 'it' | 'it-IT' | 'ja' | 'ja-JP' | 'jv-ID' | 'ka-GE' | 'kk-KZ' | 'km' | 'kl' | 'km-KH' | 'kab' | 'kn' | 'kn-IN' | 'ko' | 'ko-KR' | 'ku-TR' | 'kw' | 'la' | 'la-VA' | 'lb' | 'li-NL' | 'lt' | 'lt-LT' | 'lv' | 'lv-LV' | 'mai' | 'mg-MG' | 'mk' | 'mk-MK' | 'ml' | 'ml-IN' | 'mn-MN' | 'mr' | 'mr-IN' | 'ms' | 'ms-MY' | 'mt' | 'mt-MT' | 'my' | 'no' | 'nb' | 'nb-NO' | 'ne' | 'ne-NP' | 'nl' | 'nl-BE' | 'nl-NL' | 'nn-NO' | 'oc' | 'or-IN' | 'pa' | 'pa-IN' | 'pl' | 'pl-PL' | 'ps-AF' | 'pt' | 'pt-BR' | 'pt-PT' | 'qu-PE' | 'rm-CH' | 'ro' | 'ro-RO' | 'ru' | 'ru-RU' | 'sa-IN' | 'se-NO' | 'sh' | 'si-LK' | 'sk' | 'sk-SK' | 'sl' | 'sl-SI' | 'so-SO' | 'sq' | 'sq-AL' | 'sr' | 'sr-RS' | 'su' | 'sv' | 'sv-SE' | 'sw' | 'sw-KE' | 'ta' | 'ta-IN' | 'te' | 'te-IN' | 'tg' | 'tg-TJ' | 'th' | 'th-TH' | 'fil' | 'tlh' | 'tr' | 'tr-TR' | 'tt-RU' | 'uk' | 'uk-UA' | 'ur' | 'ur-PK' | 'uz' | 'uz-UZ' | 'vi' | 'vi-VN' | 'xh-ZA' | 'yi' | 'yi-DE' | 'zh' | 'zh-Hans' | 'zh-Hant' | 'zh-CN' | 'zh-HK' | 'zh-SG' | 'zh-TW' | 'zu-ZA';
|
lang?: null | 'ach' | 'ady' | 'af' | 'ak' | 'ar' | 'ay' | 'az' | 'be' | 'bg' | 'bn' | 'br' | 'bs' | 'ca' | 'cak' | 'chr' | 'cs' | 'cy' | 'da' | 'de' | 'dsb' | 'el' | 'en' | 'eo' | 'es' | 'et' | 'eu' | 'fa' | 'ff' | 'fi' | 'fo' | 'fr' | 'fy' | 'ga' | 'gd' | 'gl' | 'gn' | 'gu' | 'gv' | 'grc' | 'he' | 'hi' | 'hr' | 'hsb' | 'ht' | 'hu' | 'hy' | 'id' | 'is' | 'it' | 'ja' | 'jv' | 'ka' | 'kk' | 'km' | 'kl' | 'kab' | 'kn' | 'ko' | 'ku' | 'kw' | 'la' | 'lb' | 'li' | 'lt' | 'lv' | 'mai' | 'mg' | 'mk' | 'ml' | 'mn' | 'mr' | 'ms' | 'mt' | 'my' | 'nan' | 'no' | 'nb' | 'ne' | 'nl' | 'nn' | 'oc' | 'or' | 'pa' | 'pl' | 'ps' | 'pt' | 'qu' | 'rm' | 'ro' | 'ru' | 'sa' | 'se' | 'sh' | 'si' | 'sk' | 'sl' | 'so' | 'sq' | 'sr' | 'su' | 'sv' | 'sw' | 'ta' | 'te' | 'tg' | 'th' | 'fil' | 'tlh' | 'tok' | 'tr' | 'tt' | 'uk' | 'ur' | 'uz' | 'vi' | 'xh' | 'yi' | 'yue' | 'zh' | 'zu' | 'zh-Hans' | 'zh-Hant';
|
||||||
/** Format: misskey:id */
|
/** Format: misskey:id */
|
||||||
avatarId?: string | null;
|
avatarId?: string | null;
|
||||||
avatarDecorations?: ({
|
avatarDecorations?: ({
|
||||||
|
@ -21002,6 +21004,8 @@ export type operations = {
|
||||||
*/
|
*/
|
||||||
visibility?: 'public' | 'home' | 'followers' | 'specified';
|
visibility?: 'public' | 'home' | 'followers' | 'specified';
|
||||||
visibleUserIds?: string[];
|
visibleUserIds?: string[];
|
||||||
|
/** @enum {string|null} */
|
||||||
|
lang?: 'ach' | 'ady' | 'af' | 'ak' | 'ar' | 'ay' | 'az' | 'be' | 'bg' | 'bn' | 'br' | 'bs' | 'ca' | 'cak' | 'chr' | 'cs' | 'cy' | 'da' | 'de' | 'dsb' | 'el' | 'en' | 'eo' | 'es' | 'et' | 'eu' | 'fa' | 'ff' | 'fi' | 'fo' | 'fr' | 'fy' | 'ga' | 'gd' | 'gl' | 'gn' | 'gu' | 'gv' | 'grc' | 'he' | 'hi' | 'hr' | 'hsb' | 'ht' | 'hu' | 'hy' | 'id' | 'is' | 'it' | 'ja' | 'jv' | 'ka' | 'kk' | 'km' | 'kl' | 'kab' | 'kn' | 'ko' | 'ku' | 'kw' | 'la' | 'lb' | 'li' | 'lt' | 'lv' | 'mai' | 'mg' | 'mk' | 'ml' | 'mn' | 'mr' | 'ms' | 'mt' | 'my' | 'nan' | 'no' | 'nb' | 'ne' | 'nl' | 'nn' | 'oc' | 'or' | 'pa' | 'pl' | 'ps' | 'pt' | 'qu' | 'rm' | 'ro' | 'ru' | 'sa' | 'se' | 'sh' | 'si' | 'sk' | 'sl' | 'so' | 'sq' | 'sr' | 'su' | 'sv' | 'sw' | 'ta' | 'te' | 'tg' | 'th' | 'fil' | 'tlh' | 'tok' | 'tr' | 'tt' | 'uk' | 'ur' | 'uz' | 'vi' | 'xh' | 'yi' | 'yue' | 'zh' | 'zu' | 'zh-Hans' | 'zh-Hant';
|
||||||
cw?: string | null;
|
cw?: string | null;
|
||||||
/** @default false */
|
/** @default false */
|
||||||
localOnly?: boolean;
|
localOnly?: boolean;
|
||||||
|
@ -22759,6 +22763,8 @@ export type operations = {
|
||||||
*/
|
*/
|
||||||
visibility?: 'public' | 'home' | 'followers' | 'specified';
|
visibility?: 'public' | 'home' | 'followers' | 'specified';
|
||||||
visibleUserIds?: string[];
|
visibleUserIds?: string[];
|
||||||
|
/** @enum {string|null} */
|
||||||
|
lang?: 'ach' | 'ady' | 'af' | 'ak' | 'ar' | 'ay' | 'az' | 'be' | 'bg' | 'bn' | 'br' | 'bs' | 'ca' | 'cak' | 'chr' | 'cs' | 'cy' | 'da' | 'de' | 'dsb' | 'el' | 'en' | 'eo' | 'es' | 'et' | 'eu' | 'fa' | 'ff' | 'fi' | 'fo' | 'fr' | 'fy' | 'ga' | 'gd' | 'gl' | 'gn' | 'gu' | 'gv' | 'grc' | 'he' | 'hi' | 'hr' | 'hsb' | 'ht' | 'hu' | 'hy' | 'id' | 'is' | 'it' | 'ja' | 'jv' | 'ka' | 'kk' | 'km' | 'kl' | 'kab' | 'kn' | 'ko' | 'ku' | 'kw' | 'la' | 'lb' | 'li' | 'lt' | 'lv' | 'mai' | 'mg' | 'mk' | 'ml' | 'mn' | 'mr' | 'ms' | 'mt' | 'my' | 'nan' | 'no' | 'nb' | 'ne' | 'nl' | 'nn' | 'oc' | 'or' | 'pa' | 'pl' | 'ps' | 'pt' | 'qu' | 'rm' | 'ro' | 'ru' | 'sa' | 'se' | 'sh' | 'si' | 'sk' | 'sl' | 'so' | 'sq' | 'sr' | 'su' | 'sv' | 'sw' | 'ta' | 'te' | 'tg' | 'th' | 'fil' | 'tlh' | 'tok' | 'tr' | 'tt' | 'uk' | 'ur' | 'uz' | 'vi' | 'xh' | 'yi' | 'yue' | 'zh' | 'zu' | 'zh-Hans' | 'zh-Hant';
|
||||||
cw?: string | null;
|
cw?: string | null;
|
||||||
/** @default false */
|
/** @default false */
|
||||||
localOnly?: boolean;
|
localOnly?: boolean;
|
||||||
|
|
|
@ -338,3 +338,96 @@ export type ModerationLogPayloads = {
|
||||||
fileId: string;
|
fileId: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const languages = [
|
||||||
|
'ach',
|
||||||
|
'ady',
|
||||||
|
'af',
|
||||||
|
'ak',
|
||||||
|
'ar',
|
||||||
|
'az',
|
||||||
|
'bg',
|
||||||
|
'bn',
|
||||||
|
'br',
|
||||||
|
'ca',
|
||||||
|
'cak',
|
||||||
|
'cs',
|
||||||
|
'cy',
|
||||||
|
'da',
|
||||||
|
'de',
|
||||||
|
'dsb',
|
||||||
|
'el',
|
||||||
|
'en',
|
||||||
|
'eo',
|
||||||
|
'es',
|
||||||
|
'et',
|
||||||
|
'eu',
|
||||||
|
'fa',
|
||||||
|
'ff',
|
||||||
|
'fi',
|
||||||
|
'fo',
|
||||||
|
'fr',
|
||||||
|
'ga',
|
||||||
|
'gd',
|
||||||
|
'gl',
|
||||||
|
'gv',
|
||||||
|
'he',
|
||||||
|
'hi',
|
||||||
|
'hr',
|
||||||
|
'hsb',
|
||||||
|
'ht',
|
||||||
|
'hu',
|
||||||
|
'hy',
|
||||||
|
'id',
|
||||||
|
'is',
|
||||||
|
'it',
|
||||||
|
'ja',
|
||||||
|
'km',
|
||||||
|
'kl',
|
||||||
|
'kab',
|
||||||
|
'kn',
|
||||||
|
'ko',
|
||||||
|
'kw',
|
||||||
|
'la',
|
||||||
|
'lb',
|
||||||
|
'lt',
|
||||||
|
'lv',
|
||||||
|
'mai',
|
||||||
|
'mk',
|
||||||
|
'ml',
|
||||||
|
'mr',
|
||||||
|
'ms',
|
||||||
|
'mt',
|
||||||
|
'my',
|
||||||
|
'no',
|
||||||
|
'nb',
|
||||||
|
'ne',
|
||||||
|
'nl',
|
||||||
|
'oc',
|
||||||
|
'pa',
|
||||||
|
'pl',
|
||||||
|
'pt',
|
||||||
|
'ro',
|
||||||
|
'ru',
|
||||||
|
'sh',
|
||||||
|
'sk',
|
||||||
|
'sl',
|
||||||
|
'sq',
|
||||||
|
'sr',
|
||||||
|
'su',
|
||||||
|
'sv',
|
||||||
|
'sw',
|
||||||
|
'ta',
|
||||||
|
'te',
|
||||||
|
'tg',
|
||||||
|
'th',
|
||||||
|
'fil',
|
||||||
|
'tlh',
|
||||||
|
'tr',
|
||||||
|
'uk',
|
||||||
|
'ur',
|
||||||
|
'uz',
|
||||||
|
'vi',
|
||||||
|
'yi',
|
||||||
|
'zh',
|
||||||
|
] as const;
|
||||||
|
|
|
@ -19,6 +19,7 @@ export const mutedNoteReasons = consts.mutedNoteReasons;
|
||||||
export const followingVisibilities = consts.followingVisibilities;
|
export const followingVisibilities = consts.followingVisibilities;
|
||||||
export const followersVisibilities = consts.followersVisibilities;
|
export const followersVisibilities = consts.followersVisibilities;
|
||||||
export const moderationLogTypes = consts.moderationLogTypes;
|
export const moderationLogTypes = consts.moderationLogTypes;
|
||||||
|
export const languages = consts.languages;
|
||||||
|
|
||||||
// api extractor not supported yet
|
// api extractor not supported yet
|
||||||
//export * as api from './api.js';
|
//export * as api from './api.js';
|
||||||
|
|
Loading…
Reference in a new issue