Merge remote-tracking branch 'upstream/develop' into bnakkoma
This commit is contained in:
commit
d51c12f717
23 changed files with 129 additions and 40 deletions
|
@ -68,7 +68,7 @@ To use Akkoma-FE in Akkoma, use the [frontend](https://docs.akkoma.dev/stable/ad
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# install dependencies
|
# install dependencies
|
||||||
npm install -g yarn
|
corepack enable
|
||||||
yarn
|
yarn
|
||||||
|
|
||||||
# serve with hot reload at localhost:8080
|
# serve with hot reload at localhost:8080
|
||||||
|
@ -83,7 +83,7 @@ npm run unit
|
||||||
|
|
||||||
## For Contributors:
|
## For Contributors:
|
||||||
|
|
||||||
You can create file `/config/local.json` (see [example](https://git.pleroma.social/pleroma/pleroma-fe/blob/develop/config/local.example.json)) to enable some convenience dev options:
|
You can create file `/config/local.json` (see [example](https://akkoma.dev/AkkomaGang/akkoma-fe/src/branch/develop/config/local.example.json)) to enable some convenience dev options:
|
||||||
|
|
||||||
- `target`: makes local dev server redirect to some existing instance's BE instead of local BE, useful for testing things in near-production environment and searching for real-life use-cases.
|
- `target`: makes local dev server redirect to some existing instance's BE instead of local BE, useful for testing things in near-production environment and searching for real-life use-cases.
|
||||||
- `staticConfigPreference`: makes FE's `/static/config.json` take preference of BE-served `/api/statusnet/config.json`. Only works in dev mode.
|
- `staticConfigPreference`: makes FE's `/static/config.json` take preference of BE-served `/api/statusnet/config.json`. Only works in dev mode.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
"target": "https://pleroma.soykaf.com/",
|
"target": "https://otp.akkoma.dev/",
|
||||||
"staticConfigPreference": false
|
"staticConfigPreference": false
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
font-size: 14px;
|
font-size: 0.875rem;
|
||||||
// overflow-x: clip causes my browser's tab to crash with SIGILL lul
|
// overflow-x: clip causes my browser's tab to crash with SIGILL lul
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: scroll;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.-static {
|
&.-static {
|
||||||
|
|
|
@ -93,9 +93,6 @@ const MentionLink = {
|
||||||
this.highlightType
|
this.highlightType
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
useAtIcon () {
|
|
||||||
return this.mergedConfig.useAtIcon
|
|
||||||
},
|
|
||||||
isRemote () {
|
isRemote () {
|
||||||
return this.userName !== this.userNameFull
|
return this.userName !== this.userNameFull
|
||||||
},
|
},
|
||||||
|
|
|
@ -151,7 +151,6 @@
|
||||||
>
|
>
|
||||||
<Timeago
|
<Timeago
|
||||||
:time="notification.created_at"
|
:time="notification.created_at"
|
||||||
:with-direction="true"
|
|
||||||
:auto-update="240"
|
:auto-update="240"
|
||||||
/>
|
/>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
|
@ -105,9 +105,12 @@
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding-left: 0.8em;
|
padding-left: 0.8em;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading-right, .notification-right {
|
||||||
.timeago {
|
.timeago {
|
||||||
min-width: 3em;
|
display: inline-block;
|
||||||
|
min-width: 6em;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,6 +143,7 @@ const PostStatusForm = {
|
||||||
postLanguage: language,
|
postLanguage: language,
|
||||||
sensitiveByDefault,
|
sensitiveByDefault,
|
||||||
sensitiveIfSubject,
|
sensitiveIfSubject,
|
||||||
|
alwaysShowSubjectInput,
|
||||||
} = this.$store.getters.mergedConfig
|
} = this.$store.getters.mergedConfig
|
||||||
|
|
||||||
let statusParams = {
|
let statusParams = {
|
||||||
|
@ -205,6 +206,10 @@ const PostStatusForm = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When first loading the form, hide the subject (CW) field if it's disabled or doesn't have a starting value.
|
||||||
|
// "disableSubject" seems to take priority over "alwaysShowSubjectInput".
|
||||||
|
const showSubject = !this.disableSubject && (statusParams.spoilerText || alwaysShowSubjectInput)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dropFiles: [],
|
dropFiles: [],
|
||||||
uploadingFiles: false,
|
uploadingFiles: false,
|
||||||
|
@ -219,7 +224,10 @@ const PostStatusForm = {
|
||||||
preview: null,
|
preview: null,
|
||||||
previewLoading: false,
|
previewLoading: false,
|
||||||
emojiInputShown: false,
|
emojiInputShown: false,
|
||||||
idempotencyKey: ''
|
idempotencyKey: '',
|
||||||
|
activeEmojiInput: undefined,
|
||||||
|
activeTextInput: undefined,
|
||||||
|
subjectVisible: showSubject
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -680,8 +688,33 @@ const PostStatusForm = {
|
||||||
this.$refs['emoji-input'].resize()
|
this.$refs['emoji-input'].resize()
|
||||||
},
|
},
|
||||||
showEmojiPicker () {
|
showEmojiPicker () {
|
||||||
this.$refs['textarea'].focus()
|
if (!this.activeEmojiInput || !this.activeTextInput)
|
||||||
this.$refs['emoji-input'].triggerShowPicker()
|
this.focusStatusInput()
|
||||||
|
|
||||||
|
this.$refs[this.activeTextInput].focus()
|
||||||
|
this.$refs[this.activeEmojiInput].triggerShowPicker()
|
||||||
|
},
|
||||||
|
focusStatusInput() {
|
||||||
|
this.activeEmojiInput = 'emoji-input'
|
||||||
|
this.activeTextInput = 'textarea'
|
||||||
|
},
|
||||||
|
focusSubjectInput() {
|
||||||
|
this.activeEmojiInput = 'subject-emoji-input'
|
||||||
|
this.activeTextInput = 'subject-input'
|
||||||
|
},
|
||||||
|
toggleSubjectVisible() {
|
||||||
|
// If hiding CW, then we need to clear the subject and reset focus
|
||||||
|
if (this.subjectVisible)
|
||||||
|
{
|
||||||
|
this.focusStatusInput()
|
||||||
|
|
||||||
|
// "nsfw" property is normally set by the @change listener, but this bypasses it.
|
||||||
|
// We need to clear it manually instead.
|
||||||
|
this.newStatus.spoilerText = ''
|
||||||
|
this.newStatus.nsfw = false
|
||||||
|
}
|
||||||
|
|
||||||
|
this.subjectVisible = !this.subjectVisible
|
||||||
},
|
},
|
||||||
clearError () {
|
clearError () {
|
||||||
this.error = null
|
this.error = null
|
||||||
|
|
|
@ -118,13 +118,16 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<EmojiInput
|
<EmojiInput
|
||||||
v-if="!disableSubject && (newStatus.spoilerText || alwaysShowSubject)"
|
ref="subject-emoji-input"
|
||||||
|
v-if="subjectVisible"
|
||||||
v-model="newStatus.spoilerText"
|
v-model="newStatus.spoilerText"
|
||||||
enable-emoji-picker
|
enable-emoji-picker
|
||||||
|
hide-emoji-button
|
||||||
:suggest="emojiSuggestor"
|
:suggest="emojiSuggestor"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
ref="subject-input"
|
||||||
v-model="newStatus.spoilerText"
|
v-model="newStatus.spoilerText"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="$t('post_status.content_warning')"
|
:placeholder="$t('post_status.content_warning')"
|
||||||
|
@ -132,6 +135,7 @@
|
||||||
size="1"
|
size="1"
|
||||||
class="form-post-subject"
|
class="form-post-subject"
|
||||||
@input="onSubjectInput"
|
@input="onSubjectInput"
|
||||||
|
@focus="focusSubjectInput()"
|
||||||
>
|
>
|
||||||
</EmojiInput>
|
</EmojiInput>
|
||||||
<i18n-t
|
<i18n-t
|
||||||
|
@ -177,6 +181,7 @@
|
||||||
@input="resize"
|
@input="resize"
|
||||||
@compositionupdate="resize"
|
@compositionupdate="resize"
|
||||||
@paste="paste"
|
@paste="paste"
|
||||||
|
@focus="focusStatusInput()"
|
||||||
/>
|
/>
|
||||||
<p
|
<p
|
||||||
v-if="hasStatusLengthLimit"
|
v-if="hasStatusLengthLimit"
|
||||||
|
@ -280,6 +285,15 @@
|
||||||
>
|
>
|
||||||
<FAIcon icon="poll-h" />
|
<FAIcon icon="poll-h" />
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="!disableSubject"
|
||||||
|
class="spoiler-icon button-unstyled"
|
||||||
|
:class="{ selected: subjectVisible }"
|
||||||
|
:title="$t('post_status.toggle_content_warning')"
|
||||||
|
@click="toggleSubjectVisible"
|
||||||
|
>
|
||||||
|
<FAIcon icon="eye-slash" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
v-if="posting"
|
v-if="posting"
|
||||||
|
@ -460,7 +474,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-upload-icon, .poll-icon, .emoji-icon {
|
.media-upload-icon, .poll-icon, .emoji-icon, .spoiler-icon {
|
||||||
font-size: 1.85em;
|
font-size: 1.85em;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -503,6 +517,11 @@
|
||||||
|
|
||||||
.poll-icon {
|
.poll-icon {
|
||||||
order: 3;
|
order: 3;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spoiler-icon {
|
||||||
|
order: 4;
|
||||||
justify-content: right;
|
justify-content: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -491,14 +491,6 @@
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<li>
|
|
||||||
<BooleanSetting
|
|
||||||
path="useAtIcon"
|
|
||||||
expert="1"
|
|
||||||
>
|
|
||||||
{{ $t('settings.use_at_icon') }}
|
|
||||||
</BooleanSetting>
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting path="mentionLinkShowAvatar">
|
<BooleanSetting path="mentionLinkShowAvatar">
|
||||||
{{ $t('settings.mention_link_show_avatar') }}
|
{{ $t('settings.mention_link_show_avatar') }}
|
||||||
|
|
|
@ -190,7 +190,7 @@
|
||||||
>
|
>
|
||||||
<Timeago
|
<Timeago
|
||||||
:time="status.created_at"
|
:time="status.created_at"
|
||||||
:with-direction="true"
|
:with-direction="!compact"
|
||||||
:auto-update="60"
|
:auto-update="60"
|
||||||
/>
|
/>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
|
@ -43,20 +43,22 @@ const StillImage = {
|
||||||
detectAnimation (image) {
|
detectAnimation (image) {
|
||||||
const mediaProxyAvailable = this.$store.state.instance.mediaProxyAvailable
|
const mediaProxyAvailable = this.$store.state.instance.mediaProxyAvailable
|
||||||
|
|
||||||
// harmless CORS errors without-- clean console with
|
|
||||||
if (!mediaProxyAvailable) {
|
if (!mediaProxyAvailable) {
|
||||||
// It's a bit aggressive to assume all images we can't find the mimetype of is animated, but necessary for
|
// It's a bit aggressive to assume all images we can't find the mimetype of is animated, but necessary for
|
||||||
// people in need of reduced motion accessibility. As such, we'll consider those images animated if the user
|
// people in need of reduced motion accessibility. As such, we'll consider those images animated if the user
|
||||||
// agent is set to prefer reduced motion. Otherwise, it'll just be used as an early exit.
|
// agent is set to prefer reduced motion. Otherwise, it'll just be used as an early exit.
|
||||||
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
||||||
// Only for no media-proxy for now, since we're capturing gif, webp, and apng (are there others?)
|
|
||||||
// Since the canvas and images are not pixel-perfect matching (due to scaling),
|
// Since the canvas and images are not pixel-perfect matching (due to scaling),
|
||||||
// It makes the images jiggle on hover, which is not ideal for accessibility, methinks
|
// It makes the images jiggle on hover, which is not ideal for accessibility, methinks
|
||||||
this.isAnimated = true
|
this.isAnimated = true
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
this.detectWithoutMediaProxy(image)
|
||||||
|
} else {
|
||||||
|
this.detectWithMediaProxy(image)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
detectAnimationWithFetch (image) {
|
||||||
// Browser Cache should ensure image doesn't get loaded twice if cache exists
|
// Browser Cache should ensure image doesn't get loaded twice if cache exists
|
||||||
fetch(image.src, {
|
fetch(image.src, {
|
||||||
referrerPolicy: 'same-origin'
|
referrerPolicy: 'same-origin'
|
||||||
|
@ -86,6 +88,36 @@ const StillImage = {
|
||||||
// this.imageLoadError && this.imageLoadError()
|
// this.imageLoadError && this.imageLoadError()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
detectWithMediaProxy (image) {
|
||||||
|
this.detectAnimationWithFetch(image)
|
||||||
|
},
|
||||||
|
detectWithoutMediaProxy (image) {
|
||||||
|
// We'll just assume that gifs and webp are animated
|
||||||
|
const extension = image.src.split('.').pop().toLowerCase()
|
||||||
|
|
||||||
|
if (extension === 'gif') {
|
||||||
|
this.isAnimated = true
|
||||||
|
this.setLabel('GIF')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (extension === 'webp') {
|
||||||
|
this.isAnimated = true
|
||||||
|
this.setLabel('WEBP')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Beware the apng! use this if ye dare
|
||||||
|
// if (extension === 'png') {
|
||||||
|
// this.isAnimated = true
|
||||||
|
// this.setLabel('PNG')
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Hail mary for extensionless
|
||||||
|
if (extension.includes('/')) {
|
||||||
|
// Don't mind the CORS error barrage
|
||||||
|
this.detectAnimationWithFetch(image)
|
||||||
|
}
|
||||||
|
},
|
||||||
setLabel (name) {
|
setLabel (name) {
|
||||||
this.imageTypeLabel = name;
|
this.imageTypeLabel = name;
|
||||||
},
|
},
|
||||||
|
|
|
@ -884,7 +884,6 @@
|
||||||
"upload_a_photo": "Pujar una foto",
|
"upload_a_photo": "Pujar una foto",
|
||||||
"useStreamingApi": "Rebre apunts i notificacions en temps real",
|
"useStreamingApi": "Rebre apunts i notificacions en temps real",
|
||||||
"useStreamingApiWarning": "És genial emprar-lo. Si es trenca, refresca, suposo?",
|
"useStreamingApiWarning": "És genial emprar-lo. Si es trenca, refresca, suposo?",
|
||||||
"use_at_icon": "Mostra el símbol {'@'} com a icona enlloc de text",
|
|
||||||
"use_contain_fit": "No retallar els adjunts en miniatures",
|
"use_contain_fit": "No retallar els adjunts en miniatures",
|
||||||
"use_one_click_nsfw": "Obre els adjunts NSFW amb només un clic",
|
"use_one_click_nsfw": "Obre els adjunts NSFW amb només un clic",
|
||||||
"user_mutes": "Usuaris",
|
"user_mutes": "Usuaris",
|
||||||
|
|
|
@ -916,7 +916,6 @@
|
||||||
"upload_a_photo": "Lade ein Foto hoch",
|
"upload_a_photo": "Lade ein Foto hoch",
|
||||||
"useStreamingApi": "Empfange Posts und Benachrichtigungen in Echtzeit",
|
"useStreamingApi": "Empfange Posts und Benachrichtigungen in Echtzeit",
|
||||||
"useStreamingApiWarning": "(Nicht empfohlen, experimentell, bekannt dafür, Posts zu überspringen)",
|
"useStreamingApiWarning": "(Nicht empfohlen, experimentell, bekannt dafür, Posts zu überspringen)",
|
||||||
"use_at_icon": "{'@'}-Symbol als Icon und nicht als Text anzeigen",
|
|
||||||
"use_blurhash": "Blurhash für NSFW-Vorschauen verwenden",
|
"use_blurhash": "Blurhash für NSFW-Vorschauen verwenden",
|
||||||
"use_contain_fit": "Vorschaubilder nicht zuschneiden",
|
"use_contain_fit": "Vorschaubilder nicht zuschneiden",
|
||||||
"use_one_click_nsfw": "Heikle Anhänge mit nur einem Klick öffnen",
|
"use_one_click_nsfw": "Heikle Anhänge mit nur einem Klick öffnen",
|
||||||
|
|
|
@ -380,6 +380,7 @@
|
||||||
"text/x.misskeymarkdown": "MFM"
|
"text/x.misskeymarkdown": "MFM"
|
||||||
},
|
},
|
||||||
"content_warning": "Content Warning (optional)",
|
"content_warning": "Content Warning (optional)",
|
||||||
|
"toggle_content_warning": "Toggle content warning",
|
||||||
"default": "Just arrived at Luna Nova Academy",
|
"default": "Just arrived at Luna Nova Academy",
|
||||||
"direct_warning_to_all": "This post will be visible to all the mentioned users.",
|
"direct_warning_to_all": "This post will be visible to all the mentioned users.",
|
||||||
"direct_warning_to_first_only": "This post will only be visible to the mentioned users at the beginning of the message.",
|
"direct_warning_to_first_only": "This post will only be visible to the mentioned users at the beginning of the message.",
|
||||||
|
@ -926,7 +927,6 @@
|
||||||
"upload_a_photo": "Upload a photo",
|
"upload_a_photo": "Upload a photo",
|
||||||
"useStreamingApi": "Receive posts and notifications real-time",
|
"useStreamingApi": "Receive posts and notifications real-time",
|
||||||
"useStreamingApiWarning": "It's cool use it. If it breaks refresh I guess?",
|
"useStreamingApiWarning": "It's cool use it. If it breaks refresh I guess?",
|
||||||
"use_at_icon": "Display {'@'} symbol as an icon instead of text",
|
|
||||||
"use_contain_fit": "Don't crop the attachment in thumbnails",
|
"use_contain_fit": "Don't crop the attachment in thumbnails",
|
||||||
"use_one_click_nsfw": "Open NSFW attachments with just one click",
|
"use_one_click_nsfw": "Open NSFW attachments with just one click",
|
||||||
"user_mutes": "Users",
|
"user_mutes": "Users",
|
||||||
|
|
|
@ -920,7 +920,6 @@
|
||||||
"upload_a_photo": "Envoyer une photo",
|
"upload_a_photo": "Envoyer une photo",
|
||||||
"useStreamingApi": "Recevoir les messages et notifications en temps réel",
|
"useStreamingApi": "Recevoir les messages et notifications en temps réel",
|
||||||
"useStreamingApiWarning": "(Non recommandé, expérimental, connu pour rater des messages)",
|
"useStreamingApiWarning": "(Non recommandé, expérimental, connu pour rater des messages)",
|
||||||
"use_at_icon": "Afficher le symbol {'@'} comme une image",
|
|
||||||
"use_contain_fit": "Ne pas rogner les miniatures des pièces-jointes",
|
"use_contain_fit": "Ne pas rogner les miniatures des pièces-jointes",
|
||||||
"use_one_click_nsfw": "Ouvrir les pièces-jointes sensibles avec un seul clic",
|
"use_one_click_nsfw": "Ouvrir les pièces-jointes sensibles avec un seul clic",
|
||||||
"user_mutes": "Comptes",
|
"user_mutes": "Comptes",
|
||||||
|
|
|
@ -925,7 +925,6 @@
|
||||||
"upload_a_photo": "画像をアップロード",
|
"upload_a_photo": "画像をアップロード",
|
||||||
"useStreamingApi": "ストリーミングAPIを利用して投稿と通知を受け取る",
|
"useStreamingApi": "ストリーミングAPIを利用して投稿と通知を受け取る",
|
||||||
"useStreamingApiWarning": "(開発中につき投稿を取りこぼす可能性があるため、非推奨です)",
|
"useStreamingApiWarning": "(開発中につき投稿を取りこぼす可能性があるため、非推奨です)",
|
||||||
"use_at_icon": "{'@'}マークをアイコンにする",
|
|
||||||
"use_contain_fit": "画像のサムネイルを切り抜かない",
|
"use_contain_fit": "画像のサムネイルを切り抜かない",
|
||||||
"use_one_click_nsfw": "NSFWに設定されたメディアをワンクリックで開く",
|
"use_one_click_nsfw": "NSFWに設定されたメディアをワンクリックで開く",
|
||||||
"user_mutes": "ユーザー",
|
"user_mutes": "ユーザー",
|
||||||
|
|
|
@ -918,7 +918,6 @@
|
||||||
"upload_a_photo": "Foto uploaden",
|
"upload_a_photo": "Foto uploaden",
|
||||||
"useStreamingApi": "Berichten en meldingen in real-time ontvangen",
|
"useStreamingApi": "Berichten en meldingen in real-time ontvangen",
|
||||||
"useStreamingApiWarning": "Iets experimenteels met berichten streamen uwu miss kun je beter uit laten ofzo?",
|
"useStreamingApiWarning": "Iets experimenteels met berichten streamen uwu miss kun je beter uit laten ofzo?",
|
||||||
"use_at_icon": "{'@'} symbool als icoon tonen in plaats van tekst",
|
|
||||||
"use_blurhash": "Waas tonen over NSFW-miniaturen",
|
"use_blurhash": "Waas tonen over NSFW-miniaturen",
|
||||||
"use_contain_fit": "Bijlage in miniaturen niet bijsnijden",
|
"use_contain_fit": "Bijlage in miniaturen niet bijsnijden",
|
||||||
"use_one_click_nsfw": "Gevoelige bijlagen met slechts één klik openen",
|
"use_one_click_nsfw": "Gevoelige bijlagen met slechts één klik openen",
|
||||||
|
|
|
@ -922,7 +922,6 @@
|
||||||
"upload_a_photo": "Вивантажити фото",
|
"upload_a_photo": "Вивантажити фото",
|
||||||
"useStreamingApi": "Отримувати дописи та сповіщення наживо",
|
"useStreamingApi": "Отримувати дописи та сповіщення наживо",
|
||||||
"useStreamingApiWarning": "Загалом працює. Якщо не зовсім, спробуєте оновити сторінку?",
|
"useStreamingApiWarning": "Загалом працює. Якщо не зовсім, спробуєте оновити сторінку?",
|
||||||
"use_at_icon": "Значок {'@'} замість символу",
|
|
||||||
"use_blurhash": "Показувати дражливі мініатюри (як розмиті кольори)",
|
"use_blurhash": "Показувати дражливі мініатюри (як розмиті кольори)",
|
||||||
"use_contain_fit": "Не обрізати краї мініатюр",
|
"use_contain_fit": "Не обрізати краї мініатюр",
|
||||||
"use_one_click_nsfw": "Відкривати NSFW вкладення одним кліком миші",
|
"use_one_click_nsfw": "Відкривати NSFW вкладення одним кліком миші",
|
||||||
|
|
|
@ -922,7 +922,6 @@
|
||||||
"upload_a_photo": "上传照片",
|
"upload_a_photo": "上传照片",
|
||||||
"useStreamingApi": "实时接收帖文和通知",
|
"useStreamingApi": "实时接收帖文和通知",
|
||||||
"useStreamingApiWarning": "十分炫酷推荐使用。要是崩了试试刷新?",
|
"useStreamingApiWarning": "十分炫酷推荐使用。要是崩了试试刷新?",
|
||||||
"use_at_icon": "将 {'@'} 符号显示为图标而不是文本",
|
|
||||||
"use_blurhash": "对NSFW的缩略图使用模糊处理",
|
"use_blurhash": "对NSFW的缩略图使用模糊处理",
|
||||||
"use_contain_fit": "生成缩略图时不要裁剪附件",
|
"use_contain_fit": "生成缩略图时不要裁剪附件",
|
||||||
"use_one_click_nsfw": "点击一次以打开工作场所不适宜(NSFW)的附件",
|
"use_one_click_nsfw": "点击一次以打开工作场所不适宜(NSFW)的附件",
|
||||||
|
|
|
@ -97,7 +97,6 @@ export const defaultState = {
|
||||||
disableStickyHeaders: false,
|
disableStickyHeaders: false,
|
||||||
showScrollbars: false,
|
showScrollbars: false,
|
||||||
greentext: undefined, // instance default
|
greentext: undefined, // instance default
|
||||||
useAtIcon: undefined, // instance default
|
|
||||||
mentionLinkDisplay: undefined, // instance default
|
mentionLinkDisplay: undefined, // instance default
|
||||||
mentionLinkShowTooltip: undefined, // instance default
|
mentionLinkShowTooltip: undefined, // instance default
|
||||||
mentionLinkShowAvatar: undefined, // instance default
|
mentionLinkShowAvatar: undefined, // instance default
|
||||||
|
@ -231,7 +230,7 @@ const config = {
|
||||||
break
|
break
|
||||||
case 'interfaceLanguage':
|
case 'interfaceLanguage':
|
||||||
messages.setLanguage(this.getters.i18n, value)
|
messages.setLanguage(this.getters.i18n, value)
|
||||||
Cookies.set(BACKEND_LANGUAGE_COOKIE_NAME, localeService.internalToBackendLocale(value))
|
Cookies.set(BACKEND_LANGUAGE_COOKIE_NAME, localeService.internalToBackendLocale(value), {sameSite: 'Lax'})
|
||||||
dispatch('setInstanceOption', { name: 'interfaceLanguage', value })
|
dispatch('setInstanceOption', { name: 'interfaceLanguage', value })
|
||||||
break
|
break
|
||||||
case 'thirdColumnMode':
|
case 'thirdColumnMode':
|
||||||
|
|
|
@ -21,7 +21,6 @@ const defaultState = {
|
||||||
background: '/static/aurora_borealis.jpg',
|
background: '/static/aurora_borealis.jpg',
|
||||||
collapseMessageWithSubject: true,
|
collapseMessageWithSubject: true,
|
||||||
greentext: false,
|
greentext: false,
|
||||||
useAtIcon: false,
|
|
||||||
mentionLinkDisplay: 'short',
|
mentionLinkDisplay: 'short',
|
||||||
mentionLinkShowTooltip: true,
|
mentionLinkShowTooltip: true,
|
||||||
mentionLinkShowAvatar: false,
|
mentionLinkShowAvatar: false,
|
||||||
|
|
|
@ -8,7 +8,30 @@ const specialLanguageCodes = {
|
||||||
'zh': 'zh-Hans'
|
'zh': 'zh-Hans'
|
||||||
}
|
}
|
||||||
|
|
||||||
const internalToBrowserLocale = code => specialLanguageCodes[code] || code
|
// Find a browser language that matches the configured UI language.
|
||||||
|
// Browser language should match the configured generic short code prefix:
|
||||||
|
// eg 'en-GB' browser language matches 'en' UI language.
|
||||||
|
const findBrowserRegionMatch = genericLang => {
|
||||||
|
for (const blang of window.navigator.languages) {
|
||||||
|
if (genericLang === blang.split('-')[0])
|
||||||
|
return blang;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const internalToBrowserLocale = (() => {
|
||||||
|
const resolvedBrowserLocales = {}
|
||||||
|
|
||||||
|
return i18nLocale => {
|
||||||
|
if (resolvedBrowserLocales[i18nLocale]) {
|
||||||
|
return resolvedBrowserLocales[i18nLocale]
|
||||||
|
}
|
||||||
|
const lang = specialLanguageCodes[i18nLocale] || i18nLocale;
|
||||||
|
const resolved = findBrowserRegionMatch(lang) || lang;
|
||||||
|
resolvedBrowserLocales[i18nLocale] = resolved
|
||||||
|
return resolved
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
const internalToBackendLocale = code => internalToBrowserLocale(code).replace('_', '-')
|
const internalToBackendLocale = code => internalToBrowserLocale(code).replace('_', '-')
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue