forked from AkkomaGang/akkoma-fe
Merge branch 'develop' of git.pleroma.social:pleroma/pleroma-fe into develop
This commit is contained in:
commit
c7c4e2e03e
40 changed files with 585 additions and 214 deletions
|
@ -7,6 +7,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
### Added
|
### Added
|
||||||
- Added a quick settings to timeline header for easier access
|
- Added a quick settings to timeline header for easier access
|
||||||
- Added option to mark posts as sensitive by default
|
- Added option to mark posts as sensitive by default
|
||||||
|
- Added quick filters for notifications
|
||||||
|
|
||||||
|
|
||||||
## [2.3.0] - 2021-03-01
|
## [2.3.0] - 2021-03-01
|
||||||
### Fixed
|
### Fixed
|
||||||
|
@ -20,6 +22,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Display 'people voted' instead of 'votes' for multi-choice polls
|
- Display 'people voted' instead of 'votes' for multi-choice polls
|
||||||
|
- Changed the "Timelines" link in side panel to toggle show all timeline options inside the panel
|
||||||
|
- Renamed "Timeline" to "Home Timeline" to be more clear
|
||||||
- Optimized chat to not get horrible performance after keeping the same chat open for a long time
|
- Optimized chat to not get horrible performance after keeping the same chat open for a long time
|
||||||
- When opening emoji picker or react picker, it automatically focuses the search field
|
- When opening emoji picker or react picker, it automatically focuses the search field
|
||||||
- Language picker now uses native language names
|
- Language picker now uses native language names
|
||||||
|
@ -28,6 +32,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Added reason field for registration when approval is required
|
- Added reason field for registration when approval is required
|
||||||
- Group staff members by role in the About page
|
- Group staff members by role in the About page
|
||||||
|
|
||||||
|
|
||||||
## [2.2.3] - 2021-01-18
|
## [2.2.3] - 2021-01-18
|
||||||
### Added
|
### Added
|
||||||
- Added Report button to status ellipsis menu for easier reporting
|
- Added Report button to status ellipsis menu for easier reporting
|
||||||
|
@ -35,6 +40,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
### Fixed
|
### Fixed
|
||||||
- Follows/Followers tabs on user profiles now display the content properly.
|
- Follows/Followers tabs on user profiles now display the content properly.
|
||||||
- Handle punycode in screen names
|
- Handle punycode in screen names
|
||||||
|
- Fixed local dev mode having non-functional websockets in some cases
|
||||||
|
- Show notices for websocket events (errors, abnormal closures, reconnections)
|
||||||
|
- Fix not being able to re-enable websocket until page refresh
|
||||||
|
- Fix annoying issue where timeline might have few posts when streaming is enabled
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Don't filter own posts when they hit your wordfilter
|
- Don't filter own posts when they hit your wordfilter
|
||||||
|
|
|
@ -3,6 +3,11 @@ const path = require('path')
|
||||||
let settings = {}
|
let settings = {}
|
||||||
try {
|
try {
|
||||||
settings = require('./local.json')
|
settings = require('./local.json')
|
||||||
|
if (settings.target && settings.target.endsWith('/')) {
|
||||||
|
// replacing trailing slash since it can conflict with some apis
|
||||||
|
// and that's how actual BE reports its url
|
||||||
|
settings.target = settings.target.replace(/\/$/, '')
|
||||||
|
}
|
||||||
console.log('Using local dev server settings (/config/local.json):')
|
console.log('Using local dev server settings (/config/local.json):')
|
||||||
console.log(JSON.stringify(settings, null, 2))
|
console.log(JSON.stringify(settings, null, 2))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -34,7 +34,6 @@
|
||||||
"punycode.js": "^2.1.0",
|
"punycode.js": "^2.1.0",
|
||||||
"v-click-outside": "^2.1.1",
|
"v-click-outside": "^2.1.1",
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue-chat-scroll": "^1.2.1",
|
|
||||||
"vue-i18n": "^7.3.2",
|
"vue-i18n": "^7.3.2",
|
||||||
"vue-router": "^3.0.1",
|
"vue-router": "^3.0.1",
|
||||||
"vue-template-compiler": "^2.6.11",
|
"vue-template-compiler": "^2.6.11",
|
||||||
|
|
|
@ -706,6 +706,15 @@ nav {
|
||||||
color: var(--alertWarningPanelText, $fallback--text);
|
color: var(--alertWarningPanelText, $fallback--text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
background-color: var(--alertSuccess, $fallback--alertWarning);
|
||||||
|
color: var(--alertSuccessText, $fallback--text);
|
||||||
|
|
||||||
|
.panel-heading & {
|
||||||
|
color: var(--alertSuccessPanelText, $fallback--text);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.faint {
|
.faint {
|
||||||
|
|
|
@ -35,6 +35,18 @@ const chatPanel = {
|
||||||
userProfileLink (user) {
|
userProfileLink (user) {
|
||||||
return generateProfileLink(user.id, user.username, this.$store.state.instance.restrictedNicknames)
|
return generateProfileLink(user.id, user.username, this.$store.state.instance.restrictedNicknames)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
messages (newVal) {
|
||||||
|
const scrollEl = this.$el.querySelector('.chat-window')
|
||||||
|
if (!scrollEl) return
|
||||||
|
if (scrollEl.scrollTop + scrollEl.offsetHeight + 20 > scrollEl.scrollHeight) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (!scrollEl) return
|
||||||
|
scrollEl.scrollTop = scrollEl.scrollHeight - scrollEl.offsetHeight
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,17 +10,15 @@
|
||||||
@click.stop.prevent="togglePanel"
|
@click.stop.prevent="togglePanel"
|
||||||
>
|
>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span>{{ $t('shoutbox.title') }}</span>
|
{{ $t('shoutbox.title') }}
|
||||||
<FAIcon
|
<FAIcon
|
||||||
v-if="floating"
|
v-if="floating"
|
||||||
icon="times"
|
icon="times"
|
||||||
|
class="close-icon"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="chat-window">
|
||||||
v-chat-scroll
|
|
||||||
class="chat-window"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
v-for="message in messages"
|
v-for="message in messages"
|
||||||
:key="message.id"
|
:key="message.id"
|
||||||
|
@ -94,6 +92,13 @@
|
||||||
.icon {
|
.icon {
|
||||||
color: $fallback--text;
|
color: $fallback--text;
|
||||||
color: var(--text, $fallback--text);
|
color: var(--text, $fallback--text);
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.global-success {
|
||||||
|
background-color: var(--alertPopupSuccess, $fallback--cGreen);
|
||||||
|
color: var(--alertPopupSuccessText, $fallback--text);
|
||||||
|
.svg-inline--fa {
|
||||||
|
color: var(--alertPopupSuccessText, $fallback--text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.global-info {
|
.global-info {
|
||||||
background-color: var(--alertPopupNeutral, $fallback--fg);
|
background-color: var(--alertPopupNeutral, $fallback--fg);
|
||||||
color: var(--alertPopupNeutralText, $fallback--text);
|
color: var(--alertPopupNeutralText, $fallback--text);
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
import { faChevronDown } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
import DialogModal from '../dialog_modal/dialog_modal.vue'
|
import DialogModal from '../dialog_modal/dialog_modal.vue'
|
||||||
import Popover from '../popover/popover.vue'
|
import Popover from '../popover/popover.vue'
|
||||||
|
|
||||||
|
library.add(faChevronDown)
|
||||||
|
|
||||||
const FORCE_NSFW = 'mrf_tag:media-force-nsfw'
|
const FORCE_NSFW = 'mrf_tag:media-force-nsfw'
|
||||||
const STRIP_MEDIA = 'mrf_tag:media-strip'
|
const STRIP_MEDIA = 'mrf_tag:media-strip'
|
||||||
const FORCE_UNLISTED = 'mrf_tag:force-unlisted'
|
const FORCE_UNLISTED = 'mrf_tag:force-unlisted'
|
||||||
|
|
|
@ -124,10 +124,11 @@
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
class="btn button-default btn-block"
|
class="btn button-default btn-block moderation-tools-button"
|
||||||
:class="{ toggled }"
|
:class="{ toggled }"
|
||||||
>
|
>
|
||||||
{{ $t('user_card.admin_menu.moderation') }}
|
{{ $t('user_card.admin_menu.moderation') }}
|
||||||
|
<FAIcon icon="chevron-down" />
|
||||||
</button>
|
</button>
|
||||||
</Popover>
|
</Popover>
|
||||||
<portal to="modal">
|
<portal to="modal">
|
||||||
|
@ -170,4 +171,10 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.moderation-tools-button {
|
||||||
|
svg,i {
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { timelineNames } from '../timeline_menu/timeline_menu.js'
|
import TimelineMenuContent from '../timeline_menu/timeline_menu_content.vue'
|
||||||
import { mapState, mapGetters } from 'vuex'
|
import { mapState, mapGetters } from 'vuex'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
@ -7,10 +7,12 @@ import {
|
||||||
faGlobe,
|
faGlobe,
|
||||||
faBookmark,
|
faBookmark,
|
||||||
faEnvelope,
|
faEnvelope,
|
||||||
faHome,
|
faChevronDown,
|
||||||
|
faChevronUp,
|
||||||
faComments,
|
faComments,
|
||||||
faBell,
|
faBell,
|
||||||
faInfoCircle
|
faInfoCircle,
|
||||||
|
faStream
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
|
@ -18,10 +20,12 @@ library.add(
|
||||||
faGlobe,
|
faGlobe,
|
||||||
faBookmark,
|
faBookmark,
|
||||||
faEnvelope,
|
faEnvelope,
|
||||||
faHome,
|
faChevronDown,
|
||||||
|
faChevronUp,
|
||||||
faComments,
|
faComments,
|
||||||
faBell,
|
faBell,
|
||||||
faInfoCircle
|
faInfoCircle,
|
||||||
|
faStream
|
||||||
)
|
)
|
||||||
|
|
||||||
const NavPanel = {
|
const NavPanel = {
|
||||||
|
@ -30,16 +34,20 @@ const NavPanel = {
|
||||||
this.$store.dispatch('startFetchingFollowRequests')
|
this.$store.dispatch('startFetchingFollowRequests')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
components: {
|
||||||
|
TimelineMenuContent
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
showTimelines: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleTimelines () {
|
||||||
|
this.showTimelines = !this.showTimelines
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
onTimelineRoute () {
|
|
||||||
return !!timelineNames()[this.$route.name]
|
|
||||||
},
|
|
||||||
timelinesRoute () {
|
|
||||||
if (this.$store.state.interface.lastTimeline) {
|
|
||||||
return this.$store.state.interface.lastTimeline
|
|
||||||
}
|
|
||||||
return this.currentUser ? 'friends' : 'public-timeline'
|
|
||||||
},
|
|
||||||
...mapState({
|
...mapState({
|
||||||
currentUser: state => state.users.currentUser,
|
currentUser: state => state.users.currentUser,
|
||||||
followRequestCount: state => state.api.followRequests.length,
|
followRequestCount: state => state.api.followRequests.length,
|
||||||
|
|
|
@ -3,19 +3,33 @@
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<ul>
|
<ul>
|
||||||
<li v-if="currentUser || !privateMode">
|
<li v-if="currentUser || !privateMode">
|
||||||
<router-link
|
<button
|
||||||
:to="{ name: timelinesRoute }"
|
class="button-unstyled menu-item"
|
||||||
:class="onTimelineRoute && 'router-link-active'"
|
@click="toggleTimelines"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
fixed-width
|
fixed-width
|
||||||
class="fa-scale-110"
|
class="fa-scale-110"
|
||||||
icon="home"
|
icon="stream"
|
||||||
/>{{ $t("nav.timelines") }}
|
/>{{ $t("nav.timelines") }}
|
||||||
</router-link>
|
<FAIcon
|
||||||
|
class="timelines-chevron"
|
||||||
|
fixed-width
|
||||||
|
:icon="showTimelines ? 'chevron-up' : 'chevron-down'"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
v-show="showTimelines"
|
||||||
|
class="timelines-background"
|
||||||
|
>
|
||||||
|
<TimelineMenuContent class="timelines" />
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="currentUser">
|
<li v-if="currentUser">
|
||||||
<router-link :to="{ name: 'interactions', params: { username: currentUser.screen_name } }">
|
<router-link
|
||||||
|
class="menu-item"
|
||||||
|
:to="{ name: 'interactions', params: { username: currentUser.screen_name } }"
|
||||||
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
fixed-width
|
fixed-width
|
||||||
class="fa-scale-110"
|
class="fa-scale-110"
|
||||||
|
@ -24,7 +38,10 @@
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="currentUser && pleromaChatMessagesAvailable">
|
<li v-if="currentUser && pleromaChatMessagesAvailable">
|
||||||
<router-link :to="{ name: 'chats', params: { username: currentUser.screen_name } }">
|
<router-link
|
||||||
|
class="menu-item"
|
||||||
|
:to="{ name: 'chats', params: { username: currentUser.screen_name } }"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-if="unreadChatCount"
|
v-if="unreadChatCount"
|
||||||
class="badge badge-notification"
|
class="badge badge-notification"
|
||||||
|
@ -39,7 +56,10 @@
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="currentUser && currentUser.locked">
|
<li v-if="currentUser && currentUser.locked">
|
||||||
<router-link :to="{ name: 'friend-requests' }">
|
<router-link
|
||||||
|
class="menu-item"
|
||||||
|
:to="{ name: 'friend-requests' }"
|
||||||
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
fixed-width
|
fixed-width
|
||||||
class="fa-scale-110"
|
class="fa-scale-110"
|
||||||
|
@ -54,7 +74,10 @@
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<router-link :to="{ name: 'about' }">
|
<router-link
|
||||||
|
class="menu-item"
|
||||||
|
:to="{ name: 'about' }"
|
||||||
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
fixed-width
|
fixed-width
|
||||||
class="fa-scale-110"
|
class="fa-scale-110"
|
||||||
|
@ -91,14 +114,14 @@
|
||||||
border-color: var(--border, $fallback--border);
|
border-color: var(--border, $fallback--border);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
&:first-child a {
|
&:first-child .menu-item {
|
||||||
border-top-right-radius: $fallback--panelRadius;
|
border-top-right-radius: $fallback--panelRadius;
|
||||||
border-top-right-radius: var(--panelRadius, $fallback--panelRadius);
|
border-top-right-radius: var(--panelRadius, $fallback--panelRadius);
|
||||||
border-top-left-radius: $fallback--panelRadius;
|
border-top-left-radius: $fallback--panelRadius;
|
||||||
border-top-left-radius: var(--panelRadius, $fallback--panelRadius);
|
border-top-left-radius: var(--panelRadius, $fallback--panelRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-child a {
|
&:last-child .menu-item {
|
||||||
border-bottom-right-radius: $fallback--panelRadius;
|
border-bottom-right-radius: $fallback--panelRadius;
|
||||||
border-bottom-right-radius: var(--panelRadius, $fallback--panelRadius);
|
border-bottom-right-radius: var(--panelRadius, $fallback--panelRadius);
|
||||||
border-bottom-left-radius: $fallback--panelRadius;
|
border-bottom-left-radius: $fallback--panelRadius;
|
||||||
|
@ -110,13 +133,15 @@
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
.menu-item {
|
||||||
display: block;
|
display: block;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
align-items: stretch;
|
|
||||||
height: 3.5em;
|
height: 3.5em;
|
||||||
line-height: 3.5em;
|
line-height: 3.5em;
|
||||||
padding: 0 1em;
|
padding: 0 1em;
|
||||||
|
width: 100%;
|
||||||
|
color: $fallback--link;
|
||||||
|
color: var(--link, $fallback--link);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: $fallback--lightBg;
|
background-color: $fallback--lightBg;
|
||||||
|
@ -146,6 +171,25 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.timelines-chevron {
|
||||||
|
margin-left: 0.8em;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timelines-background {
|
||||||
|
padding: 0 0 0 0.6em;
|
||||||
|
background-color: $fallback--lightBg;
|
||||||
|
background-color: var(--selectedMenu, $fallback--lightBg);
|
||||||
|
border-top: 1px solid;
|
||||||
|
border-color: $fallback--border;
|
||||||
|
border-color: var(--border, $fallback--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.timelines {
|
||||||
|
background-color: $fallback--bg;
|
||||||
|
background-color: var(--bg, $fallback--bg);
|
||||||
|
}
|
||||||
|
|
||||||
.fa-scale-110 {
|
.fa-scale-110 {
|
||||||
margin-right: 0.8em;
|
margin-right: 0.8em;
|
||||||
}
|
}
|
||||||
|
|
122
src/components/notifications/notification_filters.vue
Normal file
122
src/components/notifications/notification_filters.vue
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
<template>
|
||||||
|
<Popover
|
||||||
|
trigger="click"
|
||||||
|
class="NotificationFilters"
|
||||||
|
placement="bottom"
|
||||||
|
:bound-to="{ x: 'container' }"
|
||||||
|
>
|
||||||
|
<template
|
||||||
|
v-slot:content
|
||||||
|
>
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item"
|
||||||
|
@click="toggleNotificationFilter('likes')"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="menu-checkbox"
|
||||||
|
:class="{ 'menu-checkbox-checked': filters.likes }"
|
||||||
|
/>{{ $t('settings.notification_visibility_likes') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item"
|
||||||
|
@click="toggleNotificationFilter('repeats')"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="menu-checkbox"
|
||||||
|
:class="{ 'menu-checkbox-checked': filters.repeats }"
|
||||||
|
/>{{ $t('settings.notification_visibility_repeats') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item"
|
||||||
|
@click="toggleNotificationFilter('follows')"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="menu-checkbox"
|
||||||
|
:class="{ 'menu-checkbox-checked': filters.follows }"
|
||||||
|
/>{{ $t('settings.notification_visibility_follows') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item"
|
||||||
|
@click="toggleNotificationFilter('mentions')"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="menu-checkbox"
|
||||||
|
:class="{ 'menu-checkbox-checked': filters.mentions }"
|
||||||
|
/>{{ $t('settings.notification_visibility_mentions') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item"
|
||||||
|
@click="toggleNotificationFilter('emojiReactions')"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="menu-checkbox"
|
||||||
|
:class="{ 'menu-checkbox-checked': filters.emojiReactions }"
|
||||||
|
/>{{ $t('settings.notification_visibility_emoji_reactions') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item"
|
||||||
|
@click="toggleNotificationFilter('moves')"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="menu-checkbox"
|
||||||
|
:class="{ 'menu-checkbox-checked': filters.moves }"
|
||||||
|
/>{{ $t('settings.notification_visibility_moves') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-slot:trigger>
|
||||||
|
<FAIcon icon="filter" />
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Popover from '../popover/popover.vue'
|
||||||
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
import { faFilter } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
library.add(
|
||||||
|
faFilter
|
||||||
|
)
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { Popover },
|
||||||
|
computed: {
|
||||||
|
filters () {
|
||||||
|
return this.$store.getters.mergedConfig.notificationVisibility
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleNotificationFilter (type) {
|
||||||
|
this.$store.dispatch('setOption', {
|
||||||
|
name: 'notificationVisibility',
|
||||||
|
value: {
|
||||||
|
...this.filters,
|
||||||
|
[type]: !this.filters[type]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
|
||||||
|
.NotificationFilters {
|
||||||
|
align-self: stretch;
|
||||||
|
|
||||||
|
> button {
|
||||||
|
font-size: 1.2em;
|
||||||
|
padding-left: 0.7em;
|
||||||
|
padding-right: 0.2em;
|
||||||
|
line-height: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -1,5 +1,6 @@
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
import Notification from '../notification/notification.vue'
|
import Notification from '../notification/notification.vue'
|
||||||
|
import NotificationFilters from './notification_filters.vue'
|
||||||
import notificationsFetcher from '../../services/notifications_fetcher/notifications_fetcher.service.js'
|
import notificationsFetcher from '../../services/notifications_fetcher/notifications_fetcher.service.js'
|
||||||
import {
|
import {
|
||||||
notificationsFromStore,
|
notificationsFromStore,
|
||||||
|
@ -17,6 +18,10 @@ library.add(
|
||||||
const DEFAULT_SEEN_TO_DISPLAY_COUNT = 30
|
const DEFAULT_SEEN_TO_DISPLAY_COUNT = 30
|
||||||
|
|
||||||
const Notifications = {
|
const Notifications = {
|
||||||
|
components: {
|
||||||
|
Notification,
|
||||||
|
NotificationFilters
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
// Disables display of panel header
|
// Disables display of panel header
|
||||||
noHeading: Boolean,
|
noHeading: Boolean,
|
||||||
|
@ -35,11 +40,6 @@ const Notifications = {
|
||||||
seenToDisplayCount: DEFAULT_SEEN_TO_DISPLAY_COUNT
|
seenToDisplayCount: DEFAULT_SEEN_TO_DISPLAY_COUNT
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
|
||||||
const store = this.$store
|
|
||||||
const credentials = store.state.users.currentUser.credentials
|
|
||||||
notificationsFetcher.fetchAndUpdate({ store, credentials })
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
mainClass () {
|
mainClass () {
|
||||||
return this.minimalMode ? '' : 'panel panel-default'
|
return this.minimalMode ? '' : 'panel panel-default'
|
||||||
|
@ -70,9 +70,6 @@ const Notifications = {
|
||||||
},
|
},
|
||||||
...mapGetters(['unreadChatCount'])
|
...mapGetters(['unreadChatCount'])
|
||||||
},
|
},
|
||||||
components: {
|
|
||||||
Notification
|
|
||||||
},
|
|
||||||
watch: {
|
watch: {
|
||||||
unseenCountTitle (count) {
|
unseenCountTitle (count) {
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
>
|
>
|
||||||
{{ $t('notifications.read') }}
|
{{ $t('notifications.read') }}
|
||||||
</button>
|
</button>
|
||||||
|
<NotificationFilters />
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn button-default btn-block"
|
class="btn button-default btn-block"
|
||||||
>
|
>
|
||||||
{{ $t('general.submit') }}
|
{{ $t('settings.save') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -56,6 +56,9 @@ const Popover = {
|
||||||
// Popover will be anchored around this element, trigger ref is the container, so
|
// Popover will be anchored around this element, trigger ref is the container, so
|
||||||
// its children are what are inside the slot. Expect only one slot="trigger".
|
// its children are what are inside the slot. Expect only one slot="trigger".
|
||||||
const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el
|
const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el
|
||||||
|
// SVGs don't have offsetWidth/Height, use fallback
|
||||||
|
const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth
|
||||||
|
const anchorHeight = anchorEl.offsetHeight || anchorEl.clientHeight
|
||||||
const screenBox = anchorEl.getBoundingClientRect()
|
const screenBox = anchorEl.getBoundingClientRect()
|
||||||
// Screen position of the origin point for popover
|
// Screen position of the origin point for popover
|
||||||
const origin = { x: screenBox.left + screenBox.width * 0.5, y: screenBox.top }
|
const origin = { x: screenBox.left + screenBox.width * 0.5, y: screenBox.top }
|
||||||
|
@ -114,11 +117,11 @@ const Popover = {
|
||||||
|
|
||||||
const yOffset = (this.offset && this.offset.y) || 0
|
const yOffset = (this.offset && this.offset.y) || 0
|
||||||
const translateY = usingTop
|
const translateY = usingTop
|
||||||
? -anchorEl.offsetHeight + vPadding - yOffset - content.offsetHeight
|
? -anchorHeight + vPadding - yOffset - content.offsetHeight
|
||||||
: yOffset
|
: yOffset
|
||||||
|
|
||||||
const xOffset = (this.offset && this.offset.x) || 0
|
const xOffset = (this.offset && this.offset.x) || 0
|
||||||
const translateX = (anchorEl.offsetWidth * 0.5) - content.offsetWidth * 0.5 + horizOffset + xOffset
|
const translateX = anchorWidth * 0.5 - content.offsetWidth * 0.5 + horizOffset + xOffset
|
||||||
|
|
||||||
// Note, separate translateX and translateY avoids blurry text on chromium,
|
// Note, separate translateX and translateY avoids blurry text on chromium,
|
||||||
// single translate or translate3d resulted in blurry text.
|
// single translate or translate3d resulted in blurry text.
|
||||||
|
|
|
@ -272,7 +272,7 @@
|
||||||
disabled
|
disabled
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
>
|
>
|
||||||
{{ $t('general.submit') }}
|
{{ $t('post_status.post') }}
|
||||||
</button>
|
</button>
|
||||||
<!-- touchstart is used to keep the OSK at the same position after a message send -->
|
<!-- touchstart is used to keep the OSK at the same position after a message send -->
|
||||||
<button
|
<button
|
||||||
|
@ -282,7 +282,7 @@
|
||||||
@touchstart.stop.prevent="postStatus($event, newStatus)"
|
@touchstart.stop.prevent="postStatus($event, newStatus)"
|
||||||
@click.stop.prevent="postStatus($event, newStatus)"
|
@click.stop.prevent="postStatus($event, newStatus)"
|
||||||
>
|
>
|
||||||
{{ $t('general.submit') }}
|
{{ $t('post_status.post') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -230,7 +230,7 @@
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
>
|
>
|
||||||
{{ $t('general.submit') }}
|
{{ $t('registration.register') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
@click="updateNotificationSettings"
|
@click="updateNotificationSettings"
|
||||||
>
|
>
|
||||||
{{ $t('general.submit') }}
|
{{ $t('settings.save') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -153,7 +153,7 @@
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
@click="updateProfile"
|
@click="updateProfile"
|
||||||
>
|
>
|
||||||
{{ $t('general.submit') }}
|
{{ $t('settings.save') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
|
@ -227,7 +227,7 @@
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
@click="submitBanner(banner)"
|
@click="submitBanner(banner)"
|
||||||
>
|
>
|
||||||
{{ $t('general.submit') }}
|
{{ $t('settings.save') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
|
@ -266,7 +266,7 @@
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
@click="submitBackground(background)"
|
@click="submitBackground(background)"
|
||||||
>
|
>
|
||||||
{{ $t('general.submit') }}
|
{{ $t('settings.save') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
@click="changeEmail"
|
@click="changeEmail"
|
||||||
>
|
>
|
||||||
{{ $t('general.submit') }}
|
{{ $t('settings.save') }}
|
||||||
</button>
|
</button>
|
||||||
<p v-if="changedEmail">
|
<p v-if="changedEmail">
|
||||||
{{ $t('settings.changed_email') }}
|
{{ $t('settings.changed_email') }}
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
@click="changePassword"
|
@click="changePassword"
|
||||||
>
|
>
|
||||||
{{ $t('general.submit') }}
|
{{ $t('settings.save') }}
|
||||||
</button>
|
</button>
|
||||||
<p v-if="changedPassword">
|
<p v-if="changedPassword">
|
||||||
{{ $t('settings.changed_password') }}
|
{{ $t('settings.changed_password') }}
|
||||||
|
@ -133,7 +133,7 @@
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
@click="confirmDelete"
|
@click="confirmDelete"
|
||||||
>
|
>
|
||||||
{{ $t('general.submit') }}
|
{{ $t('settings.save') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import Popover from '../popover/popover.vue'
|
import Popover from '../popover/popover.vue'
|
||||||
import BooleanSetting from '../settings_modal/helpers/boolean_setting.vue'
|
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons'
|
import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
@ -12,8 +11,7 @@ library.add(
|
||||||
|
|
||||||
const TimelineQuickSettings = {
|
const TimelineQuickSettings = {
|
||||||
components: {
|
components: {
|
||||||
Popover,
|
Popover
|
||||||
BooleanSetting
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setReplyVisibility (visibility) {
|
setReplyVisibility (visibility) {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
slot="content"
|
slot="content"
|
||||||
class="timeline-settings-menu dropdown-menu"
|
class="dropdown-menu"
|
||||||
>
|
>
|
||||||
<div v-if="loggedIn">
|
<div v-if="loggedIn">
|
||||||
<button
|
<button
|
||||||
|
@ -96,12 +96,6 @@
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-settings-menu {
|
|
||||||
display: flex;
|
|
||||||
min-width: 12em;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,29 +1,17 @@
|
||||||
import Popover from '../popover/popover.vue'
|
import Popover from '../popover/popover.vue'
|
||||||
import { mapState } from 'vuex'
|
import TimelineMenuContent from './timeline_menu_content.vue'
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
faUsers,
|
|
||||||
faGlobe,
|
|
||||||
faBookmark,
|
|
||||||
faEnvelope,
|
|
||||||
faHome,
|
|
||||||
faChevronDown
|
faChevronDown
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
library.add(
|
library.add(faChevronDown)
|
||||||
faUsers,
|
|
||||||
faGlobe,
|
|
||||||
faBookmark,
|
|
||||||
faEnvelope,
|
|
||||||
faHome,
|
|
||||||
faChevronDown
|
|
||||||
)
|
|
||||||
|
|
||||||
// Route -> i18n key mapping, exported and not in the computed
|
// Route -> i18n key mapping, exported and not in the computed
|
||||||
// because nav panel benefits from the same information.
|
// because nav panel benefits from the same information.
|
||||||
export const timelineNames = () => {
|
export const timelineNames = () => {
|
||||||
return {
|
return {
|
||||||
'friends': 'nav.timeline',
|
'friends': 'nav.home_timeline',
|
||||||
'bookmarks': 'nav.bookmarks',
|
'bookmarks': 'nav.bookmarks',
|
||||||
'dms': 'nav.dms',
|
'dms': 'nav.dms',
|
||||||
'public-timeline': 'nav.public_tl',
|
'public-timeline': 'nav.public_tl',
|
||||||
|
@ -33,7 +21,8 @@ export const timelineNames = () => {
|
||||||
|
|
||||||
const TimelineMenu = {
|
const TimelineMenu = {
|
||||||
components: {
|
components: {
|
||||||
Popover
|
Popover,
|
||||||
|
TimelineMenuContent
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
@ -41,9 +30,6 @@ const TimelineMenu = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
if (this.currentUser && this.currentUser.locked) {
|
|
||||||
this.$store.dispatch('startFetchingFollowRequests')
|
|
||||||
}
|
|
||||||
if (timelineNames()[this.$route.name]) {
|
if (timelineNames()[this.$route.name]) {
|
||||||
this.$store.dispatch('setLastTimeline', this.$route.name)
|
this.$store.dispatch('setLastTimeline', this.$route.name)
|
||||||
}
|
}
|
||||||
|
@ -75,13 +61,6 @@ const TimelineMenu = {
|
||||||
const i18nkey = timelineNames()[this.$route.name]
|
const i18nkey = timelineNames()[this.$route.name]
|
||||||
return i18nkey ? this.$t(i18nkey) : route
|
return i18nkey ? this.$t(i18nkey) : route
|
||||||
}
|
}
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapState({
|
|
||||||
currentUser: state => state.users.currentUser,
|
|
||||||
privateMode: state => state.instance.private,
|
|
||||||
federating: state => state.instance.federating
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,53 +13,7 @@
|
||||||
slot="content"
|
slot="content"
|
||||||
class="timeline-menu-popover panel panel-default"
|
class="timeline-menu-popover panel panel-default"
|
||||||
>
|
>
|
||||||
<ul>
|
<TimelineMenuContent />
|
||||||
<li v-if="currentUser">
|
|
||||||
<router-link :to="{ name: 'friends' }">
|
|
||||||
<FAIcon
|
|
||||||
fixed-width
|
|
||||||
class="fa-scale-110 fa-old-padding "
|
|
||||||
icon="home"
|
|
||||||
/>{{ $t("nav.timeline") }}
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
<li v-if="currentUser">
|
|
||||||
<router-link :to="{ name: 'bookmarks'}">
|
|
||||||
<FAIcon
|
|
||||||
fixed-width
|
|
||||||
class="fa-scale-110 fa-old-padding "
|
|
||||||
icon="bookmark"
|
|
||||||
/>{{ $t("nav.bookmarks") }}
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
<li v-if="currentUser">
|
|
||||||
<router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
|
|
||||||
<FAIcon
|
|
||||||
fixed-width
|
|
||||||
class="fa-scale-110 fa-old-padding "
|
|
||||||
icon="envelope"
|
|
||||||
/>{{ $t("nav.dms") }}
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
<li v-if="currentUser || !privateMode">
|
|
||||||
<router-link :to="{ name: 'public-timeline' }">
|
|
||||||
<FAIcon
|
|
||||||
fixed-width
|
|
||||||
class="fa-scale-110 fa-old-padding "
|
|
||||||
icon="users"
|
|
||||||
/>{{ $t("nav.public_tl") }}
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
<li v-if="federating && (currentUser || !privateMode)">
|
|
||||||
<router-link :to="{ name: 'public-external-timeline' }">
|
|
||||||
<FAIcon
|
|
||||||
fixed-width
|
|
||||||
class="fa-scale-110 fa-old-padding "
|
|
||||||
icon="globe"
|
|
||||||
/>{{ $t("nav.twkn") }}
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
|
|
29
src/components/timeline_menu/timeline_menu_content.js
Normal file
29
src/components/timeline_menu/timeline_menu_content.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { mapState } from 'vuex'
|
||||||
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
import {
|
||||||
|
faUsers,
|
||||||
|
faGlobe,
|
||||||
|
faBookmark,
|
||||||
|
faEnvelope,
|
||||||
|
faHome
|
||||||
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
library.add(
|
||||||
|
faUsers,
|
||||||
|
faGlobe,
|
||||||
|
faBookmark,
|
||||||
|
faEnvelope,
|
||||||
|
faHome
|
||||||
|
)
|
||||||
|
|
||||||
|
const TimelineMenuContent = {
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
currentUser: state => state.users.currentUser,
|
||||||
|
privateMode: state => state.instance.private,
|
||||||
|
federating: state => state.instance.federating
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TimelineMenuContent
|
66
src/components/timeline_menu/timeline_menu_content.vue
Normal file
66
src/components/timeline_menu/timeline_menu_content.vue
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<template>
|
||||||
|
<ul>
|
||||||
|
<li v-if="currentUser">
|
||||||
|
<router-link
|
||||||
|
class="menu-item"
|
||||||
|
:to="{ name: 'friends' }"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
fixed-width
|
||||||
|
class="fa-scale-110 fa-old-padding "
|
||||||
|
icon="home"
|
||||||
|
/>{{ $t("nav.home_timeline") }}
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li v-if="currentUser || !privateMode">
|
||||||
|
<router-link
|
||||||
|
class="menu-item"
|
||||||
|
:to="{ name: 'public-timeline' }"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
fixed-width
|
||||||
|
class="fa-scale-110 fa-old-padding "
|
||||||
|
icon="users"
|
||||||
|
/>{{ $t("nav.public_tl") }}
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li v-if="federating && (currentUser || !privateMode)">
|
||||||
|
<router-link
|
||||||
|
class="menu-item"
|
||||||
|
:to="{ name: 'public-external-timeline' }"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
fixed-width
|
||||||
|
class="fa-scale-110 fa-old-padding "
|
||||||
|
icon="globe"
|
||||||
|
/>{{ $t("nav.twkn") }}
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li v-if="currentUser">
|
||||||
|
<router-link
|
||||||
|
class="menu-item"
|
||||||
|
:to="{ name: 'bookmarks'}"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
fixed-width
|
||||||
|
class="fa-scale-110 fa-old-padding "
|
||||||
|
icon="bookmark"
|
||||||
|
/>{{ $t("nav.bookmarks") }}
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li v-if="currentUser">
|
||||||
|
<router-link
|
||||||
|
class="menu-item"
|
||||||
|
:to="{ name: 'dms', params: { username: currentUser.screen_name } }"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
fixed-width
|
||||||
|
class="fa-scale-110 fa-old-padding "
|
||||||
|
icon="envelope"
|
||||||
|
/>{{ $t("nav.dms") }}
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./timeline_menu_content.js" ></script>
|
|
@ -141,10 +141,10 @@
|
||||||
v-model="userHighlightType"
|
v-model="userHighlightType"
|
||||||
class="userHighlightSel"
|
class="userHighlightSel"
|
||||||
>
|
>
|
||||||
<option value="disabled">No highlight</option>
|
<option value="disabled">{{ $t('user_card.highlight.disabled') }}</option>
|
||||||
<option value="solid">Solid bg</option>
|
<option value="solid">{{ $t('user_card.highlight.solid') }}</option>
|
||||||
<option value="striped">Striped bg</option>
|
<option value="striped">{{ $t('user_card.highlight.striped') }}</option>
|
||||||
<option value="side">Side stripe</option>
|
<option value="side">{{ $t('user_card.highlight.side') }}</option>
|
||||||
</select>
|
</select>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
class="select-down-icon"
|
class="select-down-icon"
|
||||||
|
|
|
@ -3,27 +3,27 @@
|
||||||
"mrf": {
|
"mrf": {
|
||||||
"federation": "Federation",
|
"federation": "Federation",
|
||||||
"keyword": {
|
"keyword": {
|
||||||
"keyword_policies": "Keyword Policies",
|
"keyword_policies": "Keyword policies",
|
||||||
"ftl_removal": "Removal from \"The Whole Known Network\" Timeline",
|
"ftl_removal": "Removal from \"The Whole Known Network\" Timeline",
|
||||||
"reject": "Reject",
|
"reject": "Reject",
|
||||||
"replace": "Replace",
|
"replace": "Replace",
|
||||||
"is_replaced_by": "→"
|
"is_replaced_by": "→"
|
||||||
},
|
},
|
||||||
"mrf_policies": "Enabled MRF Policies",
|
"mrf_policies": "Enabled MRF policies",
|
||||||
"mrf_policies_desc": "MRF policies manipulate the federation behaviour of the instance. The following policies are enabled:",
|
"mrf_policies_desc": "MRF policies manipulate the federation behaviour of the instance. The following policies are enabled:",
|
||||||
"simple": {
|
"simple": {
|
||||||
"simple_policies": "Instance-specific Policies",
|
"simple_policies": "Instance-specific policies",
|
||||||
"accept": "Accept",
|
"accept": "Accept",
|
||||||
"accept_desc": "This instance only accepts messages from the following instances:",
|
"accept_desc": "This instance only accepts messages from the following instances:",
|
||||||
"reject": "Reject",
|
"reject": "Reject",
|
||||||
"reject_desc": "This instance will not accept messages from the following instances:",
|
"reject_desc": "This instance will not accept messages from the following instances:",
|
||||||
"quarantine": "Quarantine",
|
"quarantine": "Quarantine",
|
||||||
"quarantine_desc": "This instance will send only public posts to the following instances:",
|
"quarantine_desc": "This instance will send only public posts to the following instances:",
|
||||||
"ftl_removal": "Removal from \"The Whole Known Network\" Timeline",
|
"ftl_removal": "Removal from \"Known Network\" Timeline",
|
||||||
"ftl_removal_desc": "This instance removes these instances from \"The Whole Known Network\" timeline:",
|
"ftl_removal_desc": "This instance removes these instances from \"Known Network\" timeline:",
|
||||||
"media_removal": "Media Removal",
|
"media_removal": "Media Removal",
|
||||||
"media_removal_desc": "This instance removes media from posts on the following instances:",
|
"media_removal_desc": "This instance removes media from posts on the following instances:",
|
||||||
"media_nsfw": "Media Force-set As Sensitive",
|
"media_nsfw": "Media force-set as sensitive",
|
||||||
"media_nsfw_desc": "This instance forces media to be set sensitive in posts on the following instances:"
|
"media_nsfw_desc": "This instance forces media to be set sensitive in posts on the following instances:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -118,12 +118,13 @@
|
||||||
"about": "About",
|
"about": "About",
|
||||||
"administration": "Administration",
|
"administration": "Administration",
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
"friend_requests": "Follow Requests",
|
"friend_requests": "Follow requests",
|
||||||
"mentions": "Mentions",
|
"mentions": "Mentions",
|
||||||
"interactions": "Interactions",
|
"interactions": "Interactions",
|
||||||
"dms": "Direct Messages",
|
"dms": "Direct messages",
|
||||||
"public_tl": "Public Timeline",
|
"public_tl": "Public timeline",
|
||||||
"timeline": "Timeline",
|
"timeline": "Timeline",
|
||||||
|
"home_timeline": "Home timeline",
|
||||||
"twkn": "Known Network",
|
"twkn": "Known Network",
|
||||||
"bookmarks": "Bookmarks",
|
"bookmarks": "Bookmarks",
|
||||||
"user_search": "User Search",
|
"user_search": "User Search",
|
||||||
|
@ -148,8 +149,8 @@
|
||||||
"reacted_with": "reacted with {0}"
|
"reacted_with": "reacted with {0}"
|
||||||
},
|
},
|
||||||
"polls": {
|
"polls": {
|
||||||
"add_poll": "Add Poll",
|
"add_poll": "Add poll",
|
||||||
"add_option": "Add Option",
|
"add_option": "Add option",
|
||||||
"option": "Option",
|
"option": "Option",
|
||||||
"votes": "votes",
|
"votes": "votes",
|
||||||
"people_voted_count": "{count} person voted | {count} people voted",
|
"people_voted_count": "{count} person voted | {count} people voted",
|
||||||
|
@ -178,7 +179,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."
|
"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": {
|
"interactions": {
|
||||||
"favs_repeats": "Repeats and Favorites",
|
"favs_repeats": "Repeats and favorites",
|
||||||
"follows": "New follows",
|
"follows": "New follows",
|
||||||
"moves": "User migrates",
|
"moves": "User migrates",
|
||||||
"load_older": "Load older interactions"
|
"load_older": "Load older interactions"
|
||||||
|
@ -200,6 +201,7 @@
|
||||||
"direct_warning_to_all": "This post will be visible to all the mentioned users.",
|
"direct_warning_to_all": "This post will be visible to all the mentioned users.",
|
||||||
"direct_warning_to_first_only": "This post will only be visible to the mentioned users at the beginning of the message.",
|
"direct_warning_to_first_only": "This post will only be visible to the mentioned users at the beginning of the message.",
|
||||||
"posting": "Posting",
|
"posting": "Posting",
|
||||||
|
"post": "Post",
|
||||||
"preview": "Preview",
|
"preview": "Preview",
|
||||||
"preview_empty": "Empty",
|
"preview_empty": "Empty",
|
||||||
"empty_status_error": "Can't post an empty status with no files",
|
"empty_status_error": "Can't post an empty status with no files",
|
||||||
|
@ -210,10 +212,10 @@
|
||||||
"unlisted": "This post will not be visible in Public Timeline and The Whole Known Network"
|
"unlisted": "This post will not be visible in Public Timeline and The Whole Known Network"
|
||||||
},
|
},
|
||||||
"scope": {
|
"scope": {
|
||||||
"direct": "Direct - Post to mentioned users only",
|
"direct": "Direct - post to mentioned users only",
|
||||||
"private": "Followers-only - Post to followers only",
|
"private": "Followers-only - post to followers only",
|
||||||
"public": "Public - Post to public timelines",
|
"public": "Public - post to public timelines",
|
||||||
"unlisted": "Unlisted - Do not post to public timelines"
|
"unlisted": "Unlisted - do not post to public timelines"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"registration": {
|
"registration": {
|
||||||
|
@ -230,6 +232,7 @@
|
||||||
"bio_placeholder": "e.g.\nHi, I'm Lain.\nI’m an anime girl living in suburban Japan. You may know me from the Wired.",
|
"bio_placeholder": "e.g.\nHi, I'm Lain.\nI’m an anime girl living in suburban Japan. You may know me from the Wired.",
|
||||||
"reason": "Reason to register",
|
"reason": "Reason to register",
|
||||||
"reason_placeholder": "This instance approves registrations manually.\nLet the administration know why you want to register.",
|
"reason_placeholder": "This instance approves registrations manually.\nLet the administration know why you want to register.",
|
||||||
|
"register": "Register",
|
||||||
"validations": {
|
"validations": {
|
||||||
"username_required": "cannot be left blank",
|
"username_required": "cannot be left blank",
|
||||||
"fullname_required": "cannot be left blank",
|
"fullname_required": "cannot be left blank",
|
||||||
|
@ -249,6 +252,7 @@
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"app_name": "App name",
|
"app_name": "App name",
|
||||||
|
"save": "Save changes",
|
||||||
"security": "Security",
|
"security": "Security",
|
||||||
"setting_changed": "Setting is different from default",
|
"setting_changed": "Setting is different from default",
|
||||||
"enter_current_password_to_confirm": "Enter your current password to confirm your identity",
|
"enter_current_password_to_confirm": "Enter your current password to confirm your identity",
|
||||||
|
@ -277,7 +281,7 @@
|
||||||
"attachmentRadius": "Attachments",
|
"attachmentRadius": "Attachments",
|
||||||
"attachments": "Attachments",
|
"attachments": "Attachments",
|
||||||
"avatar": "Avatar",
|
"avatar": "Avatar",
|
||||||
"avatarAltRadius": "Avatars (Notifications)",
|
"avatarAltRadius": "Avatars (notifications)",
|
||||||
"avatarRadius": "Avatars",
|
"avatarRadius": "Avatars",
|
||||||
"background": "Background",
|
"background": "Background",
|
||||||
"bio": "Bio",
|
"bio": "Bio",
|
||||||
|
@ -299,10 +303,10 @@
|
||||||
"cGreen": "Green (Retweet)",
|
"cGreen": "Green (Retweet)",
|
||||||
"cOrange": "Orange (Favorite)",
|
"cOrange": "Orange (Favorite)",
|
||||||
"cRed": "Red (Cancel)",
|
"cRed": "Red (Cancel)",
|
||||||
"change_email": "Change Email",
|
"change_email": "Change email",
|
||||||
"change_email_error": "There was an issue changing your email.",
|
"change_email_error": "There was an issue changing your email.",
|
||||||
"changed_email": "Email changed successfully!",
|
"changed_email": "Email changed successfully!",
|
||||||
"change_password": "Change Password",
|
"change_password": "Change password",
|
||||||
"change_password_error": "There was an issue changing your password.",
|
"change_password_error": "There was an issue changing your password.",
|
||||||
"changed_password": "Password changed successfully!",
|
"changed_password": "Password changed successfully!",
|
||||||
"chatMessageRadius": "Chat message",
|
"chatMessageRadius": "Chat message",
|
||||||
|
@ -313,9 +317,9 @@
|
||||||
"current_mascot": "Your current mascot",
|
"current_mascot": "Your current mascot",
|
||||||
"current_password": "Current password",
|
"current_password": "Current password",
|
||||||
"mutes_and_blocks": "Mutes and Blocks",
|
"mutes_and_blocks": "Mutes and Blocks",
|
||||||
"data_import_export_tab": "Data Import / Export",
|
"data_import_export_tab": "Data import / export",
|
||||||
"default_vis": "Default visibility scope",
|
"default_vis": "Default visibility scope",
|
||||||
"delete_account": "Delete Account",
|
"delete_account": "Delete account",
|
||||||
"delete_account_description": "Permanently delete your data and deactivate your account.",
|
"delete_account_description": "Permanently delete your data and deactivate your account.",
|
||||||
"delete_account_error": "There was an issue deleting your account. If this persists please contact your instance administrator.",
|
"delete_account_error": "There was an issue deleting your account. If this persists please contact your instance administrator.",
|
||||||
"delete_account_instructions": "Type your password in the input below to confirm account deletion.",
|
"delete_account_instructions": "Type your password in the input below to confirm account deletion.",
|
||||||
|
@ -368,18 +372,18 @@
|
||||||
"play_videos_in_modal": "Play videos in a popup frame",
|
"play_videos_in_modal": "Play videos in a popup frame",
|
||||||
"profile_fields": {
|
"profile_fields": {
|
||||||
"label": "Profile metadata",
|
"label": "Profile metadata",
|
||||||
"add_field": "Add Field",
|
"add_field": "Add field",
|
||||||
"name": "Label",
|
"name": "Label",
|
||||||
"value": "Content"
|
"value": "Content"
|
||||||
},
|
},
|
||||||
"use_contain_fit": "Don't crop the attachment in thumbnails",
|
"use_contain_fit": "Don't crop the attachment in thumbnails",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"name_bio": "Name & Bio",
|
"name_bio": "Name & bio",
|
||||||
"new_email": "New Email",
|
"new_email": "New email",
|
||||||
"new_password": "New password",
|
"new_password": "New password",
|
||||||
"notification_visibility": "Types of notifications to show",
|
"notification_visibility": "Types of notifications to show",
|
||||||
"notification_visibility_follows": "Follows",
|
"notification_visibility_follows": "Follows",
|
||||||
"notification_visibility_likes": "Likes",
|
"notification_visibility_likes": "Favorites",
|
||||||
"notification_visibility_mentions": "Mentions",
|
"notification_visibility_mentions": "Mentions",
|
||||||
"notification_visibility_repeats": "Repeats",
|
"notification_visibility_repeats": "Repeats",
|
||||||
"notification_visibility_moves": "User Migrates",
|
"notification_visibility_moves": "User Migrates",
|
||||||
|
@ -391,19 +395,19 @@
|
||||||
"hide_followers_description": "Don't show who's following me",
|
"hide_followers_description": "Don't show who's following me",
|
||||||
"hide_follows_count_description": "Don't show follow count",
|
"hide_follows_count_description": "Don't show follow count",
|
||||||
"hide_followers_count_description": "Don't show follower count",
|
"hide_followers_count_description": "Don't show follower count",
|
||||||
"show_admin_badge": "Show Admin badge in my profile",
|
"show_admin_badge": "Show \"Admin\" badge in my profile",
|
||||||
"show_moderator_badge": "Show Moderator badge in my profile",
|
"show_moderator_badge": "Show \"Moderator\" badge in my profile",
|
||||||
"nsfw_clickthrough": "Enable clickthrough attachment and link preview image hiding for NSFW statuses",
|
"nsfw_clickthrough": "Enable clickthrough attachment and link preview image hiding for NSFW statuses",
|
||||||
"oauth_tokens": "OAuth tokens",
|
"oauth_tokens": "OAuth tokens",
|
||||||
"token": "Token",
|
"token": "Token",
|
||||||
"refresh_token": "Refresh Token",
|
"refresh_token": "Refresh token",
|
||||||
"valid_until": "Valid Until",
|
"valid_until": "Valid until",
|
||||||
"revoke_token": "Revoke",
|
"revoke_token": "Revoke",
|
||||||
"panelRadius": "Panels",
|
"panelRadius": "Panels",
|
||||||
"pause_on_unfocused": "Pause streaming when tab is not focused",
|
"pause_on_unfocused": "Pause streaming when tab is not focused",
|
||||||
"presets": "Presets",
|
"presets": "Presets",
|
||||||
"profile_background": "Profile Background",
|
"profile_background": "Profile background",
|
||||||
"profile_banner": "Profile Banner",
|
"profile_banner": "Profile banner",
|
||||||
"profile_tab": "Profile",
|
"profile_tab": "Profile",
|
||||||
"radii_help": "Set up interface edge rounding (in pixels)",
|
"radii_help": "Set up interface edge rounding (in pixels)",
|
||||||
"replies_in_timeline": "Replies in timeline",
|
"replies_in_timeline": "Replies in timeline",
|
||||||
|
@ -617,8 +621,8 @@
|
||||||
},
|
},
|
||||||
"version": {
|
"version": {
|
||||||
"title": "Version",
|
"title": "Version",
|
||||||
"backend_version": "Backend Version",
|
"backend_version": "Backend version",
|
||||||
"frontend_version": "Frontend Version"
|
"frontend_version": "Frontend version"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"time": {
|
"time": {
|
||||||
|
@ -666,7 +670,9 @@
|
||||||
"reload": "Reload",
|
"reload": "Reload",
|
||||||
"up_to_date": "Up-to-date",
|
"up_to_date": "Up-to-date",
|
||||||
"no_more_statuses": "No more statuses",
|
"no_more_statuses": "No more statuses",
|
||||||
"no_statuses": "No statuses"
|
"no_statuses": "No statuses",
|
||||||
|
"socket_reconnected": "Realtime connection established",
|
||||||
|
"socket_broke": "Realtime connection lost: CloseEvent code {0}"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"favorites": "Favorites",
|
"favorites": "Favorites",
|
||||||
|
@ -750,10 +756,16 @@
|
||||||
"quarantine": "Disallow user posts from federating",
|
"quarantine": "Disallow user posts from federating",
|
||||||
"delete_user": "Delete user",
|
"delete_user": "Delete user",
|
||||||
"delete_user_confirmation": "Are you absolutely sure? This action cannot be undone."
|
"delete_user_confirmation": "Are you absolutely sure? This action cannot be undone."
|
||||||
|
},
|
||||||
|
"highlight": {
|
||||||
|
"disabled": "No highlight",
|
||||||
|
"solid": "Solid bg",
|
||||||
|
"striped": "Striped bg",
|
||||||
|
"side": "Side stripe"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"user_profile": {
|
"user_profile": {
|
||||||
"timeline_title": "User Timeline",
|
"timeline_title": "User timeline",
|
||||||
"profile_does_not_exist": "Sorry, this profile does not exist.",
|
"profile_does_not_exist": "Sorry, this profile does not exist.",
|
||||||
"profile_loading_error": "Sorry, there was an error loading this profile."
|
"profile_loading_error": "Sorry, there was an error loading this profile."
|
||||||
},
|
},
|
||||||
|
@ -771,7 +783,7 @@
|
||||||
"who_to_follow": "Who to follow"
|
"who_to_follow": "Who to follow"
|
||||||
},
|
},
|
||||||
"tool_tip": {
|
"tool_tip": {
|
||||||
"media_upload": "Upload Media",
|
"media_upload": "Upload media",
|
||||||
"repeat": "Repeat",
|
"repeat": "Repeat",
|
||||||
"reply": "Reply",
|
"reply": "Reply",
|
||||||
"favorite": "Favorite",
|
"favorite": "Favorite",
|
||||||
|
|
|
@ -28,7 +28,6 @@ import pushNotifications from './lib/push_notifications_plugin.js'
|
||||||
|
|
||||||
import messages from './i18n/messages.js'
|
import messages from './i18n/messages.js'
|
||||||
|
|
||||||
import VueChatScroll from 'vue-chat-scroll'
|
|
||||||
import VueClickOutside from 'v-click-outside'
|
import VueClickOutside from 'v-click-outside'
|
||||||
import PortalVue from 'portal-vue'
|
import PortalVue from 'portal-vue'
|
||||||
import VBodyScrollLock from './directives/body_scroll_lock'
|
import VBodyScrollLock from './directives/body_scroll_lock'
|
||||||
|
@ -42,7 +41,6 @@ const currentLocale = (window.navigator.language || 'en').split('-')[0]
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
Vue.use(VueRouter)
|
Vue.use(VueRouter)
|
||||||
Vue.use(VueI18n)
|
Vue.use(VueI18n)
|
||||||
Vue.use(VueChatScroll)
|
|
||||||
Vue.use(VueClickOutside)
|
Vue.use(VueClickOutside)
|
||||||
Vue.use(PortalVue)
|
Vue.use(PortalVue)
|
||||||
Vue.use(VBodyScrollLock)
|
Vue.use(VBodyScrollLock)
|
||||||
|
|
|
@ -3,8 +3,11 @@ import { WSConnectionStatus } from '../services/api/api.service.js'
|
||||||
import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js'
|
import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js'
|
||||||
import { Socket } from 'phoenix'
|
import { Socket } from 'phoenix'
|
||||||
|
|
||||||
|
const retryTimeout = (multiplier) => 1000 * multiplier
|
||||||
|
|
||||||
const api = {
|
const api = {
|
||||||
state: {
|
state: {
|
||||||
|
retryMultiplier: 1,
|
||||||
backendInteractor: backendInteractorService(),
|
backendInteractor: backendInteractorService(),
|
||||||
fetchers: {},
|
fetchers: {},
|
||||||
socket: null,
|
socket: null,
|
||||||
|
@ -34,18 +37,43 @@ const api = {
|
||||||
},
|
},
|
||||||
setMastoUserSocketStatus (state, value) {
|
setMastoUserSocketStatus (state, value) {
|
||||||
state.mastoUserSocketStatus = value
|
state.mastoUserSocketStatus = value
|
||||||
|
},
|
||||||
|
incrementRetryMultiplier (state) {
|
||||||
|
state.retryMultiplier = Math.max(++state.retryMultiplier, 3)
|
||||||
|
},
|
||||||
|
resetRetryMultiplier (state) {
|
||||||
|
state.retryMultiplier = 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
// Global MastoAPI socket control, in future should disable ALL sockets/(re)start relevant sockets
|
/**
|
||||||
enableMastoSockets (store) {
|
* Global MastoAPI socket control, in future should disable ALL sockets/(re)start relevant sockets
|
||||||
const { state, dispatch } = store
|
*
|
||||||
if (state.mastoUserSocket) return
|
* @param {Boolean} [initial] - whether this enabling happened at boot time or not
|
||||||
|
*/
|
||||||
|
enableMastoSockets (store, initial) {
|
||||||
|
const { state, dispatch, commit } = store
|
||||||
|
// Do not initialize unless nonexistent or closed
|
||||||
|
if (
|
||||||
|
state.mastoUserSocket &&
|
||||||
|
![
|
||||||
|
WebSocket.CLOSED,
|
||||||
|
WebSocket.CLOSING
|
||||||
|
].includes(state.mastoUserSocket.getState())
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (initial) {
|
||||||
|
commit('setMastoUserSocketStatus', WSConnectionStatus.STARTING_INITIAL)
|
||||||
|
} else {
|
||||||
|
commit('setMastoUserSocketStatus', WSConnectionStatus.STARTING)
|
||||||
|
}
|
||||||
return dispatch('startMastoUserSocket')
|
return dispatch('startMastoUserSocket')
|
||||||
},
|
},
|
||||||
disableMastoSockets (store) {
|
disableMastoSockets (store) {
|
||||||
const { state, dispatch } = store
|
const { state, dispatch, commit } = store
|
||||||
if (!state.mastoUserSocket) return
|
if (!state.mastoUserSocket) return
|
||||||
|
commit('setMastoUserSocketStatus', WSConnectionStatus.DISABLED)
|
||||||
return dispatch('stopMastoUserSocket')
|
return dispatch('stopMastoUserSocket')
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -91,11 +119,29 @@ const api = {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
state.mastoUserSocket.addEventListener('open', () => {
|
state.mastoUserSocket.addEventListener('open', () => {
|
||||||
|
// Do not show notification when we just opened up the page
|
||||||
|
if (state.mastoUserSocketStatus !== WSConnectionStatus.STARTING_INITIAL) {
|
||||||
|
dispatch('pushGlobalNotice', {
|
||||||
|
level: 'success',
|
||||||
|
messageKey: 'timeline.socket_reconnected',
|
||||||
|
timeout: 5000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Stop polling if we were errored or disabled
|
||||||
|
if (new Set([
|
||||||
|
WSConnectionStatus.ERROR,
|
||||||
|
WSConnectionStatus.DISABLED
|
||||||
|
]).has(state.mastoUserSocketStatus)) {
|
||||||
|
dispatch('stopFetchingTimeline', { timeline: 'friends' })
|
||||||
|
dispatch('stopFetchingNotifications')
|
||||||
|
dispatch('stopFetchingChats')
|
||||||
|
}
|
||||||
|
commit('resetRetryMultiplier')
|
||||||
commit('setMastoUserSocketStatus', WSConnectionStatus.JOINED)
|
commit('setMastoUserSocketStatus', WSConnectionStatus.JOINED)
|
||||||
})
|
})
|
||||||
state.mastoUserSocket.addEventListener('error', ({ detail: error }) => {
|
state.mastoUserSocket.addEventListener('error', ({ detail: error }) => {
|
||||||
console.error('Error in MastoAPI websocket:', error)
|
console.error('Error in MastoAPI websocket:', error)
|
||||||
commit('setMastoUserSocketStatus', WSConnectionStatus.ERROR)
|
// TODO is this needed?
|
||||||
dispatch('clearOpenedChats')
|
dispatch('clearOpenedChats')
|
||||||
})
|
})
|
||||||
state.mastoUserSocket.addEventListener('close', ({ detail: closeEvent }) => {
|
state.mastoUserSocket.addEventListener('close', ({ detail: closeEvent }) => {
|
||||||
|
@ -106,14 +152,26 @@ const api = {
|
||||||
const { code } = closeEvent
|
const { code } = closeEvent
|
||||||
if (ignoreCodes.has(code)) {
|
if (ignoreCodes.has(code)) {
|
||||||
console.debug(`Not restarting socket becasue of closure code ${code} is in ignore list`)
|
console.debug(`Not restarting socket becasue of closure code ${code} is in ignore list`)
|
||||||
|
commit('setMastoUserSocketStatus', WSConnectionStatus.CLOSED)
|
||||||
} else {
|
} else {
|
||||||
console.warn(`MastoAPI websocket disconnected, restarting. CloseEvent code: ${code}`)
|
console.warn(`MastoAPI websocket disconnected, restarting. CloseEvent code: ${code}`)
|
||||||
dispatch('startFetchingTimeline', { timeline: 'friends' })
|
setTimeout(() => {
|
||||||
dispatch('startFetchingNotifications')
|
dispatch('startMastoUserSocket')
|
||||||
dispatch('startFetchingChats')
|
}, retryTimeout(state.retryMultiplier))
|
||||||
dispatch('restartMastoUserSocket')
|
commit('incrementRetryMultiplier')
|
||||||
|
if (state.mastoUserSocketStatus !== WSConnectionStatus.ERROR) {
|
||||||
|
dispatch('startFetchingTimeline', { timeline: 'friends' })
|
||||||
|
dispatch('startFetchingNotifications')
|
||||||
|
dispatch('startFetchingChats')
|
||||||
|
dispatch('pushGlobalNotice', {
|
||||||
|
level: 'error',
|
||||||
|
messageKey: 'timeline.socket_broke',
|
||||||
|
messageArgs: [code],
|
||||||
|
timeout: 5000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
commit('setMastoUserSocketStatus', WSConnectionStatus.ERROR)
|
||||||
}
|
}
|
||||||
commit('setMastoUserSocketStatus', WSConnectionStatus.CLOSED)
|
|
||||||
dispatch('clearOpenedChats')
|
dispatch('clearOpenedChats')
|
||||||
})
|
})
|
||||||
resolve()
|
resolve()
|
||||||
|
@ -122,15 +180,6 @@ const api = {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
restartMastoUserSocket ({ dispatch }) {
|
|
||||||
// This basically starts MastoAPI user socket and stops conventional
|
|
||||||
// fetchers when connection reestablished
|
|
||||||
return dispatch('startMastoUserSocket').then(() => {
|
|
||||||
dispatch('stopFetchingTimeline', { timeline: 'friends' })
|
|
||||||
dispatch('stopFetchingNotifications')
|
|
||||||
dispatch('stopFetchingChats')
|
|
||||||
})
|
|
||||||
},
|
|
||||||
stopMastoUserSocket ({ state, dispatch }) {
|
stopMastoUserSocket ({ state, dispatch }) {
|
||||||
dispatch('startFetchingTimeline', { timeline: 'friends' })
|
dispatch('startFetchingTimeline', { timeline: 'friends' })
|
||||||
dispatch('startFetchingNotifications')
|
dispatch('startFetchingNotifications')
|
||||||
|
@ -156,6 +205,13 @@ const api = {
|
||||||
if (!fetcher) return
|
if (!fetcher) return
|
||||||
store.commit('removeFetcher', { fetcherName: timeline, fetcher })
|
store.commit('removeFetcher', { fetcherName: timeline, fetcher })
|
||||||
},
|
},
|
||||||
|
fetchTimeline (store, timeline, { ...rest }) {
|
||||||
|
store.state.backendInteractor.fetchTimeline({
|
||||||
|
store,
|
||||||
|
timeline,
|
||||||
|
...rest
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
// Notifications
|
// Notifications
|
||||||
startFetchingNotifications (store) {
|
startFetchingNotifications (store) {
|
||||||
|
@ -168,6 +224,12 @@ const api = {
|
||||||
if (!fetcher) return
|
if (!fetcher) return
|
||||||
store.commit('removeFetcher', { fetcherName: 'notifications', fetcher })
|
store.commit('removeFetcher', { fetcherName: 'notifications', fetcher })
|
||||||
},
|
},
|
||||||
|
fetchNotifications (store, { ...rest }) {
|
||||||
|
store.state.backendInteractor.fetchNotifications({
|
||||||
|
store,
|
||||||
|
...rest
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
// Follow requests
|
// Follow requests
|
||||||
startFetchingFollowRequests (store) {
|
startFetchingFollowRequests (store) {
|
||||||
|
|
|
@ -18,6 +18,7 @@ const chat = {
|
||||||
actions: {
|
actions: {
|
||||||
initializeChat (store, socket) {
|
initializeChat (store, socket) {
|
||||||
const channel = socket.channel('chat:public')
|
const channel = socket.channel('chat:public')
|
||||||
|
|
||||||
channel.on('new_msg', (msg) => {
|
channel.on('new_msg', (msg) => {
|
||||||
store.commit('addMessage', msg)
|
store.commit('addMessage', msg)
|
||||||
})
|
})
|
||||||
|
|
|
@ -44,7 +44,7 @@ export const defaultState = {
|
||||||
likes: true,
|
likes: true,
|
||||||
repeats: true,
|
repeats: true,
|
||||||
moves: true,
|
moves: true,
|
||||||
emojiReactions: false,
|
emojiReactions: true,
|
||||||
followRequest: true,
|
followRequest: true,
|
||||||
chatMention: true
|
chatMention: true
|
||||||
},
|
},
|
||||||
|
|
|
@ -557,9 +557,10 @@ const users = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store.getters.mergedConfig.useStreamingApi) {
|
if (store.getters.mergedConfig.useStreamingApi) {
|
||||||
store.dispatch('enableMastoSockets').catch((error) => {
|
store.dispatch('fetchTimeline', 'friends', { since: null })
|
||||||
|
store.dispatch('fetchNotifications', { since: null })
|
||||||
|
store.dispatch('enableMastoSockets', true).catch((error) => {
|
||||||
console.error('Failed initializing MastoAPI Streaming socket', error)
|
console.error('Failed initializing MastoAPI Streaming socket', error)
|
||||||
startPolling()
|
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
store.dispatch('fetchChats', { latest: true })
|
store.dispatch('fetchChats', { latest: true })
|
||||||
setTimeout(() => store.dispatch('setNotificationsSilence', false), 10000)
|
setTimeout(() => store.dispatch('setNotificationsSilence', false), 10000)
|
||||||
|
|
|
@ -1167,6 +1167,7 @@ export const ProcessedWS = ({
|
||||||
|
|
||||||
// 1000 = Normal Closure
|
// 1000 = Normal Closure
|
||||||
eventTarget.close = () => { socket.close(1000, 'Shutting down socket') }
|
eventTarget.close = () => { socket.close(1000, 'Shutting down socket') }
|
||||||
|
eventTarget.getState = () => socket.readyState
|
||||||
|
|
||||||
return eventTarget
|
return eventTarget
|
||||||
}
|
}
|
||||||
|
@ -1198,7 +1199,10 @@ export const handleMastoWS = (wsEvent) => {
|
||||||
export const WSConnectionStatus = Object.freeze({
|
export const WSConnectionStatus = Object.freeze({
|
||||||
'JOINED': 1,
|
'JOINED': 1,
|
||||||
'CLOSED': 2,
|
'CLOSED': 2,
|
||||||
'ERROR': 3
|
'ERROR': 3,
|
||||||
|
'DISABLED': 4,
|
||||||
|
'STARTING': 5,
|
||||||
|
'STARTING_INITIAL': 6
|
||||||
})
|
})
|
||||||
|
|
||||||
const chats = ({ credentials }) => {
|
const chats = ({ credentials }) => {
|
||||||
|
|
|
@ -1,17 +1,25 @@
|
||||||
import apiService, { getMastodonSocketURI, ProcessedWS } from '../api/api.service.js'
|
import apiService, { getMastodonSocketURI, ProcessedWS } from '../api/api.service.js'
|
||||||
import timelineFetcherService from '../timeline_fetcher/timeline_fetcher.service.js'
|
import timelineFetcher from '../timeline_fetcher/timeline_fetcher.service.js'
|
||||||
import notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js'
|
import notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js'
|
||||||
import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service'
|
import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service'
|
||||||
|
|
||||||
const backendInteractorService = credentials => ({
|
const backendInteractorService = credentials => ({
|
||||||
startFetchingTimeline ({ timeline, store, userId = false, tag }) {
|
startFetchingTimeline ({ timeline, store, userId = false, tag }) {
|
||||||
return timelineFetcherService.startFetching({ timeline, store, credentials, userId, tag })
|
return timelineFetcher.startFetching({ timeline, store, credentials, userId, tag })
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchTimeline (args) {
|
||||||
|
return timelineFetcher.fetchAndUpdate({ ...args, credentials })
|
||||||
},
|
},
|
||||||
|
|
||||||
startFetchingNotifications ({ store }) {
|
startFetchingNotifications ({ store }) {
|
||||||
return notificationsFetcher.startFetching({ store, credentials })
|
return notificationsFetcher.startFetching({ store, credentials })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
fetchNotifications (args) {
|
||||||
|
return notificationsFetcher.fetchAndUpdate({ ...args, credentials })
|
||||||
|
},
|
||||||
|
|
||||||
startFetchingFollowRequests ({ store }) {
|
startFetchingFollowRequests ({ store }) {
|
||||||
return followRequestFetcher.startFetching({ store, credentials })
|
return followRequestFetcher.startFetching({ store, credentials })
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,7 @@ const update = ({ store, notifications, older }) => {
|
||||||
store.dispatch('addNewNotifications', { notifications, older })
|
store.dispatch('addNewNotifications', { notifications, older })
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchAndUpdate = ({ store, credentials, older = false }) => {
|
const fetchAndUpdate = ({ store, credentials, older = false, since }) => {
|
||||||
const args = { credentials }
|
const args = { credentials }
|
||||||
const { getters } = store
|
const { getters } = store
|
||||||
const rootState = store.rootState || store.state
|
const rootState = store.rootState || store.state
|
||||||
|
@ -22,8 +22,10 @@ const fetchAndUpdate = ({ store, credentials, older = false }) => {
|
||||||
return fetchNotifications({ store, args, older })
|
return fetchNotifications({ store, args, older })
|
||||||
} else {
|
} else {
|
||||||
// fetch new notifications
|
// fetch new notifications
|
||||||
if (timelineData.maxId !== Number.POSITIVE_INFINITY) {
|
if (since === undefined && timelineData.maxId !== Number.POSITIVE_INFINITY) {
|
||||||
args['since'] = timelineData.maxId
|
args['since'] = timelineData.maxId
|
||||||
|
} else if (since !== null) {
|
||||||
|
args['since'] = since
|
||||||
}
|
}
|
||||||
const result = fetchNotifications({ store, args, older })
|
const result = fetchNotifications({ store, args, older })
|
||||||
|
|
||||||
|
|
|
@ -616,6 +616,23 @@ export const SLOT_INHERITANCE = {
|
||||||
textColor: true
|
textColor: true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
alertSuccess: {
|
||||||
|
depends: ['cGreen'],
|
||||||
|
opacity: 'alert'
|
||||||
|
},
|
||||||
|
alertSuccessText: {
|
||||||
|
depends: ['text'],
|
||||||
|
layer: 'alert',
|
||||||
|
variant: 'alertSuccess',
|
||||||
|
textColor: true
|
||||||
|
},
|
||||||
|
alertSuccessPanelText: {
|
||||||
|
depends: ['panelText'],
|
||||||
|
layer: 'alertPanel',
|
||||||
|
variant: 'alertSuccess',
|
||||||
|
textColor: true
|
||||||
|
},
|
||||||
|
|
||||||
alertNeutral: {
|
alertNeutral: {
|
||||||
depends: ['text'],
|
depends: ['text'],
|
||||||
opacity: 'alert'
|
opacity: 'alert'
|
||||||
|
@ -656,6 +673,17 @@ export const SLOT_INHERITANCE = {
|
||||||
textColor: true
|
textColor: true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
alertPopupSuccess: {
|
||||||
|
depends: ['alertSuccess'],
|
||||||
|
opacity: 'alertPopup'
|
||||||
|
},
|
||||||
|
alertPopupSuccessText: {
|
||||||
|
depends: ['alertSuccessText'],
|
||||||
|
layer: 'popover',
|
||||||
|
variant: 'alertPopupSuccess',
|
||||||
|
textColor: true
|
||||||
|
},
|
||||||
|
|
||||||
alertPopupNeutral: {
|
alertPopupNeutral: {
|
||||||
depends: ['alertNeutral'],
|
depends: ['alertNeutral'],
|
||||||
opacity: 'alertPopup'
|
opacity: 'alertPopup'
|
||||||
|
|
|
@ -23,7 +23,8 @@ const fetchAndUpdate = ({
|
||||||
showImmediately = false,
|
showImmediately = false,
|
||||||
userId = false,
|
userId = false,
|
||||||
tag = false,
|
tag = false,
|
||||||
until
|
until,
|
||||||
|
since
|
||||||
}) => {
|
}) => {
|
||||||
const args = { timeline, credentials }
|
const args = { timeline, credentials }
|
||||||
const rootState = store.rootState || store.state
|
const rootState = store.rootState || store.state
|
||||||
|
@ -35,7 +36,11 @@ const fetchAndUpdate = ({
|
||||||
if (older) {
|
if (older) {
|
||||||
args['until'] = until || timelineData.minId
|
args['until'] = until || timelineData.minId
|
||||||
} else {
|
} else {
|
||||||
args['since'] = timelineData.maxId
|
if (since === undefined) {
|
||||||
|
args['since'] = timelineData.maxId
|
||||||
|
} else if (since !== null) {
|
||||||
|
args['since'] = since
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
args['userId'] = userId
|
args['userId'] = userId
|
||||||
|
|
|
@ -8923,10 +8923,6 @@ void-elements@^2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
|
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
|
||||||
|
|
||||||
vue-chat-scroll@^1.2.1:
|
|
||||||
version "1.3.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/vue-chat-scroll/-/vue-chat-scroll-1.3.5.tgz#a5ee5bae5058f614818a96eac5ee3be4394a2f68"
|
|
||||||
|
|
||||||
vue-eslint-parser@^5.0.0:
|
vue-eslint-parser@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz#00f4e4da94ec974b821a26ff0ed0f7a78402b8a1"
|
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz#00f4e4da94ec974b821a26ff0ed0f7a78402b8a1"
|
||||||
|
|
Loading…
Reference in a new issue