aboutsummaryrefslogtreecommitdiff
path: root/src/services
diff options
context:
space:
mode:
authorHenry Jameson <me@hjkos.com>2019-04-27 12:26:17 +0300
committerHenry Jameson <me@hjkos.com>2019-04-27 12:26:17 +0300
commite0247e21f6fad72481d8f04271d3a15e0d827acc (patch)
treeacc8a11074865a9cbdc50e9b52da918c48f7235e /src/services
parent2b5cf61a8f9a84467495721f170df7ae1288959b (diff)
parent9e2fa50b74eb83e3c3eadb9a68d24ddaa1d9da48 (diff)
Merge remote-tracking branch 'upstream/develop' into webpack-4-dart-sass
* upstream/develop: (126 commits) entity normalizer: hook up in_reply_to_account_acct add BBCode strings fix follow button not updating bug in follow-card refer searched user objects from the global user rep set max-width of textarea in settings page Remove space in the timeline setting copy user_card.vue: Fix .emoji to apply to img Update oc.json Update oc.json Update oc.json Update oc.json replace pencil with wrench icon rebuild fontello with wrench icon added fetch all friends using pagination stop fetching user relationship when user is unauthorized Revert "recover border between basic-user-card using list component" remove extra spacing code readability fix typos clean up ...
Diffstat (limited to 'src/services')
-rw-r--r--src/services/api/api.service.js117
-rw-r--r--src/services/backend_interactor_service/backend_interactor_service.js42
-rw-r--r--src/services/entity_normalizer/entity_normalizer.service.js58
-rw-r--r--src/services/follow_manipulate/follow_manipulate.js20
-rw-r--r--src/services/notification_utils/notification_utils.js6
-rw-r--r--src/services/notifications_fetcher/notifications_fetcher.service.js28
6 files changed, 211 insertions, 60 deletions
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index 030c2f5e..6b255e9f 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -8,7 +8,6 @@ const BG_UPDATE_URL = '/api/qvitter/update_background_image.json'
const BANNER_UPDATE_URL = '/api/account/update_profile_banner.json'
const PROFILE_UPDATE_URL = '/api/account/update_profile.json'
const EXTERNAL_PROFILE_URL = '/api/externalprofile/show.json'
-const QVITTER_USER_NOTIFICATIONS_URL = '/api/qvitter/statuses/notifications.json'
const QVITTER_USER_NOTIFICATIONS_READ_URL = '/api/qvitter/statuses/notifications/read.json'
const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import'
const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account'
@@ -16,9 +15,14 @@ const CHANGE_PASSWORD_URL = '/api/pleroma/change_password'
const FOLLOW_REQUESTS_URL = '/api/pleroma/friend_requests'
const APPROVE_USER_URL = '/api/pleroma/friendships/approve'
const DENY_USER_URL = '/api/pleroma/friendships/deny'
+const TAG_USER_URL = '/api/pleroma/admin/users/tag'
+const PERMISSION_GROUP_URL = '/api/pleroma/admin/permission_group'
+const ACTIVATION_STATUS_URL = '/api/pleroma/admin/activation_status'
+const ADMIN_USER_URL = '/api/pleroma/admin/user'
const SUGGESTIONS_URL = '/api/v1/suggestions'
const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites'
+const MASTODON_USER_NOTIFICATIONS_URL = '/api/v1/notifications'
const MASTODON_FAVORITE_URL = id => `/api/v1/statuses/${id}/favourite`
const MASTODON_UNFAVORITE_URL = id => `/api/v1/statuses/${id}/unfavourite`
const MASTODON_RETWEET_URL = id => `/api/v1/statuses/${id}/reblog`
@@ -46,7 +50,7 @@ const MASTODON_UNMUTE_USER_URL = id => `/api/v1/accounts/${id}/unmute`
const MASTODON_POST_STATUS_URL = '/api/v1/statuses'
const MASTODON_MEDIA_UPLOAD_URL = '/api/v1/media'
-import { each, map } from 'lodash'
+import { each, map, concat, last } from 'lodash'
import { parseStatus, parseUser, parseNotification, parseAttachment } from '../entity_normalizer/entity_normalizer.service.js'
import 'whatwg-fetch'
import { StatusCodeError } from '../errors/errors'
@@ -290,10 +294,23 @@ const fetchFriends = ({id, maxId, sinceId, limit = 20, credentials}) => {
}
const exportFriends = ({id, credentials}) => {
- let url = MASTODON_FOLLOWING_URL(id) + `?all=true`
- return fetch(url, { headers: authHeaders(credentials) })
- .then((data) => data.json())
- .then((data) => data.map(parseUser))
+ return new Promise(async (resolve, reject) => {
+ try {
+ let friends = []
+ let more = true
+ while (more) {
+ const maxId = friends.length > 0 ? last(friends).id : undefined
+ const users = await fetchFriends({id, maxId, credentials})
+ friends = concat(friends, users)
+ if (users.length === 0) {
+ more = false
+ }
+ }
+ resolve(friends)
+ } catch (err) {
+ reject(err)
+ }
+ })
}
const fetchFollowers = ({id, maxId, sinceId, limit = 20, credentials}) => {
@@ -352,13 +369,93 @@ const fetchStatus = ({id, credentials}) => {
.then((data) => parseStatus(data))
}
+const tagUser = ({tag, credentials, ...options}) => {
+ const screenName = options.screen_name
+ const form = {
+ nicknames: [screenName],
+ tags: [tag]
+ }
+
+ const headers = authHeaders(credentials)
+ headers['Content-Type'] = 'application/json'
+
+ return fetch(TAG_USER_URL, {
+ method: 'PUT',
+ headers: headers,
+ body: JSON.stringify(form)
+ })
+}
+
+const untagUser = ({tag, credentials, ...options}) => {
+ const screenName = options.screen_name
+ const body = {
+ nicknames: [screenName],
+ tags: [tag]
+ }
+
+ const headers = authHeaders(credentials)
+ headers['Content-Type'] = 'application/json'
+
+ return fetch(TAG_USER_URL, {
+ method: 'DELETE',
+ headers: headers,
+ body: JSON.stringify(body)
+ })
+}
+
+const addRight = ({right, credentials, ...user}) => {
+ const screenName = user.screen_name
+
+ return fetch(`${PERMISSION_GROUP_URL}/${screenName}/${right}`, {
+ method: 'POST',
+ headers: authHeaders(credentials),
+ body: {}
+ })
+}
+
+const deleteRight = ({right, credentials, ...user}) => {
+ const screenName = user.screen_name
+
+ return fetch(`${PERMISSION_GROUP_URL}/${screenName}/${right}`, {
+ method: 'DELETE',
+ headers: authHeaders(credentials),
+ body: {}
+ })
+}
+
+const setActivationStatus = ({status, credentials, ...user}) => {
+ const screenName = user.screen_name
+ const body = {
+ status: status
+ }
+
+ const headers = authHeaders(credentials)
+ headers['Content-Type'] = 'application/json'
+
+ return fetch(`${ACTIVATION_STATUS_URL}/${screenName}.json`, {
+ method: 'PUT',
+ headers: headers,
+ body: JSON.stringify(body)
+ })
+}
+
+const deleteUser = ({credentials, ...user}) => {
+ const screenName = user.screen_name
+ const headers = authHeaders(credentials)
+
+ return fetch(`${ADMIN_USER_URL}.json?nickname=${screenName}`, {
+ method: 'DELETE',
+ headers: headers
+ })
+}
+
const fetchTimeline = ({timeline, credentials, since = false, until = false, userId = false, tag = false, withMuted = false}) => {
const timelineUrls = {
public: MASTODON_PUBLIC_TIMELINE,
friends: MASTODON_USER_HOME_TIMELINE_URL,
mentions: MENTIONS_URL,
dms: MASTODON_DIRECT_MESSAGES_TIMELINE_URL,
- notifications: QVITTER_USER_NOTIFICATIONS_URL,
+ notifications: MASTODON_USER_NOTIFICATIONS_URL,
'publicAndExternal': MASTODON_PUBLIC_TIMELINE,
user: MASTODON_USER_TIMELINE_URL,
media: MASTODON_USER_TIMELINE_URL,
@@ -666,6 +763,12 @@ const apiService = {
fetchBlocks,
fetchOAuthTokens,
revokeOAuthToken,
+ tagUser,
+ untagUser,
+ deleteUser,
+ addRight,
+ deleteRight,
+ setActivationStatus,
register,
getCaptcha,
updateAvatar,
diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js
index 71e78d2f..75bba92b 100644
--- a/src/services/backend_interactor_service/backend_interactor_service.js
+++ b/src/services/backend_interactor_service/backend_interactor_service.js
@@ -1,5 +1,6 @@
import apiService from '../api/api.service.js'
import timelineFetcherService from '../timeline_fetcher/timeline_fetcher.service.js'
+import notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js'
const backendInteractorService = (credentials) => {
const fetchStatus = ({id}) => {
@@ -58,8 +59,36 @@ const backendInteractorService = (credentials) => {
return apiService.denyUser({credentials, id})
}
- const startFetching = ({timeline, store, userId = false, tag}) => {
- return timelineFetcherService.startFetching({timeline, store, credentials, userId, tag})
+ const startFetchingTimeline = ({ timeline, store, userId = false, tag }) => {
+ return timelineFetcherService.startFetching({ timeline, store, credentials, userId, tag })
+ }
+
+ const startFetchingNotifications = ({ store }) => {
+ return notificationsFetcher.startFetching({ store, credentials })
+ }
+
+ const tagUser = ({screen_name}, tag) => {
+ return apiService.tagUser({screen_name, tag, credentials})
+ }
+
+ const untagUser = ({screen_name}, tag) => {
+ return apiService.untagUser({screen_name, tag, credentials})
+ }
+
+ const addRight = ({screen_name}, right) => {
+ return apiService.addRight({screen_name, right, credentials})
+ }
+
+ const deleteRight = ({screen_name}, right) => {
+ return apiService.deleteRight({screen_name, right, credentials})
+ }
+
+ const setActivationStatus = ({screen_name}, status) => {
+ return apiService.setActivationStatus({screen_name, status, credentials})
+ }
+
+ const deleteUser = ({screen_name}) => {
+ return apiService.deleteUser({screen_name, credentials})
}
const fetchMutes = () => apiService.fetchMutes({credentials})
@@ -97,13 +126,20 @@ const backendInteractorService = (credentials) => {
fetchUserRelationship,
fetchAllFollowing,
verifyCredentials: apiService.verifyCredentials,
- startFetching,
+ startFetchingTimeline,
+ startFetchingNotifications,
fetchMutes,
muteUser,
unmuteUser,
fetchBlocks,
fetchOAuthTokens,
revokeOAuthToken,
+ tagUser,
+ untagUser,
+ addRight,
+ deleteRight,
+ deleteUser,
+ setActivationStatus,
register,
getCaptcha,
updateAvatar,
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index ea57e6b2..e706e7d9 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -39,7 +39,7 @@ export const parseUser = (data) => {
return output
}
- // output.name = ??? missing
+ output.name = data.display_name
output.name_html = addEmojis(data.display_name, data.emojis)
// output.description = ??? missing
@@ -67,9 +67,14 @@ export const parseUser = (data) => {
output.statusnet_blocking = relationship.blocking
output.muted = relationship.muting
}
+
+ output.rights = {
+ moderator: data.pleroma.is_moderator,
+ admin: data.pleroma.is_admin
+ }
}
- // Missing, trying to recover
+ // TODO: handle is_local
output.is_local = !output.screen_name.includes('@')
} else {
output.screen_name = data.screen_name
@@ -103,7 +108,12 @@ export const parseUser = (data) => {
// QVITTER ONLY FOR NOW
// Really only applies to logged in user, really.. I THINK
- output.rights = data.rights
+ if (data.rights) {
+ output.rights = {
+ moderator: data.rights.delete_others_notice,
+ admin: data.rights.admin
+ }
+ }
output.no_rich_text = data.no_rich_text
output.default_scope = data.default_scope
output.hide_follows = data.hide_follows
@@ -119,12 +129,19 @@ export const parseUser = (data) => {
output.locked = data.locked
output.followers_count = data.followers_count
output.statuses_count = data.statuses_count
- output.friends = []
- output.followers = []
+ output.friendIds = []
+ output.followerIds = []
if (data.pleroma) {
output.follow_request_count = data.pleroma.follow_request_count
}
+ if (data.pleroma) {
+ output.tags = data.pleroma.tags
+ output.deactivated = data.pleroma.deactivated
+ }
+
+ output.tags = output.tags || []
+
return output
}
@@ -172,28 +189,28 @@ export const parseStatus = (data) => {
output.statusnet_html = addEmojis(data.content, data.emojis)
- // Not exactly the same but works?
- output.text = data.content
+ if (data.pleroma) {
+ const { pleroma } = data
+ output.text = pleroma.content ? data.pleroma.content['text/plain'] : data.content
+ output.summary = pleroma.spoiler_text ? data.pleroma.spoiler_text['text/plain'] : data.spoiler_text
+ output.statusnet_conversation_id = data.pleroma.conversation_id
+ output.is_local = pleroma.local
+ output.in_reply_to_screen_name = data.pleroma.in_reply_to_account_acct
+ } else {
+ output.text = data.content
+ output.summary = data.spoiler_text
+ }
output.in_reply_to_status_id = data.in_reply_to_id
output.in_reply_to_user_id = data.in_reply_to_account_id
output.replies_count = data.replies_count
- // Missing!! fix in UI?
- // output.in_reply_to_screen_name = ???
-
- // Not exactly the same but works
- output.statusnet_conversation_id = data.id
-
if (output.type === 'retweet') {
output.retweeted_status = parseStatus(data.reblog)
}
- output.summary = data.spoiler_text
output.summary_html = addEmojis(data.spoiler_text, data.emojis)
output.external_url = data.url
-
- // output.is_local = ??? missing
} else {
output.favorited = data.favorited
output.fave_num = data.fave_num
@@ -221,7 +238,6 @@ export const parseStatus = (data) => {
output.in_reply_to_status_id = data.in_reply_to_status_id
output.in_reply_to_user_id = data.in_reply_to_user_id
output.in_reply_to_screen_name = data.in_reply_to_screen_name
-
output.statusnet_conversation_id = data.statusnet_conversation_id
if (output.type === 'retweet') {
@@ -272,9 +288,11 @@ export const parseNotification = (data) => {
if (masto) {
output.type = mastoDict[data.type] || data.type
- // output.seen = ??? missing
- output.status = parseStatus(data.status)
- output.action = output.status // not sure
+ output.seen = data.pleroma.is_seen
+ output.status = output.type === 'follow'
+ ? null
+ : parseStatus(data.status)
+ output.action = output.status // TODO: Refactor, this is unneeded
output.from_profile = parseUser(data.account)
} else {
const parsedNotice = parseStatus(data.notice)
diff --git a/src/services/follow_manipulate/follow_manipulate.js b/src/services/follow_manipulate/follow_manipulate.js
index 51dafe84..b2486e7c 100644
--- a/src/services/follow_manipulate/follow_manipulate.js
+++ b/src/services/follow_manipulate/follow_manipulate.js
@@ -23,18 +23,12 @@ export const requestFollow = (user, store) => new Promise((resolve, reject) => {
// For locked users we just mark it that we sent the follow request
if (updated.locked) {
- resolve({
- sent: true,
- updated
- })
+ resolve({ sent: true })
}
if (updated.following) {
// If we get result immediately, just stop.
- resolve({
- sent: false,
- updated
- })
+ resolve({ sent: false })
}
// But usually we don't get result immediately, so we ask server
@@ -48,16 +42,10 @@ export const requestFollow = (user, store) => new Promise((resolve, reject) => {
.then((following) => {
if (following) {
// We confirmed and everything's good.
- resolve({
- sent: false,
- updated
- })
+ resolve({ sent: false })
} else {
// If after all the tries, just treat it as if user is locked
- resolve({
- sent: false,
- updated
- })
+ resolve({ sent: false })
}
})
})
diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js
index cd8f3f9e..8afd114e 100644
--- a/src/services/notification_utils/notification_utils.js
+++ b/src/services/notification_utils/notification_utils.js
@@ -10,8 +10,8 @@ export const visibleTypes = store => ([
].filter(_ => _))
const sortById = (a, b) => {
- const seqA = Number(a.action.id)
- const seqB = Number(b.action.id)
+ const seqA = Number(a.id)
+ const seqB = Number(b.id)
const isSeqA = !Number.isNaN(seqA)
const isSeqB = !Number.isNaN(seqB)
if (isSeqA && isSeqB) {
@@ -21,7 +21,7 @@ const sortById = (a, b) => {
} else if (!isSeqA && isSeqB) {
return -1
} else {
- return a.action.id > b.action.id ? -1 : 1
+ return a.id > b.id ? -1 : 1
}
}
diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js
index 3ecdae6a..60c497ae 100644
--- a/src/services/notifications_fetcher/notifications_fetcher.service.js
+++ b/src/services/notifications_fetcher/notifications_fetcher.service.js
@@ -11,29 +11,35 @@ const fetchAndUpdate = ({store, credentials, older = false}) => {
const rootState = store.rootState || store.state
const timelineData = rootState.statuses.notifications
+ args['timeline'] = 'notifications'
if (older) {
if (timelineData.minId !== Number.POSITIVE_INFINITY) {
args['until'] = timelineData.minId
}
+ return fetchNotifications({ store, args, older })
} else {
- // load unread notifications repeadedly to provide consistency between browser tabs
+ // fetch new notifications
+ if (timelineData.maxId !== Number.POSITIVE_INFINITY) {
+ args['since'] = timelineData.maxId
+ }
+ const result = fetchNotifications({ store, args, older })
+
+ // load unread notifications repeatedly to provide consistency between browser tabs
const notifications = timelineData.data
const unread = notifications.filter(n => !n.seen).map(n => n.id)
- if (!unread.length) {
- args['since'] = timelineData.maxId
- } else {
- args['since'] = Math.min(...unread) - 1
- if (timelineData.maxId !== Math.max(...unread)) {
- args['until'] = Math.max(...unread, args['since'] + 20)
- }
+ if (unread.length) {
+ args['since'] = Math.min(...unread)
+ fetchNotifications({ store, args, older })
}
- }
- args['timeline'] = 'notifications'
+ return result
+ }
+}
+const fetchNotifications = ({ store, args, older }) => {
return apiService.fetchTimeline(args)
.then((notifications) => {
- update({store, notifications, older})
+ update({ store, notifications, older })
return notifications
}, () => store.dispatch('setNotificationsError', { value: true }))
.catch(() => store.dispatch('setNotificationsError', { value: true }))