aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShpuld Shpuldson <shp@cock.li>2020-11-19 12:38:35 +0200
committerShpuld Shpuldson <shp@cock.li>2020-11-19 12:38:35 +0200
commitb1ab09b348014a8e70097158bda4d2e1226e4cb6 (patch)
tree65740e91d81cbaaca444440a95713fdb2a46b849 /src
parent419df9d44673bf1abe3cda7ba6cafee9dd6eaba4 (diff)
parentd770bab1b0e5d543547c59d86aef533f77ceafe2 (diff)
fix merge conflict
Diffstat (limited to 'src')
-rw-r--r--src/boot/after_store.js3
-rw-r--r--src/components/notifications/notifications.js3
-rw-r--r--src/components/notifications/notifications.vue7
-rw-r--r--src/components/timeline/timeline.js12
-rw-r--r--src/components/timeline/timeline.vue24
-rw-r--r--src/components/timeline_menu/timeline_menu.js5
-rw-r--r--src/i18n/en.json3
-rw-r--r--src/modules/statuses.js23
-rw-r--r--src/modules/users.js6
-rw-r--r--src/services/api/api.service.js2
-rw-r--r--src/services/entity_normalizer/entity_normalizer.service.js12
-rw-r--r--src/services/favicon_service/favicon_service.js61
-rw-r--r--src/services/notifications_fetcher/notifications_fetcher.service.js18
-rw-r--r--src/services/timeline_fetcher/timeline_fetcher.service.js18
14 files changed, 116 insertions, 81 deletions
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index 3cbbf020..b472fcf6 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -7,6 +7,7 @@ import { getOrCreateApp, getClientToken } from '../services/new_api/oauth.js'
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
import { applyTheme } from '../services/style_setter/style_setter.js'
+import FaviconService from '../services/favicon_service/favicon_service.js'
let staticInitialResults = null
@@ -326,6 +327,8 @@ const afterStoreSetup = async ({ store, i18n }) => {
const width = windowWidth()
store.dispatch('setMobileLayout', width <= 800)
+ FaviconService.initFaviconService()
+
const overrides = window.___pleromafe_dev_overrides || {}
const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin
store.dispatch('setInstanceOption', { name: 'server', value: server })
diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js
index 4b479e13..49258563 100644
--- a/src/components/notifications/notifications.js
+++ b/src/components/notifications/notifications.js
@@ -6,6 +6,7 @@ import {
filteredNotificationsFromStore,
unseenNotificationsFromStore
} from '../../services/notification_utils/notification_utils.js'
+import FaviconService from '../../services/favicon_service/favicon_service.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
@@ -75,8 +76,10 @@ const Notifications = {
watch: {
unseenCountTitle (count) {
if (count > 0) {
+ FaviconService.drawFaviconBadge()
this.$store.dispatch('setPageTitle', `(${count})`)
} else {
+ FaviconService.clearFaviconBadge()
this.$store.dispatch('setPageTitle', '')
}
}
diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue
index bd875cca..b976026e 100644
--- a/src/components/notifications/notifications.vue
+++ b/src/components/notifications/notifications.vue
@@ -15,13 +15,6 @@
class="badge badge-notification unseen-count"
>{{ unseenCount }}</span>
</div>
- <div
- v-if="error"
- class="loadmore-error alert error"
- @click.prevent
- >
- {{ $t('timeline.error_fetching') }}
- </div>
<button
v-if="unseenCount"
class="read-button"
diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js
index cba46daf..665d195e 100644
--- a/src/components/timeline/timeline.js
+++ b/src/components/timeline/timeline.js
@@ -50,17 +50,10 @@ const Timeline = {
TimelineMenu
},
computed: {
- timelineError () {
- return this.$store.state.statuses.error
- },
- errorData () {
- return this.$store.state.statuses.errorData
- },
newStatusCount () {
return this.timeline.newStatusCount
},
showLoadButton () {
- if (this.timelineError || this.errorData) return false
return this.timeline.newStatusCount > 0 || this.timeline.flushMarker !== 0
},
loadButtonString () {
@@ -171,11 +164,12 @@ const Timeline = {
userId: this.userId,
tag: this.tag
}).then(({ statuses }) => {
- store.commit('setLoading', { timeline: this.timelineName, value: false })
if (statuses && statuses.length === 0) {
this.bottomedOut = true
}
- })
+ }).finally(() =>
+ store.commit('setLoading', { timeline: this.timelineName, value: false })
+ )
}, 1000, this),
determineVisibleStatuses () {
if (!this.$refs.timeline) return
diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue
index 04859852..d4da2a87 100644
--- a/src/components/timeline/timeline.vue
+++ b/src/components/timeline/timeline.vue
@@ -2,22 +2,8 @@
<div :class="[classes.root, 'Timeline']">
<div :class="classes.header">
<TimelineMenu v-if="!embedded" />
- <div
- v-if="timelineError"
- class="loadmore-error alert error"
- @click.prevent
- >
- {{ $t('timeline.error_fetching') }}
- </div>
- <div
- v-else-if="errorData"
- class="loadmore-error alert error"
- @click.prevent
- >
- {{ errorData.statusText }}
- </div>
<button
- v-else-if="showLoadButton"
+ v-if="showLoadButton"
class="loadmore-button"
@click.prevent="showNewStatuses"
>
@@ -76,18 +62,12 @@
{{ $t('timeline.no_more_statuses') }}
</div>
<a
- v-else-if="!timeline.loading && !errorData"
+ v-else-if="!timeline.loading"
href="#"
@click.prevent="fetchOlderStatuses()"
>
<div class="new-status-notification text-center panel-footer">{{ $t('timeline.load_older') }}</div>
</a>
- <a
- v-else-if="errorData"
- href="#"
- >
- <div class="new-status-notification text-center panel-footer">{{ errorData.error }}</div>
- </a>
<div
v-else
class="new-status-notification text-center panel-footer"
diff --git a/src/components/timeline_menu/timeline_menu.js b/src/components/timeline_menu/timeline_menu.js
index 4ccd52b4..ef8a5813 100644
--- a/src/components/timeline_menu/timeline_menu.js
+++ b/src/components/timeline_menu/timeline_menu.js
@@ -19,7 +19,7 @@ library.add(
faChevronDown
)
-// Route -> i18n key mapping, exported andnot in the computed
+// Route -> i18n key mapping, exported and not in the computed
// because nav panel benefits from the same information.
export const timelineNames = () => {
return {
@@ -27,8 +27,7 @@ export const timelineNames = () => {
'bookmarks': 'nav.bookmarks',
'dms': 'nav.dms',
'public-timeline': 'nav.public_tl',
- 'public-external-timeline': 'nav.twkn',
- 'tag-timeline': 'tag'
+ 'public-external-timeline': 'nav.twkn'
}
}
diff --git a/src/i18n/en.json b/src/i18n/en.json
index d3d57562..3f6ae372 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -130,6 +130,7 @@
},
"notifications": {
"broken_favorite": "Unknown status, searching for it…",
+ "error": "Error fetching notifications: {0}",
"favorited_you": "favorited your status",
"followed_you": "followed you",
"follow_request": "wants to follow you",
@@ -634,7 +635,7 @@
"timeline": {
"collapse": "Collapse",
"conversation": "Conversation",
- "error_fetching": "Error fetching updates",
+ "error": "Error fetching timeline: {0}",
"load_older": "Load older statuses",
"no_retweet_hint": "Post is marked as followers-only or direct and cannot be repeated",
"repeated": "repeated",
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
index e673141d..33c68c57 100644
--- a/src/modules/statuses.js
+++ b/src/modules/statuses.js
@@ -39,8 +39,7 @@ const emptyNotifications = () => ({
minId: Number.POSITIVE_INFINITY,
data: [],
idStore: {},
- loading: false,
- error: false
+ loading: false
})
export const defaultState = () => ({
@@ -50,8 +49,6 @@ export const defaultState = () => ({
maxId: 0,
notifications: emptyNotifications(),
favorites: new Set(),
- error: false,
- errorData: null,
timelines: {
mentions: emptyTl(),
public: emptyTl(),
@@ -462,18 +459,9 @@ export const mutations = {
const newStatus = state.allStatusesObject[id]
newStatus.nsfw = nsfw
},
- setError (state, { value }) {
- state.error = value
- },
- setErrorData (state, { value }) {
- state.errorData = value
- },
setNotificationsLoading (state, { value }) {
state.notifications.loading = value
},
- setNotificationsError (state, { value }) {
- state.notifications.error = value
- },
setNotificationsSilence (state, { value }) {
state.notifications.desktopNotificationSilence = value
},
@@ -588,18 +576,9 @@ const statuses = {
}
commit('addNewNotifications', { dispatch, notifications, older, rootGetters, newNotificationSideEffects })
},
- setError ({ rootState, commit }, { value }) {
- commit('setError', { value })
- },
- setErrorData ({ rootState, commit }, { value }) {
- commit('setErrorData', { value })
- },
setNotificationsLoading ({ rootState, commit }, { value }) {
commit('setNotificationsLoading', { value })
},
- setNotificationsError ({ rootState, commit }, { value }) {
- commit('setNotificationsError', { value })
- },
setNotificationsSilence ({ rootState, commit }, { value }) {
commit('setNotificationsSilence', { value })
},
diff --git a/src/modules/users.js b/src/modules/users.js
index 13df9df4..4bdf3360 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -137,11 +137,11 @@ export const mutations = {
},
saveFriendIds (state, { id, friendIds }) {
const user = state.usersObject[id]
- user.friendIds = uniq(concat(user.friendIds, friendIds))
+ user.friendIds = uniq(concat(user.friendIds || [], friendIds))
},
saveFollowerIds (state, { id, followerIds }) {
const user = state.usersObject[id]
- user.followerIds = uniq(concat(user.followerIds, followerIds))
+ user.followerIds = uniq(concat(user.followerIds || [], followerIds))
},
// Because frontend doesn't have a reason to keep these stuff in memory
// outside of viewing someones user profile.
@@ -202,7 +202,9 @@ export const mutations = {
},
setPinnedToUser (state, status) {
const user = state.usersObject[status.user.id]
+ user.pinnedStatusIds = user.pinnedStatusIds || []
const index = user.pinnedStatusIds.indexOf(status.id)
+
if (status.pinned && index === -1) {
user.pinnedStatusIds.push(status.id)
} else if (!status.pinned && index !== -1) {
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index 22b5e8ba..8da933c4 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -560,7 +560,7 @@ const fetchTimeline = ({
})
.then((data) => data.json())
.then((data) => {
- if (!data.error) {
+ if (!data.errors) {
return { data: data.map(isNotifications ? parseNotification : parseStatus), pagination }
} else {
data.status = status
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index a4c1a1bf..e3a52489 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -2,6 +2,15 @@ import escape from 'escape-html'
import parseLinkHeader from 'parse-link-header'
import { isStatusNotification } from '../notification_utils/notification_utils.js'
+/** NOTICE! **
+ * Do not initialize UI-generated data here.
+ * It will override existing data.
+ *
+ * i.e. user.pinnedStatusIds was set to [] here
+ * UI code would update it with data but upon next user fetch
+ * it would be reverted back to []
+ */
+
const qvitterStatusType = (status) => {
if (status.is_post_verb) {
return 'status'
@@ -173,9 +182,6 @@ export const parseUser = (data) => {
output.locked = data.locked
output.followers_count = data.followers_count
output.statuses_count = data.statuses_count
- output.friendIds = []
- output.followerIds = []
- output.pinnedStatusIds = []
if (data.pleroma) {
output.follow_request_count = data.pleroma.follow_request_count
diff --git a/src/services/favicon_service/favicon_service.js b/src/services/favicon_service/favicon_service.js
new file mode 100644
index 00000000..d1ddee41
--- /dev/null
+++ b/src/services/favicon_service/favicon_service.js
@@ -0,0 +1,61 @@
+import { find } from 'lodash'
+
+const createFaviconService = () => {
+ let favimg, favcanvas, favcontext, favicon
+ const faviconWidth = 128
+ const faviconHeight = 128
+ const badgeRadius = 32
+
+ const initFaviconService = () => {
+ const nodes = document.getElementsByTagName('link')
+ favicon = find(nodes, node => node.rel === 'icon')
+ if (favicon) {
+ favcanvas = document.createElement('canvas')
+ favcanvas.width = faviconWidth
+ favcanvas.height = faviconHeight
+ favimg = new Image()
+ favimg.src = favicon.href
+ favcontext = favcanvas.getContext('2d')
+ }
+ }
+
+ const isImageLoaded = (img) => img.complete && img.naturalHeight !== 0
+
+ const clearFaviconBadge = () => {
+ if (!favimg || !favcontext || !favicon) return
+
+ favcontext.clearRect(0, 0, faviconWidth, faviconHeight)
+ if (isImageLoaded(favimg)) {
+ favcontext.drawImage(favimg, 0, 0, favimg.width, favimg.height, 0, 0, faviconWidth, faviconHeight)
+ }
+ favicon.href = favcanvas.toDataURL('image/png')
+ }
+
+ const drawFaviconBadge = () => {
+ if (!favimg || !favcontext || !favcontext) return
+
+ clearFaviconBadge()
+
+ const style = getComputedStyle(document.body)
+ const badgeColor = `${style.getPropertyValue('--badgeNotification') || 'rgb(240, 100, 100)'}`
+
+ if (isImageLoaded(favimg)) {
+ favcontext.drawImage(favimg, 0, 0, favimg.width, favimg.height, 0, 0, faviconWidth, faviconHeight)
+ }
+ favcontext.fillStyle = badgeColor
+ favcontext.beginPath()
+ favcontext.arc(faviconWidth - badgeRadius, badgeRadius, badgeRadius, 0, 2 * Math.PI, false)
+ favcontext.fill()
+ favicon.href = favcanvas.toDataURL('image/png')
+ }
+
+ return {
+ initFaviconService,
+ clearFaviconBadge,
+ drawFaviconBadge
+ }
+}
+
+const FaviconService = createFaviconService()
+
+export default FaviconService
diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js
index c908b644..beeb167c 100644
--- a/src/services/notifications_fetcher/notifications_fetcher.service.js
+++ b/src/services/notifications_fetcher/notifications_fetcher.service.js
@@ -2,7 +2,6 @@ import apiService from '../api/api.service.js'
import { promiseInterval } from '../promise_interval/promise_interval.js'
const update = ({ store, notifications, older }) => {
- store.dispatch('setNotificationsError', { value: false })
store.dispatch('addNewNotifications', { notifications, older })
}
@@ -47,11 +46,22 @@ const fetchAndUpdate = ({ store, credentials, older = false }) => {
const fetchNotifications = ({ store, args, older }) => {
return apiService.fetchTimeline(args)
- .then(({ data: notifications }) => {
+ .then((response) => {
+ if (response.errors) {
+ throw new Error(`${response.status} ${response.statusText}`)
+ }
+ const notifications = response.data
update({ store, notifications, older })
return notifications
- }, () => store.dispatch('setNotificationsError', { value: true }))
- .catch(() => store.dispatch('setNotificationsError', { value: true }))
+ })
+ .catch((error) => {
+ store.dispatch('pushGlobalNotice', {
+ level: 'error',
+ messageKey: 'notifications.error',
+ messageArgs: [error.message],
+ timeout: 5000
+ })
+ })
}
const startFetching = ({ credentials, store }) => {
diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js
index 72ea4890..921df3ed 100644
--- a/src/services/timeline_fetcher/timeline_fetcher.service.js
+++ b/src/services/timeline_fetcher/timeline_fetcher.service.js
@@ -6,9 +6,6 @@ import { promiseInterval } from '../promise_interval/promise_interval.js'
const update = ({ store, statuses, timeline, showImmediately, userId, pagination }) => {
const ccTimeline = camelCase(timeline)
- store.dispatch('setError', { value: false })
- store.dispatch('setErrorData', { value: null })
-
store.dispatch('addNewStatuses', {
timeline: ccTimeline,
userId,
@@ -52,9 +49,8 @@ const fetchAndUpdate = ({
return apiService.fetchTimeline(args)
.then(response => {
- if (response.error) {
- store.dispatch('setErrorData', { value: response })
- return
+ if (response.errors) {
+ throw new Error(`${response.status} ${response.statusText}`)
}
const { data: statuses, pagination } = response
@@ -63,7 +59,15 @@ const fetchAndUpdate = ({
}
update({ store, statuses, timeline, showImmediately, userId, pagination })
return { statuses, pagination }
- }, () => store.dispatch('setError', { value: true }))
+ })
+ .catch((error) => {
+ store.dispatch('pushGlobalNotice', {
+ level: 'error',
+ messageKey: 'timeline.error',
+ messageArgs: [error.message],
+ timeout: 5000
+ })
+ })
}
const startFetching = ({ timeline = 'friends', credentials, store, userId = false, tag = false }) => {