Merge remote-tracking branch 'upstream/develop' into neckbeard
This commit is contained in:
commit
cb193273d5
|
@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Autocomplete domains from list of known instances
|
||||
- 'Bot' settings option and badge
|
||||
- Added profile meta data fields that can be set in profile settings
|
||||
- When a post is a reply to an unavailable post, the 'Reply to'-text has a strike-through style
|
||||
|
||||
### Changed
|
||||
- Registration page no longer requires email if the server is configured not to require it
|
||||
|
@ -38,6 +39,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Fix status ellipsis menu being cut off in notifications column
|
||||
- Fixed autocomplete sometimes not returning the right user when there's already some results
|
||||
- Reply filtering options in Settings -> Filtering now work again using filtering on server
|
||||
- Don't show just blank-screen when cookies are disabled
|
||||
|
||||
## [2.0.3] - 2020-05-02
|
||||
### Fixed
|
||||
|
|
|
@ -13,6 +13,7 @@ import MobilePostStatusButton from './components/mobile_post_status_button/mobil
|
|||
import MobileNav from './components/mobile_nav/mobile_nav.vue'
|
||||
import UserReportingModal from './components/user_reporting_modal/user_reporting_modal.vue'
|
||||
import PostStatusModal from './components/post_status_modal/post_status_modal.vue'
|
||||
import GlobalNoticeList from './components/global_notice_list/global_notice_list.vue'
|
||||
import { windowWidth } from './services/window_utils/window_utils'
|
||||
import Banner from './components/banner/banner.vue'
|
||||
|
||||
|
@ -34,7 +35,8 @@ export default {
|
|||
SettingsModal,
|
||||
UserReportingModal,
|
||||
Banner,
|
||||
PostStatusModal
|
||||
PostStatusModal,
|
||||
GlobalNoticeList
|
||||
},
|
||||
data: () => ({
|
||||
mobileActivePanel: 'timeline',
|
||||
|
|
|
@ -129,6 +129,7 @@
|
|||
<PostStatusModal />
|
||||
<SettingsModal />
|
||||
<portal-target name="modal" />
|
||||
<GlobalNoticeList />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
const GlobalNoticeList = {
|
||||
computed: {
|
||||
notices () {
|
||||
return this.$store.state.interface.globalNotices
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closeNotice (notice) {
|
||||
this.$store.dispatch('removeGlobalNotice', notice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default GlobalNoticeList
|
|
@ -0,0 +1,77 @@
|
|||
<template>
|
||||
<div class="global-notice-list">
|
||||
<div
|
||||
v-for="(notice, index) in notices"
|
||||
:key="index"
|
||||
class="alert global-notice"
|
||||
:class="{ ['global-' + notice.level]: true }"
|
||||
>
|
||||
<div class="notice-message">
|
||||
{{ $t(notice.messageKey, notice.messageArgs) }}
|
||||
</div>
|
||||
<i
|
||||
class="button-icon icon-cancel"
|
||||
@click="closeNotice(notice)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./global_notice_list.js"></script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../_variables.scss';
|
||||
|
||||
.global-notice-list {
|
||||
position: fixed;
|
||||
top: 50px;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 1001;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.global-notice {
|
||||
pointer-events: auto;
|
||||
text-align: center;
|
||||
width: 40em;
|
||||
max-width: calc(100% - 3em);
|
||||
display: flex;
|
||||
padding-left: 1.5em;
|
||||
line-height: 2em;
|
||||
.notice-message {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
i {
|
||||
flex: 0 0;
|
||||
width: 1.5em;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.global-error {
|
||||
background-color: var(--alertPopupError, $fallback--cRed);
|
||||
color: var(--alertPopupErrorText, $fallback--text);
|
||||
i {
|
||||
color: var(--alertPopupErrorText, $fallback--text);
|
||||
}
|
||||
}
|
||||
|
||||
.global-warning {
|
||||
background-color: var(--alertPopupWarning, $fallback--cOrange);
|
||||
color: var(--alertPopupWarningText, $fallback--text);
|
||||
i {
|
||||
color: var(--alertPopupWarningText, $fallback--text);
|
||||
}
|
||||
}
|
||||
|
||||
.global-info {
|
||||
background-color: var(--alertPopupNeutral, $fallback--fg);
|
||||
color: var(--alertPopupNeutralText, $fallback--text);
|
||||
i {
|
||||
color: var(--alertPopupNeutralText, $fallback--text);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -27,6 +27,11 @@ const Notifications = {
|
|||
seenToDisplayCount: DEFAULT_SEEN_TO_DISPLAY_COUNT
|
||||
}
|
||||
},
|
||||
created () {
|
||||
const store = this.$store
|
||||
const credentials = store.state.users.currentUser.credentials
|
||||
notificationsFetcher.fetchAndUpdate({ store, credentials })
|
||||
},
|
||||
computed: {
|
||||
mainClass () {
|
||||
return this.minimalMode ? '' : 'panel panel-default'
|
||||
|
@ -56,11 +61,6 @@ const Notifications = {
|
|||
components: {
|
||||
Notification
|
||||
},
|
||||
created () {
|
||||
const { dispatch } = this.$store
|
||||
|
||||
dispatch('fetchAndUpdateNotifications')
|
||||
},
|
||||
watch: {
|
||||
unseenCount (count) {
|
||||
if (count > 0) {
|
||||
|
|
|
@ -197,7 +197,7 @@
|
|||
>
|
||||
<StatusPopover
|
||||
v-if="!isPreview"
|
||||
:status-id="status.in_reply_to_status_id"
|
||||
:status-id="status.parent_visible && status.in_reply_to_status_id"
|
||||
class="reply-to-popover"
|
||||
style="min-width: 0"
|
||||
>
|
||||
|
@ -208,7 +208,12 @@
|
|||
@click.prevent="gotoOriginal(status.in_reply_to_status_id)"
|
||||
>
|
||||
<i class="button-icon icon-reply" />
|
||||
<span class="faint-link reply-to-text">{{ $t('status.reply_to') }}</span>
|
||||
<span
|
||||
class="faint-link reply-to-text"
|
||||
:class="{ 'strikethrough': !status.parent_visible }"
|
||||
>
|
||||
{{ $t('status.reply_to') }}
|
||||
</span>
|
||||
</a>
|
||||
</StatusPopover>
|
||||
<span
|
||||
|
@ -526,6 +531,10 @@ $status-margin: 0.75em;
|
|||
margin: 0 0.4em 0 0.2em;
|
||||
}
|
||||
|
||||
.strikethrough {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.replies-separator {
|
||||
margin-left: 0.4em;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,10 @@ const StatusPopover = {
|
|||
methods: {
|
||||
enter () {
|
||||
if (!this.status) {
|
||||
if (!this.statusId) {
|
||||
this.error = true
|
||||
return
|
||||
}
|
||||
this.$store.dispatch('fetchStatus', this.statusId)
|
||||
.then(data => (this.error = false))
|
||||
.catch(e => (this.error = true))
|
||||
|
|
|
@ -164,6 +164,9 @@
|
|||
"load_all_hint": "Loaded first {saneAmount} emoji, loading all emoji may cause performance issues.",
|
||||
"load_all": "Loading all {emojiAmount} emoji"
|
||||
},
|
||||
"errors": {
|
||||
"storage_unavailable": "Pleroma could not access browser storage. Your login or your local settings won't be saved and you might encounter unexpected issues. Try enabling cookies."
|
||||
},
|
||||
"interactions": {
|
||||
"favs_repeats": "Repeats and Favorites",
|
||||
"follows": "New follows",
|
||||
|
|
16
src/main.js
16
src/main.js
|
@ -62,7 +62,15 @@ const persistedStateOptions = {
|
|||
};
|
||||
|
||||
(async () => {
|
||||
const persistedState = await createPersistedState(persistedStateOptions)
|
||||
let storageError = false
|
||||
const plugins = [pushNotifications]
|
||||
try {
|
||||
const persistedState = await createPersistedState(persistedStateOptions)
|
||||
plugins.push(persistedState)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
storageError = true
|
||||
}
|
||||
const store = new Vuex.Store({
|
||||
modules: {
|
||||
i18n: {
|
||||
|
@ -85,11 +93,13 @@ const persistedStateOptions = {
|
|||
polls: pollsModule,
|
||||
postStatus: postStatusModule
|
||||
},
|
||||
plugins: [persistedState, pushNotifications],
|
||||
plugins,
|
||||
strict: false // Socket modifies itself, let's ignore this for now.
|
||||
// strict: process.env.NODE_ENV !== 'production'
|
||||
})
|
||||
|
||||
if (storageError) {
|
||||
store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' })
|
||||
}
|
||||
afterStoreSetup({ store, i18n })
|
||||
})()
|
||||
|
||||
|
|
|
@ -138,9 +138,6 @@ const api = {
|
|||
if (!fetcher) return
|
||||
store.commit('removeFetcher', { fetcherName: 'notifications', fetcher })
|
||||
},
|
||||
fetchAndUpdateNotifications (store) {
|
||||
store.state.backendInteractor.fetchAndUpdateNotifications({ store })
|
||||
},
|
||||
|
||||
// Follow requests
|
||||
startFetchingFollowRequests (store) {
|
||||
|
|
|
@ -14,7 +14,8 @@ const defaultState = {
|
|||
window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)')
|
||||
)
|
||||
},
|
||||
mobileLayout: false
|
||||
mobileLayout: false,
|
||||
globalNotices: []
|
||||
}
|
||||
|
||||
const interfaceMod = {
|
||||
|
@ -58,6 +59,12 @@ const interfaceMod = {
|
|||
if (!state.settingsModalLoaded) {
|
||||
state.settingsModalLoaded = true
|
||||
}
|
||||
},
|
||||
pushGlobalNotice (state, notice) {
|
||||
state.globalNotices.push(notice)
|
||||
},
|
||||
removeGlobalNotice (state, notice) {
|
||||
state.globalNotices = state.globalNotices.filter(n => n !== notice)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
@ -81,6 +88,28 @@ const interfaceMod = {
|
|||
},
|
||||
togglePeekSettingsModal ({ commit }) {
|
||||
commit('togglePeekSettingsModal')
|
||||
},
|
||||
pushGlobalNotice (
|
||||
{ commit, dispatch },
|
||||
{
|
||||
messageKey,
|
||||
messageArgs = {},
|
||||
level = 'error',
|
||||
timeout = 0
|
||||
}) {
|
||||
const notice = {
|
||||
messageKey,
|
||||
messageArgs,
|
||||
level
|
||||
}
|
||||
if (timeout) {
|
||||
setTimeout(() => dispatch('removeGlobalNotice', notice), timeout)
|
||||
}
|
||||
commit('pushGlobalNotice', notice)
|
||||
return notice
|
||||
},
|
||||
removeGlobalNotice ({ commit }, notice) {
|
||||
commit('removeGlobalNotice', notice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,10 +12,6 @@ const backendInteractorService = credentials => ({
|
|||
return notificationsFetcher.startFetching({ store, credentials })
|
||||
},
|
||||
|
||||
fetchAndUpdateNotifications ({ store }) {
|
||||
return notificationsFetcher.fetchAndUpdate({ store, credentials })
|
||||
},
|
||||
|
||||
startFetchingFollowRequests ({ store }) {
|
||||
return followRequestFetcher.startFetching({ store, credentials })
|
||||
},
|
||||
|
|
|
@ -251,6 +251,7 @@ export const parseStatus = (data) => {
|
|||
output.in_reply_to_screen_name = data.pleroma.in_reply_to_account_acct
|
||||
output.thread_muted = pleroma.thread_muted
|
||||
output.emoji_reactions = pleroma.emoji_reactions
|
||||
output.parent_visible = pleroma.parent_visible === undefined ? true : pleroma.parent_visible
|
||||
} else {
|
||||
output.text = data.content
|
||||
output.summary = data.spoiler_text
|
||||
|
|
|
@ -27,14 +27,18 @@ const fetchAndUpdate = ({ store, credentials, older = false }) => {
|
|||
}
|
||||
const result = fetchNotifications({ store, args, older })
|
||||
|
||||
// load unread notifications repeatedly to provide consistency between browser tabs
|
||||
// If there's any unread notifications, try fetch notifications since
|
||||
// the newest read notification to check if any of the unread notifs
|
||||
// have changed their 'seen' state (marked as read in another session), so
|
||||
// we can update the state in this session to mark them as read as well.
|
||||
// The normal maxId-check does not tell if older notifications have changed
|
||||
const notifications = timelineData.data
|
||||
const readNotifsIds = notifications.filter(n => n.seen).map(n => n.id)
|
||||
if (readNotifsIds.length) {
|
||||
const numUnseenNotifs = notifications.length - readNotifsIds.length
|
||||
if (numUnseenNotifs > 0) {
|
||||
args['since'] = Math.max(...readNotifsIds)
|
||||
fetchNotifications({ store, args, older })
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ export const DEFAULT_OPACITY = {
|
|||
alert: 0.5,
|
||||
input: 0.5,
|
||||
faint: 0.5,
|
||||
underlay: 0.15
|
||||
underlay: 0.15,
|
||||
alertPopup: 0.95
|
||||
}
|
||||
|
||||
/** SUBJECT TO CHANGE IN THE FUTURE, this is all beta
|
||||
|
@ -627,6 +628,39 @@ export const SLOT_INHERITANCE = {
|
|||
textColor: true
|
||||
},
|
||||
|
||||
alertPopupError: {
|
||||
depends: ['alertError'],
|
||||
opacity: 'alertPopup'
|
||||
},
|
||||
alertPopupErrorText: {
|
||||
depends: ['alertErrorText'],
|
||||
layer: 'popover',
|
||||
variant: 'alertPopupError',
|
||||
textColor: true
|
||||
},
|
||||
|
||||
alertPopupWarning: {
|
||||
depends: ['alertWarning'],
|
||||
opacity: 'alertPopup'
|
||||
},
|
||||
alertPopupWarningText: {
|
||||
depends: ['alertWarningText'],
|
||||
layer: 'popover',
|
||||
variant: 'alertPopupWarning',
|
||||
textColor: true
|
||||
},
|
||||
|
||||
alertPopupNeutral: {
|
||||
depends: ['alertNeutral'],
|
||||
opacity: 'alertPopup'
|
||||
},
|
||||
alertPopupNeutralText: {
|
||||
depends: ['alertNeutralText'],
|
||||
layer: 'popover',
|
||||
variant: 'alertPopupNeutral',
|
||||
textColor: true
|
||||
},
|
||||
|
||||
badgeNotification: '--cRed',
|
||||
badgeNotificationText: {
|
||||
depends: ['text', 'badgeNotification'],
|
||||
|
|
Loading…
Reference in New Issue