Merge branch 'notifications' into shigusegubu

* notifications:
  error display
  removed style for rounding bottom part of notifications because there's now always "load more" footer
  fix custom emoji in username, fix gif avatar not being animated when hovering on the notification
  Hide initial desktop notifications spam when FE is opened and there's a lot of unseen notifications.
  Updated localization files
  Drop the entire thing about hidden "own" timeline since it doesn't necessarily contain all of the users posts (it doesn't contain DMs) even though it's "us". Since this is a workaround anyway just fetch home timeline instead. It could end up making more queries if user doesn't post that often.
This commit is contained in:
Henry Jameson 2018-08-20 20:46:18 +03:00
commit 83b2964f7c
9 changed files with 54 additions and 25 deletions

View file

@ -14,6 +14,9 @@ const Notifications = {
notifications () { notifications () {
return this.$store.state.statuses.notifications.data return this.$store.state.statuses.notifications.data
}, },
error () {
return this.$store.state.statuses.notifications.error
},
unseenNotifications () { unseenNotifications () {
return filter(this.notifications, ({seen}) => !seen) return filter(this.notifications, ({seen}) => !seen)
}, },

View file

@ -4,6 +4,10 @@
// a bit of a hack to allow scrolling below notifications // a bit of a hack to allow scrolling below notifications
padding-bottom: 15em; padding-bottom: 15em;
.title {
display: inline-block;
}
.panel { .panel {
background: $fallback--bg; background: $fallback--bg;
background: var(--bg, $fallback--bg) background: var(--bg, $fallback--bg)
@ -22,6 +26,8 @@
background: var(--btn, $fallback--btn); background: var(--btn, $fallback--btn);
color: $fallback--fg; color: $fallback--fg;
color: var(--fg, $fallback--fg); color: var(--fg, $fallback--fg);
display: flex;
align-items: baseline;
.read-button { .read-button {
position: absolute; position: absolute;
right: 0.7em; right: 0.7em;
@ -44,6 +50,19 @@
line-height: 1.3em; line-height: 1.3em;
} }
.loadmore-error {
position: absolute;
right: 0.6em;
font-size: 14px;
min-width: 6em;
font-family: sans-serif;
text-align: center;
padding: 0 0.25em 0 0.25em;
margin: 0;
color: $fallback--fg;
color: var(--fg, $fallback--fg);
}
.unseen { .unseen {
box-shadow: inset 4px 0 0 var(--cRed, $fallback--cRed); box-shadow: inset 4px 0 0 var(--cRed, $fallback--cRed);
padding-left: 0; padding-left: 0;
@ -79,7 +98,7 @@
} }
} }
&:hover .animated.avatar { &:hover .animated.avatar-compact {
canvas { canvas {
display: none; display: none;
} }
@ -155,6 +174,13 @@
max-width: 100%; max-width: 100%;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
img {
width: 14px;
height: 14px;
vertical-align: middle;
object-fit: contain
}
} }
.timeago { .timeago {
float: right; float: right;
@ -204,15 +230,4 @@
margin-bottom: 0.3em; margin-bottom: 0.3em;
} }
} }
// ugly as heck
&:last-child {
border-bottom: none;
border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius;
border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius);
.status-el {
border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius;
border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius);
}
}
} }

View file

@ -3,7 +3,10 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<span class="unseen-count" v-if="unseenCount">{{unseenCount}}</span> <span class="unseen-count" v-if="unseenCount">{{unseenCount}}</span>
{{$t('notifications.notifications')}} <div class="title"> {{$t('notifications.notifications')}}</div>
<div @click.prevent class="loadmore-error alert error" v-if="error">
{{$t('timeline.error_fetching')}}
</div>
<button v-if="unseenCount" @click.prevent="markAsSeen" class="read-button">{{$t('notifications.read')}}</button> <button v-if="unseenCount" @click.prevent="markAsSeen" class="read-button">{{$t('notifications.read')}}</button>
</div> </div>
<div class="panel-body"> <div class="panel-body">

View file

@ -355,7 +355,8 @@ const en = {
followed_you: 'followed you', followed_you: 'followed you',
favorited_you: 'favorited your status', favorited_you: 'favorited your status',
repeated_you: 'repeated your status', repeated_you: 'repeated your status',
broken_favorite: 'Unknown status, searching for it...' broken_favorite: 'Unknown status, searching for it...',
load_older: 'Load older notifications'
}, },
login: { login: {
login: 'Log in', login: 'Log in',
@ -1650,7 +1651,8 @@ const ru = {
followed_you: 'начал(а) читать вас', followed_you: 'начал(а) читать вас',
favorited_you: 'нравится ваш статус', favorited_you: 'нравится ваш статус',
repeated_you: 'повторил(а) ваш статус', repeated_you: 'повторил(а) ваш статус',
broken_favorite: 'Неизвестный статус, ищем...' broken_favorite: 'Неизвестный статус, ищем...',
load_older: 'Загрузить старые уведомления'
}, },
login: { login: {
login: 'Войти', login: 'Войти',

View file

@ -24,10 +24,12 @@ export const defaultState = {
allStatusesObject: {}, allStatusesObject: {},
maxId: 0, maxId: 0,
notifications: { notifications: {
desktopNotificationSilence: true,
maxId: 0, maxId: 0,
maxSavedId: 0, maxSavedId: 0,
minId: Number.POSITIVE_INFINITY, minId: Number.POSITIVE_INFINITY,
data: [], data: [],
error: false,
brokenFavorites: {} brokenFavorites: {}
}, },
favorites: new Set(), favorites: new Set(),
@ -36,7 +38,6 @@ export const defaultState = {
mentions: emptyTl(), mentions: emptyTl(),
public: emptyTl(), public: emptyTl(),
user: emptyTl(), user: emptyTl(),
own: emptyTl(),
publicAndExternal: emptyTl(), publicAndExternal: emptyTl(),
friends: emptyTl(), friends: emptyTl(),
tag: emptyTl() tag: emptyTl()
@ -315,7 +316,7 @@ const addNewNotifications = (state, { dispatch, notifications, older }) => {
result.image = action.attachments[0].url result.image = action.attachments[0].url
} }
if (fresh) { if (fresh && !state.notifications.desktopNotificationSilence) {
let notification = new window.Notification(title, result) let notification = new window.Notification(title, result)
// Chrome is known for not closing notifications automatically // Chrome is known for not closing notifications automatically
// according to MDN, anyway. // according to MDN, anyway.
@ -364,7 +365,10 @@ export const mutations = {
state.error = value state.error = value
}, },
setNotificationsError (state, { value }) { setNotificationsError (state, { value }) {
state.notificationsError = value state.notifications.error = value
},
setNotificationsSilence (state, { value }) {
state.notifications.desktopNotificationSilence = value
}, },
setProfileView (state, { v }) { setProfileView (state, { v }) {
// load followers / friends only when needed // load followers / friends only when needed
@ -402,6 +406,9 @@ const statuses = {
setNotificationsError ({ rootState, commit }, { value }) { setNotificationsError ({ rootState, commit }, { value }) {
commit('setNotificationsError', { value }) commit('setNotificationsError', { value })
}, },
setNotificationsSilence ({ rootState, commit }, { value }) {
commit('setNotificationsSilence', { value })
},
addFriends ({ rootState, commit }, { friends }) { addFriends ({ rootState, commit }, { friends }) {
commit('addFriends', { friends }) commit('addFriends', { friends })
}, },

View file

@ -107,8 +107,6 @@ const users = {
// Start getting fresh tweets. // Start getting fresh tweets.
store.dispatch('startFetching', 'friends') store.dispatch('startFetching', 'friends')
// Start getting our own posts, only really needed for mitigating broken favorites
store.dispatch('startFetching', ['own', user.id])
// Get user mutes and follower info // Get user mutes and follower info
store.rootState.api.backendInteractor.fetchMutes().then((mutedUsers) => { store.rootState.api.backendInteractor.fetchMutes().then((mutedUsers) => {

View file

@ -306,9 +306,6 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use
notifications: QVITTER_USER_NOTIFICATIONS_URL, notifications: QVITTER_USER_NOTIFICATIONS_URL,
'publicAndExternal': PUBLIC_AND_EXTERNAL_TIMELINE_URL, 'publicAndExternal': PUBLIC_AND_EXTERNAL_TIMELINE_URL,
user: QVITTER_USER_TIMELINE_URL, user: QVITTER_USER_TIMELINE_URL,
// separate timeline for own posts, so it won't break due to user timeline bugs
// really needed only for broken favorites
own: QVITTER_USER_TIMELINE_URL,
tag: TAG_TIMELINE_URL tag: TAG_TIMELINE_URL
} }

View file

@ -54,11 +54,11 @@ const backendInteractorService = (credentials) => {
return timelineFetcherService.startFetching({timeline, store, credentials, userId}) return timelineFetcherService.startFetching({timeline, store, credentials, userId})
} }
const fetchOldPost = ({store, postId}) => { const fetchOldPost = ({store, postId, timeline = 'friends'}) => {
return timelineFetcherService.fetchAndUpdate({ return timelineFetcherService.fetchAndUpdate({
store, store,
credentials, credentials,
timeline: 'own', timeline,
older: true, older: true,
until: postId + 1 until: postId + 1
}) })

View file

@ -30,6 +30,10 @@ const fetchAndUpdate = ({store, credentials, older = false}) => {
const startFetching = ({credentials, store}) => { const startFetching = ({credentials, store}) => {
fetchAndUpdate({ credentials, store }) fetchAndUpdate({ credentials, store })
const boundFetchAndUpdate = () => fetchAndUpdate({ credentials, store }) const boundFetchAndUpdate = () => fetchAndUpdate({ credentials, store })
// Initially there's set flag to silence all desktop notifications so
// that there won't spam of them when user just opened up the FE we
// reset that flag after a while to show new notifications once again.
setTimeout(() => store.dispatch('setNotificationsSilence', false), 10000)
return setInterval(boundFetchAndUpdate, 10000) return setInterval(boundFetchAndUpdate, 10000)
} }