Compare commits
18 commits
Author | SHA1 | Date | |
---|---|---|---|
smitten | 1cf5ac4497 | ||
smitten | f5215990bd | ||
smitten | 4d44030ca7 | ||
c047646188 | |||
383a8b1907 | |||
8c122cfaf7 | |||
28d180e944 | |||
smitten | 0a56cf37a9 | ||
smitten | 14e789cf7a | ||
smitten | 904e90e060 | ||
smitten | 68f0150712 | ||
smitten | 9638a642c7 | ||
smitten | b842f17fd2 | ||
smitten | 0e7f4586ed | ||
smitten | 28d6daf524 | ||
smitten | 1eb15e58bb | ||
smitten | 852fed047a | ||
smitten | 2cd1cd422c |
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -9,4 +9,5 @@ selenium-debug.log
|
|||
config/local.json
|
||||
config/local.*.json
|
||||
docs/site/
|
||||
.vscode/
|
||||
.vscode/
|
||||
.yarn/
|
|
@ -1 +0,0 @@
|
|||
nodejs 20.12.2
|
|
@ -1,12 +1,9 @@
|
|||
labels:
|
||||
platform: linux/amd64
|
||||
|
||||
steps:
|
||||
pipeline:
|
||||
lint:
|
||||
when:
|
||||
event:
|
||||
- pull_request
|
||||
image: node:20
|
||||
image: node:18
|
||||
commands:
|
||||
- yarn
|
||||
- yarn lint
|
||||
|
@ -16,11 +13,11 @@ steps:
|
|||
when:
|
||||
event:
|
||||
- pull_request
|
||||
image: node:20
|
||||
image: node:18
|
||||
commands:
|
||||
- apt update
|
||||
- apt install firefox-esr -y --no-install-recommends
|
||||
- yarn
|
||||
- yarn
|
||||
- yarn unit
|
||||
|
||||
build:
|
||||
|
@ -30,7 +27,7 @@ steps:
|
|||
branch:
|
||||
- develop
|
||||
- stable
|
||||
image: node:20
|
||||
image: node:18
|
||||
commands:
|
||||
- yarn
|
||||
- yarn build
|
||||
|
@ -42,15 +39,15 @@ steps:
|
|||
branch:
|
||||
- develop
|
||||
- stable
|
||||
image: node:20
|
||||
image: node:18
|
||||
secrets:
|
||||
- SCW_ACCESS_KEY
|
||||
- SCW_SECRET_KEY
|
||||
- SCW_DEFAULT_ORGANIZATION_ID
|
||||
commands:
|
||||
- apt-get update && apt-get install -y rclone wget zip
|
||||
- wget https://github.com/scaleway/scaleway-cli/releases/download/v2.30.0/scaleway-cli_2.30.0_linux_amd64
|
||||
- mv scaleway-cli_2.30.0_linux_amd64 scaleway-cli
|
||||
- wget https://github.com/scaleway/scaleway-cli/releases/download/v2.5.1/scaleway-cli_2.5.1_linux_amd64
|
||||
- mv scaleway-cli_2.5.1_linux_amd64 scaleway-cli
|
||||
- chmod +x scaleway-cli
|
||||
- ./scaleway-cli object config install type=rclone
|
||||
- zip akkoma-fe.zip -r dist
|
||||
|
@ -72,8 +69,8 @@ steps:
|
|||
- SCW_DEFAULT_ORGANIZATION_ID
|
||||
commands:
|
||||
- apt-get update && apt-get install -y rclone wget git zip
|
||||
- wget https://github.com/scaleway/scaleway-cli/releases/download/v2.30.0/scaleway-cli_2.30.0_linux_amd64
|
||||
- mv scaleway-cli_2.30.0_linux_amd64 scaleway-cli
|
||||
- wget https://github.com/scaleway/scaleway-cli/releases/download/v2.5.1/scaleway-cli_2.5.1_linux_amd64
|
||||
- mv scaleway-cli_2.5.1_linux_amd64 scaleway-cli
|
||||
- chmod +x scaleway-cli
|
||||
- ./scaleway-cli object config install type=rclone
|
||||
- cd docs
|
||||
|
@ -81,4 +78,4 @@ steps:
|
|||
- mkdocs build
|
||||
- zip -r docs.zip site/*
|
||||
- cd site
|
||||
- rclone copy . scaleway:akkoma-docs/frontend/$CI_COMMIT_BRANCH/
|
||||
- rclone copy . scaleway:akkoma-docs/frontend/$CI_COMMIT_BRANCH/
|
||||
|
|
27
README.md
27
README.md
|
@ -1,4 +1,21 @@
|
|||
# Akkoma-FE
|
||||
A soft fork of Akkoma-FE fixing a few minor but longstanding annoyances.
|
||||
|
||||
Hopefully most of these will be merged upstream when I learn how to do PRs.
|
||||
|
||||
## Comprehensive List of Fixes and Changes
|
||||
|
||||
### Added
|
||||
|
||||
- Support for 'Fedi Links' web+ap addresses on user profiles and posts
|
||||
- Approve and deny follow request buttons to the user profile page
|
||||
|
||||
### Fixed
|
||||
|
||||
- Styling adjustments for the user profile to avoid some ellipsing in the notifications panel
|
||||
- Settings backup when the word filters contain non-ASCII characters
|
||||
- The hover color of the checkmark icon on follow request notifications
|
||||
|
||||
# Akkoma-FE
|
||||
|
||||
![English OK](https://img.shields.io/badge/English-OK-blueviolet) ![日本語OK](https://img.shields.io/badge/%E6%97%A5%E6%9C%AC%E8%AA%9E-OK-blueviolet)
|
||||
|
||||
|
@ -8,7 +25,7 @@ This is a fork of Akkoma-FE from the Pleroma project, with support for new Akkom
|
|||
|
||||
# For Translators
|
||||
|
||||
The [Weblate UI](https://translate.akkoma.dev/projects/akkoma/pleroma-fe/) is recommended for adding or modifying translations for Akkoma-FE.
|
||||
The [Weblate UI](https://translate.akkoma.dev/projects/akkoma/pleroma-fe/) is recommended for adding or modifying translations for Akkoma-FE.
|
||||
|
||||
Alternatively, edit/create `src/i18n/$LANGUAGE_CODE.json` (where `$LANGUAGE_CODE` is the [ISO 639-1 code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) for your language), then add your language to [src/i18n/messages.js](https://akkoma.dev/AkkomaGang/pleroma-fe/src/branch/develop/src/i18n/messages.js) if it doesn't already exist there.
|
||||
|
||||
|
@ -22,7 +39,7 @@ To use Akkoma-FE in Akkoma, use the [frontend](https://docs.akkoma.dev/stable/ad
|
|||
|
||||
``` bash
|
||||
# install dependencies
|
||||
corepack enable
|
||||
npm install -g yarn
|
||||
yarn
|
||||
|
||||
# serve with hot reload at localhost:8080
|
||||
|
@ -37,7 +54,7 @@ npm run unit
|
|||
|
||||
# For Contributors:
|
||||
|
||||
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:
|
||||
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:
|
||||
|
||||
* `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.
|
||||
|
@ -52,4 +69,4 @@ Edit config.json for configuration.
|
|||
|
||||
### Login methods
|
||||
|
||||
```loginMethod``` can be set to either ```password``` (the default) or ```token```, which will use the full oauth redirection flow, which is useful for SSO situations.
|
||||
```loginMethod``` can be set to either ```password``` (the default) or ```token```, which will use the full oauth redirection flow, which is useful for SSO situations.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"target": "https://otp.akkoma.dev/",
|
||||
"target": "https://pleroma.soykaf.com/",
|
||||
"staticConfigPreference": false
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "pleroma_fe",
|
||||
"version": "3.10.0",
|
||||
"version": "3.5.0",
|
||||
"description": "A frontend for Akkoma instances",
|
||||
"author": "Roger Braun <roger@rogerbraun.net>",
|
||||
"private": true,
|
||||
|
|
|
@ -64,11 +64,6 @@ export default {
|
|||
'-' + this.layoutType
|
||||
]
|
||||
},
|
||||
pageBackground () {
|
||||
return this.mergedConfig.displayPageBackgrounds
|
||||
? this.$store.state.users.displayBackground
|
||||
: null
|
||||
},
|
||||
currentUser () { return this.$store.state.users.currentUser },
|
||||
userBackground () { return this.currentUser.background_image },
|
||||
instanceBackground () {
|
||||
|
@ -76,7 +71,7 @@ export default {
|
|||
? null
|
||||
: this.$store.state.instance.background
|
||||
},
|
||||
background () { return this.pageBackground || this.userBackground || this.instanceBackground },
|
||||
background () { return this.userBackground || this.instanceBackground },
|
||||
bgStyle () {
|
||||
if (this.background) {
|
||||
return {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
}
|
||||
|
||||
html {
|
||||
font-size: 0.875rem;
|
||||
font-size: 14px;
|
||||
// overflow-x: clip causes my browser's tab to crash with SIGILL lul
|
||||
}
|
||||
|
||||
|
|
13
src/assets/icons/activity-pub.js
Normal file
13
src/assets/icons/activity-pub.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
const facActivityPub = {
|
||||
prefix: "fac",
|
||||
iconName: "activity-pub",
|
||||
icon: [
|
||||
448,
|
||||
512,
|
||||
[],
|
||||
"",
|
||||
"M 253.31024,102.2775 -10,254.04683 v 60.95152 L 200.28249,193.7048 v 243.19662 l 53.02775,29.86625 z m 52.41823,0 v 60.95153 L 516.62048,284.52259 305.72847,406.42564 v 60.34203 L 569.03871,314.99835 v -60.95152 z m 0,121.90308 V 345.47412 L 411.17449,284.52259 Z M 147.86424,284.52259 42.418248,345.47412 147.86424,406.42564 Z"
|
||||
]
|
||||
}
|
||||
|
||||
export default facActivityPub
|
|
@ -37,7 +37,7 @@
|
|||
white-space: pre-line;
|
||||
word-break: break-word;
|
||||
text-overflow: ellipsis;
|
||||
overflow: auto;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
&.-static {
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
faStickyNote,
|
||||
faSmileBeam
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { trim, escapeRegExp, startCase, debounce } from 'lodash'
|
||||
import { trim, escapeRegExp, startCase } from 'lodash'
|
||||
|
||||
library.add(
|
||||
faBoxOpen,
|
||||
|
@ -42,9 +42,6 @@ const EmojiPicker = {
|
|||
EmojiGrid
|
||||
},
|
||||
methods: {
|
||||
debouncedSearch: debounce(function (e) {
|
||||
this.keyword = e.target.value
|
||||
}, 500),
|
||||
onStickerUploaded (e) {
|
||||
this.$emit('sticker-uploaded', e)
|
||||
},
|
||||
|
@ -88,6 +85,17 @@ const EmojiPicker = {
|
|||
activeGroupView () {
|
||||
return this.showingStickers ? '' : this.activeGroup
|
||||
},
|
||||
stickersAvailable () {
|
||||
if (this.$store.state.instance.stickers) {
|
||||
return this.$store.state.instance.stickers.length > 0
|
||||
}
|
||||
return 0
|
||||
},
|
||||
filteredEmoji () {
|
||||
return this.filterByKeyword(
|
||||
this.$store.state.instance.customEmoji || []
|
||||
)
|
||||
},
|
||||
emojis () {
|
||||
const recentEmojis = this.$store.getters.recentEmojis
|
||||
const standardEmojis = this.$store.state.instance.emoji || []
|
||||
|
|
|
@ -44,10 +44,11 @@
|
|||
>
|
||||
<div class="emoji-search">
|
||||
<input
|
||||
v-model="keyword"
|
||||
type="text"
|
||||
class="form-control"
|
||||
:placeholder="$t('emoji.search_emoji')"
|
||||
@input="debouncedSearch"
|
||||
@input="$event.target.composing = false"
|
||||
>
|
||||
</div>
|
||||
<EmojiGrid
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Popover from '../popover/popover.vue'
|
||||
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
|
||||
import facActivityPub from '../../assets/icons/activity-pub'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faEllipsisH,
|
||||
|
@ -9,7 +10,7 @@ import {
|
|||
faShareAlt,
|
||||
faExternalLinkAlt,
|
||||
faHistory,
|
||||
faFilePen
|
||||
faFilePen,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import {
|
||||
faBookmark as faBookmarkReg,
|
||||
|
@ -25,6 +26,7 @@ library.add(
|
|||
faThumbtack,
|
||||
faShareAlt,
|
||||
faExternalLinkAlt,
|
||||
facActivityPub,
|
||||
faFlag,
|
||||
faHistory,
|
||||
faFilePen
|
||||
|
@ -95,6 +97,11 @@ const ExtraButtons = {
|
|||
.then(() => this.$emit('onSuccess'))
|
||||
.catch(err => this.$emit('onError', err.error.error))
|
||||
},
|
||||
copyFediLink () {
|
||||
navigator.clipboard.writeText(this.fediLinkURL)
|
||||
.then(() => this.$emit('onSuccess'))
|
||||
.catch(err => this.$emit('onError', err.error.error))
|
||||
},
|
||||
bookmarkStatus () {
|
||||
this.$store.dispatch('bookmark', { id: this.status.id })
|
||||
.then(() => this.$emit('onSuccess'))
|
||||
|
@ -136,26 +143,18 @@ const ExtraButtons = {
|
|||
},
|
||||
doRedraftStatus () {
|
||||
this.$store.dispatch('fetchStatusSource', { id: this.status.id })
|
||||
.then(data => {
|
||||
let repliedUserId = this.status.in_reply_to_user_id;
|
||||
let repliedUser = this.status.attentions.filter(user =>
|
||||
user.id === repliedUserId);
|
||||
this.$store.dispatch('openPostStatusModal', {
|
||||
isRedraft: true,
|
||||
attentions: this.status.attentions,
|
||||
statusId: this.status.id,
|
||||
subject: data.spoiler_text,
|
||||
statusText: data.text,
|
||||
statusIsSensitive: this.status.nsfw,
|
||||
statusPoll: this.status.poll,
|
||||
statusFiles: [...this.status.attachments],
|
||||
statusScope: this.status.visibility,
|
||||
statusLanguage: this.status.language,
|
||||
statusContentType: data.content_type,
|
||||
replyTo: this.status.in_reply_to_status_id,
|
||||
repliedUser: repliedUser
|
||||
})
|
||||
})
|
||||
.then(data => this.$store.dispatch('openPostStatusModal', {
|
||||
isRedraft: true,
|
||||
statusId: this.status.id,
|
||||
subject: data.spoiler_text,
|
||||
statusText: data.text,
|
||||
statusIsSensitive: this.status.nsfw,
|
||||
statusPoll: this.status.poll,
|
||||
statusFiles: [...this.status.attachments],
|
||||
statusScope: this.status.visibility,
|
||||
statusLanguage: this.status.language,
|
||||
statusContentType: data.content_type
|
||||
}))
|
||||
this.doDeleteStatus()
|
||||
},
|
||||
showRedraftStatusConfirmDialog () {
|
||||
|
@ -178,7 +177,7 @@ const ExtraButtons = {
|
|||
canPin () {
|
||||
return this.ownStatus && (this.status.visibility === 'public' || this.status.visibility === 'unlisted')
|
||||
},
|
||||
canMute () {
|
||||
loggedIn () {
|
||||
return !!this.currentUser
|
||||
},
|
||||
canTranslate () {
|
||||
|
@ -194,6 +193,16 @@ const ExtraButtons = {
|
|||
return this.status.external_url
|
||||
}
|
||||
},
|
||||
showFediLinks () {
|
||||
return this.$store.getters.mergedConfig.showFediLinks === true
|
||||
},
|
||||
fediLinkURL () {
|
||||
try {
|
||||
return this.status.external_uri.replace(/^https?/, 'web+ap')
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
},
|
||||
shouldConfirmDelete () {
|
||||
return this.$store.getters.mergedConfig.modalOnDelete
|
||||
},
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<template v-slot:content="{close}">
|
||||
<div class="dropdown-menu">
|
||||
<button
|
||||
v-if="canMute && !status.thread_muted"
|
||||
v-if="loggedIn && !status.thread_muted"
|
||||
class="button-default dropdown-item dropdown-item-icon"
|
||||
@click.prevent="muteConversation"
|
||||
>
|
||||
|
@ -20,7 +20,7 @@
|
|||
/><span>{{ $t("status.mute_conversation") }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="canMute && status.thread_muted"
|
||||
v-if="loggedIn && status.thread_muted"
|
||||
class="button-default dropdown-item dropdown-item-icon"
|
||||
@click.prevent="unmuteConversation"
|
||||
>
|
||||
|
@ -52,7 +52,7 @@
|
|||
/><span>{{ $t("status.unpin") }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="!status.bookmarked"
|
||||
v-if="loggedIn && !status.bookmarked"
|
||||
class="button-default dropdown-item dropdown-item-icon"
|
||||
@click.prevent="bookmarkStatus"
|
||||
@click="close"
|
||||
|
@ -63,7 +63,7 @@
|
|||
/><span>{{ $t("status.bookmark") }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="status.bookmarked"
|
||||
v-if="loggedIn && status.bookmarked"
|
||||
class="button-default dropdown-item dropdown-item-icon"
|
||||
@click.prevent="unbookmarkStatus"
|
||||
@click="close"
|
||||
|
@ -127,6 +127,17 @@
|
|||
icon="share-alt"
|
||||
/><span>{{ $t("status.copy_link") }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="fediLinkURL && showFediLinks && loggedIn"
|
||||
class="button-default dropdown-item dropdown-item-icon"
|
||||
@click.prevent="copyFediLink"
|
||||
@click="close"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
:icon="['fac', 'activity-pub']"
|
||||
/><span>{{ $t("status.copy_fedi_link") }}</span>
|
||||
</button>
|
||||
<a
|
||||
v-if="!status.is_local"
|
||||
class="button-default dropdown-item dropdown-item-icon"
|
||||
|
@ -139,6 +150,18 @@
|
|||
icon="external-link-alt"
|
||||
/><span>{{ $t("status.external_source") }}</span>
|
||||
</a>
|
||||
<a
|
||||
v-if="fediLinkURL && showFediLinks && !loggedIn"
|
||||
class="button-default dropdown-item dropdown-item-icon"
|
||||
title="Source"
|
||||
:href="fediLinkURL"
|
||||
target="_blank"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
:icon="['fac', 'activity-pub']"
|
||||
/><span>{{ $t("status.open_fedi_link") }}</span>
|
||||
</a>
|
||||
<button
|
||||
class="button-default dropdown-item dropdown-item-icon"
|
||||
@click.prevent="reportStatus"
|
||||
|
|
|
@ -55,9 +55,6 @@
|
|||
|
||||
.interactive {
|
||||
.svg-inline--fa {
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
animation: unset;
|
||||
}
|
||||
animation-duration: 0.6s;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
<template>
|
||||
<div
|
||||
class="list"
|
||||
role="list"
|
||||
>
|
||||
<div class="list">
|
||||
<div
|
||||
v-for="item in items"
|
||||
:key="getKey(item)"
|
||||
class="list-item"
|
||||
role="listitem"
|
||||
>
|
||||
<slot
|
||||
name="item"
|
||||
|
|
|
@ -93,6 +93,9 @@ const MentionLink = {
|
|||
this.highlightType
|
||||
]
|
||||
},
|
||||
useAtIcon () {
|
||||
return this.mergedConfig.useAtIcon
|
||||
},
|
||||
isRemote () {
|
||||
return this.userName !== this.userNameFull
|
||||
},
|
||||
|
|
|
@ -157,9 +157,6 @@
|
|||
box-shadow: var(--panelShadow);
|
||||
transition-property: transform;
|
||||
transition-duration: 0.25s;
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
transition: unset;
|
||||
}
|
||||
transform: translateX(0);
|
||||
z-index: 1001;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
|
|
@ -6,7 +6,6 @@ import UserCard from '../user_card/user_card.vue'
|
|||
import Timeago from '../timeago/timeago.vue'
|
||||
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
||||
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
|
||||
import StillImage from '../still-image/still-image.vue'
|
||||
import { isStatusNotification } from '../../services/notification_utils/notification_utils.js'
|
||||
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||
|
@ -51,8 +50,7 @@ const Notification = {
|
|||
Timeago,
|
||||
Status,
|
||||
RichContent,
|
||||
ConfirmModal,
|
||||
StillImage
|
||||
ConfirmModal
|
||||
},
|
||||
methods: {
|
||||
toggleUserExpanded () {
|
||||
|
|
|
@ -116,13 +116,12 @@
|
|||
scope="global"
|
||||
keypath="notifications.reacted_with"
|
||||
>
|
||||
<still-image
|
||||
<img
|
||||
v-if="notification.emoji_url !== null"
|
||||
class="notification-reaction-emoji"
|
||||
:src="notification.emoji_url"
|
||||
:title="notification.emoji"
|
||||
:alt="notification.emoji"
|
||||
/>
|
||||
:name="notification.emoji"
|
||||
>
|
||||
<span
|
||||
v-else
|
||||
class="emoji-reaction-emoji"
|
||||
|
@ -152,6 +151,7 @@
|
|||
>
|
||||
<Timeago
|
||||
:time="notification.created_at"
|
||||
:with-direction="true"
|
||||
:auto-update="240"
|
||||
/>
|
||||
</router-link>
|
||||
|
|
|
@ -65,8 +65,8 @@
|
|||
|
||||
.follow-request-accept {
|
||||
&:hover {
|
||||
color: $fallback--text;
|
||||
color: var(--text, $fallback--text);
|
||||
color: $fallback--cGreen;
|
||||
color: var(--cGreen, $fallback--text);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,12 +105,9 @@
|
|||
flex: 1;
|
||||
padding-left: 0.8em;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.heading-right, .notification-right {
|
||||
.timeago {
|
||||
display: inline-block;
|
||||
min-width: 6em;
|
||||
min-width: 3em;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,12 +9,11 @@ import StatusContent from '../status_content/status_content.vue'
|
|||
import fileTypeService from '../../services/file_type/file_type.service.js'
|
||||
import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
|
||||
import { reject, map, uniqBy, debounce } from 'lodash'
|
||||
import { usePostLanguageOptions } from 'src/lib/post_language'
|
||||
import suggestor from '../emoji_input/suggestor.js'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
import Checkbox from '../checkbox/checkbox.vue'
|
||||
import Select from '../select/select.vue'
|
||||
|
||||
import iso6391 from 'iso-639-1'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
@ -63,13 +62,6 @@ const deleteDraft = (draftKey) => {
|
|||
localStorage.setItem('drafts', JSON.stringify(draftData));
|
||||
}
|
||||
|
||||
const interfaceToISOLanguage = (ilang) => {
|
||||
const sep = ilang.indexOf("_");
|
||||
return sep < 0 ?
|
||||
ilang :
|
||||
ilang.substr(0, sep);
|
||||
}
|
||||
|
||||
const PostStatusForm = {
|
||||
props: [
|
||||
'statusId',
|
||||
|
@ -137,13 +129,6 @@ const PostStatusForm = {
|
|||
this.$refs.textarea.focus()
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const {postLanguageOptions} = usePostLanguageOptions()
|
||||
|
||||
return {
|
||||
postLanguageOptions,
|
||||
}
|
||||
},
|
||||
data () {
|
||||
const preset = this.$route.query.message
|
||||
let statusText = preset || ''
|
||||
|
@ -153,8 +138,7 @@ const PostStatusForm = {
|
|||
statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser)
|
||||
}
|
||||
|
||||
const { postContentType: contentType, postLanguage: defaultPostLanguage, sensitiveByDefault, sensitiveIfSubject, interfaceLanguage, alwaysShowSubjectInput } = this.$store.getters.mergedConfig
|
||||
const postLanguage = defaultPostLanguage || interfaceToISOLanguage(interfaceLanguage)
|
||||
const { postContentType: contentType, sensitiveByDefault, sensitiveIfSubject, interfaceLanguage } = this.$store.getters.mergedConfig
|
||||
|
||||
let statusParams = {
|
||||
spoilerText: this.subject || '',
|
||||
|
@ -165,7 +149,7 @@ const PostStatusForm = {
|
|||
poll: {},
|
||||
mediaDescriptions: {},
|
||||
visibility: this.suggestedVisibility(),
|
||||
language: postLanguage,
|
||||
language: interfaceLanguage,
|
||||
contentType
|
||||
}
|
||||
|
||||
|
@ -180,7 +164,7 @@ const PostStatusForm = {
|
|||
poll: this.statusPoll || {},
|
||||
mediaDescriptions: this.statusMediaDescriptions || {},
|
||||
visibility: this.statusScope || this.suggestedVisibility(),
|
||||
language: this.statusLanguage || postLanguage,
|
||||
language: this.statusLanguage || interfaceLanguage,
|
||||
contentType: statusContentType
|
||||
}
|
||||
}
|
||||
|
@ -215,10 +199,6 @@ 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 {
|
||||
dropFiles: [],
|
||||
uploadingFiles: false,
|
||||
|
@ -233,10 +213,7 @@ const PostStatusForm = {
|
|||
preview: null,
|
||||
previewLoading: false,
|
||||
emojiInputShown: false,
|
||||
idempotencyKey: '',
|
||||
activeEmojiInput: undefined,
|
||||
activeTextInput: undefined,
|
||||
subjectVisible: showSubject
|
||||
idempotencyKey: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -325,6 +302,9 @@ const PostStatusForm = {
|
|||
...mapState({
|
||||
mobileLayout: state => state.interface.mobileLayout
|
||||
}),
|
||||
isoLanguages () {
|
||||
return iso6391.getAllCodes();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'newStatus': {
|
||||
|
@ -694,33 +674,8 @@ const PostStatusForm = {
|
|||
this.$refs['emoji-input'].resize()
|
||||
},
|
||||
showEmojiPicker () {
|
||||
if (!this.activeEmojiInput || !this.activeTextInput)
|
||||
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
|
||||
this.$refs['textarea'].focus()
|
||||
this.$refs['emoji-input'].triggerShowPicker()
|
||||
},
|
||||
clearError () {
|
||||
this.error = null
|
||||
|
|
|
@ -118,16 +118,13 @@
|
|||
/>
|
||||
</div>
|
||||
<EmojiInput
|
||||
ref="subject-emoji-input"
|
||||
v-if="subjectVisible"
|
||||
v-if="!disableSubject && (newStatus.spoilerText || alwaysShowSubject)"
|
||||
v-model="newStatus.spoilerText"
|
||||
enable-emoji-picker
|
||||
hide-emoji-button
|
||||
:suggest="emojiSuggestor"
|
||||
class="form-control"
|
||||
>
|
||||
<input
|
||||
ref="subject-input"
|
||||
v-model="newStatus.spoilerText"
|
||||
type="text"
|
||||
:placeholder="$t('post_status.content_warning')"
|
||||
|
@ -135,7 +132,6 @@
|
|||
size="1"
|
||||
class="form-post-subject"
|
||||
@input="onSubjectInput"
|
||||
@focus="focusSubjectInput()"
|
||||
>
|
||||
</EmojiInput>
|
||||
<i18n-t
|
||||
|
@ -177,7 +173,6 @@
|
|||
@input="resize"
|
||||
@compositionupdate="resize"
|
||||
@paste="paste"
|
||||
@focus="focusStatusInput()"
|
||||
/>
|
||||
<p
|
||||
v-if="hasStatusLengthLimit"
|
||||
|
@ -208,11 +203,11 @@
|
|||
class="form-control"
|
||||
>
|
||||
<option
|
||||
v-for="language in postLanguageOptions"
|
||||
:key="language.key"
|
||||
:value="language.value"
|
||||
v-for="language in isoLanguages"
|
||||
:key="language"
|
||||
:value="language"
|
||||
>
|
||||
{{ language.label }}
|
||||
{{ language }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
|
@ -281,15 +276,6 @@
|
|||
>
|
||||
<FAIcon icon="poll-h" />
|
||||
</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>
|
||||
<button
|
||||
v-if="posting"
|
||||
|
@ -470,7 +456,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.media-upload-icon, .poll-icon, .emoji-icon, .spoiler-icon {
|
||||
.media-upload-icon, .poll-icon, .emoji-icon {
|
||||
font-size: 1.85em;
|
||||
line-height: 1.1;
|
||||
flex: 1;
|
||||
|
@ -513,11 +499,6 @@
|
|||
|
||||
.poll-icon {
|
||||
order: 3;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.spoiler-icon {
|
||||
order: 4;
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
|
|
|
@ -74,9 +74,6 @@
|
|||
|
||||
.interactive {
|
||||
.svg-inline--fa {
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
animation: unset;
|
||||
}
|
||||
animation-duration: 0.6s;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import ScopeSelector from 'src/components/scope_selector/scope_selector.vue'
|
|||
import IntegerSetting from '../helpers/integer_setting.vue'
|
||||
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
|
||||
|
||||
import { usePostLanguageOptions } from 'src/lib/post_language'
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
import ServerSideIndicator from '../helpers/server_side_indicator.vue'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
|
@ -18,11 +17,6 @@ library.add(
|
|||
)
|
||||
|
||||
const GeneralTab = {
|
||||
setup() {
|
||||
const {postLanguageOptions} = usePostLanguageOptions()
|
||||
|
||||
return {postLanguageOptions}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
subjectLineOptions: ['email', 'noop', 'masto'].map(mode => ({
|
||||
|
@ -124,12 +118,6 @@ const GeneralTab = {
|
|||
this.$store.dispatch('setOption', { name: 'translationLanguage', value: val })
|
||||
}
|
||||
},
|
||||
postLanguage: {
|
||||
get: function () { return this.$store.getters.mergedConfig.postLanguage },
|
||||
set: function (val) {
|
||||
this.$store.dispatch('setOption', { name: 'postLanguage', value: val })
|
||||
}
|
||||
},
|
||||
...SharedComputedObject()
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -147,8 +147,11 @@
|
|||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="displayPageBackgrounds">
|
||||
{{ $t('settings.show_page_backgrounds') }}
|
||||
<BooleanSetting
|
||||
path="showFediLinks"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.show_fedi_links') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -488,6 +491,14 @@
|
|||
</BooleanSetting>
|
||||
</li>
|
||||
</ul>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="useAtIcon"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.use_at_icon') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="mentionLinkShowAvatar">
|
||||
{{ $t('settings.mention_link_show_avatar') }}
|
||||
|
@ -585,15 +596,6 @@
|
|||
{{ $t('settings.post_status_content_type') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
id="postLanguage"
|
||||
path="postLanguage"
|
||||
:options="postLanguageOptions"
|
||||
>
|
||||
{{ $t('settings.post_language') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="alwaysShowNewPostButton"
|
||||
|
|
|
@ -10,20 +10,17 @@ import SelectableList from 'src/components/selectable_list/selectable_list.vue'
|
|||
import ProgressButton from 'src/components/progress_button/progress_button.vue'
|
||||
import withSubscription from 'src/components/../hocs/with_subscription/with_subscription'
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
import withLoadMore from 'src/components/../hocs/with_load_more/with_load_more'
|
||||
|
||||
const BlockList = withLoadMore({
|
||||
const BlockList = withSubscription({
|
||||
fetch: (props, $store) => $store.dispatch('fetchBlocks'),
|
||||
select: (props, $store) => get($store.state.users.currentUser, 'blockIds', []),
|
||||
childPropName: 'items',
|
||||
destroy: () => {}
|
||||
childPropName: 'items'
|
||||
})(SelectableList)
|
||||
|
||||
const MuteList = withLoadMore({
|
||||
const MuteList = withSubscription({
|
||||
fetch: (props, $store) => $store.dispatch('fetchMutes'),
|
||||
select: (props, $store) => get($store.state.users.currentUser, 'muteIds', []),
|
||||
childPropName: 'items',
|
||||
destroy: () => {}
|
||||
childPropName: 'items'
|
||||
})(SelectableList)
|
||||
|
||||
const DomainMuteList = withSubscription({
|
||||
|
|
|
@ -33,7 +33,6 @@ const ProfileTab = {
|
|||
newName: this.$store.state.users.currentUser.name_unescaped,
|
||||
newBio: unescape(this.$store.state.users.currentUser.description),
|
||||
newLocked: this.$store.state.users.currentUser.locked,
|
||||
newPermitFollowback: this.$store.state.users.currentUser.permit_followback,
|
||||
newFields: this.$store.state.users.currentUser.fields.map(field => ({ name: field.name, value: field.value })),
|
||||
showRole: this.$store.state.users.currentUser.show_role,
|
||||
role: this.$store.state.users.currentUser.role,
|
||||
|
@ -136,7 +135,6 @@ const ProfileTab = {
|
|||
bot: this.bot,
|
||||
show_role: this.showRole,
|
||||
status_ttl_days: this.expirePosts ? this.newPostTTLDays : -1,
|
||||
permit_followback: this.permit_followback,
|
||||
accepts_direct_messages_from: this.userAcceptsDirectMessagesFrom
|
||||
/* eslint-enable camelcase */
|
||||
}
|
||||
|
|
|
@ -259,19 +259,6 @@
|
|||
<BooleanSetting path="serverSide_locked">
|
||||
{{ $t('settings.lock_account_description') }}
|
||||
</BooleanSetting>
|
||||
<ul
|
||||
class="setting-list suboptions"
|
||||
:class="[{disabled: !serverSide_locked}]"
|
||||
>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="serverSide_permitFollowback"
|
||||
:disabled="!serverSide_locked"
|
||||
>
|
||||
{{ $t('settings.permit_followback_description') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="serverSide_discoverable">
|
||||
|
|
|
@ -268,10 +268,6 @@
|
|||
.side-drawer {
|
||||
overflow-x: hidden;
|
||||
transition-timing-function: cubic-bezier(0, 1, 0.5, 1);
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
transition-timing-function: unset;
|
||||
transition: unset;
|
||||
}
|
||||
transition: 0.35s;
|
||||
transition-property: transform;
|
||||
margin: 0 0 0 -100px;
|
||||
|
|
|
@ -20,7 +20,6 @@ import generateProfileLink from 'src/services/user_profile_link_generator/user_p
|
|||
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
||||
import { muteWordHits } from '../../services/status_parser/status_parser.js'
|
||||
import { unescape, uniqBy } from 'lodash'
|
||||
import StillImage from '../still-image/still-image.vue'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
@ -118,8 +117,7 @@ const Status = {
|
|||
RichContent,
|
||||
MentionLink,
|
||||
MentionsLine,
|
||||
QuoteButton,
|
||||
StillImage
|
||||
QuoteButton
|
||||
},
|
||||
props: [
|
||||
'statusoid',
|
||||
|
|
|
@ -174,12 +174,12 @@
|
|||
>
|
||||
@{{ status.user.screen_name_ui }}
|
||||
</router-link>
|
||||
<StillImage
|
||||
<img
|
||||
v-if="!!(status.user && status.user.favicon)"
|
||||
class="status-favicon"
|
||||
:src="status.user.favicon"
|
||||
:title="faviconAlt(status)"
|
||||
/>
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -190,7 +190,7 @@
|
|||
>
|
||||
<Timeago
|
||||
:time="status.created_at"
|
||||
:with-direction="!compact"
|
||||
:with-direction="true"
|
||||
:auto-update="60"
|
||||
/>
|
||||
</router-link>
|
||||
|
|
|
@ -17,9 +17,6 @@
|
|||
|
||||
.emoji:hover {
|
||||
transform: scale(1.4);
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
transition: unset;
|
||||
}
|
||||
transition: 0.05s;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ const StillImage = {
|
|||
return {
|
||||
stopGifs: this.$store.getters.mergedConfig.stopGifs || window.matchMedia('(prefers-reduced-motion: reduce)').matches,
|
||||
isAnimated: false,
|
||||
imageTypeLabel: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -40,24 +39,14 @@ const StillImage = {
|
|||
this.imageLoadError && this.imageLoadError()
|
||||
},
|
||||
detectAnimation (image) {
|
||||
const mediaProxyAvailable = this.$store.state.instance.mediaProxyAvailable
|
||||
|
||||
if (!mediaProxyAvailable) {
|
||||
// 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
|
||||
// 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) {
|
||||
// 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
|
||||
this.isAnimated = true
|
||||
return
|
||||
}
|
||||
this.detectWithoutMediaProxy(image)
|
||||
} else {
|
||||
this.detectWithMediaProxy(image)
|
||||
if (this.mimetype === 'image/gif' || this.src.endsWith('.gif')) {
|
||||
this.isAnimated = true
|
||||
return
|
||||
}
|
||||
},
|
||||
detectAnimationWithFetch (image) {
|
||||
// harmless CORS errors without-- clean console with
|
||||
if (!this.$store.state.instance.mediaProxyAvailable) return
|
||||
// Animated JPEGs?
|
||||
if (!(this.src.endsWith('.webp') || this.src.endsWith('.png'))) return
|
||||
// Browser Cache should ensure image doesn't get loaded twice if cache exists
|
||||
fetch(image.src, {
|
||||
referrerPolicy: 'same-origin'
|
||||
|
@ -66,20 +55,12 @@ const StillImage = {
|
|||
// We don't need to read the whole file so only call it once
|
||||
data.body.getReader().read()
|
||||
.then(reader => {
|
||||
// Ordered from least to most intensive
|
||||
if (this.isGIF(reader.value)) {
|
||||
if (this.src.endsWith('.webp') && this.isAnimatedWEBP(reader.value)) {
|
||||
this.isAnimated = true
|
||||
this.setLabel('GIF')
|
||||
return
|
||||
}
|
||||
if (this.isAnimatedWEBP(reader.value)) {
|
||||
if (this.src.endsWith('.png') && this.isAnimatedPNG(reader.value)) {
|
||||
this.isAnimated = true
|
||||
this.setLabel('WEBP')
|
||||
return
|
||||
}
|
||||
if (this.isAnimatedPNG(reader.value)) {
|
||||
this.isAnimated = true
|
||||
this.setLabel('APNG')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -87,53 +68,6 @@ const StillImage = {
|
|||
// 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) {
|
||||
this.imageTypeLabel = name;
|
||||
},
|
||||
isGIF (data) {
|
||||
// I am a perfectly sane individual
|
||||
//
|
||||
// GIF HEADER CHUNK
|
||||
// === START HEADER ===
|
||||
// 47 49 46 38 ("GIF8")
|
||||
const gifHeader = [0x47, 0x49, 0x46];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (data[i] !== gifHeader[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
isAnimatedWEBP (data) {
|
||||
/**
|
||||
* WEBP HEADER CHUNK
|
||||
|
@ -167,55 +101,16 @@ const StillImage = {
|
|||
const idatPos = str.indexOf('IDAT')
|
||||
return (str.substring(0, idatPos > 0 ? idatPos : 0).indexOf('acTL') > 0)
|
||||
},
|
||||
drawThumbnail() {
|
||||
const canvas = this.$refs.canvas;
|
||||
if (!canvas) return;
|
||||
|
||||
const context = canvas.getContext('2d');
|
||||
const image = this.$refs.src;
|
||||
const parentElement = canvas.parentElement;
|
||||
|
||||
// Draw the quick, unscaled version first
|
||||
context.drawImage(image, 0, 0, parentElement.clientWidth, parentElement.clientHeight);
|
||||
|
||||
// Use requestAnimationFrame to schedule the scaling to the next frame
|
||||
requestAnimationFrame(() => {
|
||||
// Compute scaling ratio between the natural dimensions of the image and its display dimensions
|
||||
const scalingRatioWidth = parentElement.clientWidth / image.naturalWidth;
|
||||
const scalingRatioHeight = parentElement.clientHeight / image.naturalHeight;
|
||||
|
||||
// Adjust for high-DPI displays
|
||||
const ratio = window.devicePixelRatio || 1;
|
||||
canvas.width = image.naturalWidth * scalingRatioWidth * ratio;
|
||||
canvas.height = image.naturalHeight * scalingRatioHeight * ratio;
|
||||
canvas.style.width = `${parentElement.clientWidth}px`;
|
||||
canvas.style.height = `${parentElement.clientHeight}px`;
|
||||
context.scale(ratio, ratio);
|
||||
|
||||
// Maintain the aspect ratio of the image
|
||||
const imgAspectRatio = image.naturalWidth / image.naturalHeight;
|
||||
const canvasAspectRatio = parentElement.clientWidth / parentElement.clientHeight;
|
||||
|
||||
let drawWidth, drawHeight;
|
||||
|
||||
if (imgAspectRatio > canvasAspectRatio) {
|
||||
drawWidth = parentElement.clientWidth;
|
||||
drawHeight = parentElement.clientWidth / imgAspectRatio;
|
||||
} else {
|
||||
drawHeight = parentElement.clientHeight;
|
||||
drawWidth = parentElement.clientHeight * imgAspectRatio;
|
||||
}
|
||||
|
||||
context.clearRect(0, 0, canvas.width, canvas.height); // Clear the previous unscaled image
|
||||
context.imageSmoothingEnabled = true;
|
||||
context.imageSmoothingQuality = 'high';
|
||||
|
||||
// Draw the good one for realsies
|
||||
const dx = (parentElement.clientWidth - drawWidth) / 2;
|
||||
const dy = (parentElement.clientHeight - drawHeight) / 2;
|
||||
context.drawImage(image, dx, dy, drawWidth, drawHeight);
|
||||
});
|
||||
}
|
||||
drawThumbnail () {
|
||||
const canvas = this.$refs.canvas
|
||||
if (!this.$refs.canvas) return
|
||||
const image = this.$refs.src
|
||||
const width = image.naturalWidth
|
||||
const height = image.naturalHeight
|
||||
canvas.width = width
|
||||
canvas.height = height
|
||||
canvas.getContext('2d').drawImage(image, 0, 0, width, height)
|
||||
}
|
||||
},
|
||||
updated () {
|
||||
// On computed animated change
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
<template>
|
||||
<div
|
||||
ref="still-image"
|
||||
class="still-image"
|
||||
:class="{ animated: animated }"
|
||||
:style="style"
|
||||
>
|
||||
<div
|
||||
v-if="animated && imageTypeLabel"
|
||||
class="image-type-label">
|
||||
{{ imageTypeLabel }}
|
||||
</div>
|
||||
<canvas
|
||||
v-if="animated"
|
||||
ref="canvas"
|
||||
|
@ -63,26 +57,30 @@
|
|||
}
|
||||
}
|
||||
|
||||
.image-type-label {
|
||||
position: absolute;
|
||||
top: 0.25em;
|
||||
left: 0.25em;
|
||||
line-height: 1;
|
||||
font-size: 0.6em;
|
||||
background: rgba(127, 127, 127, 0.5);
|
||||
color: #fff;
|
||||
padding: 2px 4px;
|
||||
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
|
||||
z-index: 2;
|
||||
visibility: var(--_still-image-label-visibility, visible);
|
||||
}
|
||||
|
||||
&.animated {
|
||||
&::before {
|
||||
zoom: var(--_still_image-label-scale, 1);
|
||||
content: 'gif';
|
||||
position: absolute;
|
||||
line-height: 1;
|
||||
font-size: 0.7em;
|
||||
top: 0.5em;
|
||||
left: 0.5em;
|
||||
background: rgba(127, 127, 127, 0.5);
|
||||
color: #fff;
|
||||
display: block;
|
||||
padding: 2px 4px;
|
||||
border-radius: $fallback--tooltipRadius;
|
||||
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
|
||||
z-index: 2;
|
||||
visibility: var(--_still-image-label-visibility, visible);
|
||||
}
|
||||
|
||||
&:hover canvas {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover .image-type-label {
|
||||
&:hover::before {
|
||||
visibility: var(--_still-image-label-visibility, hidden);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,9 +62,6 @@
|
|||
border-top-right-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
transform: translateY(-100%);
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
transition: unset;
|
||||
}
|
||||
transition: transform 100ms;
|
||||
}
|
||||
|
||||
|
@ -92,9 +89,6 @@
|
|||
|
||||
svg {
|
||||
margin-left: 0.6em;
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
transition: unset;
|
||||
}
|
||||
transition: transform 100ms;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
--_avatarShadowBox: var(--avatarStatusShadow);
|
||||
--_avatarShadowFilter: var(--avatarStatusShadowFilter);
|
||||
--_avatarShadowInset: var(--avatarStatusShadowInset);
|
||||
// --_still-image-label-visibility: hidden;
|
||||
--_still-image-label-visibility: hidden;
|
||||
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
|
|
@ -8,6 +8,8 @@ import Select from '../select/select.vue'
|
|||
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
||||
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
|
||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||
import facActivityPub from '../../assets/icons/activity-pub'
|
||||
import { notificationsFromStore } from 'src/services/notification_utils/notification_utils.js'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
@ -15,7 +17,7 @@ import {
|
|||
faRss,
|
||||
faSearchPlus,
|
||||
faExternalLinkAlt,
|
||||
faEdit
|
||||
faEdit,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
|
@ -23,7 +25,8 @@ library.add(
|
|||
faBell,
|
||||
faSearchPlus,
|
||||
faExternalLinkAlt,
|
||||
faEdit
|
||||
faEdit,
|
||||
facActivityPub
|
||||
)
|
||||
|
||||
export default {
|
||||
|
@ -34,7 +37,9 @@ export default {
|
|||
return {
|
||||
followRequestInProgress: false,
|
||||
betterShadow: this.$store.state.interface.browserSupport.cssFilter,
|
||||
showingConfirmMute: false
|
||||
showingConfirmMute: false,
|
||||
showingApproveConfirmDialog: false,
|
||||
showingDenyConfirmDialog: false
|
||||
}
|
||||
},
|
||||
created () {
|
||||
|
@ -44,6 +49,16 @@ export default {
|
|||
user () {
|
||||
return this.$store.getters.findUser(this.userId)
|
||||
},
|
||||
showFediLinks () {
|
||||
return this.$store.getters.mergedConfig.showFediLinks === true
|
||||
},
|
||||
fediLinkURL () {
|
||||
try {
|
||||
return this.user._original.pleroma.ap_id.replace(/^https?/, 'web+ap')
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
},
|
||||
relationship () {
|
||||
return this.$store.getters.relationship(this.userId)
|
||||
},
|
||||
|
@ -117,6 +132,12 @@ export default {
|
|||
shouldConfirmMute () {
|
||||
return this.mergedConfig.modalOnMute
|
||||
},
|
||||
shouldConfirmApprove () {
|
||||
return this.mergedConfig.modalOnApproveFollow
|
||||
},
|
||||
shouldConfirmDeny () {
|
||||
return this.mergedConfig.modalOnDenyFollow
|
||||
},
|
||||
...mapGetters(['mergedConfig'])
|
||||
},
|
||||
components: {
|
||||
|
@ -193,6 +214,58 @@ export default {
|
|||
},
|
||||
mentionUser () {
|
||||
this.$store.dispatch('openPostStatusModal', { replyTo: true, repliedUser: this.user })
|
||||
},
|
||||
showApproveConfirmDialog () {
|
||||
this.showingApproveConfirmDialog = true
|
||||
},
|
||||
hideApproveConfirmDialog () {
|
||||
this.showingApproveConfirmDialog = false
|
||||
},
|
||||
showDenyConfirmDialog () {
|
||||
this.showingDenyConfirmDialog = true
|
||||
},
|
||||
hideDenyConfirmDialog () {
|
||||
this.showingDenyConfirmDialog = false
|
||||
},
|
||||
approveUser () {
|
||||
if (this.shouldConfirmApprove) {
|
||||
this.showApproveConfirmDialog()
|
||||
} else {
|
||||
this.doApprove()
|
||||
}
|
||||
},
|
||||
doApprove () {
|
||||
const notifId = this.findFollowRequestNotificationId()
|
||||
this.$store.dispatch('approveUser', this.user.id)
|
||||
this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId })
|
||||
this.$store.dispatch('updateNotification', {
|
||||
id: notifId,
|
||||
updater: notification => {
|
||||
notification.type = 'follow'
|
||||
}
|
||||
})
|
||||
this.hideApproveConfirmDialog()
|
||||
},
|
||||
denyUser () {
|
||||
if (this.shouldConfirmDeny) {
|
||||
this.showDenyConfirmDialog()
|
||||
} else {
|
||||
this.doDeny()
|
||||
}
|
||||
},
|
||||
doDeny () {
|
||||
const notifId = this.findFollowRequestNotificationId()
|
||||
this.$store.dispatch('denyUser', this.user.id)
|
||||
.then(() => {
|
||||
this.$store.dispatch('dismissNotificationLocal', { id: notifId })
|
||||
})
|
||||
this.hideDenyConfirmDialog()
|
||||
},
|
||||
findFollowRequestNotificationId () {
|
||||
const notif = notificationsFromStore(this.$store).find(
|
||||
(notif) => notif.from_profile.id === this.user.id && notif.type === 'follow_request'
|
||||
)
|
||||
return notif && notif.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,14 +97,13 @@
|
|||
.user-info {
|
||||
color: $fallback--lightText;
|
||||
color: var(--lightText, $fallback--lightText);
|
||||
padding: 0 26px;
|
||||
padding: 0 1.5em;
|
||||
|
||||
.container {
|
||||
min-width: 0;
|
||||
padding: 16px 0 6px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
max-height: 56px;
|
||||
|
||||
> * {
|
||||
min-width: 0;
|
||||
|
@ -165,7 +164,8 @@
|
|||
}
|
||||
|
||||
.user-summary {
|
||||
display: block;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-left: 0.6em;
|
||||
text-align: left;
|
||||
text-overflow: ellipsis;
|
||||
|
@ -184,6 +184,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.user-names {
|
||||
flex-grow: 1;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
@ -228,6 +233,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
.button-line {
|
||||
display: flex;
|
||||
min-width: 10em;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.user-meta {
|
||||
margin-bottom: .15em;
|
||||
display: flex;
|
||||
|
@ -242,6 +254,12 @@
|
|||
text-align: left;
|
||||
}
|
||||
|
||||
.requested_by {
|
||||
.btn {
|
||||
margin-right: .25em;
|
||||
}
|
||||
}
|
||||
|
||||
.highlighter {
|
||||
flex: 0 1 auto;
|
||||
display: flex;
|
||||
|
|
|
@ -1,175 +1,108 @@
|
|||
<template>
|
||||
<div
|
||||
class="user-card"
|
||||
:class="classes"
|
||||
>
|
||||
<div
|
||||
:class="{ 'hide-bio': hideBio }"
|
||||
:style="style"
|
||||
class="background-image"
|
||||
/>
|
||||
<div class="user-card" :class="classes">
|
||||
<div :class="{ 'hide-bio': hideBio }" :style="style" class="background-image" />
|
||||
<div class="panel-heading -flexible-height">
|
||||
<div class="user-info">
|
||||
<div class="container">
|
||||
<a
|
||||
v-if="allowZoomingAvatar"
|
||||
class="user-info-avatar-link"
|
||||
@click="zoomAvatar"
|
||||
>
|
||||
<UserAvatar
|
||||
:better-shadow="betterShadow"
|
||||
:user="user"
|
||||
/>
|
||||
<a v-if="allowZoomingAvatar" class="user-info-avatar-link" @click="zoomAvatar">
|
||||
<UserAvatar :better-shadow="betterShadow" :user="user" />
|
||||
<div class="user-info-avatar-link-overlay">
|
||||
<FAIcon
|
||||
class="fa-scale-110 fa-old-padding"
|
||||
icon="search-plus"
|
||||
/>
|
||||
<FAIcon class="fa-scale-110 fa-old-padding" icon="search-plus" />
|
||||
</div>
|
||||
</a>
|
||||
<router-link
|
||||
v-else
|
||||
:to="userProfileLink(user)"
|
||||
>
|
||||
<UserAvatar
|
||||
:better-shadow="betterShadow"
|
||||
:user="user"
|
||||
/>
|
||||
<router-link v-else :to="userProfileLink(user)">
|
||||
<UserAvatar :better-shadow="betterShadow" :user="user" />
|
||||
</router-link>
|
||||
<div class="user-summary">
|
||||
<div class="top-line">
|
||||
<RichContent
|
||||
:title="user.name"
|
||||
class="user-name"
|
||||
:html="user.name"
|
||||
:emoji="user.emoji"
|
||||
/>
|
||||
<button
|
||||
v-if="!isOtherUser && user.is_local"
|
||||
class="button-unstyled edit-profile-button"
|
||||
@click.stop="openProfileTab"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
class="icon"
|
||||
icon="edit"
|
||||
:title="$t('user_card.edit_profile')"
|
||||
/>
|
||||
</button>
|
||||
<a
|
||||
v-if="isOtherUser && !user.is_local"
|
||||
:href="user.statusnet_profile_url"
|
||||
target="_blank"
|
||||
class="button-unstyled external-link-button"
|
||||
>
|
||||
<FAIcon
|
||||
class="icon"
|
||||
icon="external-link-alt"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
v-if="isOtherUser"
|
||||
:href="user.statusnet_profile_url + '.rss'"
|
||||
target="_blank"
|
||||
class="button-unstyled external-link-button"
|
||||
>
|
||||
<FAIcon
|
||||
class="icon"
|
||||
icon="rss"
|
||||
/>
|
||||
</a>
|
||||
<AccountActions
|
||||
v-if="isOtherUser && loggedIn"
|
||||
:user="user"
|
||||
:relationship="relationship"
|
||||
/>
|
||||
<div class="user-names">
|
||||
<div class="top-line">
|
||||
<RichContent :title="user.name" class="user-name" :html="user.name" :emoji="user.emoji" />
|
||||
</div>
|
||||
<div class="bottom-line">
|
||||
<router-link class="user-screen-name" :title="user.screen_name_ui" :to="userProfileLink(user)">
|
||||
@{{ user.screen_name_ui }}
|
||||
</router-link>
|
||||
<template v-if="!hideBio">
|
||||
<span v-if="user.deactivated" class="alert user-role">
|
||||
{{ $t('user_card.deactivated') }}
|
||||
</span>
|
||||
<span v-if="!!visibleRole" class="alert user-role">
|
||||
{{ $t(`general.role.${visibleRole}`) }}
|
||||
</span>
|
||||
<span v-if="user.bot" class="alert user-role">
|
||||
{{ $t('user_card.bot') }}
|
||||
</span>
|
||||
</template>
|
||||
<span v-if="user.locked">
|
||||
<FAIcon class="lock-icon" icon="lock" size="sm" />
|
||||
</span>
|
||||
<span v-if="!mergedConfig.hideUserStats && !hideBio" class="dailyAvg">{{ dailyAvg }} {{
|
||||
$t('user_card.per_day') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom-line">
|
||||
<router-link
|
||||
class="user-screen-name"
|
||||
:title="user.screen_name_ui"
|
||||
:to="userProfileLink(user)"
|
||||
>
|
||||
@{{ user.screen_name_ui }}
|
||||
</router-link>
|
||||
<template v-if="!hideBio">
|
||||
<span
|
||||
v-if="user.deactivated"
|
||||
class="alert user-role"
|
||||
>
|
||||
{{ $t('user_card.deactivated') }}
|
||||
</span>
|
||||
<span
|
||||
v-if="!!visibleRole"
|
||||
class="alert user-role"
|
||||
>
|
||||
{{ $t(`general.role.${visibleRole}`) }}
|
||||
</span>
|
||||
<span
|
||||
v-if="user.bot"
|
||||
class="alert user-role"
|
||||
>
|
||||
{{ $t('user_card.bot') }}
|
||||
</span>
|
||||
</template>
|
||||
<span v-if="user.locked">
|
||||
<FAIcon
|
||||
class="lock-icon"
|
||||
icon="lock"
|
||||
size="sm"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
v-if="!mergedConfig.hideUserStats && !hideBio"
|
||||
class="dailyAvg"
|
||||
>{{ dailyAvg }} {{ $t('user_card.per_day') }}</span>
|
||||
<div class="button-line">
|
||||
<button v-if="!isOtherUser && user.is_local" class="button-unstyled edit-profile-button"
|
||||
@click.stop="openProfileTab">
|
||||
<FAIcon fixed-width class="icon" icon="edit" :title="$t('user_card.edit_profile')" />
|
||||
</button>
|
||||
<a v-if="isOtherUser && !user.is_local" :href="user.statusnet_profile_url" target="_blank"
|
||||
class="button-unstyled external-link-button" :title="$t('user_card.external_source')">
|
||||
<FAIcon class="icon" icon="external-link-alt" />
|
||||
</a>
|
||||
<a v-if="fediLinkURL && showFediLinks && !hideBio" :href="fediLinkURL" target="_blank"
|
||||
class="button-unstyled external-link-button" :title="$t('user_card.open_fedi_link')">
|
||||
<FAIcon class="icon" :icon="['fac', 'activity-pub']" />
|
||||
</a>
|
||||
<a v-if="isOtherUser" :href="user.statusnet_profile_url + '.rss'" target="_blank"
|
||||
class="button-unstyled external-link-button">
|
||||
<FAIcon class="icon" icon="rss" />
|
||||
</a>
|
||||
<AccountActions v-if="isOtherUser && loggedIn" :user="user" :relationship="relationship" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-meta">
|
||||
<div
|
||||
v-if="relationship.blocked_by && loggedIn && isOtherUser"
|
||||
class="blocking"
|
||||
>
|
||||
<div v-if="relationship.blocked_by && loggedIn && isOtherUser" class="blocking">
|
||||
{{ $t('user_card.blocks_you') }}
|
||||
</div>
|
||||
<div
|
||||
v-if="relationship.followed_by && loggedIn && isOtherUser"
|
||||
class="following"
|
||||
>
|
||||
<div v-if="relationship.followed_by && loggedIn && isOtherUser" class="following">
|
||||
{{ $t('user_card.follows_you') }}
|
||||
</div>
|
||||
<div
|
||||
v-if="relationship.requested_by && loggedIn && isOtherUser"
|
||||
class="requested_by"
|
||||
style="white-space: nowrap;"
|
||||
>
|
||||
{{ $t('user_card.requested_by') }}
|
||||
<button
|
||||
class="btn button-default"
|
||||
:title="$t('tool_tip.accept_follow_request')"
|
||||
@click="approveUser()"
|
||||
>
|
||||
<FAIcon
|
||||
icon="check"
|
||||
class="fa-scale-110 fa-old-padding follow-request-accept"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="btn button-default"
|
||||
:title="$t('tool_tip.reject_follow_request')"
|
||||
@click="denyUser()"
|
||||
>
|
||||
<FAIcon
|
||||
icon="times"
|
||||
class="fa-scale-110 fa-old-padding follow-request-reject"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-if="isOtherUser && (loggedIn || !switcher)"
|
||||
class="highlighter"
|
||||
>
|
||||
<div v-if="isOtherUser && (loggedIn || !switcher)" class="highlighter">
|
||||
<!-- id's need to be unique, otherwise vue confuses which user-card checkbox belongs to -->
|
||||
<input
|
||||
v-if="userHighlightType !== 'disabled'"
|
||||
:id="'userHighlightColorTx'+user.id"
|
||||
v-model="userHighlightColor"
|
||||
class="userHighlightText"
|
||||
type="text"
|
||||
>
|
||||
<input
|
||||
v-if="userHighlightType !== 'disabled'"
|
||||
:id="'userHighlightColor'+user.id"
|
||||
v-model="userHighlightColor"
|
||||
class="userHighlightCl"
|
||||
type="color"
|
||||
>
|
||||
<input v-if="userHighlightType !== 'disabled'" :id="'userHighlightColorTx' + user.id"
|
||||
v-model="userHighlightColor" class="userHighlightText" type="text">
|
||||
<input v-if="userHighlightType !== 'disabled'" :id="'userHighlightColor' + user.id" v-model="userHighlightColor"
|
||||
class="userHighlightCl" type="color">
|
||||
{{ ' ' }}
|
||||
<Select
|
||||
:id="'userHighlightSel'+user.id"
|
||||
v-model="userHighlightType"
|
||||
class="userHighlightSel"
|
||||
>
|
||||
<Select :id="'userHighlightSel' + user.id" v-model="userHighlightType" class="userHighlightSel">
|
||||
<option value="disabled">
|
||||
{{ $t('user_card.highlight.disabled') }}
|
||||
</option>
|
||||
|
@ -185,144 +118,93 @@
|
|||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="loggedIn && isOtherUser"
|
||||
class="user-interactions"
|
||||
>
|
||||
<div v-if="loggedIn && isOtherUser" class="user-interactions">
|
||||
<div class="btn-group">
|
||||
<FollowButton
|
||||
:relationship="relationship"
|
||||
:user="user"
|
||||
:disabled="relationship.blocked_by"
|
||||
/>
|
||||
<FollowButton :relationship="relationship" :user="user" :disabled="relationship.blocked_by" />
|
||||
<template v-if="relationship.following">
|
||||
<ProgressButton
|
||||
v-if="!relationship.subscribing"
|
||||
class="btn button-default"
|
||||
:click="subscribeUser"
|
||||
:title="$t('user_card.subscribe')"
|
||||
>
|
||||
<ProgressButton v-if="!relationship.subscribing" class="btn button-default" :click="subscribeUser"
|
||||
:title="$t('user_card.subscribe')">
|
||||
<FAIcon icon="bell" />
|
||||
</ProgressButton>
|
||||
<ProgressButton
|
||||
v-else
|
||||
class="btn button-default toggled"
|
||||
:click="unsubscribeUser"
|
||||
:title="$t('user_card.unsubscribe')"
|
||||
>
|
||||
<ProgressButton v-else class="btn button-default toggled" :click="unsubscribeUser"
|
||||
:title="$t('user_card.unsubscribe')">
|
||||
<FALayers>
|
||||
<FAIcon
|
||||
icon="rss"
|
||||
transform="left-5 shrink-6 up-3 rotate-20"
|
||||
flip="horizontal"
|
||||
/>
|
||||
<FAIcon
|
||||
icon="rss"
|
||||
transform="right-5 shrink-6 up-3 rotate-20"
|
||||
/>
|
||||
<FAIcon icon="rss" transform="left-5 shrink-6 up-3 rotate-20" flip="horizontal" />
|
||||
<FAIcon icon="rss" transform="right-5 shrink-6 up-3 rotate-20" />
|
||||
<FAIcon icon="bell" />
|
||||
</FALayers>
|
||||
</ProgressButton>
|
||||
</template>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
v-if="relationship.muting"
|
||||
class="btn button-default btn-block toggled"
|
||||
:disabled="user.deactivated"
|
||||
@click="unmuteUser"
|
||||
>
|
||||
<button v-if="relationship.muting" class="btn button-default btn-block toggled" :disabled="user.deactivated"
|
||||
@click="unmuteUser">
|
||||
{{ $t('user_card.muted') }}
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="btn button-default btn-block"
|
||||
:disabled="user.deactivated"
|
||||
@click="muteUser"
|
||||
>
|
||||
<button v-else class="btn button-default btn-block" :disabled="user.deactivated" @click="muteUser">
|
||||
{{ $t('user_card.mute') }}
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
class="btn button-default btn-block"
|
||||
:disabled="user.deactivated"
|
||||
@click="mentionUser"
|
||||
>
|
||||
<button class="btn button-default btn-block" :disabled="user.deactivated" @click="mentionUser">
|
||||
{{ $t('user_card.mention') }}
|
||||
</button>
|
||||
</div>
|
||||
<ModerationTools
|
||||
v-if="loggedIn.role === 'admin'"
|
||||
:user="user"
|
||||
/>
|
||||
<ModerationTools v-if="loggedIn.role === 'admin'" :user="user" />
|
||||
</div>
|
||||
<div
|
||||
v-if="!loggedIn && user.is_local"
|
||||
class="user-interactions"
|
||||
>
|
||||
<div v-if="!loggedIn && user.is_local" class="user-interactions">
|
||||
<RemoteFollow :user="user" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!hideBio"
|
||||
class="panel-body"
|
||||
>
|
||||
<div
|
||||
v-if="!mergedConfig.hideUserStats && switcher"
|
||||
class="user-counts"
|
||||
>
|
||||
<div
|
||||
class="user-count"
|
||||
@click.prevent="setProfileView('statuses')"
|
||||
>
|
||||
<div v-if="!hideBio" class="panel-body">
|
||||
<div v-if="!mergedConfig.hideUserStats && switcher" class="user-counts">
|
||||
<div class="user-count" @click.prevent="setProfileView('statuses')">
|
||||
<h5>{{ $t('user_card.statuses') }}</h5>
|
||||
<span>{{ user.statuses_count }} <br></span>
|
||||
</div>
|
||||
<div
|
||||
class="user-count"
|
||||
@click.prevent="setProfileView('friends')"
|
||||
>
|
||||
<div class="user-count" @click.prevent="setProfileView('friends')">
|
||||
<h5>{{ $t('user_card.followees') }}</h5>
|
||||
<span>{{ hideFollowsCount ? $t('user_card.hidden') : user.friends_count }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="user-count"
|
||||
@click.prevent="setProfileView('followers')"
|
||||
>
|
||||
<div class="user-count" @click.prevent="setProfileView('followers')">
|
||||
<h5>{{ $t('user_card.followers') }}</h5>
|
||||
<span>{{ hideFollowersCount ? $t('user_card.hidden') : user.followers_count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<RichContent
|
||||
v-if="!hideBio"
|
||||
class="user-card-bio"
|
||||
:html="user.description_html"
|
||||
:emoji="user.emoji"
|
||||
:handle-links="true"
|
||||
/>
|
||||
<RichContent v-if="!hideBio" class="user-card-bio" :html="user.description_html" :emoji="user.emoji"
|
||||
:handle-links="true" />
|
||||
</div>
|
||||
<teleport to="#modal">
|
||||
<confirm-modal
|
||||
v-if="showingConfirmMute"
|
||||
:title="$t('user_card.mute_confirm_title')"
|
||||
<confirm-modal v-if="showingConfirmMute" :title="$t('user_card.mute_confirm_title')"
|
||||
:confirm-text="$t('user_card.mute_confirm_accept_button')"
|
||||
:cancel-text="$t('user_card.mute_confirm_cancel_button')"
|
||||
@accepted="doMuteUser"
|
||||
@cancelled="hideConfirmMute"
|
||||
>
|
||||
<i18n-t
|
||||
keypath="user_card.mute_confirm"
|
||||
tag="span"
|
||||
>
|
||||
:cancel-text="$t('user_card.mute_confirm_cancel_button')" @accepted="doMuteUser" @cancelled="hideConfirmMute">
|
||||
<i18n-t keypath="user_card.mute_confirm" tag="span">
|
||||
<template #user>
|
||||
<span
|
||||
v-text="user.screen_name_ui"
|
||||
/>
|
||||
<span v-text="user.screen_name_ui" />
|
||||
</template>
|
||||
</i18n-t>
|
||||
</confirm-modal>
|
||||
<confirm-modal
|
||||
v-if="showingApproveConfirmDialog"
|
||||
:title="$t('user_card.approve_confirm_title')"
|
||||
:confirm-text="$t('user_card.approve_confirm_accept_button')"
|
||||
:cancel-text="$t('user_card.approve_confirm_cancel_button')"
|
||||
@accepted="doApprove"
|
||||
@cancelled="hideApproveConfirmDialog"
|
||||
>
|
||||
{{ $t('user_card.approve_confirm', { user: user.screen_name_ui }) }}
|
||||
</confirm-modal>
|
||||
<confirm-modal
|
||||
v-if="showingDenyConfirmDialog"
|
||||
:title="$t('user_card.deny_confirm_title')"
|
||||
:confirm-text="$t('user_card.deny_confirm_accept_button')"
|
||||
:cancel-text="$t('user_card.deny_confirm_cancel_button')"
|
||||
@accepted="doDeny"
|
||||
@cancelled="hideDenyConfirmDialog"
|
||||
>
|
||||
{{ $t('user_card.deny_confirm', { user: user.screen_name_ui }) }}
|
||||
</confirm-modal>
|
||||
</teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -145,12 +145,10 @@ const UserProfile = {
|
|||
if (user) {
|
||||
loadById(user.id)
|
||||
this.note = user.relationship.note
|
||||
this.$store.dispatch('setDisplayBackground', user.background_image)
|
||||
} else {
|
||||
this.$store.dispatch('fetchUser', userNameOrId)
|
||||
.then(({ id, relationship, background_image }) => {
|
||||
.then(({ id, relationship }) => {
|
||||
this.note = relationship.note
|
||||
this.$store.dispatch('setDisplayBackground', background_image)
|
||||
return loadById(id)
|
||||
})
|
||||
.catch((reason) => {
|
||||
|
@ -227,9 +225,6 @@ const UserProfile = {
|
|||
Conversation,
|
||||
RichContent,
|
||||
FollowedTagList
|
||||
},
|
||||
beforeRouteLeave(to, from) {
|
||||
this.$store.dispatch('setDisplayBackground', null)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -884,6 +884,7 @@
|
|||
"upload_a_photo": "Pujar una foto",
|
||||
"useStreamingApi": "Rebre apunts i notificacions en temps real",
|
||||
"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_one_click_nsfw": "Obre els adjunts NSFW amb només un clic",
|
||||
"user_mutes": "Usuaris",
|
||||
|
|
|
@ -916,6 +916,7 @@
|
|||
"upload_a_photo": "Lade ein Foto hoch",
|
||||
"useStreamingApi": "Empfange Posts und Benachrichtigungen in Echtzeit",
|
||||
"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_contain_fit": "Vorschaubilder nicht zuschneiden",
|
||||
"use_one_click_nsfw": "Heikle Anhänge mit nur einem Klick öffnen",
|
||||
|
|
215
src/i18n/el.json
215
src/i18n/el.json
|
@ -1,215 +0,0 @@
|
|||
{
|
||||
"about": {
|
||||
"mrf": {
|
||||
"keyword": {
|
||||
"keyword_policies": "Πολιτικές λέξεων-κλειδιών",
|
||||
"reject": "Απόρριψη",
|
||||
"replace": "Αντικατάσταση"
|
||||
},
|
||||
"mrf_policies": "Ενεργοποιημένες πολιτικές MRF",
|
||||
"mrf_policies_desc": "",
|
||||
"simple": {
|
||||
"accept": "Αποδοχή",
|
||||
"accept_desc": "Αυτό το instance αποδέχεται μηνύματα μόνο από τα ακόλουθα instances:",
|
||||
"ftl_removal": "Αφαίρεση από το χρονολόγιο \"Γνωστού Δίκτυου\"",
|
||||
"ftl_removal_desc": "Αυτό το instance αφαιρεί αυτά τα instances από το χρονολόγιο \"Γνωστού Δικτύου\":",
|
||||
"quarantine": "Καραντίνα",
|
||||
"quarantine_desc": "Αυτό το instance δε θα στέλνει αναρτήσεις στα ακόλουθα instances:",
|
||||
"reason": "Λόγος",
|
||||
"simple_policies": "Πολιτικές του instance"
|
||||
}
|
||||
}
|
||||
},
|
||||
"announcements": {
|
||||
"all_day_prompt": "Αυτό είναι ένα ολοήμερο συμβάν",
|
||||
"cancel_edit_action": "Ακύρωση",
|
||||
"close_error": "Κλείσιμο",
|
||||
"delete_action": "Διαγραφή",
|
||||
"edit_action": "Επεξεργασία",
|
||||
"end_time_display": "Λήγει στις {time}",
|
||||
"page_header": "Ανακοινώσεις",
|
||||
"title": "Ανακοίνωση"
|
||||
},
|
||||
"chats": {
|
||||
"empty_message_error": "Δε μπορεί να σταλεί κενό μήνυμα",
|
||||
"error_sending_message": "Κάτι πήγε λάθος κατά την αποστολή του μηνύματος.",
|
||||
"message_user": "Στείλε μήνυμα στον/στην {nickname}",
|
||||
"more": "Περισσότερα",
|
||||
"new": "Νέο Chat",
|
||||
"you": "Εσείς:"
|
||||
},
|
||||
"display_date": {
|
||||
"today": "Σήμερα"
|
||||
},
|
||||
"domain_mute_card": {
|
||||
"mute": "Σίγαση"
|
||||
},
|
||||
"emoji": {
|
||||
"add_emoji": "Εισαγωγή emoji",
|
||||
"load_all": "Φόρτωση όλων των {emojiAmount} emoji",
|
||||
"recent": "Χρησιμοποιήθηκαν πρόσφατα",
|
||||
"search_emoji": "Αναζήτηση για ένα emoji",
|
||||
"stickers": "Αυτοκόλλητα"
|
||||
},
|
||||
"errors": {
|
||||
"storage_unavailable": "Το Pleroma δε μπόρεσε να προσπελάσει τον αποθηκευτικό χώρο του browser. Η σύνδεσή σας ή οι τοπικές ρυθμίσεις σας δε θα αποθηκευτούν και μπορεί να αντιμετωπίσετε απρόοπτα θέματα. Προσπαθήστε να ενεργοποιήσετε τα cookies."
|
||||
},
|
||||
"exporter": {
|
||||
"export": "Εξαγωγή"
|
||||
},
|
||||
"features_panel": {
|
||||
"text_limit": "Όριο κειμένου"
|
||||
},
|
||||
"file_type": {
|
||||
"audio": "Ήχος",
|
||||
"file": "Αρχείο",
|
||||
"image": "Εικόνα",
|
||||
"video": "Βίντεο"
|
||||
},
|
||||
"general": {
|
||||
"apply": "Εφαρμογή",
|
||||
"cancel": "Ακύρωση",
|
||||
"close": "Κλείσιμο",
|
||||
"disable": "Απενεργοποίηση",
|
||||
"enable": "Ενεργοποίηση",
|
||||
"error_retry": "Παρακαλώ δοκιμάστε ξανά",
|
||||
"flash_content": "Κάντε κλικ για την εμφάνιση Flash περιεχομένου με τη χρήση του Ruffle (Πειραματικό, μπορεί να μη λειτουργεί).",
|
||||
"loading": "Φόρτωση…",
|
||||
"more": "Περισσότερα",
|
||||
"optional": "προαιρετικό",
|
||||
"retry": "Δοκιμάστε ξανά",
|
||||
"role": {
|
||||
"admin": "Διαχειριστής",
|
||||
"moderator": "Συντονιστής"
|
||||
},
|
||||
"scope_in_timeline": {
|
||||
"direct": "Άμεσο",
|
||||
"local": "Τοπικό",
|
||||
"private": "Μόνο για ακόλουθους",
|
||||
"public": "Δημόσιο",
|
||||
"unlisted": "Εκτός Λίστας"
|
||||
},
|
||||
"show_less": "Δείξε λιγότερα",
|
||||
"show_more": "Δείξε περισσότερα"
|
||||
},
|
||||
"image_cropper": {
|
||||
"cancel": "Ακύρωση",
|
||||
"crop_picture": "Περικοπή εικόνας",
|
||||
"save": "Αποθήκευση",
|
||||
"save_without_cropping": "Αποθήκευση χωρίς περικοπή"
|
||||
},
|
||||
"importer": {
|
||||
"success": "Εισήχθη επιτυχώς."
|
||||
},
|
||||
"languages": {
|
||||
"ar": "Αραβικά",
|
||||
"az": "Αζερικά",
|
||||
"bg": "Βουλγαρικά",
|
||||
"cs": "Τσεχικά",
|
||||
"da": "Δανικά",
|
||||
"de": "Γερμανικά",
|
||||
"el": "Ελληνικά",
|
||||
"en": "Αγγλικά",
|
||||
"eo": "Εσπεράντο",
|
||||
"es": "Ισπανικά",
|
||||
"fa": "Περσικά",
|
||||
"fi": "Φινλανδικά",
|
||||
"fr": "Γαλλικά",
|
||||
"ga": "Ιρλανδικά",
|
||||
"he": "Εβραϊκά",
|
||||
"hi": "Χίντι",
|
||||
"hu": "Ουγγρικά",
|
||||
"id": "Ινδονησιακά",
|
||||
"it": "Ιταλικά",
|
||||
"ja": "Ιαπωνικά",
|
||||
"ko": "Κορεατικά",
|
||||
"lt": "Λιθουανικά",
|
||||
"lv": "Λετονικά",
|
||||
"nl": "Ολλανδικά",
|
||||
"pl": "Πολωνικά",
|
||||
"pt": "Πορτογαλικά",
|
||||
"ru": "Ρωσικά",
|
||||
"sk": "Σλοβακικά",
|
||||
"sv": "Σουηδικά",
|
||||
"tr": "Τουρκικά",
|
||||
"translated_from": {
|
||||
"ar": "Μεταφράστηκε από τα @:languages.ar",
|
||||
"az": "Μεταφράστηκε από τα @:languages.az",
|
||||
"bg": "Μεταφράστηκε από τα @:languages.bg",
|
||||
"cs": "Μεταφράστηκε από τα @:languages.cs",
|
||||
"da": "Μεταφράστηκε από τα @:languages.da",
|
||||
"de": "Μεταφράστηκε από τα @:languages.de",
|
||||
"el": "Μεταφράστηκε από τα @:languages.el",
|
||||
"en": "Μεταφράστηκε από τα @:languages.en",
|
||||
"eo": "Μεταφράστηκε από τα @:languages.eo",
|
||||
"es": "Μεταφράστηκε από τα @:languages.es",
|
||||
"fa": "Μεταφράστηκε από τα @:languages.fa",
|
||||
"fi": "Μεταφράστηκε από τα @:languages.fi",
|
||||
"fr": "Μεταφράστηκε από τα @:languages.fr",
|
||||
"ga": "Μεταφράστηκε από τα @:languages.ga",
|
||||
"he": "Μεταφράστηκε από τα @:languages.he",
|
||||
"hi": "Μεταφράστηκε από τα @:languages.hi",
|
||||
"hu": "Μεταφράστηκε από τα @:languages.hu",
|
||||
"id": "Μεταφράστηκε από τα @:languages.id",
|
||||
"it": "Μεταφράστηκε από τα @:languages.it",
|
||||
"ja": "Μεταφράστηκε από τα @:languages.ja",
|
||||
"ko": "Μεταφράστηκε από τα @:languages.ko",
|
||||
"lt": "Μεταφράστηκε από τα @:languages.lt",
|
||||
"lv": "Μεταφράστηκε από τα @:languages.lv",
|
||||
"nl": "Μεταφράστηκε από τα @:languages.nl",
|
||||
"pl": "Μεταφράστηκε από τα @:languages.pl",
|
||||
"pt": "Μεταφράστηκε από τα @:languages.pt",
|
||||
"ru": "Μεταφράστηκε από τα @:languages.ru",
|
||||
"sk": "Μεταφράστηκε από τα @:languages.sk",
|
||||
"sv": "Μεταφράστηκε από τα @:languages.sv",
|
||||
"tr": "tr",
|
||||
"uk": "Μεταφράστηκε από τα @:languages.uk",
|
||||
"zh": "Μεταφράστηκε από τα @:languages.zh"
|
||||
},
|
||||
"uk": "Ουκρανικά",
|
||||
"zh": "Κινεζικά"
|
||||
},
|
||||
"lists": {
|
||||
"create": "Δημιουργία",
|
||||
"delete": "Διαγραφή λίστας",
|
||||
"lists": "Λίστες",
|
||||
"new": "Νέα Λίστα",
|
||||
"save": "Αποθήκευση αλλαγών",
|
||||
"search": "Αναζήτηση χρηστών",
|
||||
"title": "Τίτλος λίστας"
|
||||
},
|
||||
"login": {
|
||||
"authentication_code": "Κωδικός επαλήθευσης",
|
||||
"description": "Σύνδεση με OAuth",
|
||||
"enter_recovery_code": "Εισάγετε τον κωδικό ανάκτησης",
|
||||
"hint": "Συνδεθείτε για να μπείτε στη συζήτηση",
|
||||
"login": "Σύνδεση",
|
||||
"logout": "Αποσύνδεση",
|
||||
"logout_confirm": "Θέλετε σίγουρα να αποσυνδεθείτε;",
|
||||
"logout_confirm_accept_button": "Αποσύνδεση",
|
||||
"logout_confirm_cancel_button": "Ακύρωση",
|
||||
"logout_confirm_title": "Αποσύνδεση",
|
||||
"password": "Κωδικός πρόσβασης",
|
||||
"placeholder": "τοονομαχρηστημου",
|
||||
"recovery_code": "Κωδικός ανάκτησης",
|
||||
"register": "Εγγραφή",
|
||||
"username": "Όνομα χρήστη"
|
||||
},
|
||||
"media_modal": {
|
||||
"next": "Επόμενο",
|
||||
"previous": "Προηγούμενο"
|
||||
},
|
||||
"moderation": {
|
||||
"reports": {
|
||||
"add_note": "Προσθήκη σημείωσης",
|
||||
"close": "Κλείσιμο",
|
||||
"delete_note": "Διαγραφή",
|
||||
"delete_note_accept": "Ναι, διάγραψέ το",
|
||||
"delete_note_cancel": "Όχι, κράτα το",
|
||||
"delete_note_confirm": "Θέλετε σίγουρα να διαγράψετε αυτήν τη σημείωση;",
|
||||
"note_placeholder": "Αφήστε μια σημείωση",
|
||||
"notes": "{ count } σημείωση | { count } σημειώσεις",
|
||||
"statuses": "{ count } ανάρτηση| { count } αναρτήσεις"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -380,7 +380,6 @@
|
|||
"text/x.misskeymarkdown": "MFM"
|
||||
},
|
||||
"content_warning": "Content Warning (optional)",
|
||||
"toggle_content_warning": "Toggle content warning",
|
||||
"default": "Just arrived at Luna Nova Academy",
|
||||
"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.",
|
||||
|
@ -601,7 +600,6 @@
|
|||
"list_aliases_error": "Error fetching aliases: {error}",
|
||||
"list_backups_error": "Error fetching backup list: {error}",
|
||||
"lock_account_description": "Restrict your account to approved followers only",
|
||||
"permit_followback_description": "Automatically approve requests from already followed users",
|
||||
"loop_video": "Loop videos",
|
||||
"loop_video_silent_only": "Loop only videos without sound (i.e. Mastodon's \"gifs\")",
|
||||
"mascot": "Mastodon FE Mascot",
|
||||
|
@ -684,7 +682,6 @@
|
|||
"play_videos_in_modal": "Play videos in a popup frame",
|
||||
"post_look_feel": "Posts Look & Feel",
|
||||
"post_status_content_type": "Default post content type",
|
||||
"post_language": "Default post language",
|
||||
"posts": "Posts",
|
||||
"preload_images": "Preload images",
|
||||
"presets": "Presets",
|
||||
|
@ -752,8 +749,8 @@
|
|||
"show_nav_shortcuts": "Show extra navigation shortcuts in top panel",
|
||||
"show_panel_nav_shortcuts": "Show timeline navigation shortcuts at the top of the panel",
|
||||
"show_scrollbars": "Show side column's scrollbars",
|
||||
"show_page_backgrounds": "Show page-specific backgrounds, e.g. for user profiles",
|
||||
"show_wider_shortcuts": "Show wider gap between top panel shortcuts",
|
||||
"show_fedi_links": "Show Fedi Links (web+ap URLs)",
|
||||
"show_yous": "Show (You)s",
|
||||
"stop_gifs": "Pause animated images until you hover on them",
|
||||
"streaming": "Automatically show new posts when scrolled to the top",
|
||||
|
@ -926,6 +923,7 @@
|
|||
"upload_a_photo": "Upload a photo",
|
||||
"useStreamingApi": "Receive posts and notifications real-time",
|
||||
"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_one_click_nsfw": "Open NSFW attachments with just one click",
|
||||
"user_mutes": "Users",
|
||||
|
@ -964,6 +962,7 @@
|
|||
"bookmark": "Bookmark",
|
||||
"collapse_attachments": "Collapse attachments",
|
||||
"copy_link": "Copy link to post",
|
||||
"copy_fedi_link": "Copy fedi link",
|
||||
"delete": "Delete post",
|
||||
"delete_confirm": "Do you really want to delete this post?",
|
||||
"delete_confirm_accept_button": "Yes, delete it",
|
||||
|
@ -975,6 +974,7 @@
|
|||
"edited_at": "Edited {time}",
|
||||
"expand": "Expand",
|
||||
"external_source": "External source",
|
||||
"open_fedi_link": "Open fedi link",
|
||||
"favorites": "Favorites",
|
||||
"hide_attachment": "Hide attachment",
|
||||
"hide_content": "Hide content",
|
||||
|
@ -1141,6 +1141,7 @@
|
|||
"deny_confirm_title": "Deny follow request",
|
||||
"domain_muted": "Unblock domain",
|
||||
"edit_profile": "Edit profile",
|
||||
"external_source": "External source",
|
||||
"favorites": "Favorites",
|
||||
"follow": "Follow",
|
||||
"follow_cancel": "Cancel request",
|
||||
|
@ -1174,6 +1175,7 @@
|
|||
"mute_progress": "Muting…",
|
||||
"muted": "Muted",
|
||||
"note": "Private note",
|
||||
"open_fedi_link": "Open fedi link",
|
||||
"per_day": "per day",
|
||||
"remote_follow": "Remote follow",
|
||||
"remove_follower": "Remove follower",
|
||||
|
|
|
@ -425,8 +425,6 @@
|
|||
"reason_placeholder": "Cette instance modère les inscriptions manuellement.\nExpliquer ce qui motive votre inscription à l'administration.",
|
||||
"register": "Enregistrer",
|
||||
"registration": "Inscription",
|
||||
"request_sent": "Votre demande d'enregistrement est en attente d'approbation. Vous recevrez un courriel lorsque votre compte sera approuvé.",
|
||||
"request_sent_title": "Demande d'abonnement envoyée",
|
||||
"token": "Jeton d'invitation",
|
||||
"username_placeholder": "ex : misato",
|
||||
"validations": {
|
||||
|
@ -920,6 +918,7 @@
|
|||
"upload_a_photo": "Envoyer une photo",
|
||||
"useStreamingApi": "Recevoir les messages et notifications en temps réel",
|
||||
"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_one_click_nsfw": "Ouvrir les pièces-jointes sensibles avec un seul clic",
|
||||
"user_mutes": "Comptes",
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
{
|
||||
"about": {
|
||||
"bubble_instances": "Instance Bubble Lokal",
|
||||
"bubble_instances_description": "Instansi yang dipilih oleh admin untuk mewakili instance ini",
|
||||
"mrf": {
|
||||
"federation": "Federasi",
|
||||
"keyword": {
|
||||
"ftl_removal": "Penghapusan dari Linimasa \"Jaringan Yang Dikenal\"",
|
||||
"is_replaced_by": "→",
|
||||
"keyword_policies": "Kebijakan Kata Kunci",
|
||||
"reject": "Tolak",
|
||||
"replace": "Ganti"
|
||||
"reject": "Tolak"
|
||||
},
|
||||
"mrf_policies": "Kebijakan MRF yang diaktifkan",
|
||||
"mrf_policies_desc": "Kebijakan MRF memanipulasi federasi yang terjadi pada instansi ini. Kebijakan berikut adalah yang aktif:",
|
||||
|
@ -84,7 +80,6 @@
|
|||
"keep_open": "Tetap buka pemilih",
|
||||
"load_all": "Memuat semua {emojiAmount} emoji",
|
||||
"load_all_hint": "Memuat {saneAmount} emoji pertama, memuat semua emoji dapat menyebabkan masalah performa.",
|
||||
"recent": "Baru-baru ini digunakan",
|
||||
"search_emoji": "Cari emoji",
|
||||
"stickers": "Stiker",
|
||||
"unicode": "Emoji Unicode"
|
||||
|
@ -98,11 +93,9 @@
|
|||
},
|
||||
"features_panel": {
|
||||
"media_proxy": "Proxy media",
|
||||
"scope_options": "Opsi cakupan",
|
||||
"text_limit": "Batas teks",
|
||||
"title": "Fitur-fitur",
|
||||
"upload_limit": "Batas unggahan",
|
||||
"who_to_follow": "Siapa untuk diikuti"
|
||||
"upload_limit": "Batas unggahan"
|
||||
},
|
||||
"file_type": {
|
||||
"audio": "Audio",
|
||||
|
@ -120,7 +113,6 @@
|
|||
"close": "Tutup",
|
||||
"confirm": "Konfirmasi",
|
||||
"disable": "Nonaktifkan",
|
||||
"dismiss": "Tutup",
|
||||
"enable": "Aktifkan",
|
||||
"error_retry": "Harap coba lagi",
|
||||
"flash_content": "Klik untuk menampilkan konten Flash menggunakan Ruffle (Eksperimental, mungkin tidak bekerja).",
|
||||
|
@ -140,8 +132,7 @@
|
|||
"direct": "Langsung",
|
||||
"local": "Lokal - hanya instansi kamu yang dapat melihat postingan ini",
|
||||
"private": "Hanya pengikut",
|
||||
"public": "Publik",
|
||||
"unlisted": "Tidak Tercantum"
|
||||
"public": "Publik"
|
||||
},
|
||||
"show_less": "Tampilkan lebih sedikit",
|
||||
"show_more": "Tampilkan lebih banyak",
|
||||
|
@ -236,7 +227,6 @@
|
|||
"lists": {
|
||||
"create": "Buat",
|
||||
"delete": "Hapus daftar",
|
||||
"following_only": "Batas mengikuti",
|
||||
"lists": "Daftar",
|
||||
"new": "Buat Daftar",
|
||||
"save": "Simpan perubahan",
|
||||
|
@ -286,7 +276,6 @@
|
|||
"note_placeholder": "Tinggalkan catatan",
|
||||
"notes": "{ count } catatan",
|
||||
"reopen": "Buka kembali",
|
||||
"report": "Laporan di",
|
||||
"reports": "Laporan",
|
||||
"resolve": "Selesaikan",
|
||||
"show_closed": "Tampilkan yang telah ditutup",
|
||||
|
@ -302,8 +291,6 @@
|
|||
"administration": "Administrasi",
|
||||
"announcements": "Pengumuman",
|
||||
"back": "Kembali",
|
||||
"bookmarks": "Bookmark",
|
||||
"bubble_timeline": "Timeline Bubble",
|
||||
"bubble_timeline_description": "Postingan dari instansi yang dekat dengan instansimu, yang direkomendasikan oleh admin kamu",
|
||||
"chats": "Obrolan",
|
||||
"dms": "Pesan langsung",
|
||||
|
@ -322,8 +309,7 @@
|
|||
"timelines": "Linimasa",
|
||||
"twkn": "Jaringan Yang Dikenal",
|
||||
"twkn_timeline_description": "Postingan dari seluruh jaringan",
|
||||
"user_search": "Penelusuran Pengguna",
|
||||
"who_to_follow": "Siapa untuk diikuti"
|
||||
"user_search": "Penelusuran Pengguna"
|
||||
},
|
||||
"notifications": {
|
||||
"broken_favorite": "Postingan tak dikenal, mencarinya…",
|
||||
|
@ -399,8 +385,7 @@
|
|||
"direct": "Langsung - posting hanya kepada pengguna yang disebut",
|
||||
"local": "Lokal - postingan tidak akan difederasi",
|
||||
"private": "Hanya-pengikut - posting hanya kepada pengikut",
|
||||
"public": "Publik - posting ke linimasa publik",
|
||||
"unlisted": "Tidak Tercantum - Tidak memposting ke timeline publik"
|
||||
"public": "Publik - posting ke linimasa publik"
|
||||
},
|
||||
"scope_notice": {
|
||||
"local": "Postingan ini tidak akan terlihat di instansi lain",
|
||||
|
@ -417,7 +402,6 @@
|
|||
"captcha": "CAPTCHA",
|
||||
"email": "Surel",
|
||||
"email_language": "Dalam bahasa apa kamu ingin menerima surel dari server ini?",
|
||||
"fullname": "Tampilan Nama",
|
||||
"fullname_placeholder": "cth. Atsuko Kagari",
|
||||
"new_captcha": "Klik gambarnya untuk mendapatkan captcha baru",
|
||||
"password_confirm": "Konfirmasi kata sandi",
|
||||
|
@ -440,7 +424,6 @@
|
|||
},
|
||||
"remote_user_resolver": {
|
||||
"error": "Tidak ditemukan.",
|
||||
"remote_user_resolver": "Pencarian pengguna jarak jauh",
|
||||
"searching_for": "Mencari"
|
||||
},
|
||||
"search": {
|
||||
|
@ -454,22 +437,16 @@
|
|||
"select_all": "Pilih semua"
|
||||
},
|
||||
"settings": {
|
||||
"accent": "Aksen",
|
||||
"account_alias": "Akun alias",
|
||||
"account_backup": "Pencadangan akun",
|
||||
"account_backup_description": "Ini memungkinkan kamu untuk mengunduh arsip yang berisi informasi tentang akun dan postingan kamu, namun belum bisa diimpor ke akun Pleroma.",
|
||||
"account_privacy": "Privasi",
|
||||
"add_alias_error": "Gagal menambahkan alias: {error}",
|
||||
"add_backup": "Buat cadangan baru",
|
||||
"add_backup_error": "Gagal menambahkan cadangan baru: {error}",
|
||||
"added_alias": "Alias telah ditambahkan.",
|
||||
"added_backup": "Cadangan baru ditambahkan.",
|
||||
"allow_following_move": "Ikuti otomatis apabila akun yang diikuti pindah",
|
||||
"always_show_post_button": "Selalu tampilkan tombol posting baru yang mengambang",
|
||||
"app_name": "Nama aplikasi",
|
||||
"attachmentRadius": "Lampiran",
|
||||
"attachments": "Lampiran",
|
||||
"autohide_floating_post_button": "Sembunyikan tombol buat posting secara otomatis (Ponsel)",
|
||||
"avatar": "Avatar",
|
||||
"avatarAltRadius": "Avatar (notifikasi)",
|
||||
"avatarRadius": "Avatar",
|
||||
|
@ -497,8 +474,6 @@
|
|||
"changed_password": "Kata sandi berhasil diubah!",
|
||||
"chatMessageRadius": "Pesan obrolan",
|
||||
"checkboxRadius": "Kotak centang",
|
||||
"collapse_subject": "Tampilkan post dengan peringatan",
|
||||
"columns": "Kolum",
|
||||
"composing": "Menulis",
|
||||
"confirm_dialogs": "Perlukan konfirmasi sebelum:",
|
||||
"confirm_dialogs_approve_follow": "Menerima permintaan mengikuti",
|
||||
|
@ -509,23 +484,17 @@
|
|||
"confirm_dialogs_repeat": "Mengulangi postingan",
|
||||
"confirm_dialogs_unfollow": "Berhenti mengikuti seseorang",
|
||||
"confirm_new_password": "Konfirmasi kata sandi baru",
|
||||
"confirmation_dialogs": "Opsi Konfirmasi",
|
||||
"conversation_display": "Gaya tampilan percakapan",
|
||||
"conversation_display_linear": "Gaya Linier",
|
||||
"conversation_display_tree": "Bercabang",
|
||||
"conversation_other_replies_button": "Tampilkan tombol \"Balasan lainnya\"",
|
||||
"conversation_other_replies_button_below": "Di bawah postingan",
|
||||
"conversation_other_replies_button_inside": "Di postingan",
|
||||
"current_avatar": "Avatarmu saat ini",
|
||||
"current_mascot": "Maskot utamamu",
|
||||
"current_password": "Kata sandi saat ini",
|
||||
"data_import_export_tab": "Impor / ekspor data",
|
||||
"default_vis": "Cakupan visibilitas default",
|
||||
"delete_account": "Hapus akun",
|
||||
"delete_account_description": "Hapus data kamu secara permanen dan nonaktifkan akunmu.",
|
||||
"delete_account_error": "Ada masalah ketika menghapus akun kamu. Jika ini terus terjadi harap hubungi adminstrator instansi kamu.",
|
||||
"delete_account_instructions": "Ketik kata sandi kamu pada input di bawah untuk mengonfirmasi penghapusan akun.",
|
||||
"disable_sticky_headers": "Jangan tempel tajuk kolom ke bagian atas layar",
|
||||
"discoverable": "Izinkan penelusuran akun ini pada hasil pencarian dan layanan lainnya",
|
||||
"domain_mutes": "Domain",
|
||||
"download_backup": "Unduh",
|
||||
|
@ -536,7 +505,6 @@
|
|||
"expert_mode": "Tampilkan pengaturan lanjutan",
|
||||
"expire_posts_enabled": "Hapus postingan setelah jumlah hari yang ditentukan",
|
||||
"expire_posts_input_placeholder": "Jumlah hari",
|
||||
"export_theme": "Simpan preset",
|
||||
"file_export_import": {
|
||||
"backup_restore": "Pencadangan pengaturan",
|
||||
"backup_settings": "Cadangkan pengaturan ke berkas",
|
||||
|
@ -551,11 +519,7 @@
|
|||
},
|
||||
"filtering": "Penyaringan",
|
||||
"filtering_explanation": "Semua postingan yang mengandung kata-kata ini akan dibisukan, satu kata per baris",
|
||||
"follow_export": "Ekspor Follow",
|
||||
"follow_export_button": "Export yang kamu ikuti ke dalam file csv",
|
||||
"follow_import": "Import pengikut",
|
||||
"follow_import_error": "Terjadi kesalahan ketika mengimpor pengikut",
|
||||
"follows_imported": "Pengguna yang diikuti telak diimpor! Proses mungkin membutuhkan beberapa saat.",
|
||||
"fun": "Seru",
|
||||
"general": "Umum",
|
||||
"greentext": "Panah meme",
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
"mrf": {
|
||||
"federation": "フェデレーション",
|
||||
"keyword": {
|
||||
"ftl_removal": "「せつぞくしているすべてのネットワーク」タイムラインからのぞく",
|
||||
"is_replaced_by": "→",
|
||||
"keyword_policies": "キーワードポリシー",
|
||||
"reject": "おことわり",
|
||||
|
@ -121,7 +120,7 @@
|
|||
"error_retry": "もういちど、ためしてください",
|
||||
"flash_content": "クリックすると、Ruffle をつかって、フラッシュさくひんをひょうじします。(うまくうごかないかもしれません)",
|
||||
"flash_fail": "フラッシュさくひんのロードに、しっぱいしました。コンソールに、くわしいことがかかれています。",
|
||||
"flash_security": "フラッシュさくひんは、あなたのコンピューターに、あぶないことをしてくるかもしれないので、ちゅういしてください。",
|
||||
"flash_security": "フラッシュさくひんは、あぶないことをしてくるかもしれないので、ちゅういしてください。",
|
||||
"generic_error": "エラーになりました",
|
||||
"loading": "ロードしています…",
|
||||
"more": "つづき",
|
||||
|
@ -534,7 +533,7 @@
|
|||
"expert_mode": "こまかいせっていをひょうじ",
|
||||
"export_theme": "セーブ",
|
||||
"filtering": "フィルタリング",
|
||||
"filtering_explanation": "これらのことばをふくむすべてのとうこうがミュートされます。1ぎょうに1つのことばをかいてください",
|
||||
"filtering_explanation": "これらのことばをふくむすべてのものがミュートされます。1ぎょうに1つのことばをかいてください",
|
||||
"follow_export": "フォローのエクスポート",
|
||||
"follow_export_button": "エクスポート",
|
||||
"follow_import": "フォローインポート",
|
||||
|
@ -888,7 +887,6 @@
|
|||
"media": "メディア",
|
||||
"mention": "メンション",
|
||||
"mute": "ミュート",
|
||||
"mute_domain": "ドメインをブロック",
|
||||
"mute_progress": "ミュートしています…",
|
||||
"muted": "ミュートしています!",
|
||||
"per_day": "/日",
|
||||
|
@ -899,7 +897,6 @@
|
|||
"subscribe": "サブスクライブ",
|
||||
"unblock": "ブロックをやめる",
|
||||
"unblock_progress": "ブロックをとりけしています…",
|
||||
"unfollow_confirm": "{user}のフォローをやめますか?",
|
||||
"unmute": "ミュートをやめる",
|
||||
"unmute_progress": "ミュートをとりけしています…",
|
||||
"unsubscribe": "サブスクライブをやめる"
|
||||
|
|
|
@ -921,6 +921,7 @@
|
|||
"upload_a_photo": "画像をアップロード",
|
||||
"useStreamingApi": "投稿と通知を、すぐに受け取る",
|
||||
"useStreamingApiWarning": "(実験中で、投稿を取りこぼすかもしれないので、おすすめしません)",
|
||||
"use_at_icon": "{'@'}マークをアイコンにする",
|
||||
"use_contain_fit": "画像のサムネイルを、切り抜かない",
|
||||
"use_one_click_nsfw": "NSFWなファイルを1クリックで開く",
|
||||
"user_mutes": "ユーザー",
|
||||
|
|
|
@ -21,7 +21,6 @@ const loaders = {
|
|||
ga: () => import('./ga.json'),
|
||||
he: () => import('./he.json'),
|
||||
hu: () => import('./hu.json'),
|
||||
id: () => import('./id.json'),
|
||||
it: () => import('./it.json'),
|
||||
ja: () => import('./ja_pedantic.json'),
|
||||
ja_easy: () => import('./ja_easy.json'),
|
||||
|
@ -36,7 +35,6 @@ const loaders = {
|
|||
sk: () => import('./sk.json'),
|
||||
te: () => import('./te.json'),
|
||||
uk: () => import('./uk.json'),
|
||||
vi: () => import('./vi.json'),
|
||||
zh: () => import('./zh.json'),
|
||||
zh_Hant: () => import('./zh_Hant.json')
|
||||
}
|
||||
|
|
|
@ -918,6 +918,7 @@
|
|||
"upload_a_photo": "Foto uploaden",
|
||||
"useStreamingApi": "Berichten en meldingen in real-time ontvangen",
|
||||
"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_contain_fit": "Bijlage in miniaturen niet bijsnijden",
|
||||
"use_one_click_nsfw": "Gevoelige bijlagen met slechts één klik openen",
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
"simple_policies": "Políticas especificas do domínio"
|
||||
}
|
||||
},
|
||||
"staff": "Equipe"
|
||||
"staff": "Staff"
|
||||
},
|
||||
"announcements": {
|
||||
"all_day_prompt": "Este evento dura o dia inteiro",
|
||||
|
@ -167,7 +167,7 @@
|
|||
"password": "Palavra-passe",
|
||||
"placeholder": "ex. lain",
|
||||
"recovery_code": "Código de recuperação",
|
||||
"register": "Registrar",
|
||||
"register": "Registar",
|
||||
"username": "Nome de Utilizador"
|
||||
},
|
||||
"media_modal": {
|
||||
|
@ -244,7 +244,7 @@
|
|||
"text/plain": "Texto puro"
|
||||
},
|
||||
"content_warning": "Assunto (opcional)",
|
||||
"default": "Acabei de chegar em",
|
||||
"default": "Acabei de chegar a Lisboa.",
|
||||
"direct_warning_to_all": "Esta publicação será visível para todos os utilizadores mencionados.",
|
||||
"direct_warning_to_first_only": "Esta publicação só será visível para os utilizadores mencionados no início da mensagem.",
|
||||
"empty_status_error": "Não consegues publicar um post vazio e sem ficheiros",
|
||||
|
@ -268,7 +268,7 @@
|
|||
},
|
||||
"registration": {
|
||||
"bio": "Biografia",
|
||||
"bio_placeholder": "Ex.:\nBem-vindo a minha bio.\nEu amo assistir anime e jogar. Eu espero que possamos ser amigos!",
|
||||
"bio_placeholder": "ex.\nOlá, sou a Lain\nSou uma menina de anime que vive no Japão suburbano. Devem conhecer-me do \"the Wired\".",
|
||||
"captcha": "CAPTCHA",
|
||||
"email": "Endereço de e-mail",
|
||||
"fullname": "Nome para exibição",
|
||||
|
@ -401,7 +401,7 @@
|
|||
"scan": {
|
||||
"desc": "Utilizando a sua aplicação de dois fatores, faça scan deste código QR ou insira a chave de texto:",
|
||||
"secret_code": "Chave",
|
||||
"title": "Escanear"
|
||||
"title": "Scan"
|
||||
},
|
||||
"setup_otp": "Configurar palavra-passe de utilização única",
|
||||
"title": "Autenticação de Dois Fatores",
|
||||
|
@ -566,7 +566,7 @@
|
|||
"fine_print": "Leia nosso {0} para não aprender nada!",
|
||||
"header": "Pré-visualizar",
|
||||
"header_faint": "Isto está bem",
|
||||
"input": "Acabei de chegar na Lua",
|
||||
"input": "Acabei de chegar a Lisboa.",
|
||||
"link": "um belo link",
|
||||
"mono": "conteúdo",
|
||||
"text": "Vários {0} e {1}"
|
||||
|
@ -648,7 +648,7 @@
|
|||
"type_domains_to_mute": "Pesquisar domínios para silenciar",
|
||||
"upload_a_photo": "Enviar uma foto",
|
||||
"useStreamingApi": "Receber publicações e notificações em tempo real",
|
||||
"useStreamingApiWarning": "É legal usar isso. Se der algum problema, atualize, eu acho?",
|
||||
"useStreamingApiWarning": "(não recomendado, experimental, pode omitir publicações)",
|
||||
"use_contain_fit": "Não cortar o anexo na miniatura",
|
||||
"use_one_click_nsfw": "Abrir anexos sensíveis com um clique",
|
||||
"user_mutes": "Utilizadores",
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
"about": {
|
||||
"bubble_instances": "เซิร์ฟเวอร์เพื่อนบ้าน",
|
||||
"bubble_instances_description": "เซิร์ฟเวอร์ที่แอดมินตั้งค่าไว้เพื่อแสดงเป็นเพื่อนบ้าน",
|
||||
"mrf": {
|
||||
"keyword": {
|
||||
"keyword_policies": "นโยบายของคีย์เวิร์ดค้นหา",
|
||||
"reject": "ไม่อนุมัติ",
|
||||
"replace": "แทนที่"
|
||||
},
|
||||
"simple": {
|
||||
"accept": "ยอมรับ",
|
||||
"accept_desc": "เซิร์ฟเวอร์นี้จะรับข้อความเฉพาะเซิฟร์เวอร์ต่อไปนี้:",
|
||||
"instance": "เซิฟเวอร์",
|
||||
"media_nsfw": "มีเดียถูกบังคับตั้งให้เป็น NSFW",
|
||||
"not_applicable": "ไม่สามารถใช้ได้",
|
||||
"quarantine": "คัดกรอง",
|
||||
"reason": "เหตุผล",
|
||||
"reject": "ปฎิเสธ"
|
||||
}
|
||||
},
|
||||
"staff": "ผู้ดูแล"
|
||||
},
|
||||
"announcements": {
|
||||
"cancel_edit_action": "ยกเลิก",
|
||||
"close_error": "ปิด",
|
||||
"delete_action": "ลบประกาศ",
|
||||
"edit_action": "แก้ไขประกาศ",
|
||||
"end_time_prompt": "เวลาสิ้นสุด: ",
|
||||
"mark_as_read_action": "ตั้งเป็นอ่านแล้ว",
|
||||
"page_header": "ประกาศ",
|
||||
"post_action": "โพสต์"
|
||||
}
|
||||
}
|
|
@ -49,86 +49,6 @@
|
|||
"post_error": "Hata: {error}",
|
||||
"post_form_header": "Post sonrası",
|
||||
"post_placeholder": "Duyuru içeriği",
|
||||
"published_time_display": "{time} tarihinde yayınlandı",
|
||||
"start_time_display": "{time} saatinde başlar",
|
||||
"start_time_prompt": "Başlangıç saati: ",
|
||||
"submit_edit_action": "Gönder",
|
||||
"title": "Duyuru"
|
||||
},
|
||||
"chats": {
|
||||
"chats": "Sohbetler",
|
||||
"delete": "Sil",
|
||||
"delete_confirm": "Bu mesajı gerçekten silmek istiyor musun?",
|
||||
"empty_chat_list_placeholder": "Henüz hiç sohbetiniz yok. Yeni bir sohbet başlatın!",
|
||||
"empty_message_error": "Boş mesaj gönderilemiyor",
|
||||
"error_loading_chat": "Sohbet yüklenirken bir şeyler ters gitti.",
|
||||
"error_sending_message": "Mesajı gönderirken bir şeyler ters gitti.",
|
||||
"message_user": "{nickname} mesajı",
|
||||
"more": "Daha",
|
||||
"new": "Yeni Sohbet",
|
||||
"you": "Sen:"
|
||||
},
|
||||
"display_date": {
|
||||
"today": "Bugün"
|
||||
},
|
||||
"domain_mute_card": {
|
||||
"mute": "Sesini kapat",
|
||||
"mute_progress": "Ses kapatılıyor…",
|
||||
"unmute": "Sesini aç",
|
||||
"unmute_progress": "Sessize alınıyor…"
|
||||
},
|
||||
"emoji": {
|
||||
"add_emoji": "Emoji ekle",
|
||||
"custom": "Özel emoji",
|
||||
"emoji": "Emoji",
|
||||
"keep_open": "Seçiciyi açık tut",
|
||||
"load_all": "Tüm {emojiAmount} emoji yükleniyor",
|
||||
"load_all_hint": "İlk {saneAmount} emoji yüklendi, tüm emojilerin yüklenmesi performans sorunlarına neden olabilir.",
|
||||
"recent": "Son zamanlarda kullanılmış",
|
||||
"search_emoji": "Emoji ara",
|
||||
"stickers": "Çıkartmalar",
|
||||
"unicode": "Unicode emojisi"
|
||||
},
|
||||
"errors": {
|
||||
"storage_unavailable": "Pleroma, tarayıcı depolama alanına erişemedi. Oturum açma bilgileriniz veya yerel ayarlarınız kaydedilmeyecek ve beklenmeyen sorunlarla karşılaşabilirsiniz. Çerezleri etkinleştirmeyi deneyin."
|
||||
},
|
||||
"exporter": {
|
||||
"export": "Dışa aktar",
|
||||
"processing": "İşleniyor, yakında dosyanızı indirmeniz istenecek"
|
||||
},
|
||||
"features_panel": {
|
||||
"media_proxy": "Medya proxy'si",
|
||||
"scope_options": "Kapsam seçenekleri",
|
||||
"text_limit": "Metin sınırı",
|
||||
"title": "Özellikler",
|
||||
"upload_limit": "Yükleme sınırı",
|
||||
"who_to_follow": "Kimi takip etmeli"
|
||||
},
|
||||
"file_type": {
|
||||
"audio": "Ses",
|
||||
"file": "Dosya",
|
||||
"image": "Resim",
|
||||
"video": "Video"
|
||||
},
|
||||
"finder": {
|
||||
"error_fetching_user": "Kullanıcı getirilirken hata oluştu",
|
||||
"find_user": "Kullanıcı bul"
|
||||
},
|
||||
"general": {
|
||||
"apply": "Uygula",
|
||||
"cancel": "İptal",
|
||||
"close": "Kapalı",
|
||||
"confirm": "Onayla",
|
||||
"disable": "Devre dışı bırak",
|
||||
"dismiss": "Reddetmek",
|
||||
"enable": "Etkinleştir",
|
||||
"error_retry": "Lütfen tekrar deneyin",
|
||||
"flash_content": "Ruffle kullanarak Flash içeriğini göstermek için tıklayın (Deneysel, çalışmayabilir).",
|
||||
"flash_fail": "Flash içeriği yüklenemedi, ayrıntılar için konsola bakın.",
|
||||
"flash_security": "Flash içeriği hala isteğe bağlı kod olduğundan, bunun potansiyel olarak tehlikeli olabileceğini unutmayın.",
|
||||
"generic_error": "Bir hata oluştu",
|
||||
"loading": "Yükleniyor…",
|
||||
"more": "Daha",
|
||||
"optional": "Seçenek"
|
||||
"published_time_display": "{time} tarihinde yayınlandı"
|
||||
}
|
||||
}
|
||||
|
|
302
src/i18n/uk.json
302
src/i18n/uk.json
|
@ -23,7 +23,6 @@
|
|||
"media_nsfw_desc": "Сервер примусово позначає дражливими медіа з таких серверів:",
|
||||
"media_removal": "Видалення медіа",
|
||||
"media_removal_desc": "Сервер вилучає медіа в дописах із таких серверів:",
|
||||
"not_applicable": "Не застосовується",
|
||||
"quarantine": "Карантин",
|
||||
"quarantine_desc": "Сервер не надсилатиме дописів таким серверам:",
|
||||
"reason": "Підстава",
|
||||
|
@ -32,7 +31,7 @@
|
|||
"simple_policies": "Відносно цілих серверів"
|
||||
}
|
||||
},
|
||||
"staff": "Команда"
|
||||
"staff": "Адміністрація"
|
||||
},
|
||||
"announcements": {
|
||||
"all_day_prompt": "Триває весь день",
|
||||
|
@ -72,10 +71,10 @@
|
|||
"today": "Сьогодні"
|
||||
},
|
||||
"domain_mute_card": {
|
||||
"mute": "Приглушити",
|
||||
"mute_progress": "Глушіння…",
|
||||
"unmute": "Відновити",
|
||||
"unmute_progress": "Відновлення…"
|
||||
"mute": "Ігнорувати",
|
||||
"mute_progress": "Вимикаю…",
|
||||
"unmute": "Вимкнути заглушення",
|
||||
"unmute_progress": "Вмикаю…"
|
||||
},
|
||||
"emoji": {
|
||||
"add_emoji": "Додати емоджі",
|
||||
|
@ -84,7 +83,6 @@
|
|||
"keep_open": "Тримати панель відкритою",
|
||||
"load_all": "Всі {emojiAmount} емоджі завантажуються",
|
||||
"load_all_hint": "Завантажено {saneAmount} емоджі, більша кількість може спричинити гальмування.",
|
||||
"recent": "Нещодавно вжиті",
|
||||
"search_emoji": "Пошук емоджі",
|
||||
"stickers": "Наліпки",
|
||||
"unicode": "Стандартні емоджі"
|
||||
|
@ -133,19 +131,19 @@
|
|||
"peek": "Глянути",
|
||||
"retry": "Спробуйте ще раз",
|
||||
"role": {
|
||||
"admin": "Адміністрація",
|
||||
"moderator": "Модерування"
|
||||
"admin": "Адмініструє",
|
||||
"moderator": "Модерує"
|
||||
},
|
||||
"scope_in_timeline": {
|
||||
"direct": "Особисто",
|
||||
"local": "Місцево — допис видно тільки на вашому сервері",
|
||||
"private": "Приватно",
|
||||
"public": "Загальнодоступно",
|
||||
"unlisted": "Приховано"
|
||||
"unlisted": "Непублічно"
|
||||
},
|
||||
"show_less": "Показати менше",
|
||||
"show_more": "Показати більше",
|
||||
"submit": "Надіслати",
|
||||
"submit": "Відправити",
|
||||
"verify": "Перевірити"
|
||||
},
|
||||
"image_cropper": {
|
||||
|
@ -156,7 +154,7 @@
|
|||
},
|
||||
"importer": {
|
||||
"error": "Під час імпортування файлу сталася помилка.",
|
||||
"submit": "Надіслати",
|
||||
"submit": "Відправити",
|
||||
"success": "Імпортовано успішно."
|
||||
},
|
||||
"interactions": {
|
||||
|
@ -166,72 +164,8 @@
|
|||
"moves": "Переїзди користувач_ок"
|
||||
},
|
||||
"languages": {
|
||||
"ar": "арабська",
|
||||
"az": "азербайджанська",
|
||||
"bg": "болгарська",
|
||||
"cs": "чеська",
|
||||
"da": "данська",
|
||||
"de": "німецька",
|
||||
"el": "грецька",
|
||||
"en": "англійська",
|
||||
"eo": "есперанто",
|
||||
"es": "іспанська",
|
||||
"fa": "перська",
|
||||
"fi": "фінська",
|
||||
"fr": "французька",
|
||||
"ga": "ірландська",
|
||||
"he": "іврит",
|
||||
"hi": "гінді",
|
||||
"hu": "угорська",
|
||||
"id": "індонезійська",
|
||||
"it": "італійська",
|
||||
"ja": "японська",
|
||||
"ko": "корейська",
|
||||
"lt": "литовська",
|
||||
"lv": "латвійська",
|
||||
"nl": "нідерландська",
|
||||
"pl": "польська",
|
||||
"pt": "португальська",
|
||||
"ru": "російська",
|
||||
"sk": "словацька",
|
||||
"sv": "шведська",
|
||||
"tr": "турецька",
|
||||
"translated_from": {
|
||||
"ar": "Перекладено з арабської",
|
||||
"az": "Перекладено з азербайджанської",
|
||||
"bg": "Перекладено з болгарської",
|
||||
"cs": "Перекладено з чеської",
|
||||
"da": "Перекладено з данської",
|
||||
"de": "Перекладено з німецької",
|
||||
"el": "Перекладено з грецької",
|
||||
"en": "Перекладено з англійської",
|
||||
"eo": "Перекладено з есперанто",
|
||||
"es": "Перекладено з іспанської",
|
||||
"fa": "Перекладено з перської",
|
||||
"fi": "Перекладено з фінської",
|
||||
"fr": "Перекладено з французької",
|
||||
"ga": "Перекладено з ірландської",
|
||||
"he": "Перекладено з івриту",
|
||||
"hi": "Перекладено з гінді",
|
||||
"hu": "Перекладено з угорської",
|
||||
"id": "Перекладено з індонезійської",
|
||||
"it": "Перекладено з італійської",
|
||||
"ja": "Перекладено з японської",
|
||||
"ko": "Перекладено з корейської",
|
||||
"lt": "Перекладено з литовської",
|
||||
"lv": "Перекладено з латвійської",
|
||||
"nl": "Перекладено з нідерландської",
|
||||
"pl": "Перекладено з польської",
|
||||
"pt": "Перекладено з португальської",
|
||||
"ru": "Перекладено з російської",
|
||||
"sk": "Перекладено зі словацької",
|
||||
"sv": "Перекладено зі словенської",
|
||||
"tr": "Перекладено з турецької",
|
||||
"uk": "Перекладено з української",
|
||||
"zh": "Перекладено з китайської"
|
||||
},
|
||||
"uk": "українська",
|
||||
"zh": "китайська"
|
||||
"uk": "українська"
|
||||
},
|
||||
"lists": {
|
||||
"create": "Створити",
|
||||
|
@ -239,7 +173,6 @@
|
|||
"following_only": "Лише за ким ви стежите",
|
||||
"lists": "Списки",
|
||||
"new": "Новий список",
|
||||
"save": "Зберегти зміни",
|
||||
"search": "Знайти користувач_ок",
|
||||
"title": "Назва списку"
|
||||
},
|
||||
|
@ -266,7 +199,6 @@
|
|||
"username": "Логін"
|
||||
},
|
||||
"media_modal": {
|
||||
"counter": "{current} / {total}",
|
||||
"hide": "Закрити медіаперегляд",
|
||||
"next": "Наступна",
|
||||
"previous": "Попередня"
|
||||
|
@ -290,9 +222,7 @@
|
|||
"reports": "Скарги",
|
||||
"resolve": "Вирішено",
|
||||
"show_closed": "Показати закриті",
|
||||
"statuses": "Дописів: { count } | Дописів: { count }",
|
||||
"tag_policy_notice": "Увімкніть MRF TagPolicy, щоб обмежувати окремі обліковки",
|
||||
"tags": "Обмежити обліковку"
|
||||
"statuses": "Дописів: { count } | Дописів: { count }"
|
||||
},
|
||||
"statuses": "Дописи",
|
||||
"users": "Користувач_ки"
|
||||
|
@ -312,21 +242,21 @@
|
|||
"home_timeline_description": "Дописи від тих, на кого ви підписані",
|
||||
"interactions": "Взаємодії",
|
||||
"lists": "Списки",
|
||||
"mentions": "Згадки",
|
||||
"mentions": "Згадування",
|
||||
"moderation": "Модерування",
|
||||
"preferences": "Налаштування",
|
||||
"public_timeline_description": "Загальнодоступні дописи всіх на цьому сервері",
|
||||
"public_tl": "Спільна стрічка",
|
||||
"search": "Пошук",
|
||||
"timeline": "Стрічка",
|
||||
"timeline": "Домашня стрічка",
|
||||
"timelines": "Стрічки",
|
||||
"twkn": "Уся відома мережа",
|
||||
"twkn_timeline_description": "Дописи з усієї мережі",
|
||||
"user_search": "Пошук користувач_ок",
|
||||
"who_to_follow": "На кого підписатись"
|
||||
"who_to_follow": "Кого відстежувати"
|
||||
},
|
||||
"notifications": {
|
||||
"broken_favorite": "Допис невідомий, пошук триває…",
|
||||
"broken_favorite": "Невідомий допис, шукаю його…",
|
||||
"error": "Помилка при оновленні сповіщень: {0}",
|
||||
"favorited_you": "вподобує",
|
||||
"follow_request": "хоче підписатись",
|
||||
|
@ -355,12 +285,12 @@
|
|||
"polls": {
|
||||
"add_option": "Додати варіант",
|
||||
"add_poll": "Додати опитування",
|
||||
"expired": "Завершено {0} тому",
|
||||
"expires_in": "До кінця {0}",
|
||||
"expired": "Опитування закінчилось {0} тому",
|
||||
"expires_in": "Опитування закінчується через {0}",
|
||||
"expiry": "Термін опитування",
|
||||
"multiple_choices": "Декілька варіантів",
|
||||
"not_enough_options": "Замало унікальних варіантів в опитуванні",
|
||||
"option": "Варіант",
|
||||
"option": "Відповідь",
|
||||
"people_voted_count": "Осіб: {count} | Осіб: {count}",
|
||||
"single_choice": "Один варіант",
|
||||
"type": "Тип опитування",
|
||||
|
@ -392,7 +322,7 @@
|
|||
"media_not_sensitive_warning": "Ви вказали засторогу, але не позначили вкладення дражливими!",
|
||||
"new_status": "Новий допис",
|
||||
"post": "Розмістити",
|
||||
"posting": "Розміщення",
|
||||
"posting": "Відправляється",
|
||||
"preview": "Попередній перегляд",
|
||||
"preview_empty": "Порожньо",
|
||||
"scope": {
|
||||
|
@ -410,13 +340,11 @@
|
|||
}
|
||||
},
|
||||
"registration": {
|
||||
"awaiting_email_confirmation": "Вам надіслано лист підтвердження створення обліковки. Перевірте е-пошту, щоб завершити реєстрацію.",
|
||||
"awaiting_email_confirmation_title": "Очікування підтвердження поштою",
|
||||
"bio": "Опис",
|
||||
"bio_placeholder": "як-от:\nВолинянка. Подорожую.\nДемократична соціалістка. Пишу драматичні поеми. Жити хочу! Геть, думи сумні!",
|
||||
"captcha": "CAPTCHA",
|
||||
"email": "Е-пошта",
|
||||
"email_language": "Мова листів сервера",
|
||||
"email_language": "Якою мовою отримувати листи з сервера?",
|
||||
"fullname": "Ім'я",
|
||||
"fullname_placeholder": "як-от Лариса Косач",
|
||||
"new_captcha": "Натисніть на зображення, щоб оновити код, якщо він нерозбірливий",
|
||||
|
@ -425,8 +353,6 @@
|
|||
"reason_placeholder": "Запити на реєстрацію підтверджуються вручну.\nРозкажіть адміністрації, чому хочете зареєструватися.",
|
||||
"register": "Зареєструватися",
|
||||
"registration": "Реєстрація",
|
||||
"request_sent": "Ваш запит на реєстрацію надіслано на розгляд. Ви отримаєте лист, коли вашу реєстрацію схвалять.",
|
||||
"request_sent_title": "Запит на реєстрацію надіслано",
|
||||
"token": "Ключ запрошення",
|
||||
"username_placeholder": "як-от lesia",
|
||||
"validations": {
|
||||
|
@ -444,7 +370,7 @@
|
|||
"searching_for": "Шукаю"
|
||||
},
|
||||
"search": {
|
||||
"hashtags": "Теги",
|
||||
"hashtags": "Хештеги",
|
||||
"no_results": "Немає результатів",
|
||||
"people": "Люди",
|
||||
"people_talking": "{count} людей говорять про це",
|
||||
|
@ -456,34 +382,34 @@
|
|||
"settings": {
|
||||
"accent": "Акцент",
|
||||
"account_alias": "Інші обліковки",
|
||||
"account_alias_table_head": "Попередня обліковка",
|
||||
"account_alias_table_head": "Нова обліковка",
|
||||
"account_backup": "Резервне копіювання обліковки",
|
||||
"account_backup_description": "Можете завантажити архів даних і дописів своєї обліковки. Але імпортувати їх на іншу обліковку поки неможливо.",
|
||||
"account_backup_table_head": "Резервна копія",
|
||||
"account_privacy": "Приватність",
|
||||
"add_alias_error": "Помилка додання обліковки: {error}",
|
||||
"add_alias_error": "Помилка додання нової обліковки: {error}",
|
||||
"add_backup": "Створити резервну копію",
|
||||
"add_backup_error": "Помилка резервного копіювання: {error}",
|
||||
"added_alias": "Обліковку додано.",
|
||||
"added_alias": "Нову обліковку додано.",
|
||||
"added_backup": "Резервну копію створено.",
|
||||
"allow_following_move": "Дозволити автостеження при переміщенні на інший інстанс",
|
||||
"always_show_post_button": "Завжди показувати кнопку «Новий допис»",
|
||||
"app_name": "Назва програми",
|
||||
"attachmentRadius": "Вкладення",
|
||||
"attachments": "Вкладення",
|
||||
"autohide_floating_post_button": "Автоматично ховати кнопку «Новий допис» (на мобільному)",
|
||||
"autohide_floating_post_button": "Автоматично ховати кнопку \"Новий допис\" (в мобільній версії)",
|
||||
"avatar": "Аватар",
|
||||
"avatarAltRadius": "Аватари у сповіщеннях",
|
||||
"avatarRadius": "Аватари",
|
||||
"avatar_size_instruction": "Рекомендований мінімальний розмір для зображень аватара становить 150x150 пікселів.",
|
||||
"background": "Тло",
|
||||
"background": "Обкладинка",
|
||||
"backup_not_ready": "Резервна копія ще не готова.",
|
||||
"bio": "Опис",
|
||||
"block_export": "Експорт блокувань",
|
||||
"block_export_button": "Експорт блокувань у CSV-файл",
|
||||
"block_export_button": "Експорт блокувань у файл CSV",
|
||||
"block_import": "Імпорт блокувань",
|
||||
"block_import_error": "Помилка імпорту блокувань",
|
||||
"blocks_imported": "Блокування імпортовано! Їх буде застосовано поступово.",
|
||||
"block_import_error": "Помилка імпортування блокувань",
|
||||
"blocks_imported": "Блокування імпортовані! Їх обробка триватиме певний час.",
|
||||
"blocks_tab": "Блокування",
|
||||
"bot": "Це обліковка бота",
|
||||
"btnRadius": "Кнопки",
|
||||
|
@ -515,11 +441,6 @@
|
|||
"conversation_display": "Стиль показу розмови",
|
||||
"conversation_display_linear": "Колонка",
|
||||
"conversation_display_tree": "Дерево",
|
||||
"conversation_other_replies_button": "Кнопка «Переглянути ще»",
|
||||
"conversation_other_replies_button_below": "Під дописами",
|
||||
"conversation_other_replies_button_inside": "Всередині дописів",
|
||||
"current_avatar": "Ваш поточний аватар",
|
||||
"current_mascot": "Ваш поточний маскот",
|
||||
"current_password": "Поточний пароль",
|
||||
"data_import_export_tab": "Імпорт/експорт даних",
|
||||
"default_vis": "Типовий рівень приватності",
|
||||
|
@ -540,24 +461,24 @@
|
|||
"expire_posts_input_placeholder": "Скільки днів",
|
||||
"export_theme": "Завантажити",
|
||||
"file_export_import": {
|
||||
"backup_restore": "Копія налаштувань",
|
||||
"backup_restore": "Резервне копіювання налаштувань",
|
||||
"backup_settings": "Завантажити файл налаштувань",
|
||||
"backup_settings_theme": "Завантажити налаштування й тему",
|
||||
"errors": {
|
||||
"file_slightly_new": "Файл призначено для трохи новішої версії й може спрацювати не повністю",
|
||||
"file_too_new": "Версія {fileMajor} новіша, ніж ця AkkomaFE ({feMajor}) може обробити",
|
||||
"file_too_old": "Версія {fileMajor} більше не підтримується (треба {feMajor} чи новіше)",
|
||||
"file_slightly_new": "Другорядна версія файлу відрізняється, деякі налаштування можуть бути не прийняті",
|
||||
"file_too_new": "Несумісна основна версія: {fileMajor}, ця версія PleromaFE ({feMajor}) занадто стара для його обробки",
|
||||
"file_too_old": "Несумісна основна версія: {fileMajor}, версія файлу занадто стара і не підтримується (мінімальна версія налаштувань {feMajor})",
|
||||
"invalid_file": "Вибраний файл не є резервною копією налаштувань Pleroma. Ніяких змін не було зроблено."
|
||||
},
|
||||
"restore_settings": "Відновити налаштування з файлу"
|
||||
},
|
||||
"filtering": "Фільтр",
|
||||
"filtering_explanation": "Усі дописи з цими словами буде сховано. Пишіть кожне слово з нового рядка",
|
||||
"follow_export": "Експорт підписок",
|
||||
"follow_export_button": "Експорт підписок до CSV-файлу",
|
||||
"follow_import": "Імпорт підписок",
|
||||
"follow_import_error": "Помилка імпорту підписок",
|
||||
"follows_imported": "Підписки імпортовано! Їх буде застосовано поступово.",
|
||||
"follow_export": "Експортувати відстежуваних",
|
||||
"follow_export_button": "Експортувати відстежуваних до csv файлу",
|
||||
"follow_import": "Імпортувати відстежуваних",
|
||||
"follow_import_error": "Помилка імпортування відстежуваних",
|
||||
"follows_imported": "Відстежуваних імпортовано! Їхня обробка потребує часу.",
|
||||
"foreground": "Передній план",
|
||||
"fun": "Іграшки",
|
||||
"general": "Загальні",
|
||||
|
@ -576,7 +497,7 @@
|
|||
"hide_list_aliases_error_action": "Закрити",
|
||||
"hide_media_previews": "Приховати попередній перегляд медіа",
|
||||
"hide_muted_posts": "Ховати повідомлення приглушених користувач_ок",
|
||||
"hide_muted_threads": "Ховати приглушені гілки",
|
||||
"hide_muted_threads": "Ховати заглушені гілки",
|
||||
"hide_post_stats": "Ховати статистику дописів (скільки вподобань тощо)",
|
||||
"hide_shoutbox": "Приховати оголошення сервера",
|
||||
"hide_site_favicon": "Вилучити значок сервера з шапки",
|
||||
|
@ -585,9 +506,9 @@
|
|||
"hide_user_stats": "Ховати користувацьку статистику (скільки підписок тощо)",
|
||||
"hide_wallpaper": "Сховати шпалери сервера",
|
||||
"hide_wordfiltered_statuses": "Ховати дописи зі словами з фільтру",
|
||||
"import_blocks_from_a_csv_file": "Імпортуйте CSV-файл блокувань",
|
||||
"import_followers_from_a_csv_file": "Імпортуйте CSV-файл підписок",
|
||||
"import_mutes_from_a_csv_file": "Імпортуйте CSV-файл приглушень",
|
||||
"import_blocks_from_a_csv_file": "Імпортувати заблокованих з csv файлу",
|
||||
"import_followers_from_a_csv_file": "Імпортувати відстежуваних з csv файлу",
|
||||
"import_mutes_from_a_csv_file": "Імпорт заглушених з csv файлу",
|
||||
"import_theme": "Вивантажити",
|
||||
"inputRadius": "Поля вводу",
|
||||
"instance_default": "(усталено: {value})",
|
||||
|
@ -597,16 +518,13 @@
|
|||
"invalid_theme_imported": "Вибраний файл не є темою Pleroma. У вашу тему не внесено жодних змін.",
|
||||
"limited_availability": "Недоступно у вашому браузері",
|
||||
"links": "Посилання",
|
||||
"list_aliases_error": "Помилка завантаження обліковок: {error}",
|
||||
"list_aliases_error": "Помилка завантаження нових обліковок: {error}",
|
||||
"list_backups_error": "Помилка завантаження переліку копій: {error}",
|
||||
"lock_account_description": "Показувати лише схваленим підписни_цям",
|
||||
"loop_video": "Зациклити відео",
|
||||
"loop_video_silent_only": "Зациклити відео без звуку (напр. Mastodon \"gifs\")",
|
||||
"mascot": "Маскот Mastodon FE",
|
||||
"max_depth_in_thread": "Максимальна кількість типово показаних рівнів гілки обговорення",
|
||||
"max_thumbnails": "Максимальна кількість мініатюр на повідомлення",
|
||||
"mention_link_bolden_you": "Підсвічувати посилання, коли вас згадують",
|
||||
"mention_link_display": "Формат посилань згадок",
|
||||
"mention_link_display": "Показувати згадування",
|
||||
"mention_link_display_full": "завжди повністю (як-от {'@'}lesia{'@'}example.org)",
|
||||
"mention_link_display_full_for_remote": "повністю, якщо це інший сервер (як-от {'@'}lesia{'@'}example.org)",
|
||||
"mention_link_display_short": "завжди коротко (як-от {'@'}lesia)",
|
||||
|
@ -639,37 +557,34 @@
|
|||
"more_settings": "Більше налаштувань",
|
||||
"move_account": "Переїхати",
|
||||
"move_account_error": "Помилка переїзду обліковки: {error}",
|
||||
"move_account_notes": "Щоб переїхати з цієї обліковки на іншу, додайте в її налаштуваннях цю обліковку як попередню.",
|
||||
"move_account_target": "Нова обліковка (як-от {example})",
|
||||
"moved_account": "Переїзд обліковки виконано.",
|
||||
"move_account_notes": "Щоб переїхати з цієї обліковки на іншу, вкажіть у її налаштуваннях цю облікову як нову.",
|
||||
"mute_bot_posts": "Сховати дописи ботів",
|
||||
"mute_export": "Експорт приглушень",
|
||||
"mute_export_button": "Експорт приглушених у csv файл",
|
||||
"mute_import": "Імпорт приглушень",
|
||||
"mute_import_error": "Під час імпорту приглушених сталася помилка",
|
||||
"mutes_and_blocks": "Приглушення та блокування",
|
||||
"mutes_imported": "Приглушення імпортовано! Їх буде застосовано поступово.",
|
||||
"mutes_tab": "Приглушення",
|
||||
"mute_export": "Експорт ігнорувань",
|
||||
"mute_export_button": "Експорт заглушених у csv файл",
|
||||
"mute_import": "Імпорт ігнорувань",
|
||||
"mute_import_error": "Під час імпорту заглушених сталася помилка",
|
||||
"mutes_and_blocks": "Заглушення та блокування",
|
||||
"mutes_imported": "Заглушені імпортовані! Їх обробка триватиме певний час.",
|
||||
"mutes_tab": "Заглушені",
|
||||
"name": "Ім'я",
|
||||
"name_bio": "Особисті дані",
|
||||
"new_alias_target": "Додайте стару обліковку ({example})",
|
||||
"new_alias_target": "Вкажіть нову обліковку (як-от {example})",
|
||||
"new_email": "Нова е-пошта",
|
||||
"new_password": "Новий пароль",
|
||||
"no_blocks": "Нікого не заблоковано",
|
||||
"no_mutes": "Нікого не приглушено",
|
||||
"no_blocks": "Блокування відсутні",
|
||||
"no_mutes": "Заглушені відсутні",
|
||||
"no_rich_text_description": "Видалення всього форматування тексту з усіх дописів",
|
||||
"notification_blocks": "Щоб не отримувати сповіщень від когось і вилучити їх із підписни_ць, заблокуйте їх.",
|
||||
"notification_mutes": "Щоб просто не отримувати сповіщень від когось, приглушіть їх.",
|
||||
"notification_blocks": "Блокування користувача зупиняє всі сповіщення від нього, а також скасовує його відстеження.",
|
||||
"notification_mutes": "Щоб перестати отримувати сповіщення від певного користувача, заглушіть його.",
|
||||
"notification_setting_block_from_strangers": "Блокувати сповіщення від тих, на кого ви не підписані",
|
||||
"notification_setting_filters": "Фільтри",
|
||||
"notification_setting_hide_if_cw": "Ховати вміст push-сповіщення за наявності застороги",
|
||||
"notification_setting_hide_notification_contents": "Ховати відправника та вміст push-сповіщень",
|
||||
"notification_setting_privacy": "Приватність",
|
||||
"notification_visibility": "Сповіщати про такі події",
|
||||
"notification_visibility_emoji_reactions": "Реакції",
|
||||
"notification_visibility_follows": "Підписни_ці",
|
||||
"notification_visibility_likes": "Вподобане",
|
||||
"notification_visibility_mentions": "Згадки",
|
||||
"notification_visibility_mentions": "Згадування",
|
||||
"notification_visibility_moves": "Переїзди користувач_ок",
|
||||
"notification_visibility_polls": "Результати голосувань",
|
||||
"notification_visibility_repeats": "Поширення",
|
||||
|
@ -678,7 +593,7 @@
|
|||
"oauth_tokens": "OAuth-ключі",
|
||||
"pad_emoji": "Автоматично додавати простір з обидвох сторін емоджі",
|
||||
"panelRadius": "Панелі",
|
||||
"pause_on_unfocused": "Лише коли вкладка активна",
|
||||
"pause_on_unfocused": "Призупинити трансляцію, коли вкладка неактивна",
|
||||
"play_videos_in_modal": "Відтворювати відео у спливаючій рамці",
|
||||
"post_look_feel": "Вигляд дописів",
|
||||
"post_status_content_type": "Типова розмітка тексту",
|
||||
|
@ -696,7 +611,7 @@
|
|||
"profile_tab": "Профіль",
|
||||
"radii_help": "Радіус заокруглення кутів інтерфейсу (в пікселях)",
|
||||
"refresh_token": "Оновити ключ",
|
||||
"remove_alias": "Вилучити обліковку",
|
||||
"remove_alias": "Вилучити нову обліковку",
|
||||
"remove_backup": "Вилучити",
|
||||
"render_mfm": "Показувати розмітку Misskey",
|
||||
"render_mfm_on_hover": "Анімувати MFM лише при наведенні",
|
||||
|
@ -718,14 +633,13 @@
|
|||
"saving_err": "Помилка при збереженні налаштувань",
|
||||
"saving_ok": "Налаштування збережені",
|
||||
"scope_copy": "Відповідати на тому ж рівні приватності (завжди ввімкнено для особистих повідомлень)",
|
||||
"search_user_to_block": "Шукайте, кого слід заблокувати",
|
||||
"search_user_to_mute": "Шукайте, кого слід приглушити",
|
||||
"search_user_to_block": "Шукайте кого ви хочете заблокувати",
|
||||
"search_user_to_mute": "Шукайте кого ви хочете заглушити",
|
||||
"security": "Безпека",
|
||||
"security_tab": "Безпека",
|
||||
"sensitive_by_default": "Одразу позначати дописи дражливими",
|
||||
"sensitive_if_subject": "Позначати зображення дражливими, якщо вказано засторогу",
|
||||
"sensitive_if_subject": "Позначати зображення дражливими, якщо вказано застереження",
|
||||
"set_new_avatar": "Встановити новий аватар",
|
||||
"set_new_mascot": "Встановити нового маскота",
|
||||
"set_new_profile_background": "Встановити нову обкладинку профілю",
|
||||
"set_new_profile_banner": "Встановити новий банер",
|
||||
"setting_changed": "Конфігурація відрізняється від типової",
|
||||
|
@ -750,7 +664,6 @@
|
|||
"show_panel_nav_shortcuts": "Ярлики стрічок у заголовку панелі",
|
||||
"show_scrollbars": "Смуги прокрутки в бокових панелях",
|
||||
"show_wider_shortcuts": "Більша відстань між ярликами в заголовку",
|
||||
"show_yous": "Показувати «(ви)»",
|
||||
"stop_gifs": "Анімувати GIF лише при наведенні",
|
||||
"streaming": "Автоматично показувати нові дописи вгорі стрічки",
|
||||
"style": {
|
||||
|
@ -831,10 +744,10 @@
|
|||
"faint_link": "корисний підручник",
|
||||
"fine_print": "Прочитайте наш {0} аби нічого нового не дізнатись!",
|
||||
"header": "Попередній перегляд",
|
||||
"header_faint": "Усе гаразд",
|
||||
"header_faint": "Все гаразд",
|
||||
"input": "Що нового?",
|
||||
"link": "невеличке посилання",
|
||||
"mono": "вмісту",
|
||||
"mono": "змісту",
|
||||
"text": "Трохи більше {0} та {1}"
|
||||
},
|
||||
"radii": {
|
||||
|
@ -906,7 +819,7 @@
|
|||
"subject_line_noop": "не копіювати",
|
||||
"text": "Текст",
|
||||
"theme": "Тема",
|
||||
"theme_help": "Замініть кольори теми своїми в форматі #rrggbb.",
|
||||
"theme_help": "Замініть кольори теми своїми в форматі (#rrggbb).",
|
||||
"theme_help_v2_1": "Деякі кольори й прозорості розраховуються автоматично. Позначайте їх пташками, щоб замінити їх вручну. Кнопка «Очистити все» видаляє всі такі заміни.",
|
||||
"theme_help_v2_2": "Значки під кольорами показують, наскільки добре видно текст на тлі. При наведенні побачите деталі. Якщо використовуєте прозорість, буде оцінено найгірший варіант.",
|
||||
"third_column_mode": "Третя панель, якщо достатньо місця",
|
||||
|
@ -915,20 +828,13 @@
|
|||
"third_column_mode_postform": "Новий допис і меню",
|
||||
"token": "Ключ",
|
||||
"tooltipRadius": "Підказки/попередження",
|
||||
"translation_language": "Мова автоматичного перекладу",
|
||||
"tree_advanced": "Показувати додаткові кнопки відкриття й закриття ланцюгів відповідей у гілках",
|
||||
"tree_fade_ancestors": "Показувати менш виразно дописи, що передують поточному",
|
||||
"type_domains_to_mute": "Шукайте домени, які слід приглушити",
|
||||
"type_domains_to_mute": "Пошук доменів для заглушення",
|
||||
"upload_a_photo": "Вивантажити фото",
|
||||
"useStreamingApi": "Отримувати дописи та сповіщення наживо",
|
||||
"useStreamingApiWarning": "Загалом працює. Якщо не зовсім, спробуєте оновити сторінку?",
|
||||
"use_blurhash": "Показувати дражливі мініатюри (як розмиті кольори)",
|
||||
"useStreamingApiWarning": "(Не рекомендується, експериментально, повідомлення можуть зникати)",
|
||||
"use_at_icon": "Значок {'@'} замість символа",
|
||||
"use_contain_fit": "Не обрізати краї мініатюр",
|
||||
"use_one_click_nsfw": "Відкривати NSFW вкладення одним кліком миші",
|
||||
"user_accepts_direct_messages_from": "Приймати особисті повідомлення",
|
||||
"user_accepts_direct_messages_from_everybody": "Будь-чиї",
|
||||
"user_accepts_direct_messages_from_nobody": "Нічиї",
|
||||
"user_accepts_direct_messages_from_people_i_follow": "Від тих, на кого я підписуюсь",
|
||||
"user_mutes": "Користувач_ки",
|
||||
"user_profile_default_tab": "Типова вкладка профілів",
|
||||
"user_profiles": "Користувацькі профілі",
|
||||
|
@ -947,18 +853,8 @@
|
|||
"word_filter": "Фільтр слів",
|
||||
"wordfilter": "Фільтр слів"
|
||||
},
|
||||
"settings_profile": {
|
||||
"creating": "Створення профілю налаштувань «{profile}»…",
|
||||
"synchronization_error": "Не вдалося синхронізувати налаштування: {err}",
|
||||
"synchronized": "Налаштування синхронізовано!",
|
||||
"synchronizing": "Синхронізація профілю налаштувань «{profile}»…"
|
||||
},
|
||||
"status": {
|
||||
"ancestor_follow": "Переглянути ще {numReplies} під цим дописом | Переглянути ще {numReplies} під цим дописом",
|
||||
"ancestor_follow_with_icon": "{icon} {text}",
|
||||
"attachment_stop_flash": "Зупинити Flash-плеєр",
|
||||
"bookmark": "Додати до закладок",
|
||||
"collapse_attachments": "Згорнути вкладення",
|
||||
"copy_link": "Скопіювати посилання на допис",
|
||||
"delete": "Видалити допис",
|
||||
"delete_confirm": "Точно видалити допис?",
|
||||
|
@ -972,17 +868,11 @@
|
|||
"expand": "Розгорнути",
|
||||
"external_source": "Зовнішнє джерело",
|
||||
"favorites": "Вподобане",
|
||||
"hide_attachment": "Сховати вкладення",
|
||||
"hide_content": "Сховати вміст",
|
||||
"hide_full_subject": "Згорнути засторогу",
|
||||
"many_attachments": "Вкладень: {number} | Вкладень: {number}",
|
||||
"mentions": "Згадки",
|
||||
"move_down": "Посунути вкладення праворуч",
|
||||
"move_up": "Посунути вкладення ліворуч",
|
||||
"mute_conversation": "Приглушити розмову",
|
||||
"hide_full_subject": "Сховати всю тему",
|
||||
"mute_conversation": "Заглушити розмову",
|
||||
"nsfw": "Дражливий вміст",
|
||||
"open_gallery": "Відкрити галерею",
|
||||
"override_translation_source_language": "Замістити початкову мову",
|
||||
"pin": "Закріпити в профілі",
|
||||
"pinned": "Закріплено",
|
||||
"plus_more": "ще {number}",
|
||||
|
@ -991,33 +881,21 @@
|
|||
"redraft_confirm_accept_button": "Так, видалити й переписати",
|
||||
"redraft_confirm_cancel_button": "Ні, лишити оригінал",
|
||||
"redraft_confirm_title": "Підтвердьте видалення й переписання",
|
||||
"remove_attachment": "Видалити вкладення",
|
||||
"repeat_confirm": "Точно поширити допис?",
|
||||
"repeat_confirm_accept_button": "Так, поширити",
|
||||
"repeat_confirm_cancel_button": "Ні, не поширювати",
|
||||
"repeat_confirm_title": "Підтвердьте поширення",
|
||||
"remove_attachment": "Видалити долучення",
|
||||
"repeats": "Поширення",
|
||||
"replies_list": "Відповіді:",
|
||||
"replies_list_with_others": "Ще відповідей: {numReplies} | Ще відповідей: {numReplies}",
|
||||
"reply_to": "Відповідь",
|
||||
"show_all_attachments": "Показати всі вкладення",
|
||||
"show_all_conversation": "Показати всю розмову (ще дописів: {numStatus}) | Показати всю розмову (ще дописів: {numStatus})",
|
||||
"show_all_conversation_with_icon": "{icon} {text}",
|
||||
"show_attachment_description": "Переглянути опис (натисніть саме вкладення, якщо опис не вміщається)",
|
||||
"show_attachment_in_modal": "Показати вкладення у вікні",
|
||||
"show_content": "Показати вміст",
|
||||
"show_full_subject": "Показати всю засторогу",
|
||||
"show_only_conversation_under_this": "Показати відповіді лише до цього допису",
|
||||
"status_deleted": "Цей допис був видалений",
|
||||
"status_unavailable": "Допис недоступний",
|
||||
"thread_follow": "Ще відповідей: {numStatus} | Ще відповідей: {numStatus}",
|
||||
"thread_follow_with_icon": "{icon} {text}",
|
||||
"thread_hide": "Сховати гілку",
|
||||
"thread_muted": "Гілку приглушено",
|
||||
"thread_muted_and_words": ", має слова:",
|
||||
"thread_show": "Показати гілку",
|
||||
"thread_show_full": "Показати відповіді: {numStatus} | Показати відповіді: {numStatus}",
|
||||
"thread_show_full_with_icon": "{icon} {text}",
|
||||
"translate": "Перекласти",
|
||||
"translated_from": "В оригіналі: {language}",
|
||||
"unbookmark": "Видалити із закладок",
|
||||
|
@ -1051,7 +929,6 @@
|
|||
"collapse": "Згорнути",
|
||||
"conversation": "Розмова",
|
||||
"error": "Помилка завантаження стрічки: {0}",
|
||||
"follow_tag": "Підписатись на тег",
|
||||
"load_older": "Завантажити давніші дописи",
|
||||
"no_more_statuses": "Більше немає дописів",
|
||||
"no_retweet_hint": "Допис приватний чи особистий, тож його не можна поширити чи цитувати",
|
||||
|
@ -1061,12 +938,8 @@
|
|||
"show_new": "Показати нові",
|
||||
"socket_broke": "Втрачено з'єднання у реальному часі: код {0}",
|
||||
"socket_reconnected": "Встановлено з'єднання у реальному часі",
|
||||
"unfollow_tag": "Відписатись від тегу",
|
||||
"up_to_date": "Оновлено"
|
||||
},
|
||||
"toast": {
|
||||
"no_translation_target_set": "Не обрано цільової мови перекладу — може не вдатися. Оберіть у налаштуваннях, на яку мову перекладати."
|
||||
},
|
||||
"tool_tip": {
|
||||
"accept_follow_request": "Дозволити підписатись",
|
||||
"add_reaction": "Додати реакцію",
|
||||
|
@ -1105,14 +978,14 @@
|
|||
"disable_remote_subscription": "Заборонити підписуватись користувач_кам з інших серверів",
|
||||
"force_nsfw": "Позначити всі дописи дражливими",
|
||||
"force_unlisted": "Не показувати дописи в стрічці",
|
||||
"grant_admin": "Дозволити адміністрування",
|
||||
"grant_moderator": "Дозволити модерування",
|
||||
"grant_admin": "Надати права адміністратора",
|
||||
"grant_moderator": "Надати права модератора",
|
||||
"moderation": "Модерування",
|
||||
"quarantine": "Не розповсюджувати дописи на інших інстансах",
|
||||
"revoke_admin": "Позбавити прав адміністрування",
|
||||
"revoke_moderator": "Позбавити прав модерування",
|
||||
"revoke_admin": "Позбавити прав адміністратора",
|
||||
"revoke_moderator": "Позбавити прав модератора",
|
||||
"sandbox": "Показувати дописи лише підписни_цям",
|
||||
"strip_media": "Вилучити медіа з дописів"
|
||||
"strip_media": "Вилучити медіа з дописів користувача"
|
||||
},
|
||||
"approve": "Дозволити",
|
||||
"approve_confirm": "Точно дозволити користувач_ці на вас підписатись?",
|
||||
|
@ -1133,18 +1006,13 @@
|
|||
"deny_confirm": "Точно відхилити запит на підписку?",
|
||||
"deny_confirm_accept_button": "Так, відхилити",
|
||||
"deny_confirm_cancel_button": "Ні, скасувати",
|
||||
"deny_confirm_title": "Відхилити запит на підписку",
|
||||
"domain_muted": "Розблокувати домен",
|
||||
"edit_profile": "Редагувати профіль",
|
||||
"favorites": "Вподобання",
|
||||
"follow": "Підписатись",
|
||||
"follow_cancel": "Скасувати запит",
|
||||
"follow_progress": "Надсилання…",
|
||||
"follow_sent": "Запит надіслано!",
|
||||
"follow_tag": "Підписатись на тег",
|
||||
"follow_progress": "Запитую…",
|
||||
"follow_sent": "Запит відправлено!",
|
||||
"follow_unfollow": "Відписатись",
|
||||
"followed_tags": "Підписки на теги",
|
||||
"followed_users": "Підписки на користувач_ок",
|
||||
"followees": "Підписки",
|
||||
"followers": "Підписни_ці",
|
||||
"following": "Ви підписані!",
|
||||
|
@ -1168,8 +1036,7 @@
|
|||
"mute_confirm_title": "Приглушення",
|
||||
"mute_domain": "Заблокувати домен",
|
||||
"mute_progress": "Глушимо…",
|
||||
"muted": "Приглушено",
|
||||
"not_following_any_hashtags": "У вас нема підписок на теги",
|
||||
"muted": "Заглушено",
|
||||
"note": "Приватна нотатка",
|
||||
"per_day": "на день",
|
||||
"remote_follow": "Підписатись",
|
||||
|
@ -1186,9 +1053,8 @@
|
|||
"unfollow_confirm_accept_button": "Так, відписатись",
|
||||
"unfollow_confirm_cancel_button": "Ні, не відписуватись",
|
||||
"unfollow_confirm_title": "Відписка",
|
||||
"unfollow_tag": "Відписатись від тегу",
|
||||
"unmute": "Відновити",
|
||||
"unmute_progress": "Відновлення…",
|
||||
"unmute": "Зняти глушення",
|
||||
"unmute_progress": "Знімаємо глушення…",
|
||||
"unsubscribe": "Відписатись"
|
||||
},
|
||||
"user_profile": {
|
||||
|
@ -1203,7 +1069,7 @@
|
|||
"forward_description": "Це обліковка з іншого сервера. Надіслати його адміністрації копію скарги?",
|
||||
"forward_to": "Переслати до {0}",
|
||||
"generic_error": "Виникла помилка під час обробки вашого запиту.",
|
||||
"submit": "Надіслати",
|
||||
"submit": "Відправити",
|
||||
"title": "Поскаржитись на {0}"
|
||||
},
|
||||
"who_to_follow": {
|
||||
|
|
|
@ -922,13 +922,10 @@
|
|||
"upload_a_photo": "上传照片",
|
||||
"useStreamingApi": "实时接收帖文和通知",
|
||||
"useStreamingApiWarning": "十分炫酷推荐使用。要是崩了试试刷新?",
|
||||
"use_at_icon": "将 {'@'} 符号显示为图标而不是文本",
|
||||
"use_blurhash": "对NSFW的缩略图使用模糊处理",
|
||||
"use_contain_fit": "生成缩略图时不要裁剪附件",
|
||||
"use_one_click_nsfw": "点击一次以打开工作场所不适宜(NSFW)的附件",
|
||||
"user_accepts_direct_messages_from": "允许私信自",
|
||||
"user_accepts_direct_messages_from_everybody": "所有人",
|
||||
"user_accepts_direct_messages_from_nobody": "没有人",
|
||||
"user_accepts_direct_messages_from_people_i_follow": "我关注的人",
|
||||
"user_mutes": "用户",
|
||||
"user_profile_default_tab": "用户资料中的默认标签页",
|
||||
"user_profiles": "用户资料",
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
import iso6391 from 'iso-639-1'
|
||||
import { computed } from 'vue'
|
||||
|
||||
export const usePostLanguageOptions = () => {
|
||||
const postLanguageOptions = computed(() => {
|
||||
return iso6391.getAllCodes().map(lang => ({
|
||||
key: lang,
|
||||
value: lang,
|
||||
label: lang,
|
||||
}));
|
||||
})
|
||||
|
||||
return {
|
||||
postLanguageOptions,
|
||||
}
|
||||
}
|
|
@ -41,6 +41,7 @@ export const defaultState = {
|
|||
showNavShortcuts: undefined, // instance default
|
||||
showPanelNavShortcuts: undefined, // instance default
|
||||
showWiderShortcuts: undefined, // instance default
|
||||
showFediLinks: true,
|
||||
hideSiteFavicon: undefined, // instance default
|
||||
hideSiteName: undefined, // instance default
|
||||
hideAttachments: false,
|
||||
|
@ -55,7 +56,6 @@ export const defaultState = {
|
|||
alwaysShowNewPostButton: false,
|
||||
autohideFloatingPostButton: false,
|
||||
pauseOnUnfocused: true,
|
||||
displayPageBackgrounds: true,
|
||||
stopGifs: undefined,
|
||||
replyVisibility: 'all',
|
||||
thirdColumnMode: 'notifications',
|
||||
|
@ -96,6 +96,7 @@ export const defaultState = {
|
|||
disableStickyHeaders: false,
|
||||
showScrollbars: false,
|
||||
greentext: undefined, // instance default
|
||||
useAtIcon: undefined, // instance default
|
||||
mentionLinkDisplay: undefined, // instance default
|
||||
mentionLinkShowTooltip: undefined, // instance default
|
||||
mentionLinkShowAvatar: undefined, // instance default
|
||||
|
@ -116,7 +117,6 @@ export const defaultState = {
|
|||
conversationTreeFadeAncestors: undefined, // instance default
|
||||
maxDepthInThread: undefined, // instance default
|
||||
translationLanguage: undefined, // instance default,
|
||||
postLanguage: undefined, // instance default,
|
||||
supportedTranslationLanguages: {}, // instance default
|
||||
userProfileDefaultTab: 'statuses',
|
||||
useBlurhash: true,
|
||||
|
@ -230,7 +230,7 @@ const config = {
|
|||
break
|
||||
case 'interfaceLanguage':
|
||||
messages.setLanguage(this.getters.i18n, value)
|
||||
Cookies.set(BACKEND_LANGUAGE_COOKIE_NAME, localeService.internalToBackendLocale(value), {sameSite: 'Lax'})
|
||||
Cookies.set(BACKEND_LANGUAGE_COOKIE_NAME, localeService.internalToBackendLocale(value))
|
||||
dispatch('setInstanceOption', { name: 'interfaceLanguage', value })
|
||||
break
|
||||
case 'thirdColumnMode':
|
||||
|
|
|
@ -21,6 +21,7 @@ const defaultState = {
|
|||
background: '/static/aurora_borealis.jpg',
|
||||
collapseMessageWithSubject: true,
|
||||
greentext: false,
|
||||
useAtIcon: false,
|
||||
mentionLinkDisplay: 'short',
|
||||
mentionLinkShowTooltip: true,
|
||||
mentionLinkShowAvatar: false,
|
||||
|
|
|
@ -37,18 +37,11 @@ const recentEmojis = {
|
|||
|
||||
getters: {
|
||||
recentEmojis: (state, getters, rootState) => state.emojis.reduce((objects, displayText) => {
|
||||
let comparator = emoji => emoji.displayText === displayText
|
||||
|
||||
let emojiObject = rootState.instance.emoji.find(comparator)
|
||||
const allEmojis = rootState.instance.emoji.concat(rootState.instance.customEmoji)
|
||||
let emojiObject = allEmojis.find(emoji => emoji.displayText === displayText)
|
||||
if (emojiObject !== undefined) {
|
||||
objects.push(emojiObject)
|
||||
} else {
|
||||
emojiObject = rootState.instance.customEmoji.find(comparator)
|
||||
if (emojiObject !== undefined) {
|
||||
objects.push(emojiObject)
|
||||
}
|
||||
}
|
||||
|
||||
return objects
|
||||
}, []),
|
||||
},
|
||||
|
|
|
@ -47,10 +47,6 @@ export const settingsMap = {
|
|||
},
|
||||
// Privacy
|
||||
'locked': 'locked',
|
||||
'permitFollowback': {
|
||||
get: 'akkoma.permit_followback',
|
||||
set: 'permit_followback'
|
||||
},
|
||||
'allowFollowingMove': {
|
||||
get: 'pleroma.allow_following_move',
|
||||
set: 'allow_following_move'
|
||||
|
|
|
@ -314,7 +314,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
|
|||
})
|
||||
|
||||
// Keep the visible statuses sorted
|
||||
if (timeline && !(['bookmarks', 'favorites'].includes(timeline))) {
|
||||
if (timeline && !(timeline === 'bookmarks')) {
|
||||
sortTimeline(timelineObject)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,26 @@ const unblockUser = (store, id) => {
|
|||
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
||||
}
|
||||
|
||||
const approveUser = (store, id) => {
|
||||
const predictedRelationship = store.state.relationships[id] || { id }
|
||||
predictedRelationship.requested_by = false
|
||||
predictedRelationship.followed_by = true
|
||||
store.commit('updateUserRelationship', [predictedRelationship])
|
||||
|
||||
return store.rootState.api.backendInteractor.approveUser({ id })
|
||||
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
||||
}
|
||||
|
||||
const denyUser = (store, id) => {
|
||||
const predictedRelationship = store.state.relationships[id] || { id }
|
||||
predictedRelationship.requested_by = false
|
||||
predictedRelationship.followed_by = false
|
||||
store.commit('updateUserRelationship', [predictedRelationship])
|
||||
|
||||
return store.rootState.api.backendInteractor.denyUser({ id })
|
||||
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
||||
}
|
||||
|
||||
const removeUserFromFollowers = (store, id) => {
|
||||
return store.rootState.api.backendInteractor.removeUserFromFollowers({ id })
|
||||
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
||||
|
@ -135,10 +155,6 @@ export const mutations = {
|
|||
const user = state.usersObject[id]
|
||||
user['deactivated'] = deactivated
|
||||
},
|
||||
setDisplayBackground(state, url) {
|
||||
console.log("Commiting user profile bg mutation")
|
||||
state.displayBackground = url
|
||||
},
|
||||
setCurrentUser (state, user) {
|
||||
state.lastLoginName = user.screen_name
|
||||
state.currentUser = mergeWith(state.currentUser || {}, user, mergeArrayLength)
|
||||
|
@ -203,28 +219,21 @@ export const mutations = {
|
|||
})
|
||||
},
|
||||
saveBlockIds (state, blockIds) {
|
||||
console.log("ADDING BLOCK IDS", blockIds);
|
||||
state.currentUser.blockIds = uniq(concat(state.currentUser.blockIds || [], blockIds))
|
||||
state.currentUser.blockIds = blockIds
|
||||
},
|
||||
addBlockId (state, blockId) {
|
||||
if (state.currentUser.blockIds.indexOf(blockId) === -1) {
|
||||
state.currentUser.blockIds.push(blockId)
|
||||
}
|
||||
},
|
||||
setBlockIdsMaxId (state, blockIdsMaxId) {
|
||||
state.currentUser.blockIdsMaxId = blockIdsMaxId
|
||||
},
|
||||
saveMuteIds (state, muteIds) {
|
||||
state.currentUser.muteIds = uniq(concat(state.currentUser.muteIds || [], muteIds))
|
||||
state.currentUser.muteIds = muteIds
|
||||
},
|
||||
addMuteId (state, muteId) {
|
||||
if (state.currentUser.muteIds.indexOf(muteId) === -1) {
|
||||
state.currentUser.muteIds.push(muteId)
|
||||
}
|
||||
},
|
||||
setMuteIdsMaxId (state, muteIdsMaxId) {
|
||||
state.currentUser.muteIdsMaxId = muteIdsMaxId
|
||||
},
|
||||
updateMascot (state, mascotUrl) {
|
||||
state.currentUser.mascot = mascotUrl
|
||||
},
|
||||
|
@ -311,7 +320,6 @@ export const defaultState = {
|
|||
currentUser: false,
|
||||
users: [],
|
||||
usersObject: {},
|
||||
displayBackground: null,
|
||||
signUpPending: false,
|
||||
signUpErrors: [],
|
||||
relationships: {},
|
||||
|
@ -324,10 +332,6 @@ const users = {
|
|||
mutations,
|
||||
getters,
|
||||
actions: {
|
||||
setDisplayBackground (store, url) {
|
||||
console.log("Performing user profile bg action...")
|
||||
store.commit('setDisplayBackground', url)
|
||||
},
|
||||
fetchUserIfMissing (store, id) {
|
||||
if (!store.getters.findUser(id)) {
|
||||
store.dispatch('fetchUser', id)
|
||||
|
@ -346,21 +350,10 @@ const users = {
|
|||
.then((relationships) => store.commit('updateUserRelationship', relationships))
|
||||
}
|
||||
},
|
||||
fetchBlocks (store, args) {
|
||||
const { reset } = args || {}
|
||||
|
||||
const maxId = store.state.currentUser.blockIdsMaxId
|
||||
return store.rootState.api.backendInteractor.fetchBlocks({ maxId })
|
||||
fetchBlocks (store) {
|
||||
return store.rootState.api.backendInteractor.fetchBlocks()
|
||||
.then((blocks) => {
|
||||
store.commit('saveBlockIds', map(blocks, 'id'))
|
||||
if (reset) {
|
||||
store.commit('saveBlockIds', map(blocks, 'id'))
|
||||
} else {
|
||||
map(blocks, 'id').map(id => store.commit('addBlockId', id))
|
||||
}
|
||||
if (blocks.length) {
|
||||
store.commit('setBlockIdsMaxId', last(blocks).id)
|
||||
}
|
||||
store.commit('addNewUsers', blocks)
|
||||
return blocks
|
||||
})
|
||||
|
@ -371,6 +364,12 @@ const users = {
|
|||
unblockUser (store, id) {
|
||||
return unblockUser(store, id)
|
||||
},
|
||||
approveUser (store, id) {
|
||||
return approveUser(store, id)
|
||||
},
|
||||
denyUser (store, id) {
|
||||
return denyUser(store, id)
|
||||
},
|
||||
removeUserFromFollowers (store, id) {
|
||||
return removeUserFromFollowers(store, id)
|
||||
},
|
||||
|
@ -380,22 +379,10 @@ const users = {
|
|||
unblockUsers (store, ids = []) {
|
||||
return Promise.all(ids.map(id => unblockUser(store, id)))
|
||||
},
|
||||
fetchMutes (store, args) {
|
||||
const { reset } = args || {}
|
||||
|
||||
const maxId = store.state.currentUser.muteIdsMaxId
|
||||
return store.rootState.api.backendInteractor.fetchMutes({ maxId })
|
||||
fetchMutes (store) {
|
||||
return store.rootState.api.backendInteractor.fetchMutes()
|
||||
.then((mutes) => {
|
||||
store.commit('saveMuteIds', map(mutes, 'id'))
|
||||
if (reset) {
|
||||
store.commit('saveMuteIds', map(mutes, 'id'))
|
||||
} else {
|
||||
map(mutes, 'id').map(id => store.commit('addMuteId', id))
|
||||
}
|
||||
if (mutes.length) {
|
||||
store.commit('setMuteIdsMaxId', last(mutes).id)
|
||||
}
|
||||
|
||||
store.commit('addNewUsers', mutes)
|
||||
return mutes
|
||||
})
|
||||
|
|
|
@ -1166,13 +1166,8 @@ const generateMfaBackupCodes = ({ credentials }) => {
|
|||
}).then((data) => data.json())
|
||||
}
|
||||
|
||||
const fetchMutes = ({ maxId, credentials }) => {
|
||||
const query = new URLSearchParams({ with_relationships: true })
|
||||
if (maxId) {
|
||||
query.append('max_id', maxId)
|
||||
}
|
||||
|
||||
return promisedRequest({ url: `${MASTODON_USER_MUTES_URL}?${query.toString()}`, credentials })
|
||||
const fetchMutes = ({ credentials }) => {
|
||||
return promisedRequest({ url: MASTODON_USER_MUTES_URL, credentials })
|
||||
.then((users) => users.map(parseUser))
|
||||
}
|
||||
|
||||
|
@ -1218,12 +1213,8 @@ const unsubscribeUser = ({ id, credentials }) => {
|
|||
return promisedRequest({ url: MASTODON_UNSUBSCRIBE_USER(id), credentials, method: 'POST' })
|
||||
}
|
||||
|
||||
const fetchBlocks = ({ maxId, credentials }) => {
|
||||
const query = new URLSearchParams({ with_relationships: true })
|
||||
if (maxId) {
|
||||
query.append('max_id', maxId)
|
||||
}
|
||||
return promisedRequest({ url: `${MASTODON_USER_BLOCKS_URL}?${query.toString()}`, credentials })
|
||||
const fetchBlocks = ({ credentials }) => {
|
||||
return promisedRequest({ url: MASTODON_USER_BLOCKS_URL, credentials })
|
||||
.then((users) => users.map(parseUser))
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,6 @@ export const parseUser = (data) => {
|
|||
if (data.akkoma) {
|
||||
output.instance = data.akkoma.instance
|
||||
output.status_ttl_days = data.akkoma.status_ttl_days
|
||||
output.permit_followback = data.akkoma.permit_followback
|
||||
}
|
||||
|
||||
if (data.pleroma) {
|
||||
|
@ -317,6 +316,7 @@ export const parseStatus = (data) => {
|
|||
|
||||
output.summary_raw_html = escape(data.spoiler_text)
|
||||
output.external_url = data.url
|
||||
output.external_uri = data.uri
|
||||
output.poll = data.poll
|
||||
if (output.poll) {
|
||||
output.poll.options = (output.poll.options || []).map(field => ({
|
||||
|
|
|
@ -4,11 +4,14 @@ export const newExporter = ({
|
|||
}) => ({
|
||||
exportData () {
|
||||
const stringified = JSON.stringify(getExportedObject(), null, 2) // Pretty-print and indent with 2 spaces
|
||||
const bytes = new TextEncoder().encode(stringified)
|
||||
const ascii = Array.from(bytes, (x) => String.fromCodePoint(x)).join("")
|
||||
const data = window.btoa(ascii)
|
||||
|
||||
// Create an invisible link with a data url and simulate a click
|
||||
const e = document.createElement('a')
|
||||
e.setAttribute('download', `${filename}.json`)
|
||||
e.setAttribute('href', 'data:application/json;base64,' + window.btoa(stringified))
|
||||
e.setAttribute('href', 'data:application/json;base64,' + data)
|
||||
e.style.display = 'none'
|
||||
|
||||
document.body.appendChild(e)
|
||||
|
@ -33,7 +36,9 @@ export const newImporter = ({
|
|||
const reader = new FileReader()
|
||||
reader.onload = ({ target }) => {
|
||||
try {
|
||||
const parsed = JSON.parse(target.result)
|
||||
const bytes = Uint8Array.from(target.result, (x) => x.codePointAt(0))
|
||||
const data = new TextDecoder().decode(bytes)
|
||||
const parsed = JSON.parse(data)
|
||||
const validationResult = validator(parsed)
|
||||
if (validationResult === true) {
|
||||
onImport(parsed)
|
||||
|
|
|
@ -8,30 +8,7 @@ const specialLanguageCodes = {
|
|||
'zh': 'zh-Hans'
|
||||
}
|
||||
|
||||
// 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 internalToBrowserLocale = code => specialLanguageCodes[code] || code
|
||||
|
||||
const internalToBackendLocale = code => internalToBrowserLocale(code).replace('_', '-')
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue