Compare commits

...

76 commits

Author SHA1 Message Date
0dbe31cf31 Fix reply timeline fetching 2022-08-31 01:50:38 +00:00
David
be20668f1f Separated Posts & Replies (#145) 2022-08-31 01:22:28 +00:00
0e86f707ff Disable streaming API option by default to fix reply filtering 2022-08-30 10:55:04 +00:00
bb6ee79e3e fix "who reacted" emoji display
fixes #80
2022-08-30 10:14:44 +00:00
cffcdd04bd allow selecting languages for translation 2022-08-30 09:50:31 +00:00
c23964b7a8 add translation options 2022-08-30 09:47:54 +00:00
6e31816c7d Update pleromafe version link 2022-08-30 09:28:27 +00:00
e39e7d5395 Dont copy local URL if external post 2022-08-30 08:51:07 +00:00
fa80d2627d my son 2022-08-30 02:49:55 +00:00
5c9c92669c Add privateMode error image 2022-08-30 02:42:24 +00:00
7a60b3a455 Hide staff panel if private mode which is empty anyway 2022-08-30 01:15:05 +00:00
311b38f9fb Fix i18n 2022-08-28 10:02:17 +00:00
a98d63fa00 Confirmation dialogs (#140)
supercedes #135

adapted from https://git.pleroma.social/pleroma/pleroma-fe/-/merge_requests/1431

Co-authored-by: Tusooa Zhu <tusooa@kazv.moe>
Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
Reviewed-on: AkkomaGang/pleroma-fe#140
2022-08-28 09:49:04 +00:00
acdb03f494 Revert "New option: Require confirmation for boosts"
This reverts commit e69dddee69.
2022-08-28 09:39:38 +00:00
296ea3c993 Fix MRF transparency panel showing without transparency 2022-08-28 09:27:48 +00:00
dd42d8e92c Add PWA option 2022-08-28 09:21:31 +00:00
Eris
e69dddee69 New option: Require confirmation for boosts 2022-08-25 05:13:47 +00:00
5abce529ca Revert "Remove unnecessary code from reply filtering"
This reverts commit 69fb3c9a87.
2022-08-10 04:18:07 +00:00
30a5c092b2 Fix reply filtering on bubble timeline 2022-08-10 02:15:10 +00:00
69fb3c9a87 Remove unnecessary code from reply filtering 2022-08-10 02:14:43 +00:00
ca0e15f7a8 Misskey-like quote styling 2022-08-08 03:53:20 +00:00
6bf8513433 Make all new nav options expert/advanced options 2022-08-07 07:12:09 +00:00
20c4cbfd92 Better padding for logout button 2022-08-07 06:53:38 +00:00
b2b5c81e25 Move logout button to settings modal 2022-08-07 06:53:25 +00:00
2a34a63c01 Yeet unnecessary code 2022-08-07 06:53:08 +00:00
738f8a3b9a Disable wider margins 2022-08-07 06:52:10 +00:00
783c198125 Make nav panel icon margin optional 2022-08-07 06:51:22 +00:00
0b0e49606a Make new top nav links optional 2022-08-07 06:49:59 +00:00
7751d9b62b Fix image description weirdness, render alt text properly 2022-08-07 00:53:34 +00:00
6ff06ceaf5 New option: Pause MFM until status hover 2022-08-06 20:02:05 +00:00
c876b54bee Hide quote-inline RE: links 2022-08-06 18:33:44 +00:00
98ba191c28 Fix emoji picker issues, add header 2022-08-05 23:53:05 +00:00
aa4fd497a3 Emoji Pack Picker (#102)
Reviewed-on: AkkomaGang/pleroma-fe#102
Co-authored-by: Mergan <mergan@noreply.akkoma>
Co-committed-by: Mergan <mergan@noreply.akkoma>
2022-08-05 21:20:24 +00:00
1f5ae0543c Change locales for MRF panel 2022-08-03 21:02:11 +00:00
f57f1b7c7a Update local only with biohazard icon and locales 2022-08-03 07:21:05 +00:00
a06be20eac Undo changes to bring code back to upstream 2022-08-03 06:15:53 +00:00
60f9da14dc Fix emoji rendering 2022-08-03 05:05:33 +00:00
fd8a2413ca Change defaults, add warning to pause GIFs option 2022-08-03 04:43:22 +00:00
Sol Fisher Romanoff
5d8cba4ca2 Fix code blocks not working in MFM 2022-08-02 08:30:43 +00:00
c4f5c5aac2 Fix timeago warning applying to polls 2022-08-02 06:13:25 +00:00
d0dbe0c921 Remove cyan text because wtf why is this here 2022-08-02 04:53:11 +00:00
1800cb94e8 Change instance defaults 2022-08-02 04:47:07 +00:00
f57e977c41 Add user options to hide instance favicon and name 2022-08-01 23:13:32 +00:00
2dc954ebfa Obfuscate domain names in MRF panel 2022-08-01 20:59:10 +00:00
24bded2509 Strip displaying MRF features that dont respect MRF transparency settings 2022-08-01 19:24:00 +00:00
42a13c0822 Hide muted replies if muted threads enabled 2022-08-01 05:59:37 +00:00
897728189e Hide replies to muted users 2022-08-01 02:50:58 +00:00
e8160c8403 typo fix 2022-08-01 01:53:38 +00:00
f9ac714caa Update README.md with updated translation instructions 2022-08-01 01:53:38 +00:00
Tusooa Zhu
acdfa056d1 Fix poll duration i18n 2022-08-01 01:52:37 +00:00
sola
5b95f182c5 Translated using Weblate (Catalan)
Currently translated at 100.0% (850 of 850 strings)

Translation: Pleroma fe/pleroma-fe
Translate-URL: http://translate.akkoma.dev/projects/akkoma-fe/pleroma-fe/ca/
2022-08-01 01:52:37 +00:00
Weblate Admin
c29080a141 Translated using Weblate (English)
Currently translated at 100.0% (850 of 850 strings)

Translation: Pleroma fe/pleroma-fe
Translate-URL: http://translate.akkoma.dev/projects/akkoma-fe/pleroma-fe/en/
2022-08-01 01:52:35 +00:00
Weblate Admin
a57f8d026d Translated using Weblate (English)
Currently translated at 100.0% (853 of 853 strings)

Translation: Pleroma fe/pleroma-fe
Translate-URL: http://translate.akkoma.dev/projects/akkoma-fe/pleroma-fe/en/
2022-08-01 01:50:45 +00:00
sfr
097823d790 Add toggle to hide posts mentioning blocked users (#78)
Reviewed-on: AkkomaGang/pleroma-fe#78
Co-authored-by: sfr <sol@solfisher.com>
Co-committed-by: sfr <sol@solfisher.com>
2022-08-01 00:34:12 +00:00
5135720adc Adjust user mention avatars and enable by default 2022-07-31 09:36:39 +00:00
54192564b9 Fix avatar mention links option 2022-07-31 01:12:20 +00:00
bebd5575b6 Bell to bolt icon, and no home icon if not logged in 2022-07-30 22:58:14 +00:00
cd72d1370c Don't show home icon if not logged in 2022-07-30 22:49:27 +00:00
baaec9d14b Restructure names and move lists to right side 2022-07-30 21:27:11 +00:00
f5e29ff52e Add home icon 2022-07-30 21:07:56 +00:00
f3de693f0e Indicator for active timeline in desktop nav 2022-07-30 20:38:47 +00:00
26033e62b8 Notifications -> Interactions (not in the column) 2022-07-30 19:24:20 +00:00
57aa0d8da8 Redo top bar with site favicon and more shortcuts 2022-07-30 18:54:19 +00:00
b792655f7a Remove link preview after quote for InlineQuotePolicy 2022-07-29 10:02:20 +00:00
945d4f633e Fix tall status gradient 2022-07-29 03:32:45 +00:00
1964193000 Add ability to click to expand collapsed notifications 2022-07-28 23:42:16 +00:00
64f8d854c8 Locale changes 2022-07-28 23:30:09 +00:00
18f817d73d Adjust status button padding, add emoji hovering 2022-07-28 23:23:45 +00:00
7eed382ea9 Better formatting for emoji react notifications 2022-07-28 06:58:34 +00:00
2b945a2c0c Locale changes and change bubble icon 2022-07-28 06:12:38 +00:00
b93a3c8ab0 Redo about page and staff panel 2022-07-28 00:20:18 +00:00
488cd43080 Only hide quote button to overflow menu if mobile 2022-07-27 23:08:34 +00:00
6b2322d81c Adjust sizes and padding for top nav and emojis 2022-07-27 22:34:58 +00:00
f803393219 Remove descriptions in timeline menu, change to title 2022-07-27 20:23:34 +00:00
68ef914e15 Change default options 2022-07-27 19:58:45 +00:00
6711b197ef Move quote button to extra buttons menu 2022-07-27 19:32:06 +00:00
83 changed files with 1772 additions and 489 deletions

View file

@ -6,7 +6,11 @@ This is a fork of Pleroma-FE from the Pleroma project, with support for new Akko
# For Translators
To translate Pleroma-FE, add your language to [src/i18n/messages.js](https://akkoma.dev/AkkomaGang/pleroma-fe/src/branch/develop/src/i18n/messages.js). Pleroma-FE will set your language by your browser locale, but you can temporarily force it in the code by changing the locale in main.js.
The [Weblate UI](https://translate.akkoma.dev/projects/akkoma/pleroma-fe/) is recommended for adding or modifying translations for Pleroma-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.
Pleroma-FE will set your language by your browser locale, but you can temporarily force it in the code by changing the locale in main.js.
# FOR ADMINS

View file

@ -10,11 +10,21 @@
<link rel="stylesheet" href="/static/font/css/lato.css">
<link rel="stylesheet" href="/static/mfm.css">
<!--server-generated-meta-->
<link rel="manifest" href="/static/manifest.json" />
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar" content="black-translucent" />
<link rel="apple-touch-icon" href="/images/icons/icon-512x512.png"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="theme-color" content="#000000" />
<meta name="theme-color-orig" content="#000000" />
<link rel="icon" type="image/png" href="/favicon.png">
</head>
<body class="hidden">
<noscript>To use Pleroma, please enable JavaScript.</noscript>
<script>if('serviceWorker' in navigator){navigator.serviceWorker.register('/static/service-worker.js');} else {console.log("Service worker is not supported");}</script>
<div id="app"></div>
<div id="modal"></div>
<!-- built files will be auto injected -->
</body>
</html>

View file

@ -59,7 +59,7 @@
<UserReportingModal />
<PostStatusModal />
<SettingsModal />
<div id="modal" />
<UpdateNotification />
<GlobalNoticeList />
</div>
</template>

View file

@ -250,6 +250,7 @@ const getNodeInfo = async ({ store }) => {
store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') })
store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits })
store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled })
store.dispatch('setInstanceOption', { name: 'translationEnabled', value: features.includes('akkoma:machine_translation') })
const uploadLimits = metadata.uploadLimits
store.dispatch('setInstanceOption', { name: 'uploadlimit', value: parseInt(uploadLimits.general) })

View file

@ -13,6 +13,8 @@ const About = {
MRFTransparencyPanel
},
computed: {
currentUser () { return this.$store.state.users.currentUser },
privateMode () { return this.$store.state.instance.private },
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
showInstanceSpecificPanel () {
return this.$store.state.instance.showInstanceSpecificPanel &&

View file

@ -1,7 +1,7 @@
<template>
<div class="column-inner">
<instance-specific-panel v-if="showInstanceSpecificPanel" />
<staff-panel />
<!--<instance-specific-panel v-if="showInstanceSpecificPanel" />-->
<staff-panel v-if="currentUser || !privateMode" />
<terms-of-service-panel />
<MRFTransparencyPanel />
<features-panel v-if="showFeaturesPanel" />

View file

@ -1,6 +1,8 @@
import ProgressButton from '../progress_button/progress_button.vue'
import Popover from '../popover/popover.vue'
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import { mapState } from 'vuex'
import {
faEllipsisV
} from '@fortawesome/free-solid-svg-icons'
@ -14,13 +16,22 @@ const AccountActions = {
'user', 'relationship'
],
data () {
return { }
return {
showingConfirmBlock: false
}
},
components: {
ProgressButton,
Popover
Popover,
ConfirmModal
},
methods: {
showConfirmBlock () {
this.showingConfirmBlock = true
},
hideConfirmBlock () {
this.showingConfirmBlock = false
},
showRepeats () {
this.$store.dispatch('showReblogs', this.user.id)
},
@ -28,7 +39,15 @@ const AccountActions = {
this.$store.dispatch('hideReblogs', this.user.id)
},
blockUser () {
if (!this.shouldConfirmBlock) {
this.doBlockUser()
} else {
this.showConfirmBlock()
}
},
doBlockUser () {
this.$store.dispatch('blockUser', this.user.id)
this.hideConfirmBlock()
},
unblockUser () {
this.$store.dispatch('unblockUser', this.user.id)
@ -36,6 +55,14 @@ const AccountActions = {
reportUser () {
this.$store.dispatch('openUserReportingModal', { userId: this.user.id })
}
},
computed: {
shouldConfirmBlock () {
return this.$store.getters.mergedConfig.modalOnBlock
},
...mapState({
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable
})
}
}

View file

@ -59,6 +59,27 @@
</button>
</template>
</Popover>
<teleport to="#modal">
<confirm-modal
v-if="showingConfirmBlock"
:title="$t('user_card.block_confirm_title')"
:confirm-text="$t('user_card.block_confirm_accept_button')"
:cancel-text="$t('user_card.block_confirm_cancel_button')"
@accepted="doBlockUser"
@cancelled="hideConfirmBlock"
>
<i18n-t
keypath="user_card.block_confirm"
tag="span"
>
<template v-slot:user>
<span
v-text="user.screen_name_ui"
/>
</template>
</i18n-t>
</confirm-modal>
</teleport>
</div>
</template>

View file

@ -0,0 +1,37 @@
import DialogModal from '../dialog_modal/dialog_modal.vue'
/**
* This component emits the following events:
* cancelled, emitted when the action should not be performed;
* accepted, emitted when the action should be performed;
*
* The caller should close this dialog after receiving any of the two events.
*/
const ConfirmModal = {
components: {
DialogModal
},
props: {
title: {
type: String
},
cancelText: {
type: String
},
confirmText: {
type: String
}
},
computed: {
},
methods: {
onCancel () {
this.$emit('cancelled')
},
onAccept () {
this.$emit('accepted')
}
}
}
export default ConfirmModal

View file

@ -0,0 +1,39 @@
<template>
<dialog-modal
v-body-scroll-lock="true"
class="confirm-modal"
:on-cancel="onCancel"
>
<template #header>
<span v-text="title" />
</template>
<slot />
<template #footer>
<button
class="btn button-default"
@click.prevent="onCancel"
v-text="cancelText"
/>
<button
class="btn button-default button-positive"
@click.prevent="onAccept"
v-text="confirmText"
/>
</template>
</dialog-modal>
</template>
<style lang="scss" scoped>
@import '../../_variables';
.confirm-modal {
.button-positive {
border: 3px solid var(--accent, $fallback--link);
border-radius: var(--btnRadius, $fallback--btnRadius);
}
}
</style>
<script src="./confirm_modal.js"></script>

View file

@ -7,7 +7,11 @@ const conversationPage = {
computed: {
statusId () {
return this.$route.params.id
}
},
currentUser () {
return this.$store.state.users.currentUser
},
privateMode () { return this.$store.state.instance.private }
}
}

View file

@ -1,5 +1,22 @@
<template>
<div
v-if="!currentUser && privateMode"
class="panel error-placeholder"
>
<div class="panel-heading">
<div class="title">
???
</div>
</div>
<div class="panel-body error">
<img
class="error-img"
src="/static/error.gif"
>
</div>
</div>
<conversation
v-else
:collapsable="false"
is-page="true"
:status-id="statusId"
@ -7,3 +24,23 @@
</template>
<script src="./conversation-page.js"></script>
<style lang="scss">
.error-placeholder {
.panel-body {
display: flex;
justify-content: center;
align-items: middle;
padding: 7em;
&.error {
padding: 0em;
}
.error-img {
width: 100%;
border-radius: 0px 0px 10px 10px;
}
}
}
</style>

View file

@ -1,4 +1,5 @@
import SearchBar from 'components/search_bar/search_bar.vue'
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faSignInAlt,
@ -11,6 +12,11 @@ import {
faSearch,
faTachometerAlt,
faCog,
faGlobe,
faBolt,
faUsers,
faCommentMedical,
faBookmark,
faInfoCircle
} from '@fortawesome/free-solid-svg-icons'
@ -25,12 +31,18 @@ library.add(
faSearch,
faTachometerAlt,
faCog,
faGlobe,
faBolt,
faUsers,
faCommentMedical,
faBookmark,
faInfoCircle
)
export default {
components: {
SearchBar
SearchBar,
ConfirmModal
},
data: () => ({
searchBarHidden: true,
@ -40,7 +52,8 @@ export default {
window.CSS.supports('-moz-mask-size', 'contain') ||
window.CSS.supports('-ms-mask-size', 'contain') ||
window.CSS.supports('-o-mask-size', 'contain')
)
),
showingConfirmLogout: false
}),
computed: {
enableMask () { return this.supportsMask && this.$store.state.instance.logoMask },
@ -65,20 +78,34 @@ export default {
})
},
logo () { return this.$store.state.instance.logo },
mergedConfig () {
return this.$store.getters.mergedConfig
},
sitename () { return this.$store.state.instance.name },
showNavShortcuts () {
return this.mergedConfig.showNavShortcuts
},
showWiderShortcuts () {
return this.mergedConfig.showWiderShortcuts
},
hideSiteFavicon () {
return this.mergedConfig.hideSiteFavicon
},
hideSiteName () {
return this.mergedConfig.hideSiteName
},
hideSitename () { return this.$store.state.instance.hideSitename },
logoLeft () { return this.$store.state.instance.logoLeft },
currentUser () { return this.$store.state.users.currentUser },
privateMode () { return this.$store.state.instance.private }
privateMode () { return this.$store.state.instance.private },
shouldConfirmLogout () {
return this.$store.getters.mergedConfig.modalOnLogout
}
},
methods: {
scrollToTop () {
window.scrollTo(0, 0)
},
logout () {
this.$router.replace('/main/public')
this.$store.dispatch('logout')
},
onSearchBarToggled (hidden) {
this.searchBarHidden = hidden
},

View file

@ -15,16 +15,16 @@
display: grid;
grid-template-rows: var(--navbar-height);
grid-template-columns: 2fr auto 2fr;
grid-template-areas: "sitename logo actions";
grid-template-areas: "nav-left logo actions";
box-sizing: border-box;
padding: 0 1.2em;
margin: auto;
max-width: 980px;
max-width: 1110px;
}
&.-logoLeft .inner-nav {
grid-template-columns: auto 2fr 2fr;
grid-template-areas: "logo sitename actions";
grid-template-areas: "logo nav-left actions";
}
.button-default {
@ -84,24 +84,52 @@
}
.nav-icon {
margin-left: 1em;
margin-left: 0.2em;
width: 2em;
height: 100%;
font-size: 130%;
text-align: center;
&-logout {
margin-left: 2em;
}
&.router-link-active {
font-size: 1.2em;
margin-top: 0.05em;
.svg-inline--fa {
font-weight: bolder;
color: $fallback--text;
color: var(--selectedMenuText, $fallback--text);
--lightText: var(--selectedMenuLightText, $fallback--lightText);
}
}
.svg-inline--fa {
color: $fallback--link;
color: var(--topBarLink, $fallback--link);
}
}
.sitename {
grid-area: sitename;
.-wide {
.nav-icon {
margin-left: 0.7em;
}
}
.left {
padding-left: 5px;
display: flex;
}
.nav-left-wrapper {
grid-area: nav-left;
img {
height: 28px;
vertical-align: middle;
padding-right: 5px;
}
}
.actions {
@ -120,5 +148,10 @@
justify-content: flex-end;
text-align: right;
}
&.left {
justify-content: flex-start;
text-alignt: left;
}
}
}

View file

@ -5,16 +5,79 @@
:class="{ '-logoLeft': logoLeft }"
@click="scrollToTop()"
>
<div class="inner-nav">
<div class="item sitename">
<div
class="inner-nav"
:class="{ '-wide': showWiderShortcuts }"
>
<div class="item nav-left-wrapper">
<router-link
v-if="!hideSitename"
class="site-name"
class="site-brand"
:to="{ name: 'root' }"
active-class="home"
>
{{ sitename }}
<img
v-if="!hideSiteFavicon"
class="favicon"
src="/favicon.png"
>
<span
v-if="!hideSiteName"
class="site-name"
>
{{ sitename }}
</span>
</router-link>
<div
v-if="(currentUser || !privateMode) && showNavShortcuts"
class="nav-items left"
>
<router-link
v-if="currentUser"
:to="{ name: 'friends' }"
class="nav-icon"
>
<FAIcon
fixed-width
class="fa-scale-110 fa-old-padding"
icon="home"
:title="$t('nav.home_timeline')"
/>
</router-link>
<router-link
:to="{ name: 'public-timeline' }"
class="nav-icon"
>
<FAIcon
fixed-width
class="fa-scale-110 fa-old-padding"
icon="users"
:title="$t('nav.public_tl')"
/>
</router-link>
<router-link
:to="{ name: 'public-external-timeline' }"
class="nav-icon"
>
<FAIcon
fixed-width
class="fa-scale-110 fa-old-padding"
icon="globe"
:title="$t('nav.twkn')"
/>
</router-link>
<router-link
v-if="currentUser"
:to="{ name: 'bubble-timeline' }"
class="nav-icon"
>
<FAIcon
fixed-width
class="fa-scale-110 fa-old-padding"
icon="comment-medical"
:title="$t('nav.bubble_timeline')"
/>
</router-link>
</div>
</div>
<router-link
class="logo"
@ -36,6 +99,46 @@
@toggled="onSearchBarToggled"
@click.stop
/>
<div
v-if="(currentUser || !privateMode) && showNavShortcuts"
class="nav-items right"
>
<router-link
class="nav-icon"
:to="{ name: 'interactions', params: { username: currentUser.screen_name } }"
>
<FAIcon
fixed-width
class="fa-scale-110 fa-old-padding"
icon="bolt"
:title="$t('nav.interactions')"
/>
</router-link>
<router-link
v-if="currentUser"
:to="{ name: 'lists' }"
class="nav-icon"
>
<FAIcon
fixed-width
class="fa-scale-110 fa-old-padding"
icon="list"
:title="$t('nav.lists')"
/>
</router-link>
<router-link
v-if="currentUser"
:to="{ name: 'bookmarks' }"
class="nav-icon"
>
<FAIcon
fixed-width
class="fa-scale-110 fa-old-padding"
icon="bookmark"
:title="$t('nav.bookmarks')"
/>
</router-link>
</div>
<button
class="button-unstyled nav-icon"
@click.stop="openSettingsModal"
@ -61,20 +164,20 @@
:title="$t('nav.administration')"
/>
</a>
<button
v-if="currentUser"
class="button-unstyled nav-icon nav-icon-logout"
@click.prevent="logout"
>
<FAIcon
fixed-width
class="fa-scale-110 fa-old-padding"
icon="sign-out-alt"
:title="$t('login.logout')"
/>
</button>
</div>
</div>
<teleport to="#modal">
<confirm-modal
v-if="showingConfirmLogout"
:title="$t('login.logout_confirm_title')"
:confirm-text="$t('login.logout_confirm_accept_button')"
:cancel-text="$t('login.logout_confirm_cancel_button')"
@accepted="doLogout"
@cancelled="hideConfirmLogout"
>
{{ $t('login.logout_confirm') }}
</confirm-modal>
</teleport>
</nav>
</template>
<script src="./desktop_nav.js"></script>

View file

@ -39,7 +39,7 @@
right: 0;
top: 0;
background: rgba(27,31,35,.5);
z-index: 99;
z-index: 2000;
}
}
@ -51,7 +51,7 @@
margin: 15vh auto;
position: fixed;
transform: translateX(-50%);
z-index: 999;
z-index: 2001;
cursor: default;
display: block;
background-color: $fallback--bg;

View file

@ -88,9 +88,10 @@
}
}
.emoji-picker-panel {
position: absolute;
position: relative;
z-index: 20;
margin-top: 2px;
top: 0px !important;
&.hide {
display: none

View file

@ -6,7 +6,7 @@ import {
faStickyNote,
faSmileBeam
} from '@fortawesome/free-solid-svg-icons'
import { trim } from 'lodash'
import { trim, escapeRegExp, startCase } from 'lodash'
library.add(
faBoxOpen,
@ -21,23 +21,6 @@ const LOAD_EMOJI_BY = 60
// When to start loading new batch emoji, in pixels
const LOAD_EMOJI_MARGIN = 64
const filterByKeyword = (list, keyword = '') => {
if (keyword === '') return list
const keywordLowercase = keyword.toLowerCase()
let orderedEmojiList = []
for (const emoji of list) {
const indexOfKeyword = emoji.displayText.toLowerCase().indexOf(keywordLowercase)
if (indexOfKeyword > -1) {
if (!Array.isArray(orderedEmojiList[indexOfKeyword])) {
orderedEmojiList[indexOfKeyword] = []
}
orderedEmojiList[indexOfKeyword].push(emoji)
}
}
return orderedEmojiList.flat()
}
const EmojiPicker = {
props: {
enableStickerPicker: {
@ -49,7 +32,7 @@ const EmojiPicker = {
data () {
return {
keyword: '',
activeGroup: 'custom',
activeGroup: 'standard',
showingStickers: false,
groupsScrolledClass: 'scrolled-top',
keepOpen: false,
@ -80,13 +63,8 @@ const EmojiPicker = {
this.triggerLoadMore(target)
},
highlight (key) {
const ref = this.$refs['group-' + key]
const top = ref.offsetTop
this.setShowStickers(false)
this.activeGroup = key
this.$nextTick(() => {
this.$refs['emoji-groups'].scrollTop = top + 1
})
},
updateScrolledClass (target) {
if (target.scrollTop <= 5) {
@ -155,6 +133,13 @@ const EmojiPicker = {
},
setShowStickers (value) {
this.showingStickers = value
},
filterByKeyword (list) {
if (this.keyword === '') return list
const regex = new RegExp(escapeRegExp(trim(this.keyword)), 'i')
return list.filter(emoji => {
return regex.test(emoji.displayText)
})
}
},
watch: {
@ -175,9 +160,8 @@ const EmojiPicker = {
return 0
},
filteredEmoji () {
return filterByKeyword(
this.$store.state.instance.customEmoji || [],
trim(this.keyword)
return this.filterByKeyword(
this.$store.state.instance.customEmoji || []
)
},
customEmojiBuffer () {
@ -185,25 +169,50 @@ const EmojiPicker = {
},
emojis () {
const standardEmojis = this.$store.state.instance.emoji || []
const customEmojis = this.customEmojiBuffer
const customEmojis = this.sortedEmoji
const emojiPacks = []
customEmojis.forEach((pack, id) => {
emojiPacks.push({
id: id.replace(/^pack:/, ''),
text: startCase(id.replace(/^pack:/, '')),
first: pack[0],
emojis: this.filterByKeyword(pack)
})
})
return [
{
id: 'custom',
text: this.$t('emoji.custom'),
icon: 'smile-beam',
emojis: customEmojis
},
{
id: 'standard',
text: this.$t('emoji.unicode'),
icon: 'box-open',
emojis: filterByKeyword(standardEmojis, trim(this.keyword))
first: {
imageUrl: '',
replacement: '🥴'
},
emojis: this.filterByKeyword(standardEmojis)
}
]
].concat(emojiPacks)
},
sortedEmoji () {
const customEmojis = this.$store.state.instance.customEmoji || []
const sortedEmojiGroups = new Map()
customEmojis.forEach((emoji) => {
if (!sortedEmojiGroups.has(emoji.tags[0])) {
sortedEmojiGroups.set(emoji.tags[0], [emoji])
} else {
sortedEmojiGroups.get(emoji.tags[0]).push(emoji)
}
})
return new Map([...sortedEmojiGroups.entries()].sort())
},
emojisView () {
return this.emojis.filter(value => value.emojis.length > 0)
if (this.keyword === '') {
return this.emojis.filter(pack => {
return pack.id === this.activeGroup
})
} else {
return this.emojis.filter(pack => {
return pack.emojis.length > 0
})
}
},
stickerPickerEnabled () {
return (this.$store.state.instance.stickers || []).length !== 0

View file

@ -35,9 +35,12 @@
}
.heading {
display: flex;
height: 32px;
padding: 10px 7px 5px;
margin-top: 10px;
height: 5.8em;
}
.emoji-header {
margin-left: 5px;
}
.content {
@ -65,15 +68,34 @@
.additional-tabs,
.emoji-tabs {
position: absolute;
display: block;
min-width: 0;
flex-basis: auto;
flex-shrink: 1;
flex-wrap: nowrap;
overflow: auto;
width: 100%;
white-space: nowrap;
&-item {
padding: 0 7px;
vertical-align: top;
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
padding: .4em;
cursor: pointer;
font-size: 1.85em;
img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
span {
font-size: 1.9em;
}
&.disabled {
opacity: 0.5;

View file

@ -1,6 +1,7 @@
<template>
<div class="emoji-picker panel panel-default panel-body">
<div class="heading">
<span class="emoji-header">Emoji Packs</span>
<span class="emoji-tabs">
<span
v-for="group in emojis"
@ -13,10 +14,11 @@
:title="group.text"
@click.prevent="highlight(group.id)"
>
<FAIcon
:icon="group.icon"
fixed-width
/>
<span v-if="!group.first.imageUrl">{{ group.first.replacement }}</span>
<img
v-else
:src="group.first.imageUrl"
>
</span>
</span>
<span

View file

@ -27,7 +27,11 @@ const EmojiReactions = {
},
accountsForEmoji () {
return this.status.emoji_reactions.reduce((acc, reaction) => {
acc[reaction.name] = reaction.accounts || []
if (reaction.url) {
acc[reaction.url] = reaction.accounts || []
} else {
acc[reaction.name] = reaction.accounts || []
}
return acc
}, {})
},
@ -42,6 +46,14 @@ const EmojiReactions = {
reactedWith (emoji) {
return this.status.emoji_reactions.find(r => r.name === emoji).me
},
isLocalReaction (emojiUrl) {
if (!emojiUrl) return true
const reacted = this.accountsForEmoji[emojiUrl]
if (reacted.length === 0) {
return true
}
return reacted[0].is_local
},
fetchEmojiReactionsByIfMissing () {
const hasNoAccounts = this.status.emoji_reactions.find(r => !r.accounts)
if (hasNoAccounts) {

View file

@ -2,12 +2,13 @@
<div class="emoji-reactions">
<UserListPopover
v-for="(reaction) in emojiReactions"
:key="reaction.name"
:users="accountsForEmoji[reaction.name]"
:key="reaction.url || reaction.name"
:users="accountsForEmoji[reaction.url || reaction.name]"
>
<button
class="emoji-reaction btn button-default"
:class="{ 'picked-reaction': reactedWith(reaction.name), 'not-clickable': !loggedIn }"
:disabled="!isLocalReaction(reaction.url)"
@click="emojiOnClick(reaction.name, $event)"
@mouseenter="fetchEmojiReactionsByIfMissing()"
>
@ -56,13 +57,15 @@
}
.emoji-reaction {
padding: 0 0.5em;
padding: 1px 6px;
margin-right: 0.5em;
margin-top: 0.5em;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
font-size: 0.7em !important;
height: 30px;
.reaction-emoji {
width: 2.55em !important;
margin-right: 0.25em;

View file

@ -1,4 +1,5 @@
import Popover from '../popover/popover.vue'
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faEllipsisH,
@ -6,6 +7,7 @@ import {
faEyeSlash,
faThumbtack,
faShareAlt,
faQuoteLeft,
faExternalLinkAlt
} from '@fortawesome/free-solid-svg-icons'
import {
@ -20,20 +22,48 @@ library.add(
faEyeSlash,
faThumbtack,
faShareAlt,
faQuoteLeft,
faExternalLinkAlt,
faFlag
)
const ExtraButtons = {
props: [ 'status' ],
components: { Popover },
props: ['status'],
components: {
Popover,
ConfirmModal
},
data () {
return {
expanded: false,
showingDeleteDialog: false
}
},
methods: {
deleteStatus () {
const confirmed = window.confirm(this.$t('status.delete_confirm'))
if (confirmed) {
this.$store.dispatch('deleteStatus', { id: this.status.id })
if (this.shouldConfirmDelete) {
this.showDeleteStatusConfirmDialog()
} else {
this.doDeleteStatus()
}
},
doDeleteStatus () {
this.$store.dispatch('deleteStatus', { id: this.status.id })
this.hideDeleteStatusConfirmDialog()
},
showDeleteStatusConfirmDialog () {
this.showingDeleteDialog = true
},
hideDeleteStatusConfirmDialog () {
this.showingDeleteDialog = false
},
translateStatus () {
const translateTo = this.$store.getters.mergedConfig.translationLanguage || this.$store.state.instance.interfaceLanguage
this.$store.dispatch('translateStatus', { id: this.status.id, language: translateTo })
.then(() => this.$emit('onSuccess'))
.catch(err => this.$emit('onError', err.error.error))
},
pinStatus () {
this.$store.dispatch('pinStatus', this.status.id)
.then(() => this.$emit('onSuccess'))
@ -89,8 +119,18 @@ const ExtraButtons = {
canMute () {
return !!this.currentUser
},
canTranslate () {
return this.$store.state.instance.translationEnabled === true
},
statusLink () {
return `${this.$store.state.instance.server}${this.$router.resolve({ name: 'conversation', params: { id: this.status.id } }).href}`
if (this.status.is_local) {
return `${this.$store.state.instance.server}${this.$router.resolve({ name: 'conversation', params: { id: this.status.id } }).href}`
} else {
return this.status.external_url
}
},
shouldConfirmDelete () {
return this.$store.getters.mergedConfig.modalOnDelete
}
}
}

View file

@ -116,6 +116,28 @@
:icon="['far', 'flag']"
/><span>{{ $t("user_card.report") }}</span>
</button>
<button
v-if="(status.visibility === 'public' || status.visibility === 'unlisted')"
class="button-default dropdown-item dropdown-item-icon extra-quote"
@click.prevent="$emit('quote-toggle')"
@click="close"
>
<FAIcon
fixed-width
icon="quote-left"
/><span>{{ $t("tool_tip.quote") }}</span>
</button>
<button
v-if="canTranslate"
class="button-default dropdown-item dropdown-item-icon"
@click.prevent="translateStatus"
@click="close"
>
<FAIcon
fixed-width
icon="globe"
/><span>{{ $t("status.translate") }}</span>
</button>
</div>
</template>
<template v-slot:trigger>
@ -125,6 +147,18 @@
icon="ellipsis-h"
/>
</button>
<teleport to="#modal">
<ConfirmModal
v-if="showingDeleteDialog"
:title="$t('status.delete_confirm_title')"
:cancel-text="$t('status.delete_confirm_cancel_button')"
:confirm-text="$t('status.delete_confirm_accept_button')"
@cancelled="hideDeleteStatusConfirmDialog"
@accepted="doDeleteStatus"
>
{{ $t('status.delete_confirm') }}
</ConfirmModal>
</teleport>
</template>
</Popover>
</template>
@ -151,4 +185,11 @@
}
}
}
@media all and (min-width: 801px) {
.extra-quote {
display: none !important;
}
}
</style>

View file

@ -36,6 +36,7 @@
.FavoriteButton {
display: flex;
margin-right: 5px;
> :first-child {
padding: 10px;

View file

@ -1,12 +1,20 @@
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
export default {
props: ['relationship', 'user', 'labelFollowing', 'buttonClass'],
components: {
ConfirmModal
},
data () {
return {
inProgress: false
inProgress: false,
showingConfirmUnfollow: false
}
},
computed: {
shouldConfirmUnfollow () {
return this.$store.getters.mergedConfig.modalOnUnfollow
},
isPressed () {
return this.inProgress || this.relationship.following
},
@ -35,6 +43,12 @@ export default {
}
},
methods: {
showConfirmUnfollow () {
this.showingConfirmUnfollow = true
},
hideConfirmUnfollow () {
this.showingConfirmUnfollow = false
},
onClick () {
this.relationship.following || this.relationship.requested ? this.unfollow() : this.follow()
},
@ -45,12 +59,21 @@ export default {
})
},
unfollow () {
if (this.shouldConfirmUnfollow) {
this.showConfirmUnfollow()
} else {
this.doUnfollow()
}
},
doUnfollow () {
const store = this.$store
this.inProgress = true
requestUnfollow(this.relationship.id, store).then(() => {
this.inProgress = false
store.commit('removeStatus', { timeline: 'friends', userId: this.relationship.id })
})
this.hideConfirmUnfollow()
}
}
}

View file

@ -7,6 +7,27 @@
@click="onClick"
>
{{ label }}
<teleport to="#modal">
<confirm-modal
v-if="showingConfirmUnfollow"
:title="$t('user_card.unfollow_confirm_title')"
:confirm-text="$t('user_card.unfollow_confirm_accept_button')"
:cancel-text="$t('user_card.unfollow_confirm_cancel_button')"
@accepted="doUnfollow"
@cancelled="hideConfirmUnfollow"
>
<i18n-t
keypath="user_card.unfollow_confirm"
tag="span"
>
<template #user>
<span
v-text="user.screen_name_ui"
/>
</template>
</i18n-t>
</confirm-modal>
</teleport>
</button>
</template>

View file

@ -1,10 +1,18 @@
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
import { notificationsFromStore } from '../../services/notification_utils/notification_utils.js'
const FollowRequestCard = {
props: ['user'],
components: {
BasicUserCard
BasicUserCard,
ConfirmModal
},
data () {
return {
showingApproveConfirmDialog: false,
showingDenyConfirmDialog: false
}
},
methods: {
findFollowRequestNotificationId () {
@ -13,7 +21,26 @@ const FollowRequestCard = {
)
return notif && notif.id
},
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 () {
this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
this.$store.dispatch('removeFollowRequest', this.user)
@ -25,14 +52,34 @@ const FollowRequestCard = {
notification.type = 'follow'
}
})
this.hideApproveConfirmDialog()
},
denyUser () {
if (this.shouldConfirmDeny) {
this.showDenyConfirmDialog()
} else {
this.doDeny()
}
},
doDeny () {
const notifId = this.findFollowRequestNotificationId()
this.$store.state.api.backendInteractor.denyUser({ id: this.user.id })
.then(() => {
this.$store.dispatch('dismissNotificationLocal', { id: notifId })
this.$store.dispatch('removeFollowRequest', this.user)
})
this.hideDenyConfirmDialog()
}
},
computed: {
mergedConfig () {
return this.$store.getters.mergedConfig
},
shouldConfirmApprove () {
return this.mergedConfig.modalOnApproveFollow
},
shouldConfirmDeny () {
return this.mergedConfig.modalOnDenyFollow
}
}
}

View file

@ -14,6 +14,28 @@
{{ $t('user_card.deny') }}
</button>
</div>
<teleport to="#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>
</basic-user-card>
</template>

View file

@ -1,5 +1,10 @@
<template>
<div>
<FAIcon
v-if="globeIcon"
icon="globe"
/>
{{ ' ' }}
<label for="interface-language-switcher">
{{ promptText }}
</label>
@ -39,6 +44,10 @@ export default {
setLanguage: {
type: Function,
required: true
},
globeIcon: {
type: Boolean,
default: true
}
},
computed: {

View file

@ -188,9 +188,21 @@ $modal-view-button-icon-margin: 0.5em;
overflow-y: auto;
min-height: 1em;
max-width: 500px;
max-height: 9.5em;
word-break: break-word;
white-space: pre-line;
background: rgba(0,0,0,0.6);
padding: 10px;
border-radius: 10px;
transition: 0.5s;
&:not(:hover) {
max-height: 1em;
}
&:hover {
max-height: 9.5em;
transition: 1s;
}
}
.modal-image {

View file

@ -15,11 +15,15 @@
.mention-avatar {
border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
width: 1.5em;
height: 1.5em;
width: 2em;
height: 2em;
vertical-align: middle;
user-select: none;
margin-right: 0.2em;
.still-image.avatar {
border-radius: 14px;
}
}
.full {
@ -113,3 +117,11 @@
color: var(--faint, $fallback--faint);
}
}
.reply-glued-label {
.mention-avatar {
width: 1.4em;
height: 1.4em;
}
}

View file

@ -22,6 +22,11 @@
@click.prevent="onClick"
>
<!-- eslint-disable vue/no-v-html -->
<UserAvatar
v-if="shouldShowAvatar"
class="mention-avatar"
:user="user"
/>
<span class="shortName">@<span
class="userName"
v-html="userName"

View file

@ -1,7 +1,9 @@
import SideDrawer from '../side_drawer/side_drawer.vue'
import Notifications from '../notifications/notifications.vue'
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils'
import GestureService from '../../services/gesture_service/gesture_service'
import { mapGetters } from 'vuex'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faTimes,
@ -18,11 +20,13 @@ library.add(
const MobileNav = {
components: {
SideDrawer,
Notifications
Notifications,
ConfirmModal
},
data: () => ({
notificationsCloseGesture: undefined,
notificationsOpen: false
notificationsOpen: false,
showingConfirmLogout: false
}),
created () {
this.notificationsCloseGesture = GestureService.swipeGesture(
@ -41,8 +45,17 @@ const MobileNav = {
unseenNotificationsCount () {
return this.unseenNotifications.length
},
hideSitename () { return this.$store.state.instance.hideSitename },
sitename () { return this.$store.state.instance.name }
mergedConfig () {
return this.$store.getters.mergedConfig
},
hideSiteName () {
return this.mergedConfig.hideSiteName
},
sitename () { return this.$store.state.instance.name },
shouldConfirmLogout () {
return this.$store.getters.mergedConfig.modalOnLogout
},
...mapGetters(['unreadChatCount'])
},
methods: {
toggleMobileSidebar () {
@ -68,9 +81,23 @@ const MobileNav = {
scrollToTop () {
window.scrollTo(0, 0)
},
showConfirmLogout () {
this.showingConfirmLogout = true
},
hideConfirmLogout () {
this.showingConfirmLogout = false
},
logout () {
if (!this.shouldConfirmLogout) {
this.doLogout()
} else {
this.showConfirmLogout()
}
},
doLogout () {
this.$router.replace('/main/public')
this.$store.dispatch('logout')
this.hideConfirmLogout()
},
markNotificationsAsSeen () {
// this.$refs.notifications.markAsSeen()

View file

@ -22,7 +22,7 @@
/>
</button>
<router-link
v-if="!hideSitename"
v-if="!hideSiteName"
class="site-name"
:to="{ name: 'root' }"
active-class="home"
@ -76,6 +76,18 @@
ref="sideDrawer"
:logout="logout"
/>
<teleport to="#modal">
<confirm-modal
v-if="showingConfirmLogout"
:title="$t('login.logout_confirm_title')"
:confirm-text="$t('login.logout_confirm_accept_button')"
:cancel-text="$t('login.logout_confirm_cancel_button')"
@accepted="doLogout"
@cancelled="hideConfirmLogout"
>
{{ $t('login.logout_confirm') }}
</confirm-modal>
</teleport>
</div>
</template>
@ -206,6 +218,14 @@
}
}
}
.confirm-modal.dark-overlay {
&::before {
z-index: 3000;
}
.dialog-modal.panel {
z-index: 3001;
}
}
}
</style>

View file

@ -1,6 +1,6 @@
<template>
<div
v-if="federationPolicy"
v-if="hasInstanceSpecificPolicies"
class="mrf-transparency-panel"
>
<div class="panel panel-default base01-background">
@ -65,7 +65,7 @@
v-for="entry in rejectInstances"
:key="entry.instance + '_reject'"
>
<td>{{ entry.instance }}</td>
<td>{{ entry.instance.replace(/[aeiou](?=.*\.)/gi, '*') }}</td>
<td v-if="entry.reason === ''">
{{ $t("about.mrf.simple.not_applicable") }}
</td>
@ -76,7 +76,7 @@
</table>
</div>
<div v-if="quarantineInstances.length">
<!--<div v-if="quarantineInstances.length">
<h4>{{ $t("about.mrf.simple.quarantine") }}</h4>
<p>{{ $t("about.mrf.simple.quarantine_desc") }}</p>
@ -90,7 +90,7 @@
v-for="entry in quarantineInstances"
:key="entry.instance + '_quarantine'"
>
<td>{{ entry.instance }}</td>
<td>{{ entry.instance.replace(/[aeiou](?=.*\.)/gi, '*') }}</td>
<td v-if="entry.reason === ''">
{{ $t("about.mrf.simple.not_applicable") }}
</td>
@ -99,7 +99,7 @@
</td>
</tr>
</table>
</div>
</div>-->
<div v-if="ftlRemovalInstances.length">
<h4>{{ $t("about.mrf.simple.ftl_removal") }}</h4>
@ -115,7 +115,7 @@
v-for="entry in ftlRemovalInstances"
:key="entry.instance + '_ftl_removal'"
>
<td>{{ entry.instance }}</td>
<td>{{ entry.instance.replace(/[aeiou](?=.*\.)/gi, '*') }}</td>
<td v-if="entry.reason === ''">
{{ $t("about.mrf.simple.not_applicable") }}
</td>
@ -140,7 +140,7 @@
v-for="entry in mediaNsfwInstances"
:key="entry.instance + '_media_nsfw'"
>
<td>{{ entry.instance }}</td>
<td>{{ entry.instance.replace(/[aeiou](?=.*\.)/gi, '*') }}</td>
<td v-if="entry.reason === ''">
{{ $t("about.mrf.simple.not_applicable") }}
</td>
@ -165,7 +165,7 @@
v-for="entry in mediaRemovalInstances"
:key="entry.instance + '_media_removal'"
>
<td>{{ entry.instance }}</td>
<td>{{ entry.instance.replace(/[aeiou](?=.*\.)/gi, '*') }}</td>
<td v-if="entry.reason === ''">
{{ $t("about.mrf.simple.not_applicable") }}
</td>
@ -176,7 +176,7 @@
</table>
</div>
<h2 v-if="hasKeywordPolicies">
<!--<h2 v-if="hasKeywordPolicies">
{{ $t("about.mrf.keyword.keyword_policies") }}
</h2>
@ -217,7 +217,7 @@
{{ keyword.replacement }}
</li>
</ul>
</div>
</div>-->
</div>
</div>
</div>

View file

@ -10,7 +10,7 @@ import {
faChevronDown,
faChevronUp,
faComments,
faBell,
faBolt,
faInfoCircle,
faStream,
faList,
@ -25,7 +25,7 @@ library.add(
faChevronDown,
faChevronUp,
faComments,
faBell,
faBolt,
faInfoCircle,
faStream,
faList,

View file

@ -45,7 +45,7 @@
<FAIcon
fixed-width
class="fa-scale-110"
icon="bell"
icon="bolt"
/>{{ $t("nav.interactions") }}
</router-link>
</li>

View file

@ -5,6 +5,7 @@ import UserAvatar from '../user_avatar/user_avatar.vue'
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 { 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'
@ -36,7 +37,9 @@ const Notification = {
return {
userExpanded: false,
betterShadow: this.$store.state.interface.browserSupport.cssFilter,
unmuted: false
unmuted: false,
showingApproveConfirmDialog: false,
showingDenyConfirmDialog: false
}
},
props: [ 'notification' ],
@ -46,7 +49,8 @@ const Notification = {
UserCard,
Timeago,
Status,
RichContent
RichContent,
ConfirmModal
},
methods: {
toggleUserExpanded () {
@ -61,7 +65,26 @@ const Notification = {
toggleMute () {
this.unmuted = !this.unmuted
},
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 () {
this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
this.$store.dispatch('removeFollowRequest', this.user)
this.$store.dispatch('markSingleNotificationAsSeen', { id: this.notification.id })
@ -71,13 +94,22 @@ const Notification = {
notification.type = 'follow'
}
})
this.hideApproveConfirmDialog()
},
denyUser () {
if (this.shouldConfirmDeny) {
this.showDenyConfirmDialog()
} else {
this.doDeny()
}
},
doDeny () {
this.$store.state.api.backendInteractor.denyUser({ id: this.user.id })
.then(() => {
this.$store.dispatch('dismissNotificationLocal', { id: this.notification.id })
this.$store.dispatch('removeFollowRequest', this.user)
})
this.hideDenyConfirmDialog()
}
},
computed: {
@ -107,6 +139,15 @@ const Notification = {
isStatusNotification () {
return isStatusNotification(this.notification.type)
},
mergedConfig () {
return this.$store.getters.mergedConfig
},
shouldConfirmApprove () {
return this.mergedConfig.modalOnApproveFollow
},
shouldConfirmDeny () {
return this.mergedConfig.modalOnDenyFollow
},
...mapState({
currentUser: state => state.users.currentUser
})

View file

@ -1,9 +1,10 @@
@import '../../_variables.scss';
.notification-reaction-emoji {
width: 40px;
display: flex;
width: 32px;
display: inline-flex;
flex-direction: column;
vertical-align: middle;
}
// TODO Copypaste from Status, should unify it somehow

View file

@ -230,6 +230,28 @@
</template>
</div>
</div>
<teleport to="#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>

View file

@ -84,7 +84,7 @@
:key="unit"
:value="unit"
>
{{ $t(`time.unit.${unit}_short`, ['']) }}
{{ $tc(`time.unit.${unit}_short`, expiryAmount, ['']) }}
</option>
</Select>
</div>

View file

@ -24,6 +24,7 @@
.QuoteButton {
display: flex;
margin-left: -5px;
> :first-child {
padding: 10px;
@ -44,4 +45,10 @@
}
}
@media all and (max-width: 800px) {
.QuoteButton {
display: none;
}
}
</style>

View file

@ -64,11 +64,11 @@
color: $fallback--text;
color: var(--text, $fallback--text);
border-style: solid;
border-style: dashed;
border-width: 1px;
border-radius: $fallback--attachmentRadius;
border-radius: var(--attachmentRadius, $fallback--attachmentRadius);
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
border-color: $fallback--cBlue;
border-color: var(--cBlue, $fallback--cBlue);
}
</style>

View file

@ -1,3 +1,4 @@
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faRetweet } from '@fortawesome/free-solid-svg-icons'
@ -5,13 +6,24 @@ library.add(faRetweet)
const RetweetButton = {
props: ['status', 'loggedIn', 'visibility'],
components: {
ConfirmModal
},
data () {
return {
animated: false
animated: false,
showingConfirmDialog: false
}
},
methods: {
retweet () {
if (!this.status.repeated && this.shouldConfirmRepeat) {
this.showConfirmDialog()
} else {
this.doRetweet()
}
},
doRetweet () {
if (!this.status.repeated) {
this.$store.dispatch('retweet', { id: this.status.id })
} else {
@ -21,6 +33,13 @@ const RetweetButton = {
setTimeout(() => {
this.animated = false
}, 500)
this.hideConfirmDialog()
},
showConfirmDialog () {
this.showingConfirmDialog = true
},
hideConfirmDialog () {
this.showingConfirmDialog = false
}
},
computed: {
@ -29,6 +48,9 @@ const RetweetButton = {
},
mergedConfig () {
return this.$store.getters.mergedConfig
},
shouldConfirmRepeat () {
return this.mergedConfig.modalOnRepeat
}
}
}

View file

@ -33,6 +33,18 @@
>
{{ status.repeat_num }}
</span>
<teleport to="#modal">
<confirm-modal
v-if="showingConfirmDialog"
:title="$t('status.repeat_confirm_title')"
:confirm-text="$t('status.repeat_confirm_accept_button')"
:cancel-text="$t('status.repeat_confirm_cancel_button')"
@accepted="doRetweet"
@cancelled="hideConfirmDialog"
>
{{ $t('status.repeat_confirm') }}
</confirm-modal>
</teleport>
</div>
</template>

View file

@ -124,6 +124,14 @@ export default {
}
const renderMisskeyMarkdown = (content) => {
// Untangle code blocks from <br> tags
const codeblocks = content.match(/(<br\/>)?(~~~|```)\w*<br\/>.+?<br\/>\2\1?/g)
if (codeblocks) {
codeblocks.forEach((pre) => {
content = content.replace(pre, pre.replaceAll('<br/>', '\n'))
})
}
marked.use(markedMfm, {
mangle: false,
gfm: false,
@ -133,20 +141,22 @@ export default {
mfmHtml.innerHTML = marked.parse(content)
// Add options with set values to CSS
Array.from(mfmHtml.content.firstChild.getElementsByClassName('mfm')).map((el) => {
if (el.dataset.speed) {
el.style.animationDuration = el.dataset.speed
}
if (el.dataset.deg) {
el.style.transform = `rotate(${el.dataset.deg}deg)`
}
if (Array.from(el.classList).includes('_mfm_font_')) {
const font = Object.keys(el.dataset)[0]
if (['serif', 'monospace', 'cursive', 'fantasy', 'emoji', 'math'].includes(font)) {
el.style.fontFamily = font
if (mfmHtml.content.firstChild) {
Array.from(mfmHtml.content.firstChild.getElementsByClassName('mfm')).map((el) => {
if (el.dataset.speed) {
el.style.animationDuration = el.dataset.speed
}
}
})
if (el.dataset.deg) {
el.style.transform = `rotate(${el.dataset.deg}deg)`
}
if (Array.from(el.classList).includes('_mfm_font_')) {
const font = Object.keys(el.dataset)[0]
if (['serif', 'monospace', 'cursive', 'fantasy', 'emoji', 'math'].includes(font)) {
el.style.fontFamily = font
}
}
})
}
return mfmHtml.innerHTML
}
@ -359,8 +369,6 @@ export const preProcessPerLine = (html, greentext) => {
.trim()
if (cleanedString.startsWith('&gt;')) {
return `<span class='greentext'>${string}</span>`
} else if (cleanedString.startsWith('&lt;')) {
return `<span class='cyantext'>${string}</span>`
}
}

View file

@ -2,6 +2,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'
import {
faEnvelope,
faLock,
faBiohazard,
faLockOpen,
faGlobe
} from '@fortawesome/free-solid-svg-icons'
@ -9,6 +10,7 @@ import {
library.add(
faEnvelope,
faGlobe,
faBiohazard,
faLock,
faLockOpen
)

View file

@ -67,7 +67,7 @@
@click="changeVis('local')"
>
<FAIcon
icon="users"
icon="biohazard"
class="fa-scale-110 fa-old-padding"
/>
</button>

View file

@ -14,6 +14,7 @@ import {
faTimes,
faFileUpload,
faFileDownload,
faSignOutAlt,
faChevronDown
} from '@fortawesome/free-solid-svg-icons'
import {
@ -28,6 +29,7 @@ library.add(
faWindowMinimize,
faFileUpload,
faFileDownload,
faSignOutAlt,
faChevronDown
)
@ -66,6 +68,11 @@ const SettingsModal = {
closeModal () {
this.$store.dispatch('closeSettingsModal')
},
logout () {
this.$router.replace('/main/public')
this.$store.dispatch('closeSettingsModal')
this.$store.dispatch('logout')
},
peekModal () {
this.$store.dispatch('togglePeekSettingsModal')
},
@ -150,6 +157,7 @@ const SettingsModal = {
}
},
computed: {
currentUser () { return this.$store.state.users.currentUser },
currentSaveStateNotice () {
return this.$store.state.interface.settings.currentSaveStateNotice
},

View file

@ -71,5 +71,11 @@
display: flex;
flex-grow: 1;
}
.logout-button {
position: absolute;
right: 20px;
padding-right: 10px;
}
}
}

View file

@ -111,6 +111,20 @@
id="unscrolled-content"
class="extra-content"
/>
<button
v-if="currentUser"
class="button-default logout-button"
:title="$t('login.logout')"
:aria-label="$t('login.logout')"
@click.prevent="logout"
>
<FAIcon
fixed-width
class="fa-scale-110 fa-old-padding"
icon="sign-out-alt"
/>
<span>{{ $t('login.logout') }}</span>
</button>
</div>
</div>
</Modal>

View file

@ -37,6 +37,15 @@
{{ $t('settings.hide_muted_posts') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
v-if="user"
:disabled="hideFilteredStatuses"
path="hideThreadsWithBlockedUsers"
>
{{ $t('settings.hide_threads_with_blocked_users') }}
</BooleanSetting>
</li>
</ul>
</li>
<li>

View file

@ -50,6 +50,7 @@ const GeneralTab = {
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'webkitAudioDecodedByteCount') ||
// Future spec, still not supported in Nightly 63 as of 08/2018
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'audioTracks')
}
},
components: {
@ -82,11 +83,20 @@ const GeneralTab = {
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
}
},
translationLanguage: {
get: function () { return this.$store.getters.mergedConfig.translationLanguage },
set: function (val) {
this.$store.dispatch('setOption', { name: 'translationLanguage', value: val })
}
},
...SharedComputedObject()
},
methods: {
changeDefaultScope (value) {
this.$store.dispatch('setServerSideOption', { name: 'defaultScope', value })
},
setTranslationLanguage (value) {
this.$store.dispatch('setOption', { name: 'translationLanguage', value })
}
}
}

View file

@ -25,6 +25,38 @@
{{ $t('settings.hide_wallpaper') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="hideSiteFavicon"
expert="1"
>
{{ $t('settings.hide_site_favicon') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="hideSiteName"
expert="1"
>
{{ $t('settings.hide_site_name') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="showNavShortcuts"
expert="1"
>
{{ $t('settings.show_nav_shortcuts') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="showWiderShortcuts"
expert="1"
>
{{ $t('settings.show_wider_shortcuts') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="stopGifs">
{{ $t('settings.stop_gifs') }}
@ -103,6 +135,28 @@
<BooleanSetting path="renderMisskeyMarkdown">
{{ $t('settings.render_mfm') }}
</BooleanSetting>
<ul
class="setting-list suboptions"
>
<li>
<BooleanSetting
path="mfmOnHover"
:disabled="!renderMisskeyMarkdown"
>
{{ $t('settings.render_mfm_on_hover') }}
</BooleanSetting>
</li>
</ul>
</li>
<li>
<p>
<interface-language-switcher
:globe-icon="false"
:prompt-text="$t('settings.translation_language')"
:language="translationLanguage"
:set-language="setTranslationLanguage"
/>
</p>
</li>
<li>
<BooleanSetting
@ -120,6 +174,77 @@
{{ $t('settings.autohide_floating_post_button') }}
</BooleanSetting>
</li>
<li>
<h3>{{ $t('settings.columns') }}</h3>
</li>
<li>
<BooleanSetting path="disableStickyHeaders">
{{ $t('settings.disable_sticky_headers') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="showScrollbars">
{{ $t('settings.show_scrollbars') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="sidebarRight">
{{ $t('settings.right_sidebar') }}
</BooleanSetting>
</li>
<li>
<ChoiceSetting
v-if="user"
id="thirdColumnMode"
path="thirdColumnMode"
:options="thirdColumnModeOptions"
>
{{ $t('settings.third_column_mode') }}
</ChoiceSetting>
</li>
<li>
<h3>{{ $t('settings.confirmation_dialogs') }}</h3>
</li>
<li class="select-multiple">
<span class="label">{{ $t('settings.confirm_dialogs') }}</span>
<ul class="option-list">
<li>
<BooleanSetting path="modalOnRepeat">
{{ $t('settings.confirm_dialogs_repeat') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnUnfollow">
{{ $t('settings.confirm_dialogs_unfollow') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnBlock">
{{ $t('settings.confirm_dialogs_block') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnMute">
{{ $t('settings.confirm_dialogs_mute') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnDelete">
{{ $t('settings.confirm_dialogs_delete') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnApproveFollow">
{{ $t('settings.confirm_dialogs_approve_follow') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnDenyFollow">
{{ $t('settings.confirm_dialogs_deny_follow') }}
</BooleanSetting>
</li>
</ul>
</li>
</ul>
</div>
<div class="setting-item">

View file

@ -1,6 +1,6 @@
import { extractCommit } from 'src/services/version/version.service'
const pleromaFeCommitUrl = 'https://akkoma.dev/AkkomaGang/pleroma-fe/commit/'
const pleromaFeCommitUrl = 'https://akkoma.dev/eris/pleroma-fe/commit/'
const pleromaBeCommitUrl = 'https://akkoma.dev/AkkomaGang/akkoma/commit/'
const VersionTab = {

View file

@ -8,7 +8,7 @@ import {
faSignOutAlt,
faHome,
faComments,
faBell,
faBolt,
faUserPlus,
faBullhorn,
faSearch,
@ -23,7 +23,7 @@ library.add(
faSignOutAlt,
faHome,
faComments,
faBell,
faBolt,
faUserPlus,
faBullhorn,
faSearch,

View file

@ -74,7 +74,7 @@
<FAIcon
fixed-width
class="fa-scale-110 fa-old-padding"
icon="bell"
icon="bolt"
/> {{ $t("nav.interactions") }}
</router-link>
</li>

View file

@ -12,7 +12,7 @@
:key="group.role"
class="staff-group"
>
<h4>{{ $t('general.role.' + group.role) }}</h4>
<h4>The Admin Team</h4>
<basic-user-card
v-for="user in group.users"
:key="user.screen_name"
@ -30,10 +30,21 @@
.staff-group {
padding-left: 1em;
padding-top: 1em;
padding-top: 0.2em;
padding-bottom: 2px;
.basic-user-card {
padding: 0.6em 0.4em;
padding-left: 0;
display: inline-block;
}
.basic-user-card-collapsed-content {
display: none;
}
.still-image {
border-radius: 15px;
}
}

View file

@ -36,6 +36,7 @@ import {
faStar,
faEyeSlash,
faEye,
faBiohazard,
faThumbtack,
faChevronUp,
faChevronDown,
@ -56,6 +57,7 @@ library.add(
faEllipsisH,
faEyeSlash,
faEye,
faBiohazard,
faThumbtack,
faChevronUp,
faChevronDown,
@ -261,6 +263,38 @@ const Status = {
hasMentionsLine () {
return this.mentionsLine.length > 0
},
mentionsBlockedUser () {
// XXX: doesn't work on domain blocks, because users from blocked domains
// don't appear in `attentions' and therefore cannot be filtered.
let mentions = false
// find if user in mentions list is blocked
this.status.attentions.forEach((attn) => {
if (attn.id === this.currentUser.id) return
const relationship = this.$store.getters.relationship(attn.id)
if (relationship.blocking) {
mentions = true
}
})
return mentions
},
mentionsMutedUser () {
// XXX: doesn't work on domain blocks, because users from blocked domains
// don't appear in `attentions' and therefore cannot be filtered.
let mentions = false
// find if user in mentions list is blocked
this.status.attentions.forEach((attn) => {
if (attn.id === this.currentUser.id) return
const relationship = this.$store.getters.relationship(attn.id)
if (relationship.muting) {
mentions = true
}
})
return mentions
},
muted () {
if (this.statusoid.user.id === this.currentUser.id) return false
const reasonsToMute = this.userIsMuted ||
@ -269,7 +303,11 @@ const Status = {
// Wordfiltered
this.muteWordHits.length > 0 ||
// bot status
(this.muteBotStatuses && this.botStatus && !this.compact)
(this.muteBotStatuses && this.botStatus && !this.compact) ||
// mentions blocked user
this.mentionsBlockedUser ||
// mentions muted user
this.mentionsMutedUser
return !this.unmuted && !this.shouldNotMute && reasonsToMute
},
userIsMuted () {
@ -312,6 +350,9 @@ const Status = {
hideFilteredStatuses () {
return this.mergedConfig.hideFilteredStatuses
},
hideThreadsWithBlockedUsers () {
return this.mergedConfig.hideThreadsWithBlockedUsers
},
hideWordFilteredPosts () {
return this.mergedConfig.hideWordFilteredPosts
},
@ -319,8 +360,9 @@ const Status = {
return (!this.shouldNotMute) && (
(this.muted && this.hideFilteredStatuses) ||
(this.userIsMuted && this.hideMutedUsers) ||
(this.status.thread_muted && this.hideMutedThreads) ||
(this.muteWordHits.length > 0 && this.hideWordFilteredPosts)
((this.status.thread_muted || this.mentionsMutedUser) && this.hideMutedThreads) ||
(this.muteWordHits.length > 0 && this.hideWordFilteredPosts) ||
(this.mentionsBlockedUser && this.hideThreadsWithBlockedUsers)
)
},
isFocused () {
@ -409,7 +451,7 @@ const Status = {
case 'direct':
return 'envelope'
case 'local':
return 'users'
return 'biohazard'
default:
return 'globe'
}

View file

@ -453,6 +453,7 @@
:status="status"
@onError="showError"
@onSuccess="clearError"
@quote-toggle="toggleQuoting"
/>
</div>
</div>

View file

@ -4,10 +4,20 @@
display: flex;
flex-direction: column;
.translation {
border: 1px solid var(--accent, $fallback--link);
border-radius: var(--panelRadius, $fallback--panelRadius);
margin-top: 1em;
padding: 0.5em;
}
.emoji {
--_still_image-label-scale: 0.5;
--emoji-size: 50px;
--emoji-size: 50px;
--emoji-size: 38px;
}
.emoji:hover {
transform: scale(1.4);
transition: 0.05s;
}
._mfm_x2_ {
@ -93,7 +103,7 @@
overflow-y: hidden;
z-index: 1;
.media-body {
.media-body-wrapper {
min-height: 0;
mask:
linear-gradient(to top, white, transparent) bottom/100% 70px no-repeat,
@ -143,26 +153,25 @@
color: var(--postGreentext, $fallback--cGreen);
}
.cyantext {
color: var(--postCyantext, $fallback--cBlue);
}
&.-compact {
align-items: top;
flex-direction: row;
--emoji-size: 16px;
& .body,
& .body:not(:active),
& .attachments {
max-height: 3.25em;
}
.body {
overflow: hidden;
white-space: normal;
min-width: 5em;
flex: 5 1 auto;
}
.body:not(:active) {
overflow: hidden;
mask-size: auto 3.5em, auto auto;
mask-position: 0 0, 0 0;
mask-repeat: repeat-x, repeat;
@ -197,3 +206,4 @@
}
}
}

View file

@ -43,6 +43,7 @@
</button>
<div
v-if="!hideSubjectStatus && !(singleLine && status.summary_raw_html)"
class="media-body-wrapper"
>
<RichContent
:class="{ '-single-line': singleLine }"
@ -55,6 +56,23 @@
:attentions="status.attentions"
@parseReady="onParseReady"
/>
<div
v-if="status.translation"
class="translation"
>
<h4>{{ $t('status.translated_from', { language: status.translation.detected_language }) }}</h4>
<RichContent
:class="{ '-single-line': singleLine }"
class="text media-body"
:html="status.translation.text"
:emoji="status.emojis"
:handle-links="true"
:mfm="renderMisskeyMarkdown && (status.media_type === 'text/x.misskeymarkdown')"
:greentext="mergedConfig.greentext"
:attentions="status.attentions"
@parseReady="onParseReady"
/>
</div>
</div>
<button
v-show="hideSubjectStatus"

View file

@ -100,6 +100,9 @@ const StatusContent = {
maxThumbnails () {
return this.mergedConfig.maxThumbnails
},
mfmOnHover () {
return this.mergedConfig.mfmOnHover
},
...mapGetters(['mergedConfig']),
...mapState({
currentUser: state => state.users.currentUser

View file

@ -1,7 +1,7 @@
<template>
<div
class="StatusContent"
:class="{ '-compact': compact }"
:class="{ '-compact': compact, 'mfm-hover': mfmOnHover }"
>
<slot name="header" />
<StatusBody
@ -75,5 +75,17 @@
height: 50px;
}
}
&.mfm-hover:not(:hover) {
.mfm {
animation: none;
}
}
}
.quote-inline,
.quote + .link-preview {
display: none;
}
</style>

View file

@ -51,6 +51,10 @@
width: 100%;
height: 100%;
object-fit: contain;
&::before {
line-height: 20px;
}
}
&.animated {

View file

@ -58,7 +58,9 @@ export default {
<style lang="scss">
@import '../../_variables.scss';
time.warning {
color: var(--alertWarning, $fallback--alertWarning);
.timeago {
time.warning {
color: var(--alertWarning, $fallback--alertWarning);
}
}
</style>

View file

@ -6,7 +6,7 @@ import {
faBookmark,
faEnvelope,
faHome,
faCircle
faCommentMedical
} from '@fortawesome/free-solid-svg-icons'
library.add(
@ -15,7 +15,7 @@ library.add(
faBookmark,
faEnvelope,
faHome,
faCircle
faCommentMedical
)
const TimelineMenuContent = {

View file

@ -9,22 +9,8 @@
fixed-width
class="fa-scale-110 fa-old-padding "
icon="home"
/>{{ $t("nav.home_timeline") }}
/><span :title="$t('nav.home_timeline_description')">{{ $t("nav.home_timeline") }}</span>
</router-link>
<span class="timeline-desc">{{ $t("nav.home_timeline_description") }}</span>
</li>
<li v-if="currentUser">
<router-link
class="menu-item"
:to="{ name: 'bubble-timeline' }"
>
<FAIcon
fixed-width
class="fa-scale-110 fa-old-padding "
icon="circle"
/>{{ $t("nav.bubble_timeline") }}
</router-link>
<span class="timeline-desc">{{ $t("nav.bubble_timeline_description") }}</span>
</li>
<li v-if="currentUser || !privateMode">
<router-link
@ -35,9 +21,8 @@
fixed-width
class="fa-scale-110 fa-old-padding "
icon="users"
/>{{ $t("nav.public_tl") }}
/><span :title="$t('nav.public_timeline_description')">{{ $t("nav.public_tl") }}</span>
</router-link>
<span class="timeline-desc">{{ $t("nav.public_timeline_description") }}</span>
</li>
<li v-if="federating && (currentUser || !privateMode)">
<router-link
@ -48,9 +33,20 @@
fixed-width
class="fa-scale-110 fa-old-padding "
icon="globe"
/>{{ $t("nav.twkn") }}
/><span :title="$t('nav.twkn_timeline_description')">{{ $t("nav.twkn") }}</span>
</router-link>
</li>
<li v-if="currentUser">
<router-link
class="menu-item"
:to="{ name: 'bubble-timeline' }"
>
<FAIcon
fixed-width
class="fa-scale-110 fa-old-padding "
icon="comment-medical"
/><span :title="$t('nav.bubble_timeline_description')">{{ $t("nav.bubble_timeline") }}</span>
</router-link>
<span class="timeline-desc">{{ $t("nav.twkn_timeline_description") }}</span>
</li>
<li v-if="currentUser">
<router-link

View file

@ -6,6 +6,7 @@ import ModerationTools from '../moderation_tools/moderation_tools.vue'
import AccountActions from '../account_actions/account_actions.vue'
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 { mapGetters } from 'vuex'
import { library } from '@fortawesome/fontawesome-svg-core'
@ -32,7 +33,8 @@ export default {
data () {
return {
followRequestInProgress: false,
betterShadow: this.$store.state.interface.browserSupport.cssFilter
betterShadow: this.$store.state.interface.browserSupport.cssFilter,
showingConfirmMute: false
}
},
created () {
@ -112,6 +114,9 @@ export default {
hideFollowersCount () {
return this.isOtherUser && this.user.hide_followers_count
},
shouldConfirmMute () {
return this.mergedConfig.modalOnMute
},
...mapGetters(['mergedConfig'])
},
components: {
@ -122,14 +127,29 @@ export default {
ProgressButton,
FollowButton,
Select,
RichContent
RichContent,
ConfirmModal
},
methods: {
refetchRelationship () {
return this.$store.dispatch('fetchUserRelationship', this.user.id)
},
showConfirmMute () {
this.showingConfirmMute = true
},
hideConfirmMute () {
this.showingConfirmMute = false
},
muteUser () {
if (!this.shouldConfirmMute) {
this.doMuteUser()
} else {
this.showConfirmMute()
}
},
doMuteUser () {
this.$store.dispatch('muteUser', this.user.id)
this.hideConfirmMute()
},
unmuteUser () {
this.$store.dispatch('unmuteUser', this.user.id)

View file

@ -295,6 +295,27 @@
:handle-links="true"
/>
</div>
<teleport to="#modal">
<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"
>
<template #user>
<span
v-text="user.screen_name_ui"
/>
</template>
</i18n-t>
</confirm-modal>
</teleport>
</div>
</template>

View file

@ -58,6 +58,9 @@ const UserProfile = {
timeline () {
return this.$store.state.statuses.timelines.user
},
replies () {
return this.$store.state.statuses.timelines.replies
},
favorites () {
return this.$store.state.statuses.timelines.favorites
},
@ -82,7 +85,8 @@ const UserProfile = {
},
currentUser () {
return this.$store.state.users.currentUser
}
},
privateMode () { return this.$store.state.instance.private }
},
methods: {
setFooterRef (el) {
@ -100,6 +104,7 @@ const UserProfile = {
const loadById = (userId) => {
this.userId = userId
startFetchingTimeline('user', userId)
startFetchingTimeline('replies', userId)
startFetchingTimeline('media', userId)
if (this.isUs) {
startFetchingTimeline('favorites', userId)
@ -137,6 +142,7 @@ const UserProfile = {
},
stopFetching () {
this.$store.dispatch('stopFetchingTimeline', 'user')
this.$store.dispatch('stopFetchingTimeline', 'replies')
this.$store.dispatch('stopFetchingTimeline', 'favorites')
this.$store.dispatch('stopFetchingTimeline', 'media')
},

View file

@ -79,6 +79,18 @@
:in-profile="true"
:footer-slipgate="footerRef"
/>
<Timeline
key="replies"
:label="$t('user_card.replies')"
:count="user.statuses_count"
:embedded="true"
:title="$t('user_card.replies')"
:timeline="replies"
timeline-name="replies"
:user-id="userId"
:in-profile="true"
:footer-slipgate="footerRef"
/>
<div
v-if="followsTabVisible"
key="followees"
@ -136,6 +148,22 @@
class="panel-footer"
/>
</div>
<div
v-else-if="!currentUser && privateMode"
class="panel user-profile-placeholder"
>
<div class="panel-heading">
<div class="title">
???
</div>
</div>
<div class="panel-body error">
<img
class="error-img"
src="/static/error.gif"
>
</div>
</div>
<div
v-else
class="panel user-profile-placeholder"
@ -246,6 +274,15 @@
justify-content: center;
align-items: middle;
padding: 7em;
&.error {
padding: 0em;
}
.error-img {
width: 100%;
border-radius: 0px 0px 10px 10px;
}
}
}
</style>

View file

@ -164,8 +164,8 @@
"fullname_required": "no es pot deixar en blanc",
"username_required": "no es pot deixar en blanc"
},
"fullname_placeholder": "p. ex. Anna Bofarull",
"username_placeholder": "p. ex. anna",
"fullname_placeholder": "p. ex. Lain Iwakura",
"username_placeholder": "p. ex. lain",
"captcha": "CAPTCHA",
"register": "Registre",
"reason": "Raó per a registrar-se",
@ -181,99 +181,99 @@
"avatarAltRadius": "Avatars (notificacions)",
"avatarRadius": "Avatars",
"background": "Fons de pantalla",
"bio": "Presentació",
"bio": "Bio",
"btnRadius": "Botons",
"cBlue": "Blau (respon, segueix)",
"cGreen": "Verd (republica)",
"cOrange": "Taronja (marca com a preferit)",
"cOrange": "Taronja (afavoreix)",
"cRed": "Vermell (canceŀla)",
"change_password": "Canvia la contrasenya",
"change_password_error": "No s'ha pogut canviar la contrasenya.",
"change_password_error": "Hi ha hagut un problema al canviar la teva contrasenya.",
"changed_password": "S'ha canviat la contrasenya correctament!",
"collapse_subject": "Replega les entrades amb títol",
"collapse_subject": "Replega els apunts amb assumpte",
"confirm_new_password": "Confirma la nova contrasenya",
"current_avatar": "L'avatar actual",
"current_password": "La contrasenya actual",
"current_avatar": "El teu avatar actual",
"current_password": "Contrasenya actual",
"current_profile_banner": "El fons de perfil actual",
"data_import_export_tab": "Importa o exporta dades",
"default_vis": "Abast per defecte de les entrades",
"data_import_export_tab": "Importa dades / exporta",
"default_vis": "Visibilitat per defecte dels apunts",
"delete_account": "Esborra el compte",
"delete_account_description": "Esborra permanentment les teves dades i desactiva el teu compte.",
"delete_account_error": "No s'ha pogut esborrar el compte. Si continua el problema, contacta amb l'administració del node.",
"delete_account_instructions": "Confirma que vols esborrar el compte escrivint la teva contrasenya aquí sota.",
"delete_account_error": "Hi ha hagut un problema al esborrar el teu compte. Si continua així, contacta amb l'administrador de l'instància.",
"delete_account_instructions": "Escriu la teva contrasenya en el camp de sota per a confirmar esborrar el compte.",
"export_theme": "Desa el tema",
"filtering": "Filtres",
"filtering_explanation": "Es silenciaran totes les entrades que continguin aquestes paraules. Separa-les per línies",
"follow_export": "Exporta la llista de contactes",
"follow_export_button": "Exporta tots els comptes que segueixes a un fitxer CSV",
"filtering": "Filtrant",
"filtering_explanation": "Es silenciaran tots els apunts que continguin aquestes paraules, una per línia",
"follow_export": "Exporta els seguits",
"follow_export_button": "Exporta els teus seguits a un fitxer CSV",
"follow_export_processing": "S'està processant la petició. Aviat podràs descarregar el fitxer",
"follow_import": "Importa els contactes",
"follow_import_error": "No s'ha pogut importar els contactes",
"follows_imported": "S'han importat els contactes. Trigaran una estoneta en ser processats.",
"follow_import": "Importa els seguits",
"follow_import_error": "Error al importar els seguidors",
"follows_imported": "S'han importat els seguits! Processar-los portarà una estona.",
"foreground": "Primer pla",
"general": "General",
"hide_attachments_in_convo": "Amaga els adjunts en les converses",
"hide_attachments_in_tl": "Amaga els adjunts en el flux d'entrades",
"import_followers_from_a_csv_file": "Importa els contactes des d'un fitxer CSV",
"hide_attachments_in_tl": "Amaga els adjunts en la línia de temps",
"import_followers_from_a_csv_file": "Importa els seguits des d'un fitxer CSV",
"import_theme": "Carrega un tema",
"inputRadius": "Caixes d'entrada de text",
"inputRadius": "Camps d'entrada",
"instance_default": "(default: {value})",
"interfaceLanguage": "Llengua de la interfície",
"invalid_theme_imported": "No s'ha entès l'arxiu carregat perquè no és un tema vàlid de Pleroma. No s'ha fet cap canvi als temes actuals.",
"limited_availability": "No està disponible en aquest navegador",
"invalid_theme_imported": "L'arxiu seleccionat no és un tema vàlid de Akkoma. No s'ha fet cap canvi al teu tema actual.",
"limited_availability": "No està disponible en el teu navegador",
"links": "Enllaços",
"lock_account_description": "Restringeix el teu compte només a seguidores aprovades",
"loop_video": "Reprodueix els vídeos en bucle",
"loop_video_silent_only": "Reprodueix en bucles només els vídeos sense so (com els \"GIF\" de Mastodon)",
"lock_account_description": "Restringeix el teu compte només a seguidors aprovats",
"loop_video": "Vídeos en bucle",
"loop_video_silent_only": "Només bucle de vídeos sense so (com els \"GIF\" de Mastodon)",
"name": "Nom",
"name_bio": "Nom i presentació",
"name_bio": "Nom i bio",
"new_password": "Contrasenya nova",
"notification_visibility": "Notifica'm quan algú",
"notification_visibility_follows": "Comença a seguir-me",
"notification_visibility_likes": "Favorits",
"notification_visibility_mentions": "Em menciona",
"notification_visibility_repeats": "Republica una entrada meva",
"no_rich_text_description": "Neteja el formatat de text de totes les entrades",
"nsfw_clickthrough": "Amaga el contingut NSFW darrer d'una imatge clicable",
"oauth_tokens": "Llistats OAuth",
"notification_visibility": "Tipus de notificacions a mostrar",
"notification_visibility_follows": "Seguits",
"notification_visibility_likes": "m'afavoreix",
"notification_visibility_mentions": "em menciona",
"notification_visibility_repeats": "em repeteix",
"no_rich_text_description": "Neteja el format de text de tots els apunts",
"nsfw_clickthrough": "Amaga els Mèdia sensibles/NSFW",
"oauth_tokens": "Codis OAuth",
"token": "Token",
"refresh_token": "Actualitza el token",
"valid_until": "Vàlid fins",
"revoke_token": "Revocar",
"revoke_token": "Revoca",
"panelRadius": "Panells",
"pause_on_unfocused": "Pausa la reproducció en continu quan la pestanya perdi el focus",
"pause_on_unfocused": "Pausa quan la pestanya perdi el focus",
"presets": "Temes",
"profile_background": "Fons de pantalla",
"profile_banner": "Fons de perfil",
"profile_background": "Fons del perfil",
"profile_banner": "Banner del perfil",
"profile_tab": "Perfil",
"radii_help": "Configura l'arrodoniment de les vores (en píxels)",
"replies_in_timeline": "Respostes al flux",
"replies_in_timeline": "Respostes en línia de temps",
"reply_visibility_all": "Mostra totes les respostes",
"reply_visibility_following": "Mostra només les respostes a entrades meves o d'usuàries que jo segueixo",
"reply_visibility_self": "Mostra només les respostes a entrades meves",
"saving_err": "No s'ha pogut desar la configuració",
"saving_ok": "S'ha desat la configuració",
"reply_visibility_following": "Mostra només les respostes dirigides a mi o a usuaris que segueixo",
"reply_visibility_self": "Mostra només les respostes dirigides a mi",
"saving_err": "Error al desar la configuració",
"saving_ok": "Configuració desada",
"security_tab": "Seguretat",
"set_new_avatar": "Canvia l'avatar",
"set_new_profile_background": "Canvia el fons de pantalla",
"set_new_profile_banner": "Canvia el fons del perfil",
"set_new_avatar": "Establir un nou avatar",
"set_new_profile_background": "Canvia el fons del perfil",
"set_new_profile_banner": "Establir un nou banner del perfil",
"settings": "Configuració",
"stop_gifs": "Anima els GIF només en passar-hi el ratolí per sobre",
"streaming": "Carrega automàticament entrades noves quan estigui a dalt de tot",
"stop_gifs": "Anima les imatges animades fins que hi passis el cursor per sobre",
"streaming": "Mostra automàticament els nous apunts quan et desplacis a la part superior",
"text": "Text",
"theme": "Tema",
"theme_help": "Personalitza els colors del tema. Escriu-los en format RGB hexadecimal (#rrggbb).",
"tooltipRadius": "Missatges sobreposats",
"user_settings": "Configuració personal",
"theme_help": "Utilitza els codis de color hex (#rrggbb) per a personalitzar el color del teu tema.",
"tooltipRadius": "Globus/alertes",
"user_settings": "Configuració d'usuari",
"values": {
"false": "no",
"true": "sí"
},
"show_moderator_badge": "Mostra una insígnia de Moderació en el meu perfil",
"show_admin_badge": "Mostra una insígnia \"d'Administració\" en el meu perfil",
"show_moderator_badge": "Mostra l'insígnia \"Moderador\" en el meu perfil",
"show_admin_badge": "Mostra l'insígnia \"Administrador\" en el meu perfil",
"hide_followers_description": "No mostris qui m'està seguint",
"hide_follows_description": "No mostris a qui segueixo",
"notification_visibility_emoji_reactions": "Reaccions",
"notification_visibility_emoji_reactions": "reacciona",
"new_email": "Nou correu electrònic",
"profile_fields": {
"value": "Contingut",
@ -281,69 +281,69 @@
"add_field": "Afegeix un camp",
"label": "Metadades del perfil"
},
"mutes_tab": "Silenciaments",
"mutes_tab": "Silenciats",
"interface": "Interfície",
"instance_default_simple": "(per defecte)",
"checkboxRadius": "Caselles",
"import_blocks_from_a_csv_file": "Importa bloquejos des d'un arxiu csv",
"hide_post_stats": "Amaga les estadístiques de les entrades (p. ex. el nombre de favorits)",
"hide_post_stats": "Amaga les estadístiques dels apunts (p. ex. el número de favorits)",
"use_one_click_nsfw": "Obre els adjunts NSFW amb només un clic",
"hide_muted_posts": "Amaga les entrades de comptes silenciats",
"avatar_size_instruction": "La mida mínima recomanada per la imatge de l'avatar és de 150x150 píxels.",
"hide_muted_posts": "Amaga els apunts de comptes silenciats",
"avatar_size_instruction": "La mida mínima recomanada per les imatges dels avatars és de 150x150 píxels.",
"domain_mutes": "Dominis",
"discoverable": "Permet la descoberta d'aquest compte en resultats de cerques i altres serveis",
"discoverable": "Permet descobrir aquest compte en resultats de cerques i altres serveis",
"mutes_and_blocks": "Silenciaments i bloquejos",
"composing": "Composant",
"chatMessageRadius": "Missatge de xat",
"changed_email": "Correu electrònic canviat amb èxit!",
"change_email_error": "Hi ha hagut un problema al canviar el teu correu electrònic.",
"change_email": "Canvia el correu electrònic",
"bot": "Aquest és un compte automatitzat",
"bot": "Aquest és un compte bot",
"blocks_tab": "Bloquejos",
"blocks_imported": "Bloquejos importats! Processar-los pot trigar una mica.",
"block_import_error": "Error al importar bloquejos",
"block_import": "Importa bloquejos",
"block_export_button": "Exporta els teus bloquejos a un arxiu csv",
"block_export": "Exporta bloquejos",
"allow_following_move": "Permet el seguiment automàtic quan un compte a qui seguim es mou",
"allow_following_move": "Permet el seguiment automàtic quan un compte a qui seguim es mogui",
"mfa": {
"scan": {
"secret_code": "Clau",
"title": "Escanejar",
"desc": "S'està usant l'aplicació two-factor, escaneja aquest codi QR o introdueix la clau de text:"
"desc": "S'està usant la teva aplicació de dos factors, escaneja aquest codi QR o introdueix la clau de text:"
},
"authentication_methods": "Mètodes d'autenticació",
"waiting_a_recovery_codes": "Rebent còpies de seguretat dels codis…",
"recovery_codes": "Codis de recuperació.",
"warning_of_generate_new_codes": "Quan generes nous codis de recuperació, els antics ja no funcionaran més.",
"warning_of_generate_new_codes": "Quan generes nous codis de recuperació, els teus antics ja no funcionaran més.",
"generate_new_recovery_codes": "Genera nous codis de recuperació",
"otp": "OTP",
"confirm_and_enable": "Confirmar i habilitar OTP",
"recovery_codes_warning": "Anote els codis o guarda'ls en un lloc segur, o no els veuràs una altra volta. Si perds l'accés a la teua aplicació 2FA i els codis de recuperació, no podràs accedir al compte.",
"confirm_and_enable": "Confirma i habilita OTP",
"recovery_codes_warning": "Anota els codis o desa'ls en un lloc segur - si no ho fas, no els podràs veure mai més . Si perds l'accés a la teva aplicació 2FA i als codis de recuperació, no podràs accedir al compte.",
"title": "Autenticació de dos factors",
"setup_otp": "Configurar OTP",
"wait_pre_setup_otp": "preconfiguració OTP",
"verify": {
"desc": "Per habilitar l'autenticació two-factor, introdueix el codi des de la teva aplicació two-factor:"
"desc": "Per a habilitar l'autenticació de dos factors, introdueix el codi des de la teva aplicació de dos factors:"
}
},
"enter_current_password_to_confirm": "Posar la contrasenya actual per confirmar la teva identitat",
"enter_current_password_to_confirm": "Posa la teva contrasenya actual per a confirmar la teva identitat",
"security": "Seguretat",
"app_name": "Nom de l'aplicació",
"subject_line_mastodon": "Com a mastodon: copiar com és",
"mute_export_button": "Exportar silenciats a un fitxer csv",
"mute_export_button": "Exportar els teus silenciats a un fitxer csv",
"mute_import_error": "Error al importar silenciats",
"mutes_imported": "Silenciats importats! Processar-los portarà una estona.",
"import_mutes_from_a_csv_file": "Importar silenciats des d'un fitxer csv",
"word_filter": "Filtre de paraules",
"hide_media_previews": "Ocultar les vistes prèvies multimèdia",
"hide_filtered_statuses": "Amagar estats filtrats",
"hide_filtered_statuses": "Amaga apunts filtrats",
"play_videos_in_modal": "Reproduir vídeos en un marc emergent",
"file_export_import": {
"errors": {
"invalid_file": "El fitxer seleccionat no és vàlid com a còpia de seguretat de la configuració. No s'ha realitzat cap canvi.",
"invalid_file": "El fitxer seleccionat no és suportat per Akkoma com a còpia de seguretat de la configuració. No s'ha realitzat cap canvi.",
"file_too_new": "Versió important incompatible: {fileMajor}, aquest PleromaFE (configuració versió {feMajor}) és massa antiga per gestionar-lo",
"file_too_old": "Versió important incompatible: {fileMajor}, la versió del fitxer és massa antiga i no està implementada (s'ha establert un mínim ver. {feMajor})",
"file_too_old": "Versió important incompatible: {fileMajor}, la versió del fitxer és massa antiga i no està suportada (min. set. ver. {feMajor})",
"file_slightly_new": "La versió menor del fitxer és diferent, alguns paràmetres podrien no carregar-se"
},
"backup_settings": "Còpia de seguretat de la configuració a un fitxer",
@ -352,42 +352,42 @@
"backup_restore": "Còpia de seguretat de la configuració"
},
"user_mutes": "Usuaris",
"subject_line_email": "Com a l'email: \"re: tema\"",
"subject_line_email": "Com a l'email: \"re: assumpte\"",
"search_user_to_block": "Busca a qui vols bloquejar",
"save": "Guardar els canvis",
"save": "Desar els canvis",
"use_contain_fit": "No retallar els adjunts en miniatures",
"reset_profile_background": "Restablir fons del perfil",
"reset_profile_banner": "Restablir banner del perfil",
"emoji_reactions_on_timeline": "Mostrar reaccions emoji al flux",
"max_thumbnails": "Quantitat màxima de miniatures per publicació",
"hide_user_stats": "Amagar les estadístiques de l'usuari (p. ex. el nombre de seguidors)",
"emoji_reactions_on_timeline": "Mostra reaccions emoji en la línia de temps",
"max_thumbnails": "Quantitat màxima de miniatures per apunt (buit = sense limit)",
"hide_user_stats": "Amaga les estadístiques de l'usuari (p. ex. el número de seguidors)",
"reset_banner_confirm": "Realment vols restablir el banner?",
"reset_background_confirm": "Realment vols restablir el fons del perfil?",
"subject_input_always_show": "Sempre mostrar el camp del tema",
"reset_background_confirm": "Realment vols restablir el fons?",
"subject_input_always_show": "Sempre mostrar el camp del assumpte",
"subject_line_noop": "No copiar",
"subject_line_behavior": "Copiar el tema a les respostes",
"subject_line_behavior": "Copiar l'assumpte en les respostes",
"search_user_to_mute": "Busca a qui vols silenciar",
"mute_export": "Exportar silenciats",
"scope_copy": "Copiar visibilitat quan contestes (En els missatges directes sempre es copia)",
"reset_avatar": "Restablir avatar",
"right_sidebar": "Mostrar barra lateral a la dreta",
"scope_copy": "Copiar visibilitat quan responguis (en els missatges directes sempre es copia)",
"reset_avatar": "Restablir l'avatar",
"right_sidebar": "Ordre invers de les columnes",
"no_blocks": "No hi han bloquejats",
"no_mutes": "No hi han silenciats",
"hide_follows_count_description": "No mostrar el nombre de comptes que segueixo",
"hide_follows_count_description": "No mostrar el número dels meus seguits",
"mute_import": "Importar silenciats",
"hide_all_muted_posts": "Ocultar publicacions silenciades",
"hide_all_muted_posts": "Ocultar apunts silenciades",
"hide_wallpaper": "Amagar el fons de la instància",
"notification_visibility_moves": "Usuari Migrat",
"reply_visibility_following_short": "Mostrar respostes als meus seguidors",
"reply_visibility_self_short": "Mostrar respostes només a un mateix",
"autohide_floating_post_button": "Ocultar automàticament el botó 'Nova Publicació' (mòbil)",
"minimal_scopes_mode": "Minimitzar les opcions de visibilitat de la publicació",
"sensitive_by_default": "Marcar publicacions com a sensibles per defecte",
"useStreamingApi": "Rebre publicacions i notificacions en temps real",
"hide_isp": "Ocultar el panell especific de la instància",
"notification_visibility_moves": "es mou",
"reply_visibility_following_short": "Mostrar respostes als meus seguits",
"reply_visibility_self_short": "Mostrar només respostes a mi mateix",
"autohide_floating_post_button": "Ocultar automàticament el botó 'Nou Apunt' (mòbil)",
"minimal_scopes_mode": "Minimitzar les opcions de selecció del abast del apunt",
"sensitive_by_default": "Marcar apunts com a sensibles per defecte",
"useStreamingApi": "Rebre apunts i notificacions en temps real",
"hide_isp": "Amaga el panell especific de la instància",
"preload_images": "Precarregar les imatges",
"setting_changed": "La configuració és diferent a la predeterminada",
"hide_followers_count_description": "No mostrar el nombre de seguidors",
"hide_followers_count_description": "No mostrar el número de seguidors",
"reset_avatar_confirm": "Realment vols restablir l'avatar?",
"accent": "Accent",
"useStreamingApiWarning": "És genial emprar-lo. Si es trenca, refresca, suposo?",
@ -397,10 +397,10 @@
"size": "Mida (en píxels)",
"custom": "Personalitza",
"_tab_label": "Fonts",
"help": "Selecciona la font per als elements de la interfície. Per a \"personalitzat\" deus escriure el nom de la font exactament com apareix al sistema.",
"help": "Selecciona la font per als elements de la interfície. Per a \"personalitzat\" has d'escriure el nom de la font exactament com apareix al sistema.",
"components": {
"post": "Text de les publicacions",
"postCode": "Text monoespai en publicació (text enriquit)",
"post": "Text dels apunts",
"postCode": "Text mono-espai en un apunt (text enriquit)",
"input": "Camps d'entrada",
"interface": "Interfície"
},
@ -418,19 +418,19 @@
"checkbox": "He llegit els termes i condicions",
"link": "un bonic enllaç",
"fine_print": "Llegiu el nostre {0} per no aprendre res útil!",
"text": "Un grapat més de {0} i {1}"
"text": "Un grapat més {0} i {1}"
},
"shadows": {
"spread": "Difon",
"filter_hint": {
"drop_shadow_syntax": "{0} no suporta el paràmetre {1} i la paraula clau {2}.",
"avatar_inset": "Tingues en compte que combinar ombres interiors i no interiors als avatars podria donar resultats inesperats amb avatars transparents.",
"inset_classic": "Les ombres interiors estaran usant {0}",
"inset_classic": "Les ombres interiors usaran {0}",
"always_drop_shadow": "Advertència, aquesta ombra sempre utilitza {0} quan el navegador ho suporta.",
"spread_zero": "Ombres amb propagació > 0 apareixeran com si estigueren posades a zero"
},
"components": {
"popup": "Texts i finestres emergents (popups & tooltips)",
"popup": "Globus i finestres emergents",
"panel": "Panell",
"panelHeader": "Capçalera del panell",
"avatar": "Avatar de l'usuari (en vista de perfil)",
@ -438,8 +438,8 @@
"buttonHover": "Botó (surant)",
"buttonPressed": "Botó (pressionat)",
"topBar": "Barra superior",
"buttonPressedHover": "Botó (surant i pressionat)",
"avatarStatus": "Avatar de l'usuari (en vista de publicació)",
"buttonPressedHover": "Botó (pressionat i surant)",
"avatarStatus": "Avatar de l'usuari (en vista de apunt)",
"button": "Botó"
},
"hintV3": "per a les ombres també pots usar la notació {0} per a utilitzar un altre espai de color.",
@ -456,24 +456,24 @@
"future_version_imported": "El fitxer importat es va crear per a una versió del front-end més recent.",
"migration_snapshot_ok": "Per a estar segurs, s'ha carregat la instantània del tema. Pots intentar carregar les dades del tema.",
"migration_napshot_gone": "Per alguna raó, faltava la instantània, algunes coses podrien veure's diferents del que recordes.",
"snapshot_source_mismatch": "Conflicte de versions: probablement el front-end s'ha revertit i actualitzat una altra volta, si has canviat el tema en una versió anterior, segurament vols utilitzar la versió antiga; d'altra banda utilitza la nova versió.",
"snapshot_source_mismatch": "Conflicte de versions: probablement el front-end s'ha revertit i actualitzat de nou, si has canviat el tema en una versió anterior, segurament vols utilitzar la versió antiga; d'altra banda utilitza la nova versió.",
"v2_imported": "El fitxer que has importat va ser creat per a un front-end més antic. Intentem maximitzar la compatibilitat, però podrien haver inconsistències.",
"fe_upgraded": "El motor de temes de PleromaFE es va actualitzar després de l'actualització de la versió.",
"snapshot_missing": "No hi havia cap instantània del tema al fitxer, per tant podria veure's diferent del previst originalment.",
"upgraded_from_v2": "PleromaFE s'ha actualitzat, el tema pot veure's un poc diferent de com recordes.",
"upgraded_from_v2": "PleromaFE s'ha actualitzat, el tema es pot veure una mica diferent de com el recordes.",
"fe_downgraded": "Versió de PleromaFE revertida.",
"older_version_imported": "El fitxer que has importat va ser creat en una versió del front-end més antiga.",
"snapshot_present": "S'ha carregat la instantània del tema, de manera que tots els valors estan sobreescrits. En canvi, podeu carregar les dades reals del tema."
},
"keep_as_is": "Mantindre com està",
"save_load_hint": "Les opcions \"Mantindre\" conserven les opcions configurades actualment al seleccionar o carregar temes, també emmagatzema aquestes opcions quan s'exporta un tema. Quan es desactiven totes les caselles de verificació, el tema exportat ho guardarà tot.",
"save_load_hint": "Les opcions \"Mantindre\" conserven les opcions configurades actualment al seleccionar o carregar temes, també emmagatzema aquestes opcions quan s'exporta un tema. Quan es desactiven totes les caselles de verificació, exportar el tema ho desarà tot.",
"keep_color": "Mantindre colors",
"keep_opacity": "Mantindre opacitat",
"keep_shadows": "Mantindre ombres",
"keep_fonts": "Mantindre fonts",
"keep_roundness": "Mantindre rodoneses",
"clear_all": "Netejar tot",
"reset": "Reinciar",
"reset": "Reiniciar",
"load_theme": "Carregar tema",
"use_source": "Nova versió",
"clear_opacity": "Netejar opacitat"
@ -482,9 +482,9 @@
"contrast": {
"hint": "El ràtio de contrast és {ratio}. {level} {context}",
"level": {
"bad": "no compleix amb cap pauta d'accecibilitat",
"aaa": "Compleix amb el nivell AA (recomanat)",
"aa": "Compleix amb el nivell AA (mínim)"
"bad": "no compleix amb cap pauta d'accessibilitat",
"aaa": "Compleix amb la guia del nivell AA (recomanat)",
"aa": "Compleix amb la guia del nivell AA (mínim)"
},
"context": {
"18pt": "per a textos grans (+18pt)",
@ -501,8 +501,8 @@
"pressed": "Pressionat",
"chat": {
"outgoing": "Eixint",
"border": "Borde",
"incoming": "Entrants"
"border": "Vora",
"incoming": "Entrant"
},
"borders": "Bordes",
"panel_header": "Capçalera del panell",
@ -512,8 +512,8 @@
"toggled": "Commutat",
"alert": "Fons d'alertes",
"alert_error": "Error",
"alert_warning": "Precaució",
"post": "Publicacions/Biografies d'usuaris",
"alert_warning": "Avís",
"post": "Apunts/Bio d'usuari",
"badge_notification": "Notificacions",
"selectedMenu": "Element del menú seleccionat",
"tabs": "Pestanyes",
@ -524,7 +524,7 @@
"highlight": "Elements destacats",
"disabled": "Deshabilitat",
"icons": "Icones",
"selectedPost": "Publicació seleccionada",
"selectedPost": "Apunt seleccionat",
"underlay": "Subratllat"
},
"common_colors": {
@ -538,32 +538,32 @@
}
},
"version": {
"frontend_version": "Versió \"Frontend\"",
"backend_version": "Versió \"backend\"",
"frontend_version": "Versió del \"Frontend\"",
"backend_version": "Versió del \"backend\"",
"title": "Versió"
},
"theme_help_v2_1": "També pots anular alguns components de color i opacitat activant la casella. Usa el botó \"Esborrar tot\" per esborrar totes les anulacions.",
"theme_help_v2_1": "També pots anul·lar els colors d'alguns components i la seva opacitat activant la casella. Usa el botó \"Esborrar tot\" per esborrar totes les anul·lacions.",
"type_domains_to_mute": "Buscar dominis per a silenciar",
"greentext": "Text verd (meme arrows)",
"fun": "Divertit",
"notification_setting_filters": "Filtres",
"virtual_scrolling": "Optimitzar la representació del flux",
"virtual_scrolling": "Optimitza el renderitzat de la línia de temps",
"notification_setting_block_from_strangers": "Bloqueja les notificacions dels usuaris que no segueixes",
"enable_web_push_notifications": "Habilitar notificacions del navegador",
"notification_blocks": "Bloquejar a un usuari para totes les notificacions i també les cancel·la.",
"notification_blocks": "Bloquejar a un usuari para totes les notificacions i cancel·la la subscripció.",
"more_settings": "Més opcions",
"notification_setting_privacy": "Privacitat",
"upload_a_photo": "Pujar una foto",
"notification_setting_hide_notification_contents": "Amagar el remitent i els continguts de les notificacions push",
"notifications": "Notificacions",
"notification_mutes": "Per a deixar de rebre notificacions d'un usuari en concret, silencia'l-ho.",
"theme_help_v2_2": "Les icones per baix d'algunes entrades són indicadors del contrast del fons/text, desplaça el ratolí per a més informació. Tingues en compte que quan s'utilitzen indicadors de contrast de transparència es mostra el pitjor cas possible.",
"hide_shoutbox": "Oculta la casella de gàbia de grills",
"always_show_post_button": "Mostra sempre el botó flotant de publicació nova",
"pad_emoji": "Acompanya els emojis amb espais en afegir des del selector",
"notification_mutes": "Per a deixar de rebre notificacions d'un usuari en concret, silencia'l.",
"theme_help_v2_2": "Les icones per sota d'algunes entrades són indicadors del contrast del fons/text, posiciona el ratolí al damunt per a més informació. Tingues en compte que quan s'utilitzen indicadors de contrast de transparència es mostra el pitjor cas possible.",
"hide_shoutbox": "Amaga la casella de gàbia de grills",
"always_show_post_button": "Mostra sempre el botó flotant d'Apunt Nou",
"pad_emoji": "Acompanya els emojis amb espais al afegir-los des del selector",
"mentions_new_style": "Enllaços d'esment més elegants",
"mentions_new_place": "Posa les mencions en una línia separada",
"post_status_content_type": "Format de publicació",
"post_status_content_type": "Tipus de contingut del apunt",
"expert_mode": "Mostra avançat",
"setting_server_side": "Aquest ajust està lligat al teu perfil i afectarà a totes les sessions i clients",
"account_backup": "Copia de seguretat del compte",
@ -572,7 +572,7 @@
"account_alias": "Àlies del compte",
"list_aliases_error": "Error al obtenir els àlies: {error}",
"move_account_notes": "Si vols moure el compte a un altre lloc has d'anar a aquest altre compte i afegir un àlies que apunti a aquest.",
"hide_wordfiltered_statuses": "Amaga publicacions filtrades per paraules",
"hide_wordfiltered_statuses": "Amaga apunts filtrats per paraules",
"account_privacy": "Privacitat",
"hide_favorites_description": "No mostrar la llista dels meus favorits (la gent seguirà sent notificada)",
"mascot": "Mascota de Mastodon FE",
@ -585,7 +585,7 @@
"mention_link_show_tooltip": "Mostra noms d'usuari complet com a globus per a usuaris remots",
"notification_setting_hide_if_cw": "Amaga els continguts de les publicacions push si son sota un Avís de Contingut",
"mute_bot_posts": "Silencia publicacions de bot",
"post_look_feel": "Aspecte i Sensació de les Publicacions",
"post_look_feel": "Aspecte i Sensació dels apunts",
"mention_links": "Enllaços de mencions",
"email_language": "Llengua per a rebre correus des d'el servidor",
"wordfilter": "Filtre de paraules",
@ -594,7 +594,7 @@
"remove_backup": "Treure",
"list_backups_error": "Error al recuperar la llista de copies de seguretat: {error}",
"add_backup": "Crea una nova copia de seguretat",
"added_backup": "Afegida una nova còpia de seguretat nova.",
"added_backup": "Afegida una nova còpia de seguretat.",
"add_backup_error": "Error al afegir una nova còpia de seguretat: {error}",
"current_mascot": "La teva mascota actual",
"account_alias_table_head": "Àlies",
@ -607,11 +607,11 @@
"move_account_target": "Compte destí (p.ex. {example})",
"moved_account": "El compte s'ha mogut.",
"move_account_error": "Error al moure el compte: {error}",
"hide_bot_indication": "Amaga l'indicació de bot en las publicacions",
"hide_bot_indication": "Amaga l'indicació de bot en els apunts",
"hide_muted_threads": "Amaga fils silenciats",
"posts": "Publicacions",
"posts": "Apunts",
"user_profiles": "Perfils d'usuari",
"notification_visibility_polls": "Terminis de les enquestes on has votat",
"notification_visibility_polls": "finalitza una enquesta on hi has votat",
"conversation_display": "Estil de visualització de la conversa",
"conversation_display_tree": "Estil d'arbre",
"show_scrollbars": "Mostra les barres de desplaçament de la columna lateral",
@ -631,60 +631,60 @@
"mention_link_display_full": "sempre com a noms complets (p. ex. {'@'}maria{'@'}exemple.cat)",
"mention_link_show_avatar": "Mostra l'avatar del usuari sota l'enllaç",
"mention_link_fade_domain": "Dominis esvaïts (p. ex. {'@'}exemple.cat a {'@'}joan{'@'}exemple.cat)",
"mention_link_bolden_you": "Destaca mencions per tu quan siguis mencionat",
"mention_link_bolden_you": "Destaca mencions per tu quan et mencionin",
"show_yous": "Mostra (Tu)s"
},
"time": {
"now": "ara mateix",
"now_short": "ara mateix",
"in_future": "in {0}",
"in_future": "en {0}",
"in_past": "fa {0}",
"unit": {
"day": "{0} dia",
"days": "{0} dies",
"days": "{0} dia | {0} dies",
"day_short": "{0} dia",
"days_short": "{0} dies",
"days_short": "{0}d",
"hour": "{0} hora",
"hours": "{0} hores",
"hours": "{0} hora | {0} hores",
"hour_short": "{0}h",
"hours_short": "{0}h",
"minute": "{0} minute",
"minutes": "{0} minutes",
"minutes": "{0} minuts | {0} minuts",
"minute_short": "{0}min",
"minutes_short": "{0}min",
"month": "{0} mes",
"months": "{0} mesos",
"months": "{0} mes | {0} mesos",
"month_short": "{0} mes",
"months_short": "{0} mesos",
"months_short": "{0}mes",
"second": "{0} segon",
"seconds": "{0} segons",
"seconds": "{0} segon | {0} segons",
"second_short": "{0}s",
"seconds_short": "{0}s",
"week": "{0} setmana",
"weeks": "{0} setmanes",
"weeks": "{0} setmana | {0} setmanes",
"week_short": "{0} setm.",
"weeks_short": "{0} setm.",
"weeks_short": "{0}setm.",
"year": "{0} any",
"years": "{0} anys",
"years": "{0} any | {0} anys",
"year_short": "{0} any",
"years_short": "{0} anys"
"years_short": "{0}anys"
}
},
"timeline": {
"collapse": "Replega",
"conversation": "Conversa",
"error_fetching": "S'ha produït un error en carregar les entrades",
"load_older": "Carrega entrades anteriors",
"no_retweet_hint": "L'entrada és només per a seguidores o és \"directa\", i per tant no es pot republicar",
"repeated": "republicat",
"load_older": "Carrega apunts anteriors",
"no_retweet_hint": "L'apunt és només per a seguidors o és \"directe\" i no es pot repetir o citar",
"repeated": "repetit",
"show_new": "Mostra els nous",
"up_to_date": "Actualitzat",
"socket_reconnected": "Connexió a temps real establerta",
"socket_broke": "Connexió a temps real perduda: codi CloseEvent {0}",
"error": "Error de càrrega de la línia de temps: {0}",
"no_statuses": "No hi ha entrades",
"error": "Error carregant la línia de temps: {0}",
"no_statuses": "No hi ha apunts",
"reload": "Recarrega",
"no_more_statuses": "No hi ha més entrades"
"no_more_statuses": "No hi ha més apunts"
},
"user_card": {
"approve": "Aprova",
@ -692,35 +692,35 @@
"blocked": "Bloquejat!",
"deny": "Denega",
"follow": "Segueix",
"followees": "Segueixo",
"followers": "Seguidors/es",
"followees": "Seguint",
"followers": "Seguidors",
"following": "Seguint!",
"follows_you": "Et segueix!",
"mute": "Silencia",
"muted": "Silenciat",
"per_day": "per dia",
"remote_follow": "Seguiment remot",
"statuses": "Estats",
"statuses": "Apunts",
"unblock_progress": "Desbloquejant…",
"unmute": "Deixa de silenciar",
"follow_progress": "Sol·licitant…",
"admin_menu": {
"force_nsfw": "Marca totes les entrades amb \"No segur per a entorns laborals\"",
"strip_media": "Esborra els audiovisuals de les entrades",
"force_nsfw": "Marca tots els apunts amb \"No segur per a entorns laborals\"",
"strip_media": "Esborra els Mèdia dels apunts",
"disable_any_subscription": "Deshabilita completament seguir algú",
"quarantine": "Deshabilita la federació a les entrades de les usuàries",
"quarantine": "Deshabilita la federació dels apunts dels usuaris",
"moderation": "Moderació",
"revoke_admin": "Revoca l'Admin",
"activate_account": "Activa el compte",
"deactivate_account": "Desactiva el compte",
"revoke_moderator": "Revoca Moderació",
"revoke_moderator": "Revoca Moderador",
"delete_account": "Esborra el compte",
"disable_remote_subscription": "Deshabilita seguir algú des d'una instància remota",
"delete_user": "Esborra la usuària",
"grant_admin": "Concedir permisos d'Administració",
"grant_moderator": "Concedir permisos de Moderació",
"force_unlisted": "Força que les publicacions no estiguin llistades",
"sandbox": "Força que els missatges siguin només seguidors",
"delete_user": "Esborra l'usuari",
"grant_admin": "Concedir permisos d'Administrador",
"grant_moderator": "Concedir permisos de Moderador",
"force_unlisted": "Força que els apunts no estiguin llistats",
"sandbox": "Força que els apunts siguin només per a seguidors",
"delete_user_data_and_deactivate_confirmation": "Això esborrarà permanentment les dades d'aquest compte i el desactivarà. Estàs absolutament segur?"
},
"edit_profile": "Edita el perfil",
@ -747,15 +747,15 @@
"striped": "Fons a ratlles",
"side": "Ratlla lateral"
},
"media": "Media",
"media": "Mèdia",
"domain_muted": "Desbloqueja el domini",
"deactivated": "Desactivat",
"follow_cancel": "Cancel·la la sol·licitut",
"follow_cancel": "Cancel·la la sol·licitud",
"mute_domain": "Bloqueja el domini",
"note": "Nota privada"
},
"user_profile": {
"timeline_title": "Flux personal",
"timeline_title": "Línia de temps del usuari",
"profile_loading_error": "Disculpes, hi ha hagut un error carregant aquest perfil.",
"profile_does_not_exist": "Disculpes, aquest perfil no existeix."
},
@ -865,29 +865,29 @@
}
},
"status": {
"delete": "Esborra l'entrada",
"delete_confirm": "Segur que vols esborrar aquesta entrada?",
"delete": "Esborra l'apunt",
"delete_confirm": "Segur que vols esborrar aquest apunt?",
"thread_muted_and_words": ", té les paraules:",
"show_full_subject": "Mostra tot el tema",
"show_full_subject": "Mostra tot l'assumpte",
"show_content": "Mostra el contingut",
"repeats": "Repeticions",
"bookmark": "Marcadors",
"status_unavailable": "Entrada no disponible",
"bookmark": "Marcador",
"status_unavailable": "Apunt no disponible",
"expand": "Expandeix",
"copy_link": "Copia l'enllaç a l'entrada",
"hide_full_subject": "Amaga tot el tema",
"copy_link": "Copia l'enllaç al apunt",
"hide_full_subject": "Amaga tot l'assumpte",
"favorites": "Favorits",
"replies_list": "Contestacions:",
"replies_list": "Respostes:",
"mute_conversation": "Silencia la conversa",
"thread_muted": "Fil silenciat",
"hide_content": "Amaga el contingut",
"status_deleted": "S'ha esborrat aquesta entrada",
"status_deleted": "Aquest apunt ha estat esborrat",
"nsfw": "No segur per a entorns laborals",
"unbookmark": "Desmarca",
"external_source": "Font externa",
"unpin": "Deixa de destacar al perfil",
"pinned": "Destacat",
"reply_to": "Contesta a",
"reply_to": "Respon a",
"pin": "Destaca al perfil",
"unmute_conversation": "Deixa de silenciar la conversa",
"mentions": "Mencions",
@ -919,21 +919,21 @@
},
"user_reporting": {
"additional_comments": "Comentaris addicionals",
"forward_description": "Aquest compte és d'un altre servidor. Vols enviar una còpia del report allà també?",
"forward_description": "Aquest compte és d'un altre servidor. Vols enviar-hi una còpia del informe?",
"forward_to": "Endavant a {0}",
"generic_error": "Hi ha hagut un error mentre s'estava processant la teva sol·licitud.",
"title": "Reportant {0}",
"add_comment_description": "Aquest report serà enviat a la moderació a la instància. Pots donar una explicació de per què estàs reportant aquest compte:",
"add_comment_description": "El informe serà enviat als moderadors de l'instància. Pots donar una explicació de per què estàs reportant aquest compte:",
"submit": "Envia"
},
"tool_tip": {
"add_reaction": "Afegeix una Reacció",
"accept_follow_request": "Accepta la sol·licitud de seguir",
"accept_follow_request": "Accepta la sol·licitud de seguiment",
"repeat": "Repeteix",
"reply": "Respon",
"favorite": "Favorit",
"user_settings": "Configuració d'usuària",
"reject_follow_request": "Rebutja la sol·licitud de seguir",
"user_settings": "Configuració d'usuari",
"reject_follow_request": "Rebutja la sol·licitud de seguiment",
"bookmark": "Marcador",
"media_upload": "Pujar multimèdia",
"quote": "Cita"
@ -967,13 +967,13 @@
"password_reset": "Reinicia la contrasenya",
"forgot_password": "Has oblidat la contrasenya?",
"too_many_requests": "Has arribat al límit d'intents. Prova de nou d'aquí una estona.",
"password_reset_required_but_mailer_is_disabled": "Has de reiniciar la teva contrasenya però el reinici de la contrasenya està deshabilitat. Si us plau, contacta l'administració de la teva instància.",
"placeholder": "El teu correu electrònic o nom d'usuària",
"instruction": "Introdueix la teva adreça de correu electrònic o nom d'usuària. T'enviarem un enllaç per reiniciar la teva contrasenya.",
"password_reset_required_but_mailer_is_disabled": "Has de reiniciar la teva contrasenya però el reinici de la contrasenya està desactivat. Si us plau, contacta l'administrador de la teva instància.",
"placeholder": "El teu correu electrònic o nom d'usuari",
"instruction": "Introdueix la teva adreça de correu electrònic o nom d'usuari. T'enviarem un enllaç per a reiniciar la teva contrasenya.",
"return_home": "Torna a la pàgina principal",
"password_reset_required": "Has de reiniciar la teva contrasenya per iniciar la sessió.",
"password_reset_disabled": "El reinici de la contrasenya està deshabilitat. Si us plau, contacta l'administració de la teva instància.",
"check_email": "Comprova que has rebut al correu electrònic un enllaç per reiniciar la teva contrasenya."
"password_reset_required": "Has de reiniciar la teva contrasenya per a iniciar la sessió.",
"password_reset_disabled": "El reinici de la contrasenya està desactivat. Si us plau, contacta l'administrador de la teva instància.",
"check_email": "Comprova que has rebut al correu electrònic un enllaç per a reiniciar la teva contrasenya."
},
"file_type": {
"image": "Imatge",

View file

@ -18,12 +18,12 @@
"not_applicable": "N/A",
"accept": "Accept",
"accept_desc": "This instance only accepts messages from the following instances:",
"reject": "Reject",
"reject_desc": "This instance will not accept messages from the following instances:",
"reject": "Instance Blocks",
"reject_desc": "This instance blocks posts to and from the following instances:",
"quarantine": "Quarantine",
"quarantine_desc": "This instance will send only public posts to the following instances:",
"ftl_removal": "Removal from \"Known Network\" Timeline",
"ftl_removal_desc": "This instance removes these instances from \"Known Network\" timeline:",
"quarantine_desc": "This instance will not send posts to the following instances:",
"ftl_removal": "Removal from Federated Timeline",
"ftl_removal_desc": "This instance removes these instances from the Federated Timeline:",
"media_removal": "Media Removal",
"media_removal_desc": "This instance removes media from posts on the following instances:",
"media_nsfw": "Media force-set as sensitive",
@ -106,7 +106,8 @@
"direct": "Direct",
"private": "Followers-only",
"public": "Public",
"unlisted": "Unlisted"
"unlisted": "Unlisted",
"local": "Local-only post -- only your instance can see this status"
}
},
"image_cropper": {
@ -125,7 +126,7 @@
"description": "Log in with OAuth",
"logout": "Log out",
"password": "Password",
"placeholder": "myusername",
"placeholder": "i.e. bobvibes420",
"register": "Register",
"username": "Username",
"hint": "Log in to join the discussion",
@ -148,18 +149,18 @@
"about": "About",
"administration": "Administration",
"back": "Back",
"friend_requests": "Follow requests",
"friend_requests": "Follow Requests",
"mentions": "Mentions",
"interactions": "Interactions",
"dms": "Direct messages",
"public_tl": "Public timeline",
"dms": "Direct Messages",
"public_tl": "Local Timeline",
"public_timeline_description": "Public posts from this instance",
"timeline": "Timeline",
"home_timeline": "Home timeline",
"home_timeline": "Home Timeline",
"home_timeline_description": "Posts from people you follow",
"bubble_timeline": "Bubble timeline",
"bubble_timeline": "Recommended Instances",
"bubble_timeline_description": "Posts from instances close to yours, as recommended by the admins",
"twkn": "Known Network",
"twkn": "Federated Timeline",
"twkn_timeline_description": "Posts from the entire network",
"bookmarks": "Bookmarks",
"user_search": "User Search",
@ -180,7 +181,7 @@
"load_older": "Load older notifications",
"notifications": "Notifications",
"read": "Read!",
"repeated_you": "repeated your status",
"repeated_you": "boosted your status",
"no_more_notifications": "No more notifications",
"migrated_to": "migrated to",
"reacted_with": "reacted with {0}",
@ -217,7 +218,7 @@
"storage_unavailable": "Pleroma could not access browser storage. Your login or your local settings won't be saved and you might encounter unexpected issues. Try enabling cookies."
},
"interactions": {
"favs_repeats": "Repeats and favorites",
"favs_repeats": "Boosts and favorites",
"follows": "New follows",
"moves": "User migrates",
"load_older": "Load older interactions"
@ -236,8 +237,8 @@
"text/bbcode": "BBCode",
"text/x.misskeymarkdown": "MFM"
},
"content_warning": "Subject (optional)",
"default": "Just arrived at Luna Nova Academy",
"content_warning": "Content Warning / Subject (optional)",
"default": "Just landed on Neptune",
"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.",
"posting": "Posting",
@ -257,7 +258,7 @@
"private": "Followers-only - post to followers only",
"public": "Public - post to public timelines",
"unlisted": "Unlisted - do not post to public timelines",
"local": "Local - do not federate this post"
"local": "Local - only your instance can see this status"
}
},
"registration": {
@ -369,7 +370,17 @@
"changed_password": "Password changed successfully!",
"chatMessageRadius": "Chat message",
"collapse_subject": "Collapse posts with subjects",
"columns": "Columns",
"composing": "Composing",
"confirmation_dialogs": "Confirmation options",
"confirm_dialogs": "Require confirmation for:",
"confirm_dialogs_repeat": "Repeating a post",
"confirm_dialogs_unfollow": "Unfollowing someone",
"confirm_dialogs_block": "Blocking someone",
"confirm_dialogs_mute": "Muting someone",
"confirm_dialogs_delete": "Deleting a post",
"confirm_dialogs_approve_follow": "Accepting a follow request",
"confirm_dialogs_deny_follow": "Rejecting a follow request",
"confirm_new_password": "Confirm new password",
"current_avatar": "Your current avatar",
"current_mascot": "Your current mascot",
@ -424,6 +435,10 @@
"hide_shoutbox": "Hide instance shoutbox",
"right_sidebar": "Reverse order of columns",
"always_show_post_button": "Always show floating New Post button",
"hide_site_favicon": "Hide instance favicon in top panel",
"hide_site_name": "Hide instance name in top panel",
"hide_threads_with_blocked_users": "Hide threads mentioning blocked users",
"hide_user_stats": "Hide user statistics (e.g. the number of followers)",
"hide_wallpaper": "Hide instance wallpaper",
"preload_images": "Preload images",
"use_one_click_nsfw": "Open NSFW attachments with just one click",
@ -479,7 +494,7 @@
"notification_visibility_follows": "Follows",
"notification_visibility_likes": "Favorites",
"notification_visibility_mentions": "Mentions",
"notification_visibility_repeats": "Repeats",
"notification_visibility_repeats": "Boosts",
"notification_visibility_moves": "User Migrates",
"notification_visibility_emoji_reactions": "Reactions",
"notification_visibility_polls": "Ends of polls you voted in",
@ -540,6 +555,7 @@
"conversation_display": "Conversation display style",
"conversation_display_tree": "Tree-style",
"disable_sticky_headers": "Don't stick column headers to top of the screen",
"show_nav_shortcuts": "Show extra navigation shortcuts in top panel",
"show_scrollbars": "Show side column's scrollbars",
"third_column_mode": "When there's enough space, show third column containing",
"third_column_mode_none": "Don't show third column at all",
@ -556,8 +572,10 @@
"sensitive_by_default": "Mark posts as sensitive by default",
"sensitive_if_subject": "Automatically mark images as sensitive if a subject line is specified",
"render_mfm": "Render Misskey Markdown",
"render_mfm_on_hover": "Pause MFM animations until status hover",
"useStreamingApiWarning": "It's cool use it. If it breaks refresh I guess?",
"stop_gifs": "Pause animated images until you hover on them",
"stop_gifs": "Pause animated images until you hover on them (breaks MFM emojis)",
"show_wider_shortcuts": "Show wider gap between top panel shortcuts",
"streaming": "Automatically show new posts when scrolled to the top",
"user_mutes": "Users",
"useStreamingApi": "Receive posts and notifications real-time",
@ -567,6 +585,7 @@
"theme_help_v2_1": "You can also override certain component's colors and opacity by toggling the checkbox, use \"Clear all\" button to clear all overrides.",
"theme_help_v2_2": "Icons underneath some entries are background/text contrast indicators, hover over for detailed info. Please keep in mind that when using transparency contrast indicators show the worst possible case.",
"tooltipRadius": "Tooltips/alerts",
"translation_language": "Automatic Translation Language",
"type_domains_to_mute": "Search domains to mute",
"upload_a_photo": "Upload a photo",
"user_settings": "User Settings",
@ -776,8 +795,8 @@
"conversation": "Conversation",
"error": "Error fetching timeline: {0}",
"load_older": "Load older statuses",
"no_retweet_hint": "Post is marked as followers-only or direct and cannot be repeated or quoted",
"repeated": "repeated",
"no_retweet_hint": "Post is marked as followers-only or direct and cannot be boosted or quoted",
"repeated": "boosted",
"show_new": "Show new",
"reload": "Reload",
"up_to_date": "Up-to-date",
@ -788,15 +807,24 @@
},
"status": {
"favorites": "Favorites",
"repeats": "Repeats",
"repeats": "Boosts",
"delete": "Delete status",
"pin": "Pin on profile",
"unpin": "Unpin from profile",
"pinned": "Pinned",
"bookmark": "Bookmark",
"unbookmark": "Unbookmark",
"translate": "Translate",
"translated_from": "Translated from {language}",
"delete_confirm": "Do you really want to delete this status?",
"reply_to": "Reply to",
"delete_confirm_title": "Confirm deletion",
"delete_confirm_accept_button": "Yes, delete it",
"delete_confirm_cancel_button": "No, keep it",
"repeat_confirm": "Do you really want to repeat this status?",
"repeat_confirm_title": "Confirm repeat",
"repeat_confirm_accept_button": "Yes, repeat it",
"repeat_confirm_cancel_button": "No, don't repeat",
"mentions": "Mentions",
"replies_list": "Replies:",
"replies_list_with_others": "Replies (+{numReplies} other): | Replies (+{numReplies} others):",
@ -841,10 +869,23 @@
},
"user_card": {
"approve": "Approve",
"approve_confirm_title": "Approve follow request",
"approve_confirm": "Are you sure you want to let this user follow you?",
"approve_confirm_accept_button": "Yes, accept",
"approve_confirm_cancel_button": "No, cancel",
"block": "Block",
"block_confirm": "Are you sure you want to block {user}?",
"block_confirm_title": "Block user",
"block_confirm_cancel_button": "No, don't block",
"block_confirm_accept_button": "Yes, block",
"block_progress": "Blocking…",
"blocked": "Blocked!",
"deactivated": "Deactivated",
"deny": "Deny",
"deny_confirm_title": "Deny follow request",
"deny_confirm": "Are you sure you want to deny this user's follow request?",
"deny_confirm_accept_button": "Yes, deny",
"deny_confirm_cancel_button": "No, cancel",
"edit_profile": "Edit profile",
"favorites": "Favorites",
"follow": "Follow",
@ -862,9 +903,14 @@
"mention": "Mention",
"message": "Message",
"mute": "Mute",
"mute_confirm": "Are you sure you want to mute {user}?",
"mute_confirm_title": "Mute user",
"mute_confirm_cancel_button": "No, don't mute",
"mute_confirm_accept_button": "Yes, mute",
"muted": "Muted",
"per_day": "per day",
"remote_follow": "Remote follow",
"replies": "With Replies",
"report": "Report",
"statuses": "Statuses",
"subscribe": "Subscribe",
@ -872,11 +918,15 @@
"unblock": "Unblock",
"unblock_progress": "Unblocking…",
"block_progress": "Blocking…",
"unfollow_confirm": "Are you sure you want to unfollow {user}?",
"unfollow_confirm_title": "Unfollow user",
"unfollow_confirm_cancel_button": "No, don't unfollow",
"unfollow_confirm_accept_button": "Yes, unfollow",
"unmute": "Unmute",
"unmute_progress": "Unmuting…",
"mute_progress": "Muting…",
"hide_repeats": "Hide repeats",
"show_repeats": "Show repeats",
"hide_repeats": "Hide boosts",
"show_repeats": "Show boosts",
"domain_muted": "Unblock domain",
"mute_domain": "Block domain",
"bot": "Bot",
@ -928,7 +978,7 @@
"tool_tip": {
"media_upload": "Upload media",
"quote": "Quote",
"repeat": "Repeat",
"repeat": "Boost",
"reply": "Reply",
"favorite": "Favorite",
"add_reaction": "Add Reaction",

View file

@ -561,69 +561,39 @@
"mention_link_display_full": "名前とドメイン、例: {'@'}foo{'@'}example.org",
"fun": "お楽しみ",
"virtual_scrolling": "タイムラインの描画を最適化する",
"type_domains_to_mute": "ミュートしたいドメインを検索",
"useStreamingApiWarning": "(実験中で、投稿を取りこぼすかもしれないので、おすすめしません)",
"useStreamingApi": "投稿と通知を、すぐに受け取る",
"user_mutes": "ユーザー",
"reset_background_confirm": "本当にバックグラウンドを初期化しますか?",
"reset_banner_confirm": "本当にバナーを初期化しますか?",
"reset_avatar_confirm": "本当にアバターを初期化しますか?",
"hide_wallpaper": "インスタンスのバックグラウンドを隠す",
"reset_profile_background": "プロフィールのバックグラウンドを初期化",
"reset_profile_banner": "プロフィールのバナーを初期化",
"reset_avatar": "アバターを初期化",
"notification_visibility_emoji_reactions": "リアクション",
"notification_visibility_moves": "ユーザーの引っ越し",
"new_email": "新しいメールアドレス",
"post_look_feel": "投稿の見た目",
"mention_links": "メンションリンク",
"profile_fields": {
"value": "内容",
"name": "ラベル",
"add_field": "枠を追加",
"label": "プロフィール補足情報"
},
"accent": "アクセント",
"mutes_imported": "ミュートをインポートしました!少し時間がかかるかもしれません。",
"emoji_reactions_on_timeline": "絵文字リアクションをタイムラインに表示",
"domain_mutes": "ドメイン",
"mutes_and_blocks": "ミュートとブロック",
"chatMessageRadius": "チャットメッセージ",
"change_email_error": "メールアドレスを変えることが、できなかったかもしれません。",
"changed_email": "メールアドレスが、変わりました!",
"change_email": "メールアドレスを変える",
"bot": "これは bot アカウントです",
"mute_export_button": "ミュートをCSVファイルにエクスポートする",
"import_mutes_from_a_csv_file": "CSVファイルからミュートをインポートする",
"mute_import_error": "ミュートのインポートに失敗しました",
"mute_import": "ミュートのインポート",
"mute_export": "ミュートのエクスポート",
"allow_following_move": "フォロー中のアカウントが引っ越したとき、自動フォローを許可する",
"setting_changed": "規定の設定と異なっています",
"greentext": "引用を緑色で表示",
"sensitive_by_default": "はじめから投稿をセンシティブとして設定",
"sensitive_if_subject": "ステータスにサブジェクトをついたらNSFWにする",
"render_mfm": "Misskey Markdownを表示",
"more_settings": "その他の設定",
"reply_visibility_self_short": "自分宛のリプライを見る",
"reply_visibility_following_short": "フォローしている人に宛てられたリプライを見る",
"hide_all_muted_posts": "ミュートした投稿を隠す",
"hide_media_previews": "メディアのプレビューを隠す",
"word_filter": "単語フィルタ",
"file_export_import": {
"errors": {
"invalid_file": "これはPleromaの設定をバックアップしたファイルではありません。",
"file_slightly_new": "ファイルのマイナーバージョンが異なり、一部の設定が読み込まれないことがあります"
},
"restore_settings": "設定をファイルから復元する",
"backup_settings_theme": "テーマを含む設定をファイルにバックアップする",
"backup_settings": "設定をファイルにバックアップする",
"backup_restore": "設定をバックアップ"
},
"save": "変更を保存",
"hide_shoutbox": "Shoutboxを表示しない",
"always_show_post_button": "投稿ボタンを常に表示",
"right_sidebar": "サイドバーを右に表示"
"word_filter": "単語フィルタ"
},
"status": {
"bookmark": "ブックマーク",
"copy_link": "リンクをコピー",
"delete": "ステータスを削除",
"delete_confirm": "本当にこのステータスを削除してもよろしいですか?",
"expand": "広げる",
"external_source": "外部ソース",
"favorites": "お気に入り",
"hide_content": "隠す",
"hide_full_subject": "隠す",
"mentions": "メンション",
"mute_conversation": "スレッドをミュート",
"nsfw": "閲覧注意",
"pin": "プロフィールにピン留め",
"pinned": "ピン留め",
"plus_more": "ほか{number}件",
"repeats": "リピート",
"replies_list": "返信:",
"reply_to": "返信",
"show_content": "見る",
"show_full_subject": "全部見る",
"status_deleted": "この投稿は削除されました",
"status_unavailable": "利用できません",
"thread_muted": "ミュートされたスレッド",
"thread_muted_and_words": "以下の単語を含むため:",
"translate": "翻訳",
"translated_from": "{language}から翻訳されました",
"unbookmark": "ブックマーク解除",
"unmute_conversation": "スレッドのミュートを解除",
"unpin": "プロフィールのピン留めを外す",
"you": "(あなた)"
},
"time": {
"now": "たった今",

View file

@ -31,10 +31,15 @@ export const defaultState = {
// bad name: actually hides posts of muted USERS
hideMutedPosts: undefined, // instance default
hideMutedThreads: undefined, // instance default
hideThreadsWithBlockedUsers: undefined, // instance default
hideWordFilteredPosts: undefined, // instance default
muteBotStatuses: undefined, // instance default
collapseMessageWithSubject: undefined, // instance default
collapseMessageWithSubject: true, // instance default
padEmoji: true,
showNavShortcuts: undefined, // instance default
showWiderShortcuts: undefined, // instance default
hideSiteFavicon: undefined, // instance default
hideSiteName: undefined, // instance default
hideAttachments: false,
hideAttachmentsInConv: false,
maxThumbnails: 16,
@ -44,11 +49,11 @@ export const defaultState = {
loopVideoSilentOnly: true,
streaming: false,
emojiReactionsOnTimeline: true,
alwaysShowNewPostButton: false,
alwaysShowNewPostButton: true,
autohideFloatingPostButton: false,
pauseOnUnfocused: true,
stopGifs: true,
replyVisibility: 'all',
stopGifs: false,
replyVisibility: 'following',
thirdColumnMode: 'notifications',
notificationVisibility: {
follows: true,
@ -66,7 +71,7 @@ export const defaultState = {
highlight: {},
interfaceLanguage: browserLocale,
hideScopeNotice: false,
useStreamingApi: true,
useStreamingApi: false,
sidebarRight: undefined, // instance default
scopeCopy: undefined, // instance default
subjectLineBehavior: undefined, // instance default
@ -75,6 +80,14 @@ export const defaultState = {
minimalScopesMode: undefined, // instance default
// This hides statuses filtered via a word filter
hideFilteredStatuses: undefined, // instance default
modalOnRepeat: undefined, // instance default
modalOnUnfollow: undefined, // instance default
modalOnBlock: undefined, // instance default
modalOnMute: undefined, // instance default
modalOnDelete: undefined, // instance default
modalOnLogout: undefined, // instance default
modalOnApproveFollow: undefined, // instance default
modalOnDenyFollow: undefined, // instance default
playVideosInModal: false,
useOneClickNsfw: false,
useContainFit: true,
@ -84,7 +97,7 @@ export const defaultState = {
useAtIcon: undefined, // instance default
mentionLinkDisplay: undefined, // instance default
mentionLinkShowTooltip: undefined, // instance default
mentionLinkShowAvatar: undefined, // instance default
mentionLinkShowAvatar: true, // instance default
mentionLinkFadeDomain: undefined, // instance default
mentionLinkShowYous: undefined, // instance default
mentionLinkBoldenYou: undefined, // instance default
@ -94,12 +107,14 @@ export const defaultState = {
virtualScrolling: undefined, // instance default
sensitiveByDefault: undefined, // instance default
sensitiveIfSubject: undefined,
renderMisskeyMarkdown: undefined,
renderMisskeyMarkdown: true,
mfmOnHover: undefined, // instance default
conversationDisplay: undefined, // instance default
conversationTreeAdvanced: undefined, // instance default
conversationOtherRepliesButton: undefined, // instance default
conversationTreeFadeAncestors: undefined, // instance default
maxDepthInThread: undefined // instance default
maxDepthInThread: undefined, // instance default
translationLanguage: undefined // instance default
}
// caching the instance default properties
@ -172,6 +187,7 @@ const config = {
case 'interfaceLanguage':
messages.setLanguage(this.getters.i18n, value)
Cookies.set(BACKEND_LANGUAGE_COOKIE_NAME, localeService.internalToBackendLocale(value))
dispatch('setInstanceOption', { name: 'interfaceLanguage', value })
break
case 'thirdColumnMode':
dispatch('setLayoutWidth', undefined)

View file

@ -17,8 +17,8 @@ const defaultState = {
defaultAvatar: '/images/avi.png',
defaultBanner: '/images/banner.png',
background: '/static/aurora_borealis.jpg',
collapseMessageWithSubject: false,
greentext: false,
collapseMessageWithSubject: true,
greentext: true,
useAtIcon: false,
mentionLinkDisplay: 'short',
mentionLinkShowTooltip: true,
@ -30,12 +30,22 @@ const defaultState = {
// bad name: actually hides posts of muted USERS
hideMutedPosts: false,
hideMutedThreads: true,
hideThreadsWithBlockedUsers: false,
hideWordFilteredPosts: false,
hidePostStats: false,
hideBotIndication: false,
hideSitename: false,
hideSiteFavicon: false,
hideSiteName: false,
hideUserStats: false,
muteBotStatuses: false,
modalOnRepeat: false,
modalOnUnfollow: false,
modalOnBlock: true,
modalOnMute: false,
modalOnDelete: true,
modalOnLogout: true,
modalOnApproveFollow: false,
modalOnDenyFollow: false,
loginMethod: 'password',
logo: '/static/logo.svg',
logoMargin: '.2em',
@ -49,13 +59,16 @@ const defaultState = {
scopeCopy: true,
showFeaturesPanel: true,
showInstanceSpecificPanel: false,
showNavShortcuts: true,
showWiderShortcuts: false,
sidebarRight: false,
subjectLineBehavior: 'email',
theme: 'pleroma-dark',
virtualScrolling: true,
sensitiveByDefault: false,
sensitiveIfSubject: true,
renderMisskeyMarkdown: false,
renderMisskeyMarkdown: true,
mfmOnHover: false,
conversationDisplay: 'linear',
conversationTreeAdvanced: false,
conversationOtherRepliesButton: 'below',

View file

@ -64,7 +64,8 @@ export const defaultState = () => ({
dms: emptyTl(),
bookmarks: emptyTl(),
list: emptyTl(),
bubble: emptyTl()
bubble: emptyTl(),
replies: emptyTl()
}
})
@ -183,7 +184,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
// This makes sure that user timeline won't get data meant for other
// user. I.e. opening different user profiles makes request which could
// return data late after user already viewing different user profile
if ((timeline === 'user' || timeline === 'media') && timelineObject.userId !== userId) {
if ((timeline === 'user' || timeline === 'media' || timeline === 'replies') && timelineObject.userId !== userId) {
return
}
@ -425,6 +426,10 @@ export const mutations = {
state.conversationsObject[newStatus.statusnet_conversation_id].forEach(status => { status.thread_muted = newStatus.thread_muted })
}
},
setTranslatedStatus (state, { id, translation }) {
const newStatus = state.allStatusesObject[id]
newStatus.translation = translation
},
setRetweeted (state, { status, value }) {
const newStatus = state.allStatusesObject[status.id]
@ -637,6 +642,10 @@ const statuses = {
rootState.api.backendInteractor.unpinOwnStatus({ id: statusId })
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
},
translateStatus ({ rootState, commit }, { id, translation, language }) {
return rootState.api.backendInteractor.translateStatus({ id: id, translation, language })
.then((translation) => commit('setTranslatedStatus', { id, translation }))
},
muteConversation ({ rootState, commit }, statusId) {
return rootState.api.backendInteractor.muteConversation({ id: statusId })
.then((status) => commit('setMutedStatus', status))

View file

@ -31,6 +31,7 @@ const MASTODON_LOGIN_URL = '/api/v1/accounts/verify_credentials'
const MASTODON_REGISTRATION_URL = '/api/v1/accounts'
const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites'
const MASTODON_USER_NOTIFICATIONS_URL = '/api/v1/notifications'
const AKKOMA_TRANSLATE_URL = (id, lang) => `/api/v1/statuses/${id}/translations/${lang}`
const MASTODON_DISMISS_NOTIFICATION_URL = id => `/api/v1/notifications/${id}/dismiss`
const MASTODON_FAVORITE_URL = id => `/api/v1/statuses/${id}/favourite`
const MASTODON_UNFAVORITE_URL = id => `/api/v1/statuses/${id}/unfavourite`
@ -615,6 +616,7 @@ const fetchTimeline = ({
notifications: MASTODON_USER_NOTIFICATIONS_URL,
'publicAndExternal': MASTODON_PUBLIC_TIMELINE,
user: MASTODON_USER_TIMELINE_URL,
replies: MASTODON_USER_TIMELINE_URL,
media: MASTODON_USER_TIMELINE_URL,
list: MASTODON_LIST_TIMELINE_URL,
favorites: MASTODON_USER_FAVORITES_TIMELINE_URL,
@ -626,7 +628,7 @@ const fetchTimeline = ({
let url = timelineUrls[timeline]
if (timeline === 'user' || timeline === 'media') {
if (timeline === 'user' || timeline === 'media' || timeline === 'replies') {
url = url(userId)
}
@ -658,6 +660,9 @@ const fetchTimeline = ({
if (replyVisibility !== 'all') {
params.push(['reply_visibility', replyVisibility])
}
if (timeline === 'user') {
params.push(['exclude_replies', 1])
}
params.push(['limit', 20])
@ -738,6 +743,13 @@ const unretweet = ({ id, credentials }) => {
.then((data) => parseStatus(data))
}
const translateStatus = ({ id, credentials, language }) => {
return promisedRequest({ url: AKKOMA_TRANSLATE_URL(id, language), method: 'GET', credentials })
.then((data) => {
return data
})
}
const bookmarkStatus = ({ id, credentials }) => {
return promisedRequest({
url: MASTODON_BOOKMARK_STATUS_URL(id),
@ -1576,7 +1588,8 @@ const apiService = {
postAnnouncement,
editAnnouncement,
deleteAnnouncement,
adminFetchAnnouncements
adminFetchAnnouncements,
translateStatus
}
export default apiService

View file

@ -49,7 +49,7 @@ const fetchAndUpdate = ({
args['listId'] = listId
args['tag'] = tag
args['withMuted'] = !hideMutedPosts
if (loggedIn && ['friends', 'public', 'publicAndExternal'].includes(timeline)) {
if (loggedIn && ['friends', 'public', 'publicAndExternal', 'bubble'].includes(timeline)) {
args['replyVisibility'] = replyVisibility
}

29
static/manifest.json Normal file
View file

@ -0,0 +1,29 @@
{
"name": "☽ D̷i̵s̴q̴ordi̴a ☾",
"short_name": "☽ D̷i̵s̴q̴ordi̴a ☾",
"start_url": "/",
"display": "standalone",
"background_color": "#000000",
"theme_color": "#000000",
"orientation": "portrait-primary",
"icons": [
{
"src": "/images/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "/images/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "/images/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable any"
}
]
}

39
static/service-worker.js Normal file
View file

@ -0,0 +1,39 @@
// Installing service worker
const CACHE_NAME = 'Disqordia';
/* Add relative URL of all the static content you want to store in
* cache storage (this will help us use our app offline)*/
let resourcesToCache = ["/", "/static/font/css/fontello.css", "/static/font/css/animation.css", "/static/font/tiresias.css", "/static/font/css/lato.css", "/static/mfm.css", "/favicon.png", "/static/css/app.d7c75a48f7d627e0493f.css", "/static/js/vendors~app.4600ad9d6a3c807e6688.js", "/static/js/app.afaee31b8c11ba3c67aa.js"];
self.addEventListener("install", e=>{
e.waitUntil(
caches.open(CACHE_NAME).then(cache =>{
return cache.addAll(resourcesToCache);
})
);
});
// Cache and return requests
self.addEventListener("fetch", e=>{
e.respondWith(
caches.match(e.request).then(response=>{
return response || fetch(e.request);
})
);
});
// Update a service worker
const cacheWhitelist = ['Disqordia'];
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});