diff --git a/README.md b/README.md index 9be1c3bd..43e177b4 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,22 @@ -# Pleroma-FE +# Akkoma-FE ![English OK](https://img.shields.io/badge/English-OK-blueviolet) ![日本語OK](https://img.shields.io/badge/%E6%97%A5%E6%9C%AC%E8%AA%9E-OK-blueviolet) -This is a fork of Pleroma-FE from the Pleroma project, with support for new Akkoma features such as: +This is a fork of Akkoma-FE from the Pleroma project, with support for new Akkoma features such as: - MFM support via [marked-mfm](https://akkoma.dev/sfr/marked-mfm) - Custom emoji reactions # For Translators -The [Weblate UI](https://translate.akkoma.dev/projects/akkoma/pleroma-fe/) is recommended for adding or modifying translations for Pleroma-FE. +The [Weblate UI](https://translate.akkoma.dev/projects/akkoma/pleroma-fe/) is recommended for adding or modifying translations for Akkoma-FE. Alternatively, edit/create `src/i18n/$LANGUAGE_CODE.json` (where `$LANGUAGE_CODE` is the [ISO 639-1 code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) for your language), then add your language to [src/i18n/messages.js](https://akkoma.dev/AkkomaGang/pleroma-fe/src/branch/develop/src/i18n/messages.js) if it doesn't already exist there. -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. +Akkoma-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 -To use Pleroma-FE in Akkoma, use the [frontend](https://docs.akkoma.dev/stable/administration/CLI_tasks/frontend/) CLI task to install Pleroma-FE, then modify your configuration as described in the [Frontend Management](https://docs.akkoma.dev/stable/configuration/frontend_management/) doc. +To use Akkoma-FE in Akkoma, use the [frontend](https://docs.akkoma.dev/stable/administration/CLI_tasks/frontend/) CLI task to install Akkoma-FE, then modify your configuration as described in the [Frontend Management](https://docs.akkoma.dev/stable/configuration/frontend_management/) doc. ## Build Setup @@ -52,4 +52,4 @@ Edit config.json for configuration. ### Login methods -```loginMethod``` can be set to either ```password``` (the default) or ```token```, which will use the full oauth redirection flow, which is useful for SSO situations. +```loginMethod``` can be set to either ```password``` (the default) or ```token```, which will use the full oauth redirection flow, which is useful for SSO situations. diff --git a/index.html b/index.html index 79613dd2..fda91b0f 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,7 @@ + diff --git a/package.json b/package.json index e712cffd..4bb94fcd 100644 --- a/package.json +++ b/package.json @@ -18,19 +18,21 @@ "dependencies": { "@babel/runtime": "7.17.8", "@chenfengyuan/vue-qrcode": "2.0.0", + "@floatingghost/pinch-zoom-element": "^1.3.1", "@fortawesome/fontawesome-svg-core": "1.3.0", "@fortawesome/free-regular-svg-icons": "^6.1.2", "@fortawesome/free-solid-svg-icons": "^6.2.0", "@fortawesome/vue-fontawesome": "3.0.1", - "@kazvmoe-infra/pinch-zoom-element": "1.2.0", "@vuelidate/core": "^2.0.0", "@vuelidate/validators": "^2.0.0", + "blurhash": "^2.0.4", "body-scroll-lock": "2.7.1", "chromatism": "3.0.0", "click-outside-vue3": "4.0.1", "cropperjs": "1.5.12", "diff": "3.5.0", "escape-html": "1.0.3", + "iso-639-1": "^2.1.15", "js-cookie": "^3.0.1", "localforage": "1.10.0", "parse-link-header": "^2.0.0", @@ -82,7 +84,6 @@ "html-webpack-plugin": "^5.5.0", "http-proxy-middleware": "0.21.0", "inject-loader": "2.0.1", - "iso-639-1": "2.1.15", "isparta-loader": "2.0.0", "json-loader": "0.5.7", "karma": "6.3.17", diff --git a/src/App.scss b/src/App.scss index 7e6d0dfc..38574cab 100644 --- a/src/App.scss +++ b/src/App.scss @@ -1,6 +1,7 @@ // stylelint-disable rscss/class-format @import './_variables.scss'; - +@import '@fortawesome/fontawesome-svg-core/styles.css'; +@import '@floatingghost/pinch-zoom-element/dist/pinch-zoom.css'; :root { --navbar-height: 3.5rem; --post-line-height: 1.4; diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 4bafca1d..36b087a5 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -4,6 +4,8 @@ import { createRouter, createWebHistory } from 'vue-router' import vClickOutside from 'click-outside-vue3' import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome' +import { config } from '@fortawesome/fontawesome-svg-core'; +config.autoAddCss = false import App from '../App.vue' import routes from './routes' diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js index 4dcacc7e..3155abf0 100644 --- a/src/components/attachment/attachment.js +++ b/src/components/attachment/attachment.js @@ -18,6 +18,7 @@ import { faPencilAlt, faAlignRight } from '@fortawesome/free-solid-svg-icons' +import Blurhash from '../blurhash/Blurhash.vue' library.add( faFile, @@ -63,7 +64,8 @@ const Attachment = { components: { Flash, StillImage, - VideoAttachment + VideoAttachment, + Blurhash }, computed: { classNames () { @@ -84,6 +86,9 @@ const Attachment = { useContainFit () { return this.$store.getters.mergedConfig.useContainFit }, + useBlurhash () { + return this.$store.getters.mergedConfig.useBlurhash + }, placeholderName () { if (this.attachment.description === '' || !this.attachment.description) { return this.type.toUpperCase() diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue index 947b1bfc..0ccdb776 100644 --- a/src/components/attachment/attachment.vue +++ b/src/components/attachment/attachment.vue @@ -64,7 +64,15 @@ :title="attachment.description" @click.prevent.stop="toggleHidden" > + + + + + + + diff --git a/src/components/emoji_grid/emoji_grid.js b/src/components/emoji_grid/emoji_grid.js new file mode 100644 index 00000000..f73b0913 --- /dev/null +++ b/src/components/emoji_grid/emoji_grid.js @@ -0,0 +1,133 @@ +const EMOJI_SIZE = 32 + 8 +const GROUP_TITLE_HEIGHT = 24 +const BUFFER_SIZE = 3 * EMOJI_SIZE + +const EmojiGrid = { + props: { + groups: { + required: true, + type: Array + } + }, + data () { + return { + containerWidth: 0, + containerHeight: 0, + scrollPos: 0, + resizeObserver: null + } + }, + mounted () { + const rect = this.$refs.container.getBoundingClientRect() + this.containerWidth = rect.width + this.containerHeight = rect.height + this.resizeObserver = new ResizeObserver((entries) => { + for (const entry of entries) { + this.containerWidth = entry.contentRect.width + this.containerHeight = entry.contentRect.height + } + }) + this.resizeObserver.observe(this.$refs.container) + }, + beforeUnmount () { + this.resizeObserver.disconnect() + this.resizeObserver = null + }, + watch: { + groups () { + // Scroll to top when grid content changes + if (this.$refs.container) { + this.$refs.container.scrollTo(0, 0) + } + }, + activeGroup (group) { + this.$emit('activeGroup', group) + } + }, + methods: { + onScroll () { + this.scrollPos = this.$refs.container.scrollTop + }, + onEmoji (emoji) { + this.$emit('emoji', emoji) + }, + scrollToItem (itemId) { + const container = this.$refs.container + if (!container) return + + for (const item of this.itemList) { + if (item.id === itemId) { + container.scrollTo(0, item.position.y) + return + } + } + } + }, + computed: { + // Total height of scroller content + gridHeight () { + if (this.itemList.length === 0) return 0 + const lastItem = this.itemList[this.itemList.length - 1] + return ( + lastItem.position.y + + ('title' in lastItem ? GROUP_TITLE_HEIGHT : EMOJI_SIZE) + ) + }, + activeGroup () { + const items = this.itemList + for (let i = items.length - 1; i >= 0; i--) { + const item = items[i] + if ('title' in item && item.position.y <= this.scrollPos) { + return item.id + } + } + return null + }, + itemList () { + const items = [] + let x = 0 + let y = 0 + for (const group of this.groups) { + items.push({ position: { x, y }, id: group.id, title: group.text }) + if (group.text.length) { + y += GROUP_TITLE_HEIGHT + } + for (const emoji of group.emojis) { + items.push({ + position: { x, y }, + id: `${group.id}-${emoji.displayText}`, + emoji + }) + x += EMOJI_SIZE + if (x + EMOJI_SIZE > this.containerWidth) { + y += EMOJI_SIZE + x = 0 + } + } + if (x > 0) { + y += EMOJI_SIZE + x = 0 + } + } + return items + }, + visibleItems () { + const startPos = this.scrollPos - BUFFER_SIZE + const endPos = this.scrollPos + this.containerHeight + BUFFER_SIZE + return this.itemList.filter((i) => { + return i.position.y >= startPos && i.position.y < endPos + }) + }, + scrolledClass () { + if (this.scrollPos <= 5) { + return 'scrolled-top' + } else if (this.scrollPos >= this.gridHeight - this.containerHeight - 5) { + return 'scrolled-bottom' + } else { + return 'scrolled-middle' + } + } + } +} + +export default EmojiGrid diff --git a/src/components/emoji_grid/emoji_grid.scss b/src/components/emoji_grid/emoji_grid.scss new file mode 100644 index 00000000..5d5b153f --- /dev/null +++ b/src/components/emoji_grid/emoji_grid.scss @@ -0,0 +1,60 @@ +.emoji { + &-grid { + flex: 1 1 1px; + position: relative; + overflow: auto; + user-select: none; + mask: linear-gradient(to top, white 0, transparent 100%) bottom no-repeat, + linear-gradient(to bottom, white 0, transparent 100%) top no-repeat, + linear-gradient(to top, white, white); + transition: mask-size 150ms; + mask-size: 100% 20px, 100% 20px, auto; + // Autoprefixed seem to ignore this one, and also syntax is different + -webkit-mask-composite: xor; + mask-composite: exclude; + &.scrolled { + &-top { + mask-size: 100% 20px, 100% 0, auto; + } + &-bottom { + mask-size: 100% 0, 100% 20px, auto; + } + } + margin-left: 5px; + min-height: 200px; + } + + &-group-title { + position: absolute; + font-size: 0.85em; + width: 100%; + margin: 0; + height: 24px; + display: flex; + align-items: end; + + &.disabled { + display: none; + } + } + + &-item { + position: absolute; + width: 32px; + height: 32px; + box-sizing: border-box; + display: flex; + font-size: 32px; + align-items: center; + justify-content: center; + margin: 4px; + + cursor: pointer; + + img { + object-fit: contain; + max-width: 100%; + max-height: 100%; + } + } +} \ No newline at end of file diff --git a/src/components/emoji_grid/emoji_grid.vue b/src/components/emoji_grid/emoji_grid.vue new file mode 100644 index 00000000..94732319 --- /dev/null +++ b/src/components/emoji_grid/emoji_grid.vue @@ -0,0 +1,48 @@ + + + + diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js index 846274b8..138a5b51 100644 --- a/src/components/emoji_input/emoji_input.js +++ b/src/components/emoji_input/emoji_input.js @@ -205,7 +205,6 @@ const EmojiInput = { }, triggerShowPicker () { this.showPicker = true - this.$refs.picker.startEmojiLoad() this.$nextTick(() => { this.scrollIntoView() this.focusPickerInput() @@ -223,7 +222,6 @@ const EmojiInput = { this.showPicker = !this.showPicker if (this.showPicker) { this.scrollIntoView() - this.$refs.picker.startEmojiLoad() this.$nextTick(this.focusPickerInput) } }, diff --git a/src/components/emoji_input/emoji_input.vue b/src/components/emoji_input/emoji_input.vue index 078253c2..d4760edc 100644 --- a/src/components/emoji_input/emoji_input.vue +++ b/src/components/emoji_input/emoji_input.vue @@ -18,6 +18,7 @@ import('../sticker_picker/sticker_picker.vue')), - Checkbox + Checkbox, + EmojiGrid }, methods: { onStickerUploaded (e) { @@ -56,12 +52,6 @@ const EmojiPicker = { const value = emoji.imageUrl ? `:${emoji.displayText}:` : emoji.replacement this.$emit('emoji', { insertion: value, keepOpen: this.keepOpen }) }, - onScroll (e) { - const target = (e && e.target) || this.$refs['emoji-groups'] - this.updateScrolledClass(target) - this.scrolledGroup(target) - this.triggerLoadMore(target) - }, onWheel (e) { e.preventDefault() this.$refs['emoji-tabs'].scrollBy(e.deltaY, 0) @@ -69,68 +59,12 @@ const EmojiPicker = { highlight (key) { this.setShowStickers(false) this.activeGroup = key - }, - updateScrolledClass (target) { - if (target.scrollTop <= 5) { - this.groupsScrolledClass = 'scrolled-top' - } else if (target.scrollTop >= target.scrollTopMax - 5) { - this.groupsScrolledClass = 'scrolled-bottom' - } else { - this.groupsScrolledClass = 'scrolled-middle' + if (this.keyword.length) { + this.$refs.emojiGrid.scrollToItem(key) } }, - triggerLoadMore (target) { - const ref = this.$refs['group-end-custom'] - if (!ref) return - const bottom = ref.offsetTop + ref.offsetHeight - - const scrollerBottom = target.scrollTop + target.clientHeight - const scrollerTop = target.scrollTop - const scrollerMax = target.scrollHeight - - // Loads more emoji when they come into view - const approachingBottom = bottom - scrollerBottom < LOAD_EMOJI_MARGIN - // Always load when at the very top in case there's no scroll space yet - const atTop = scrollerTop < 5 - // Don't load when looking at unicode category or at the very bottom - const bottomAboveViewport = bottom < scrollerTop || scrollerBottom === scrollerMax - if (!bottomAboveViewport && (approachingBottom || atTop)) { - this.loadEmoji() - } - }, - scrolledGroup (target) { - const top = target.scrollTop + 5 - this.$nextTick(() => { - this.emojisView.forEach(group => { - const ref = this.$refs['group-' + group.id] - if (ref.offsetTop <= top) { - this.activeGroup = group.id - } - }) - }) - }, - loadEmoji () { - const allLoaded = this.customEmojiBuffer.length === this.filteredEmoji.length - - if (allLoaded) { - return - } - - this.customEmojiBufferSlice += LOAD_EMOJI_BY - }, - startEmojiLoad (forceUpdate = false) { - if (!forceUpdate) { - this.keyword = '' - } - this.$nextTick(() => { - this.$refs['emoji-groups'].scrollTop = 0 - }) - const bufferSize = this.customEmojiBuffer.length - const bufferPrefilledAll = bufferSize === this.filteredEmoji.length - if (bufferPrefilledAll && !forceUpdate) { - return - } - this.customEmojiBufferSlice = LOAD_EMOJI_BY + onActiveGroup (group) { + this.activeGroup = group }, toggleStickers () { this.showingStickers = !this.showingStickers @@ -146,13 +80,6 @@ const EmojiPicker = { }) } }, - watch: { - keyword () { - this.customEmojiLoadAllConfirmed = false - this.onScroll() - this.startEmojiLoad(true) - } - }, computed: { activeGroupView () { return this.showingStickers ? '' : this.activeGroup @@ -168,9 +95,6 @@ const EmojiPicker = { this.$store.state.instance.customEmoji || [] ) }, - customEmojiBuffer () { - return this.filteredEmoji.slice(0, this.customEmojiBufferSlice) - }, emojis () { const standardEmojis = this.$store.state.instance.emoji || [] const customEmojis = this.sortedEmoji diff --git a/src/components/emoji_picker/emoji_picker.scss b/src/components/emoji_picker/emoji_picker.scss index ac7b8b5d..6ce8cbd8 100644 --- a/src/components/emoji_picker/emoji_picker.scss +++ b/src/components/emoji_picker/emoji_picker.scss @@ -1,5 +1,16 @@ @import '../../_variables.scss'; +// The worst query selector ever +// selects ONLY emojis pickers in replies in notifications +// who thought this was a good idea? +.notification > .Status > .status-container > .post-status-form > form > .form-group > .emoji-input > .emoji-picker { + max-width: 100%; + left: 0; + @media (min-width: 1300px) { + left: -30px; + } +} + .Notification { .emoji-picker { min-width: 160%; @@ -7,7 +18,7 @@ overflow: hidden; left: -70%; max-width: 100%; - @media (min-width: 800px) and (max-width: 1300px) { + @media (min-width: 800px) and (max-width: 1280px) { left: -50%; min-width: 50%; max-width: 130%; @@ -18,6 +29,10 @@ min-width: 50%; max-width: 130%; } + + .Status > .emoji-picker { + z-index: 1000; + } } } .emoji-picker { @@ -70,10 +85,6 @@ flex-grow: 1; } - .emoji-groups { - min-height: 200px; - } - .additional-tabs { border-left: 1px solid; border-left-color: $fallback--icon; @@ -152,76 +163,12 @@ } } - .emoji { - &-search { - padding: 5px; - flex: 0 0 auto; + .emoji-search { + padding: 5px; + flex: 0 0 auto; - input { - width: 100%; - } + input { + width: 100%; } - - &-groups { - flex: 1 1 1px; - position: relative; - overflow: auto; - user-select: none; - mask: linear-gradient(to top, white 0, transparent 100%) bottom no-repeat, - linear-gradient(to bottom, white 0, transparent 100%) top no-repeat, - linear-gradient(to top, white, white); - transition: mask-size 150ms; - mask-size: 100% 20px, 100% 20px, auto; - // Autoprefixed seem to ignore this one, and also syntax is different - -webkit-mask-composite: xor; - mask-composite: exclude; - &.scrolled { - &-top { - mask-size: 100% 20px, 100% 0, auto; - } - &-bottom { - mask-size: 100% 0, 100% 20px, auto; - } - } - } - - &-group { - display: flex; - align-items: center; - flex-wrap: wrap; - padding-left: 5px; - justify-content: left; - - &-title { - font-size: 0.85em; - width: 100%; - margin: 0; - - &.disabled { - display: none; - } - } - } - - &-item { - width: 32px; - height: 32px; - box-sizing: border-box; - display: flex; - font-size: 32px; - align-items: center; - justify-content: center; - margin: 4px; - - cursor: pointer; - - img { - object-fit: contain; - max-width: 100%; - max-height: 100%; - } - } - } - } diff --git a/src/components/emoji_picker/emoji_picker.vue b/src/components/emoji_picker/emoji_picker.vue index 00ffb9d2..fe2e39b2 100644 --- a/src/components/emoji_picker/emoji_picker.vue +++ b/src/components/emoji_picker/emoji_picker.vue @@ -2,9 +2,9 @@
+
-
-
- {{ group.text }} -
- - {{ emoji.replacement }} - - - -
-
-
{{ $t('emoji.keep_open') }} diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue index d9c568f6..3fe81f77 100644 --- a/src/components/emoji_reactions/emoji_reactions.vue +++ b/src/components/emoji_reactions/emoji_reactions.vue @@ -19,6 +19,7 @@ :title="reaction.name" class="reaction-emoji" width="2.55em" + height="2.55em" > {{ reaction.count }} @@ -65,6 +66,7 @@ box-sizing: border-box; .reaction-emoji { width: 2.55em !important; + height: 2.55em !important; margin-right: 0.25em; } &:focus { diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js index 4bc6144c..5eb98264 100644 --- a/src/components/extra_buttons/extra_buttons.js +++ b/src/components/extra_buttons/extra_buttons.js @@ -144,6 +144,7 @@ const ExtraButtons = { statusPoll: this.status.poll, statusFiles: [...this.status.attachments], statusScope: this.status.visibility, + statusLanguage: this.status.language, statusContentType: data.content_type })) this.doDeleteStatus() diff --git a/src/components/follow_request_card/follow_request_card.js b/src/components/follow_request_card/follow_request_card.js index b0873bb1..47c86e15 100644 --- a/src/components/follow_request_card/follow_request_card.js +++ b/src/components/follow_request_card/follow_request_card.js @@ -43,6 +43,7 @@ const FollowRequestCard = { doApprove () { this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) this.$store.dispatch('removeFollowRequest', this.user) + this.$store.dispatch('decrementFollowRequestsCount') const notifId = this.findFollowRequestNotificationId() this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId }) @@ -66,6 +67,7 @@ const FollowRequestCard = { this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) .then(() => { this.$store.dispatch('dismissNotificationLocal', { id: notifId }) + this.$store.dispatch('decrementFollowRequestsCount') this.$store.dispatch('removeFollowRequest', this.user) }) this.hideDenyConfirmDialog() @@ -80,6 +82,11 @@ const FollowRequestCard = { }, shouldConfirmDeny () { return this.mergedConfig.modalOnDenyFollow + }, + show () { + const notifId = this.$store.state.api.followRequests.find(req => req.id === this.user.id) + + return notifId !== undefined } } } diff --git a/src/components/follow_request_card/follow_request_card.vue b/src/components/follow_request_card/follow_request_card.vue index 835471e7..80445021 100644 --- a/src/components/follow_request_card/follow_request_card.vue +++ b/src/components/follow_request_card/follow_request_card.vue @@ -1,5 +1,5 @@ diff --git a/src/components/followed_tag_card/FollowedTagCard.vue b/src/components/followed_tag_card/FollowedTagCard.vue new file mode 100644 index 00000000..d9394ddc --- /dev/null +++ b/src/components/followed_tag_card/FollowedTagCard.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js index af165d47..2eda912e 100644 --- a/src/components/nav_panel/nav_panel.js +++ b/src/components/nav_panel/nav_panel.js @@ -33,11 +33,6 @@ library.add( ) const NavPanel = { - created () { - if (this.currentUser && this.currentUser.locked) { - this.$store.dispatch('startFetchingFollowRequests') - } - }, components: { TimelineMenuContent }, @@ -54,11 +49,13 @@ const NavPanel = { computed: { ...mapState({ currentUser: state => state.users.currentUser, - followRequestCount: state => state.api.followRequests.length, privateMode: state => state.instance.private, federating: state => state.instance.federating }), - ...mapGetters(['unreadAnnouncementCount']) + ...mapGetters(['unreadAnnouncementCount']), + followRequestCount () { + return this.$store.state.users.currentUser.follow_requests_count + } } } diff --git a/src/components/pinch_zoom/pinch_zoom.js b/src/components/pinch_zoom/pinch_zoom.js index 82670ddf..b7e8f673 100644 --- a/src/components/pinch_zoom/pinch_zoom.js +++ b/src/components/pinch_zoom/pinch_zoom.js @@ -1,4 +1,4 @@ -import PinchZoom from '@kazvmoe-infra/pinch-zoom-element' +import PinchZoom from '@floatingghost/pinch-zoom-element' export default { methods: { diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index c9f492c3..b7c66fc7 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -13,6 +13,7 @@ import suggestor from '../emoji_input/suggestor.js' import { mapGetters, mapState } from 'vuex' import Checkbox from '../checkbox/checkbox.vue' import Select from '../select/select.vue' +import iso6391 from 'iso-639-1' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -63,6 +64,7 @@ const PostStatusForm = { 'statusMediaDescriptions', 'statusScope', 'statusContentType', + 'statusLanguage', 'replyTo', 'quoteId', 'repliedUser', @@ -128,7 +130,7 @@ const PostStatusForm = { statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser) } - const { postContentType: contentType, sensitiveByDefault, sensitiveIfSubject } = this.$store.getters.mergedConfig + const { postContentType: contentType, sensitiveByDefault, sensitiveIfSubject, interfaceLanguage } = this.$store.getters.mergedConfig let statusParams = { spoilerText: this.subject || '', @@ -139,6 +141,7 @@ const PostStatusForm = { poll: {}, mediaDescriptions: {}, visibility: this.suggestedVisibility(), + language: interfaceLanguage, contentType } @@ -153,6 +156,7 @@ const PostStatusForm = { poll: this.statusPoll || {}, mediaDescriptions: this.statusMediaDescriptions || {}, visibility: this.statusScope || this.suggestedVisibility(), + language: this.statusLanguage || interfaceLanguage, contentType: statusContentType } } @@ -259,7 +263,10 @@ const PostStatusForm = { ...mapGetters(['mergedConfig']), ...mapState({ mobileLayout: state => state.interface.mobileLayout - }) + }), + isoLanguages () { + return iso6391.getAllCodes(); + } }, watch: { 'newStatus': { @@ -282,6 +289,7 @@ const PostStatusForm = { files: [], visibility: newStatus.visibility, contentType: newStatus.contentType, + language: newStatus.language, poll: {}, mediaDescriptions: {} } @@ -341,6 +349,7 @@ const PostStatusForm = { inReplyToStatusId: this.replyTo, quoteId: this.quoteId, contentType: newStatus.contentType, + language: newStatus.language, poll, idempotencyKey: this.idempotencyKey } @@ -375,6 +384,7 @@ const PostStatusForm = { inReplyToStatusId: this.replyTo, quoteId: this.quoteId, contentType: newStatus.contentType, + language: newStatus.language, poll: {}, preview: true }).then((data) => { diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index b6516585..02468f17 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -194,6 +194,23 @@ :on-scope-change="changeVis" /> +
+ +
+
  • + + {{ $t('settings.use_blurhash') }} + +
  • +
    + {{ $t('user_card.blocks_you') }} +