diff options
Diffstat (limited to 'src/services')
7 files changed, 109 insertions, 22 deletions
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 2f579ed2..174add70 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -1,5 +1,5 @@ import { each, map, concat, last, get } from 'lodash' -import { parseStatus, parseUser, parseNotification, parseAttachment } from '../entity_normalizer/entity_normalizer.service.js' +import { parseStatus, parseUser, parseNotification, parseAttachment, parseLinkHeaderPagination } from '../entity_normalizer/entity_normalizer.service.js' import { RegistrationError, StatusCodeError } from '../errors/errors' /* eslint-env browser */ @@ -50,6 +50,7 @@ const MASTODON_USER_URL = '/api/v1/accounts' const MASTODON_USER_RELATIONSHIPS_URL = '/api/v1/accounts/relationships' const MASTODON_USER_TIMELINE_URL = id => `/api/v1/accounts/${id}/statuses` const MASTODON_TAG_TIMELINE_URL = tag => `/api/v1/timelines/tag/${tag}` +const MASTODON_BOOKMARK_TIMELINE_URL = '/api/v1/bookmarks' const MASTODON_USER_BLOCKS_URL = '/api/v1/blocks/' const MASTODON_USER_MUTES_URL = '/api/v1/mutes/' const MASTODON_BLOCK_USER_URL = id => `/api/v1/accounts/${id}/block` @@ -58,6 +59,8 @@ const MASTODON_MUTE_USER_URL = id => `/api/v1/accounts/${id}/mute` const MASTODON_UNMUTE_USER_URL = id => `/api/v1/accounts/${id}/unmute` const MASTODON_SUBSCRIBE_USER = id => `/api/v1/pleroma/accounts/${id}/subscribe` const MASTODON_UNSUBSCRIBE_USER = id => `/api/v1/pleroma/accounts/${id}/unsubscribe` +const MASTODON_BOOKMARK_STATUS_URL = id => `/api/v1/statuses/${id}/bookmark` +const MASTODON_UNBOOKMARK_STATUS_URL = id => `/api/v1/statuses/${id}/unbookmark` const MASTODON_POST_STATUS_URL = '/api/v1/statuses' const MASTODON_MEDIA_UPLOAD_URL = '/api/v1/media' const MASTODON_VOTE_URL = id => `/api/v1/polls/${id}/votes` @@ -498,7 +501,8 @@ const fetchTimeline = ({ until = false, userId = false, tag = false, - withMuted = false + withMuted = false, + replyVisibility = 'all' }) => { const timelineUrls = { public: MASTODON_PUBLIC_TIMELINE, @@ -509,7 +513,8 @@ const fetchTimeline = ({ user: MASTODON_USER_TIMELINE_URL, media: MASTODON_USER_TIMELINE_URL, favorites: MASTODON_USER_FAVORITES_TIMELINE_URL, - tag: MASTODON_TAG_TIMELINE_URL + tag: MASTODON_TAG_TIMELINE_URL, + bookmarks: MASTODON_BOOKMARK_TIMELINE_URL } const isNotifications = timeline === 'notifications' const params = [] @@ -538,9 +543,12 @@ const fetchTimeline = ({ if (timeline === 'public' || timeline === 'publicAndExternal') { params.push(['only_media', false]) } - if (timeline !== 'favorites') { + if (timeline !== 'favorites' && timeline !== 'bookmarks') { params.push(['with_muted', withMuted]) } + if (replyVisibility !== 'all') { + params.push(['reply_visibility', replyVisibility]) + } params.push(['limit', 20]) @@ -548,16 +556,20 @@ const fetchTimeline = ({ url += `?${queryString}` let status = '' let statusText = '' + let pagination = {} return fetch(url, { headers: authHeaders(credentials) }) .then((data) => { status = data.status statusText = data.statusText + pagination = parseLinkHeaderPagination(data.headers.get('Link'), { + flakeId: timeline !== 'bookmarks' && timeline !== 'notifications' + }) return data }) .then((data) => data.json()) .then((data) => { if (!data.error) { - return data.map(isNotifications ? parseNotification : parseStatus) + return { data: data.map(isNotifications ? parseNotification : parseStatus), pagination } } else { data.status = status data.statusText = statusText @@ -608,6 +620,22 @@ const unretweet = ({ id, credentials }) => { .then((data) => parseStatus(data)) } +const bookmarkStatus = ({ id, credentials }) => { + return promisedRequest({ + url: MASTODON_BOOKMARK_STATUS_URL(id), + headers: authHeaders(credentials), + method: 'POST' + }) +} + +const unbookmarkStatus = ({ id, credentials }) => { + return promisedRequest({ + url: MASTODON_UNBOOKMARK_STATUS_URL(id), + headers: authHeaders(credentials), + method: 'POST' + }) +} + const postStatus = ({ credentials, status, @@ -1144,6 +1172,8 @@ const apiService = { unfavorite, retweet, unretweet, + bookmarkStatus, + unbookmarkStatus, postStatus, deleteStatus, uploadMedia, diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js index e1c32860..45e6bd0e 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -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 }) }, diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index 3bdb92f3..ec83c02a 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -1,4 +1,5 @@ import escape from 'escape-html' +import parseLinkHeader from 'parse-link-header' import { isStatusNotification } from '../notification_utils/notification_utils.js' const qvitterStatusType = (status) => { @@ -232,6 +233,8 @@ export const parseStatus = (data) => { output.repeated = data.reblogged output.repeat_num = data.reblogs_count + output.bookmarked = data.bookmarked + output.type = data.reblog ? 'retweet' : 'status' output.nsfw = data.sensitive @@ -248,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 @@ -381,3 +385,16 @@ const isNsfw = (status) => { const nsfwRegex = /#nsfw/i return (status.tags || []).includes('nsfw') || !!(status.text || '').match(nsfwRegex) } + +export const parseLinkHeaderPagination = (linkHeader, opts = {}) => { + const flakeId = opts.flakeId + const parsedLinkHeader = parseLinkHeader(linkHeader) + if (!parsedLinkHeader) return + const maxId = parsedLinkHeader.next.max_id + const minId = parsedLinkHeader.prev.min_id + + return { + maxId: flakeId ? maxId : parseInt(maxId, 10), + minId: flakeId ? minId : parseInt(minId, 10) + } +} diff --git a/src/services/follow_request_fetcher/follow_request_fetcher.service.js b/src/services/follow_request_fetcher/follow_request_fetcher.service.js index 786740b7..93fac9bc 100644 --- a/src/services/follow_request_fetcher/follow_request_fetcher.service.js +++ b/src/services/follow_request_fetcher/follow_request_fetcher.service.js @@ -4,6 +4,7 @@ const fetchAndUpdate = ({ store, credentials }) => { return apiService.fetchFollowRequests({ credentials }) .then((requests) => { store.commit('setFollowRequests', requests) + store.commit('addNewUsers', requests) }, () => {}) .catch(() => {}) } diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js index 64499a1b..d282074a 100644 --- a/src/services/notifications_fetcher/notifications_fetcher.service.js +++ b/src/services/notifications_fetcher/notifications_fetcher.service.js @@ -27,21 +27,25 @@ 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 } } const fetchNotifications = ({ store, args, older }) => { return apiService.fetchTimeline(args) - .then((notifications) => { + .then(({ data: notifications }) => { update({ store, notifications, older }) return notifications }, () => store.dispatch('setNotificationsError', { value: true })) diff --git a/src/services/theme_data/pleromafe.js b/src/services/theme_data/pleromafe.js index b577cfab..6b25cd6f 100644 --- a/src/services/theme_data/pleromafe.js +++ b/src/services/theme_data/pleromafe.js @@ -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'], diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js index c6b28ad5..214294eb 100644 --- a/src/services/timeline_fetcher/timeline_fetcher.service.js +++ b/src/services/timeline_fetcher/timeline_fetcher.service.js @@ -2,7 +2,7 @@ import { camelCase } from 'lodash' import apiService from '../api/api.service.js' -const update = ({ store, statuses, timeline, showImmediately, userId }) => { +const update = ({ store, statuses, timeline, showImmediately, userId, pagination }) => { const ccTimeline = camelCase(timeline) store.dispatch('setError', { value: false }) @@ -12,7 +12,8 @@ const update = ({ store, statuses, timeline, showImmediately, userId }) => { timeline: ccTimeline, userId, statuses, - showImmediately + showImmediately, + pagination }) } @@ -30,7 +31,8 @@ const fetchAndUpdate = ({ const rootState = store.rootState || store.state const { getters } = store const timelineData = rootState.statuses.timelines[camelCase(timeline)] - const hideMutedPosts = getters.mergedConfig.hideMutedPosts + const { hideMutedPosts, replyVisibility } = getters.mergedConfig + const loggedIn = !!rootState.users.currentUser if (older) { args['until'] = until || timelineData.minId @@ -41,20 +43,23 @@ const fetchAndUpdate = ({ args['userId'] = userId args['tag'] = tag args['withMuted'] = !hideMutedPosts + if (loggedIn) args['replyVisibility'] = replyVisibility const numStatusesBeforeFetch = timelineData.statuses.length return apiService.fetchTimeline(args) - .then((statuses) => { - if (statuses.error) { - store.dispatch('setErrorData', { value: statuses }) + .then(response => { + if (response.error) { + store.dispatch('setErrorData', { value: response }) return } + + const { data: statuses, pagination } = response if (!older && statuses.length >= 20 && !timelineData.loading && numStatusesBeforeFetch > 0) { store.dispatch('queueFlush', { timeline: timeline, id: timelineData.maxId }) } - update({ store, statuses, timeline, showImmediately, userId }) - return statuses + update({ store, statuses, timeline, showImmediately, userId, pagination }) + return { statuses, pagination } }, () => store.dispatch('setError', { value: true })) } |
