diff --git a/index.html b/index.html index fda91b0f..781b0ba3 100644 --- a/index.html +++ b/index.html @@ -4,8 +4,6 @@ Akkoma - - diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 36b087a5..b48bfb09 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -322,6 +322,8 @@ const getNodeInfo = async ({ store }) => { : federation.enabled }) + store.dispatch('setInstanceOption', { name: 'publicTimelineVisibility', value: metadata.publicTimelineVisibility }) + const accountActivationRequired = metadata.accountActivationRequired store.dispatch('setInstanceOption', { name: 'accountActivationRequired', value: accountActivationRequired }) @@ -396,9 +398,6 @@ const afterStoreSetup = async ({ store, i18n }) => { ]) // Start fetching things that don't need to block the UI - store.dispatch('fetchMutes') - store.dispatch('startFetchingAnnouncements') - store.dispatch('startFetchingReports') getTOS({ store }) getStickers({ store }) diff --git a/src/components/desktop_nav/desktop_nav.js b/src/components/desktop_nav/desktop_nav.js index f4900c38..4cf5a477 100644 --- a/src/components/desktop_nav/desktop_nav.js +++ b/src/components/desktop_nav/desktop_nav.js @@ -19,6 +19,7 @@ import { faInfoCircle, faUserTie } from '@fortawesome/free-solid-svg-icons' +import { mapState } from 'vuex' library.add( faSignInAlt, @@ -103,7 +104,10 @@ export default { }, showBubbleTimeline () { return this.$store.state.instance.localBubbleInstances.length > 0 - } + }, + ...mapState({ + publicTimelineVisibility: state => state.instance.publicTimelineVisibility, + }) }, methods: { scrollToTop () { diff --git a/src/components/desktop_nav/desktop_nav.vue b/src/components/desktop_nav/desktop_nav.vue index 92d3fa5b..a41e9972 100644 --- a/src/components/desktop_nav/desktop_nav.vue +++ b/src/components/desktop_nav/desktop_nav.vue @@ -46,6 +46,7 @@ { + 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_picker/emoji_picker.js b/src/components/emoji_picker/emoji_picker.js index fbd59946..c0391f6c 100644 --- a/src/components/emoji_picker/emoji_picker.js +++ b/src/components/emoji_picker/emoji_picker.js @@ -1,5 +1,6 @@ import { defineAsyncComponent } from 'vue' import Checkbox from '../checkbox/checkbox.vue' +import EmojiGrid from '../emoji_grid/emoji_grid.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faBoxOpen, @@ -14,13 +15,6 @@ library.add( faSmileBeam ) -// At widest, approximately 20 emoji are visible in a row, -// loading 3 rows, could be overkill for narrow picker -const LOAD_EMOJI_BY = 60 - -// When to start loading new batch emoji, in pixels -const LOAD_EMOJI_MARGIN = 64 - const EmojiPicker = { props: { enableStickerPicker: { @@ -39,16 +33,13 @@ const EmojiPicker = { keyword: '', activeGroup: 'standard', showingStickers: false, - groupsScrolledClass: 'scrolled-top', - keepOpen: false, - customEmojiBufferSlice: LOAD_EMOJI_BY, - customEmojiTimeout: null, - customEmojiLoadAllConfirmed: false + keepOpen: false } }, components: { StickerPicker: defineAsyncComponent(() => import('../sticker_picker/sticker_picker.vue')), - Checkbox + Checkbox, + EmojiGrid }, methods: { onStickerUploaded (e) { @@ -60,12 +51,7 @@ const EmojiPicker = { onEmoji (emoji) { 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) + this.$store.commit('emojiUsed', emoji) }, onWheel (e) { e.preventDefault() @@ -74,68 +60,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 @@ -151,13 +81,6 @@ const EmojiPicker = { }) } }, - watch: { - keyword () { - this.customEmojiLoadAllConfirmed = false - this.onScroll() - this.startEmojiLoad(true) - } - }, computed: { activeGroupView () { return this.showingStickers ? '' : this.activeGroup @@ -173,10 +96,8 @@ const EmojiPicker = { this.$store.state.instance.customEmoji || [] ) }, - customEmojiBuffer () { - return this.filteredEmoji.slice(0, this.customEmojiBufferSlice) - }, emojis () { + const recentEmojis = this.$store.getters.recentEmojis const standardEmojis = this.$store.state.instance.emoji || [] const customEmojis = this.sortedEmoji const emojiPacks = [] @@ -189,6 +110,15 @@ const EmojiPicker = { }) }) return [ + { + id: 'recent', + text: this.$t('emoji.recent'), + first: { + imageUrl: '', + replacement: 'đź•’', + }, + emojis: this.filterByKeyword(recentEmojis) + }, { id: 'standard', text: this.$t('emoji.unicode'), diff --git a/src/components/emoji_picker/emoji_picker.scss b/src/components/emoji_picker/emoji_picker.scss index 82fc831c..6ce8cbd8 100644 --- a/src/components/emoji_picker/emoji_picker.scss +++ b/src/components/emoji_picker/emoji_picker.scss @@ -85,10 +85,6 @@ flex-grow: 1; } - .emoji-groups { - min-height: 200px; - } - .additional-tabs { border-left: 1px solid; border-left-color: $fallback--icon; @@ -167,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 3ec694f0..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 }} - - - -
-
+
{ + const allEmojis = state.instance.emoji.concat(state.instance.customEmoji) + return allEmojis.find(emoji => emoji.replacement === replacement) +} + const EmojiReactions = { name: 'EmojiReactions', components: { @@ -54,6 +59,8 @@ const EmojiReactions = { }, reactWith (emoji) { this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) + const emojiObject = findEmojiByReplacement(this.$store.state, emoji) + this.$store.commit('emojiUsed', emojiObject) }, unreact (emoji) { this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji }) diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue index d9c568f6..599611b2 100644 --- a/src/components/emoji_reactions/emoji_reactions.vue +++ b/src/components/emoji_reactions/emoji_reactions.vue @@ -18,7 +18,7 @@ :src="reaction.url" :title="reaction.name" class="reaction-emoji" - width="2.55em" + height="2.55em" > {{ reaction.count }} @@ -49,6 +49,7 @@ display: flex; margin-top: 0.25em; flex-wrap: wrap; + container-type: inline-size; } .unicode-emoji { @@ -64,7 +65,9 @@ justify-content: center; box-sizing: border-box; .reaction-emoji { - width: 2.55em !important; + width: auto; + max-width: 96cqw; + 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 5eb98264..14c605fb 100644 --- a/src/components/extra_buttons/extra_buttons.js +++ b/src/components/extra_buttons/extra_buttons.js @@ -15,6 +15,7 @@ import { faBookmark as faBookmarkReg, faFlag } from '@fortawesome/free-regular-svg-icons' +import { mapState } from 'vuex' library.add( faEllipsisH, @@ -191,7 +192,7 @@ const ExtraButtons = { isEdited () { return this.status.edited_at !== null }, - editingAvailable () { return this.$store.state.instance.editingAvailable } + editingAvailable () { return this.$store.state.instance.editingAvailable }, } } 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/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js index af165d47..991cee1c 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 + federating: state => state.instance.federating, }), - ...mapGetters(['unreadAnnouncementCount']) + ...mapGetters(['unreadAnnouncementCount']), + followRequestCount () { + return this.$store.state.users.currentUser.follow_requests_count + } } } diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index b7c66fc7..f7fef499 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -54,6 +54,14 @@ const pxStringToNumber = (str) => { return Number(str.substring(0, str.length - 2)) } +const deleteDraft = (draftKey) => { + const draftData = JSON.parse(localStorage.getItem('drafts') || '{}'); + + delete draftData[draftKey]; + + localStorage.setItem('drafts', JSON.stringify(draftData)); +} + const PostStatusForm = { props: [ 'statusId', @@ -161,6 +169,34 @@ const PostStatusForm = { } } + let draftKey = 'status'; + if (this.replyTo) { + draftKey = 'reply:' + this.replyTo; + } else if (this.quoteId) { + draftKey = 'quote:' + this.quoteId; + } + + const draft = JSON.parse(localStorage.getItem('drafts') || '{}')[draftKey]; + + if (draft) { + statusParams = { + spoilerText: draft.data.spoilerText, + status: draft.data.status, + sensitiveIfSubject, + nsfw: draft.data.nsfw, + files: draft.data.files, + poll: draft.data.poll, + mediaDescriptions: draft.data.mediaDescriptions, + visibility: draft.data.visibility, + language: draft.data.language, + contentType: draft.data.contentType + } + + if (draft.data.poll) { + this.togglePollForm(); + } + } + return { dropFiles: [], uploadingFiles: false, @@ -280,6 +316,7 @@ const PostStatusForm = { statusChanged () { this.autoPreview() this.updateIdempotencyKey() + this.saveDraft() }, clearStatus () { const newStatus = this.newStatus @@ -401,8 +438,38 @@ const PostStatusForm = { }).finally(() => { this.previewLoading = false }) + + let draftKey = 'status'; + if (this.replyTo) { + draftKey = 'reply:' + this.replyTo; + } else if (this.quoteId) { + draftKey = 'quote:' + this.quoteId; + } + deleteDraft(draftKey) }, debouncePreviewStatus: debounce(function () { this.previewStatus() }, 500), + saveDraft() { + const draftData = JSON.parse(localStorage.getItem('drafts') || '{}'); + + let draftKey = 'status'; + if (this.replyTo) { + draftKey = 'reply:' + this.replyTo; + } else if (this.quoteId) { + draftKey = 'quote:' + this.quoteId; + } + + if (this.newStatus.status || this.newStatus.spoilerText || this.newStatus.files.length > 0 || this.newStatus.poll.length > 0) { + draftData[draftKey] = { + updatedAt: new Date(), + data: this.newStatus, + }; + + localStorage.setItem('drafts', JSON.stringify(draftData)); + + } else { + deleteDraft(draftKey); + } + }, autoPreview () { if (!this.preview) return this.previewLoading = true diff --git a/src/components/rich_content/rich_content.scss b/src/components/rich_content/rich_content.scss index db08ef1e..63df7d74 100644 --- a/src/components/rich_content/rich_content.scss +++ b/src/components/rich_content/rich_content.scss @@ -50,7 +50,6 @@ .emoji { display: inline-block; - width: var(--emoji-size, 32px); height: var(--emoji-size, 32px); } diff --git a/src/components/status_body/status_body.scss b/src/components/status_body/status_body.scss index 230a27ac..d618f65e 100644 --- a/src/components/status_body/status_body.scss +++ b/src/components/status_body/status_body.scss @@ -22,21 +22,18 @@ ._mfm_x2_ { .emoji { - width: 100px; height: 100px; } } ._mfm_x3_ { .emoji { - width: 150px; height: 150px; } } ._mfm_x4_ { .emoji { - width: 200px; height: 200px; } } diff --git a/src/components/status_content/status_content.vue b/src/components/status_content/status_content.vue index ab13141e..62acf5ac 100644 --- a/src/components/status_content/status_content.vue +++ b/src/components/status_content/status_content.vue @@ -71,7 +71,7 @@ img, video { &.emoji { - width: 50px; + max-width: 100%; height: 50px; } } @@ -89,7 +89,6 @@ animation: none !important; } .emoji { - width: 32px !important; height: 32px !important; } } diff --git a/src/components/timeline_menu/timeline_menu_content.js b/src/components/timeline_menu/timeline_menu_content.js index df15030b..ab277d74 100644 --- a/src/components/timeline_menu/timeline_menu_content.js +++ b/src/components/timeline_menu/timeline_menu_content.js @@ -24,7 +24,8 @@ const TimelineMenuContent = { currentUser: state => state.users.currentUser, privateMode: state => state.instance.private, federating: state => state.instance.federating, - showBubbleTimeline: state => (state.instance.localBubbleInstances.length > 0) + showBubbleTimeline: state => (state.instance.localBubbleInstances.length > 0), + publicTimelineVisibility: state => state.instance.publicTimelineVisibility, }) } } diff --git a/src/components/timeline_menu/timeline_menu_content.vue b/src/components/timeline_menu/timeline_menu_content.vue index 27aece22..0144f2fd 100644 --- a/src/components/timeline_menu/timeline_menu_content.vue +++ b/src/components/timeline_menu/timeline_menu_content.vue @@ -32,7 +32,7 @@ >{{ $t("nav.bubble_timeline") }} -
  • +
  • {{ $t("nav.public_tl") }}
  • -
  • +
  • {{ $t("nav.twkn") }} +
  • diff --git a/src/components/timeline_menu_tabs/timeline_menu_tabs.js b/src/components/timeline_menu_tabs/timeline_menu_tabs.js index 40192b39..74ed61aa 100644 --- a/src/components/timeline_menu_tabs/timeline_menu_tabs.js +++ b/src/components/timeline_menu_tabs/timeline_menu_tabs.js @@ -4,6 +4,7 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { faChevronDown } from '@fortawesome/free-solid-svg-icons' +import { mapState } from 'vuex' library.add(faChevronDown) @@ -41,7 +42,11 @@ const TimelineMenuTabs = { }, privateMode () { return this.$store.state.instance.private - } + }, + ...mapState({ + currentUser: state => state.users.currentUser, + publicTimelineVisibility: state => state.instance.publicTimelineVisibility, + }) }, methods: { timelineName () { diff --git a/src/components/timeline_menu_tabs/timeline_menu_tabs.vue b/src/components/timeline_menu_tabs/timeline_menu_tabs.vue index f017fa1f..e922ffcc 100644 --- a/src/components/timeline_menu_tabs/timeline_menu_tabs.vue +++ b/src/components/timeline_menu_tabs/timeline_menu_tabs.vue @@ -18,6 +18,7 @@
  • +
    + {{ $t('user_card.blocks_you') }} +