diff options
Diffstat (limited to 'src/services')
| -rw-r--r-- | src/services/api/api.service.js | 169 | ||||
| -rw-r--r-- | src/services/attributes_helper/attributes_helper.service.js | 8 | ||||
| -rw-r--r-- | src/services/date_utils/date_utils.js | 16 | ||||
| -rw-r--r-- | src/services/entity_normalizer/entity_normalizer.service.js | 8 | ||||
| -rw-r--r-- | src/services/file_type/file_type.service.js | 18 | ||||
| -rw-r--r-- | src/services/html_converter/utility.service.js | 2 | ||||
| -rw-r--r-- | src/services/locale/locale.service.js | 6 | ||||
| -rw-r--r-- | src/services/matcher/matcher.service.js | 7 | ||||
| -rw-r--r-- | src/services/status_poster/status_poster.service.js | 2 | ||||
| -rw-r--r-- | src/services/style_setter/style_setter.js | 4 |
10 files changed, 204 insertions, 36 deletions
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 7174cc5d..c6bca10b 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -108,6 +108,11 @@ const PLEROMA_POST_ANNOUNCEMENT_URL = '/api/v1/pleroma/admin/announcements' const PLEROMA_EDIT_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements/${id}` const PLEROMA_DELETE_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements/${id}` +const PLEROMA_ADMIN_CONFIG_URL = '/api/pleroma/admin/config' +const PLEROMA_ADMIN_DESCRIPTIONS_URL = '/api/pleroma/admin/config/descriptions' +const PLEROMA_ADMIN_FRONTENDS_URL = '/api/pleroma/admin/frontends' +const PLEROMA_ADMIN_FRONTENDS_INSTALL_URL = '/api/pleroma/admin/frontends/install' + const oldfetch = window.fetch const fetch = (url, options) => { @@ -164,7 +169,7 @@ const updateNotificationSettings = ({ credentials, settings }) => { form.append(key, value) }) - return fetch(NOTIFICATION_SETTINGS_URL, { + return fetch(`${NOTIFICATION_SETTINGS_URL}?${new URLSearchParams(settings)}`, { headers: authHeaders(credentials), method: 'PUT', body: form @@ -734,26 +739,22 @@ const fetchTimeline = ({ const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&') 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.errors) { + .then(async (response) => { + const success = response.ok + + const data = await response.json() + + if (success && !data.errors) { + const pagination = parseLinkHeaderPagination(response.headers.get('Link'), { + flakeId: timeline !== 'bookmarks' && timeline !== 'notifications' + }) + return { data: data.map(isNotifications ? parseNotification : parseStatus), pagination } } else { - data.status = status - data.statusText = statusText + data.errors ||= [] + data.status = response.status + data.statusText = response.statusText return data } }) @@ -826,6 +827,7 @@ const postStatus = ({ poll, mediaIds = [], inReplyToStatusId, + quoteId, contentType, preview, idempotencyKey @@ -844,7 +846,7 @@ const postStatus = ({ }) if (pollOptions.some(option => option !== '')) { const normalizedPoll = { - expires_in: poll.expiresIn, + expires_in: parseInt(poll.expiresIn, 10), multiple: poll.multiple } Object.keys(normalizedPoll).forEach(key => { @@ -858,6 +860,9 @@ const postStatus = ({ if (inReplyToStatusId) { form.append('in_reply_to_id', inReplyToStatusId) } + if (quoteId) { + form.append('quote_id', quoteId) + } if (preview) { form.append('preview', 'true') } @@ -901,7 +906,7 @@ const editStatus = ({ if (pollOptions.some(option => option !== '')) { const normalizedPoll = { - expires_in: poll.expiresIn, + expires_in: parseInt(poll.expiresIn, 10), multiple: poll.multiple } Object.keys(normalizedPoll).forEach(key => { @@ -927,8 +932,9 @@ const editStatus = ({ } const deleteStatus = ({ id, credentials }) => { - return fetch(MASTODON_DELETE_URL(id), { - headers: authHeaders(credentials), + return promisedRequest({ + url: MASTODON_DELETE_URL(id), + credentials, method: 'DELETE' }) } @@ -1117,13 +1123,21 @@ const generateMfaBackupCodes = ({ credentials }) => { }).then((data) => data.json()) } -const fetchMutes = ({ credentials }) => { - return promisedRequest({ url: MASTODON_USER_MUTES_URL, credentials }) +const fetchMutes = ({ maxId, credentials }) => { + const query = new URLSearchParams({ with_relationships: true }) + if (maxId) { + query.append('max_id', maxId) + } + return promisedRequest({ url: `${MASTODON_USER_MUTES_URL}?${query.toString()}`, credentials }) .then((users) => users.map(parseUser)) } -const muteUser = ({ id, credentials }) => { - return promisedRequest({ url: MASTODON_MUTE_USER_URL(id), credentials, method: 'POST' }) +const muteUser = ({ id, expiresIn, credentials }) => { + const payload = {} + if (expiresIn) { + payload.expires_in = expiresIn + } + return promisedRequest({ url: MASTODON_MUTE_USER_URL(id), credentials, method: 'POST', payload }) } const unmuteUser = ({ id, credentials }) => { @@ -1138,8 +1152,12 @@ const unsubscribeUser = ({ id, credentials }) => { return promisedRequest({ url: MASTODON_UNSUBSCRIBE_USER(id), credentials, method: 'POST' }) } -const fetchBlocks = ({ credentials }) => { - return promisedRequest({ url: MASTODON_USER_BLOCKS_URL, credentials }) +const fetchBlocks = ({ maxId, credentials }) => { + const query = new URLSearchParams({ with_relationships: true }) + if (maxId) { + query.append('max_id', maxId) + } + return promisedRequest({ url: `${MASTODON_USER_BLOCKS_URL}?${query.toString()}`, credentials }) .then((users) => users.map(parseUser)) } @@ -1659,6 +1677,94 @@ const setReportState = ({ id, state, credentials }) => { }) } +// ADMIN STUFF // EXPERIMENTAL +const fetchInstanceDBConfig = ({ credentials }) => { + return fetch(PLEROMA_ADMIN_CONFIG_URL, { + headers: authHeaders(credentials) + }) + .then((response) => { + if (response.ok) { + return response.json() + } else { + return { + error: response + } + } + }) +} + +const fetchInstanceConfigDescriptions = ({ credentials }) => { + return fetch(PLEROMA_ADMIN_DESCRIPTIONS_URL, { + headers: authHeaders(credentials) + }) + .then((response) => { + if (response.ok) { + return response.json() + } else { + return { + error: response + } + } + }) +} + +const fetchAvailableFrontends = ({ credentials }) => { + return fetch(PLEROMA_ADMIN_FRONTENDS_URL, { + headers: authHeaders(credentials) + }) + .then((response) => { + if (response.ok) { + return response.json() + } else { + return { + error: response + } + } + }) +} + +const pushInstanceDBConfig = ({ credentials, payload }) => { + return fetch(PLEROMA_ADMIN_CONFIG_URL, { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...authHeaders(credentials) + }, + method: 'POST', + body: JSON.stringify(payload) + }) + .then((response) => { + if (response.ok) { + return response.json() + } else { + return { + error: response + } + } + }) +} + +const installFrontend = ({ credentials, payload }) => { + return fetch(PLEROMA_ADMIN_FRONTENDS_INSTALL_URL, { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...authHeaders(credentials) + }, + method: 'POST', + body: JSON.stringify(payload) + }) + .then((response) => { + if (response.ok) { + return response.json() + } else { + return { + error: response + } + } + }) +} + const apiService = { verifyCredentials, fetchTimeline, @@ -1772,7 +1878,12 @@ const apiService = { postAnnouncement, editAnnouncement, deleteAnnouncement, - adminFetchAnnouncements + adminFetchAnnouncements, + fetchInstanceDBConfig, + fetchInstanceConfigDescriptions, + fetchAvailableFrontends, + pushInstanceDBConfig, + installFrontend } export default apiService diff --git a/src/services/attributes_helper/attributes_helper.service.js b/src/services/attributes_helper/attributes_helper.service.js new file mode 100644 index 00000000..74d3323c --- /dev/null +++ b/src/services/attributes_helper/attributes_helper.service.js @@ -0,0 +1,8 @@ +import { kebabCase } from 'lodash' + +const propsToNative = props => Object.keys(props).reduce((acc, cur) => { + acc[kebabCase(cur)] = props[cur] + return acc +}, {}) + +export { propsToNative } diff --git a/src/services/date_utils/date_utils.js b/src/services/date_utils/date_utils.js index c93d2176..ed8e1417 100644 --- a/src/services/date_utils/date_utils.js +++ b/src/services/date_utils/date_utils.js @@ -41,3 +41,19 @@ export const relativeTimeShort = (date, nowThreshold = 1) => { r.key += '_short' return r } + +export const unitToSeconds = (unit, amount) => { + switch (unit) { + case 'minutes': return 0.001 * amount * MINUTE + case 'hours': return 0.001 * amount * HOUR + case 'days': return 0.001 * amount * DAY + } +} + +export const secondsToUnit = (unit, amount) => { + switch (unit) { + case 'minutes': return (1000 * amount) / MINUTE + case 'hours': return (1000 * amount) / HOUR + case 'days': return (1000 * amount) / DAY + } +} diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index ea138177..610ba1ab 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -125,6 +125,8 @@ export const parseUser = (data) => { output.role = 'member' } + output.birthday = data.pleroma.birthday + if (data.pleroma.privileges) { output.privileges = data.pleroma.privileges } else if (data.pleroma.is_admin) { @@ -162,6 +164,7 @@ export const parseUser = (data) => { output.no_rich_text = data.source.pleroma.no_rich_text output.show_role = data.source.pleroma.show_role output.discoverable = data.source.pleroma.discoverable + output.show_birthday = data.pleroma.show_birthday } } @@ -322,6 +325,10 @@ export const parseStatus = (data) => { output.thread_muted = pleroma.thread_muted output.emoji_reactions = pleroma.emoji_reactions output.parent_visible = pleroma.parent_visible === undefined ? true : pleroma.parent_visible + output.quote = pleroma.quote ? parseStatus(pleroma.quote) : undefined + output.quote_id = pleroma.quote_id ? pleroma.quote_id : (output.quote ? output.quote.id : undefined) + output.quote_url = pleroma.quote_url + output.quote_visible = pleroma.quote_visible } else { output.text = data.content output.summary = data.spoiler_text @@ -438,6 +445,7 @@ export const parseNotification = (data) => { : parseUser(data.target) output.from_profile = parseUser(data.account) output.emoji = data.emoji + output.emoji_url = data.emoji_url if (data.report) { output.report = data.report output.report.content = data.report.content diff --git a/src/services/file_type/file_type.service.js b/src/services/file_type/file_type.service.js index 5182ecd1..b92c6c64 100644 --- a/src/services/file_type/file_type.service.js +++ b/src/services/file_type/file_type.service.js @@ -1,7 +1,7 @@ // TODO this func might as well take the entire file and use its mimetype // or the entire service could be just mimetype service that only operates // on mimetypes and not files. Currently the naming is confusing. -const fileType = mimetype => { +export const fileType = mimetype => { if (mimetype.match(/flash/)) { return 'flash' } @@ -25,11 +25,25 @@ const fileType = mimetype => { return 'unknown' } -const fileMatchesSomeType = (types, file) => +export const fileTypeExt = url => { + if (url.match(/\.(png|jpe?g|gif|webp|avif)$/)) { + return 'image' + } + if (url.match(/\.(ogv|mp4|webm|mov)$/)) { + return 'video' + } + if (url.match(/\.(it|s3m|mod|umx|mp3|aac|m4a|flac|alac|ogg|oga|opus|wav|ape|midi?)$/)) { + return 'audio' + } + return 'unknown' +} + +export const fileMatchesSomeType = (types, file) => types.some(type => fileType(file.mimetype) === type) const fileTypeService = { fileType, + fileTypeExt, fileMatchesSomeType } diff --git a/src/services/html_converter/utility.service.js b/src/services/html_converter/utility.service.js index a1301353..f8e62dfe 100644 --- a/src/services/html_converter/utility.service.js +++ b/src/services/html_converter/utility.service.js @@ -22,7 +22,7 @@ export const getAttrs = (tag, filter) => { .replace(new RegExp('^' + getTagName(tag)), '') .replace(/\/?$/, '') .trim() - const attrs = Array.from(innertag.matchAll(/([a-z0-9-]+)(?:=("[^"]+?"|'[^']+?'))?/gi)) + const attrs = Array.from(innertag.matchAll(/([a-z]+[a-z0-9-]*)(?:=("[^"]+?"|'[^']+?'))?/gi)) .map(([trash, key, value]) => [key, value]) .map(([k, v]) => { if (!v) return [k, true] diff --git a/src/services/locale/locale.service.js b/src/services/locale/locale.service.js index d3389785..24ed3cdb 100644 --- a/src/services/locale/locale.service.js +++ b/src/services/locale/locale.service.js @@ -11,10 +11,15 @@ const specialLanguageCodes = { const internalToBrowserLocale = code => specialLanguageCodes[code] || code const internalToBackendLocale = code => internalToBrowserLocale(code).replace('_', '-') +const internalToBackendLocaleMulti = codes => { + const langs = Array.isArray(codes) ? codes : [codes] + return langs.map(internalToBackendLocale).join(',') +} const getLanguageName = (code) => { const specialLanguageNames = { ja_easy: 'やさしいにほんご', + 'nan-TW': '臺語(閩南語)', zh: '简体中文', zh_Hant: '繁體中文' } @@ -28,6 +33,7 @@ const languages = _.map(languagesObject.languages, (code) => ({ code, name: getL const localeService = { internalToBrowserLocale, internalToBackendLocale, + internalToBackendLocaleMulti, languages, getLanguageName } diff --git a/src/services/matcher/matcher.service.js b/src/services/matcher/matcher.service.js index b6c4e909..54f02d31 100644 --- a/src/services/matcher/matcher.service.js +++ b/src/services/matcher/matcher.service.js @@ -14,8 +14,11 @@ export const mentionMatchesUrl = (attention, url) => { * @param {string} url */ export const extractTagFromUrl = (url) => { - const regex = /tag[s]*\/(\w+)$/g - const result = regex.exec(url) + const decoded = decodeURI(url) + // https://git.pleroma.social/pleroma/elixir-libraries/linkify/-/blob/master/lib/linkify/parser.ex + // https://www.pcre.org/original/doc/html/pcrepattern.html + const regex = /tag[s]*\/([\p{L}\p{N}_]*[\p{Alphabetic}_·\u{200c}][\p{L}\p{N}_·\p{M}\u{200c}]*)$/ug + const result = regex.exec(decoded) if (!result) { return false } diff --git a/src/services/status_poster/status_poster.service.js b/src/services/status_poster/status_poster.service.js index 1eb10bb6..aaef5a7a 100644 --- a/src/services/status_poster/status_poster.service.js +++ b/src/services/status_poster/status_poster.service.js @@ -10,6 +10,7 @@ const postStatus = ({ poll, media = [], inReplyToStatusId = undefined, + quoteId = undefined, contentType = 'text/plain', preview = false, idempotencyKey = '' @@ -24,6 +25,7 @@ const postStatus = ({ sensitive, mediaIds, inReplyToStatusId, + quoteId, contentType, poll, preview, diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index d6e973a1..43fe3c73 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -21,8 +21,8 @@ export const applyTheme = (input) => { body.classList.remove('hidden') } -const configColumns = ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth }) => - ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth }) +const configColumns = ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth, emojiReactionsScale }) => + ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth, emojiReactionsScale }) const defaultConfigColumns = configColumns(defaultState) |
