Merge remote-tracking branch 'pleroma/develop' into birthdays
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
commit
6649baaac9
316 changed files with 12331 additions and 6859 deletions
|
|
@ -9,6 +9,8 @@ const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import'
|
|||
const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account'
|
||||
const CHANGE_EMAIL_URL = '/api/pleroma/change_email'
|
||||
const CHANGE_PASSWORD_URL = '/api/pleroma/change_password'
|
||||
const MOVE_ACCOUNT_URL = '/api/pleroma/move_account'
|
||||
const ALIASES_URL = '/api/pleroma/aliases'
|
||||
const TAG_USER_URL = '/api/pleroma/admin/users/tag'
|
||||
const PERMISSION_GROUP_URL = (screenName, right) => `/api/pleroma/admin/users/${screenName}/permission_group/${right}`
|
||||
const ACTIVATE_USER_URL = '/api/pleroma/admin/users/activate'
|
||||
|
|
@ -74,7 +76,7 @@ const MASTODON_PIN_OWN_STATUS = id => `/api/v1/statuses/${id}/pin`
|
|||
const MASTODON_UNPIN_OWN_STATUS = id => `/api/v1/statuses/${id}/unpin`
|
||||
const MASTODON_MUTE_CONVERSATION = id => `/api/v1/statuses/${id}/mute`
|
||||
const MASTODON_UNMUTE_CONVERSATION = id => `/api/v1/statuses/${id}/unmute`
|
||||
const MASTODON_SEARCH_2 = `/api/v2/search`
|
||||
const MASTODON_SEARCH_2 = '/api/v2/search'
|
||||
const MASTODON_USER_SEARCH_URL = '/api/v1/accounts/search'
|
||||
const MASTODON_DOMAIN_BLOCKS_URL = '/api/v1/domain_blocks'
|
||||
const MASTODON_STREAMING = '/api/v1/streaming'
|
||||
|
|
@ -82,15 +84,16 @@ const MASTODON_KNOWN_DOMAIN_LIST_URL = '/api/v1/instance/peers'
|
|||
const PLEROMA_EMOJI_REACTIONS_URL = id => `/api/v1/pleroma/statuses/${id}/reactions`
|
||||
const PLEROMA_EMOJI_REACT_URL = (id, emoji) => `/api/v1/pleroma/statuses/${id}/reactions/${emoji}`
|
||||
const PLEROMA_EMOJI_UNREACT_URL = (id, emoji) => `/api/v1/pleroma/statuses/${id}/reactions/${emoji}`
|
||||
const PLEROMA_CHATS_URL = `/api/v1/pleroma/chats`
|
||||
const PLEROMA_CHATS_URL = '/api/v1/pleroma/chats'
|
||||
const PLEROMA_CHAT_URL = id => `/api/v1/pleroma/chats/by-account-id/${id}`
|
||||
const PLEROMA_CHAT_MESSAGES_URL = id => `/api/v1/pleroma/chats/${id}/messages`
|
||||
const PLEROMA_CHAT_READ_URL = id => `/api/v1/pleroma/chats/${id}/read`
|
||||
const PLEROMA_DELETE_CHAT_MESSAGE_URL = (chatId, messageId) => `/api/v1/pleroma/chats/${chatId}/messages/${messageId}`
|
||||
const PLEROMA_BACKUP_URL = '/api/v1/pleroma/backups'
|
||||
|
||||
const oldfetch = window.fetch
|
||||
|
||||
let fetch = (url, options) => {
|
||||
const fetch = (url, options) => {
|
||||
options = options || {}
|
||||
const baseUrl = ''
|
||||
const fullUrl = baseUrl + url
|
||||
|
|
@ -102,7 +105,7 @@ const promisedRequest = ({ method, url, params, payload, credentials, headers =
|
|||
const options = {
|
||||
method,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
...headers
|
||||
}
|
||||
|
|
@ -151,9 +154,15 @@ const updateNotificationSettings = ({ credentials, settings }) => {
|
|||
}).then((data) => data.json())
|
||||
}
|
||||
|
||||
const updateProfileImages = ({ credentials, avatar = null, banner = null, background = null }) => {
|
||||
const updateProfileImages = ({ credentials, avatar = null, avatarName = null, banner = null, background = null }) => {
|
||||
const form = new FormData()
|
||||
if (avatar !== null) form.append('avatar', avatar)
|
||||
if (avatar !== null) {
|
||||
if (avatarName !== null) {
|
||||
form.append('avatar', avatar, avatarName)
|
||||
} else {
|
||||
form.append('avatar', avatar)
|
||||
}
|
||||
}
|
||||
if (banner !== null) form.append('header', banner)
|
||||
if (background !== null) form.append('pleroma_background_image', background)
|
||||
return fetch(MASTODON_PROFILE_UPDATE_URL, {
|
||||
|
|
@ -191,6 +200,7 @@ const updateProfile = ({ credentials, params }) => {
|
|||
// homepage
|
||||
// location
|
||||
// token
|
||||
// language
|
||||
const register = ({ params, credentials }) => {
|
||||
const { nickname, ...rest } = params
|
||||
return fetch(MASTODON_REGISTRATION_URL, {
|
||||
|
|
@ -219,16 +229,16 @@ const getCaptcha = () => fetch('/api/pleroma/captcha').then(resp => resp.json())
|
|||
|
||||
const authHeaders = (accessToken) => {
|
||||
if (accessToken) {
|
||||
return { 'Authorization': `Bearer ${accessToken}` }
|
||||
return { Authorization: `Bearer ${accessToken}` }
|
||||
} else {
|
||||
return { }
|
||||
}
|
||||
}
|
||||
|
||||
const followUser = ({ id, credentials, ...options }) => {
|
||||
let url = MASTODON_FOLLOW_URL(id)
|
||||
const url = MASTODON_FOLLOW_URL(id)
|
||||
const form = {}
|
||||
if (options.reblogs !== undefined) { form['reblogs'] = options.reblogs }
|
||||
if (options.reblogs !== undefined) { form.reblogs = options.reblogs }
|
||||
return fetch(url, {
|
||||
body: JSON.stringify(form),
|
||||
headers: {
|
||||
|
|
@ -240,7 +250,7 @@ const followUser = ({ id, credentials, ...options }) => {
|
|||
}
|
||||
|
||||
const unfollowUser = ({ id, credentials }) => {
|
||||
let url = MASTODON_UNFOLLOW_URL(id)
|
||||
const url = MASTODON_UNFOLLOW_URL(id)
|
||||
return fetch(url, {
|
||||
headers: authHeaders(credentials),
|
||||
method: 'POST'
|
||||
|
|
@ -282,7 +292,7 @@ const unblockUser = ({ id, credentials }) => {
|
|||
}
|
||||
|
||||
const approveUser = ({ id, credentials }) => {
|
||||
let url = MASTODON_APPROVE_USER_URL(id)
|
||||
const url = MASTODON_APPROVE_USER_URL(id)
|
||||
return fetch(url, {
|
||||
headers: authHeaders(credentials),
|
||||
method: 'POST'
|
||||
|
|
@ -290,7 +300,7 @@ const approveUser = ({ id, credentials }) => {
|
|||
}
|
||||
|
||||
const denyUser = ({ id, credentials }) => {
|
||||
let url = MASTODON_DENY_USER_URL(id)
|
||||
const url = MASTODON_DENY_USER_URL(id)
|
||||
return fetch(url, {
|
||||
headers: authHeaders(credentials),
|
||||
method: 'POST'
|
||||
|
|
@ -298,13 +308,13 @@ const denyUser = ({ id, credentials }) => {
|
|||
}
|
||||
|
||||
const fetchUser = ({ id, credentials }) => {
|
||||
let url = `${MASTODON_USER_URL}/${id}`
|
||||
const url = `${MASTODON_USER_URL}/${id}`
|
||||
return promisedRequest({ url, credentials })
|
||||
.then((data) => parseUser(data))
|
||||
}
|
||||
|
||||
const fetchUserRelationship = ({ id, credentials }) => {
|
||||
let url = `${MASTODON_USER_RELATIONSHIPS_URL}/?id=${id}`
|
||||
const url = `${MASTODON_USER_RELATIONSHIPS_URL}/?id=${id}`
|
||||
return fetch(url, { headers: authHeaders(credentials) })
|
||||
.then((response) => {
|
||||
return new Promise((resolve, reject) => response.json()
|
||||
|
|
@ -323,7 +333,7 @@ const fetchFriends = ({ id, maxId, sinceId, limit = 20, credentials }) => {
|
|||
maxId && `max_id=${maxId}`,
|
||||
sinceId && `since_id=${sinceId}`,
|
||||
limit && `limit=${limit}`,
|
||||
`with_relationships=true`
|
||||
'with_relationships=true'
|
||||
].filter(_ => _).join('&')
|
||||
|
||||
url = url + (args ? '?' + args : '')
|
||||
|
|
@ -333,6 +343,7 @@ const fetchFriends = ({ id, maxId, sinceId, limit = 20, credentials }) => {
|
|||
}
|
||||
|
||||
const exportFriends = ({ id, credentials }) => {
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
let friends = []
|
||||
|
|
@ -358,7 +369,7 @@ const fetchFollowers = ({ id, maxId, sinceId, limit = 20, credentials }) => {
|
|||
maxId && `max_id=${maxId}`,
|
||||
sinceId && `since_id=${sinceId}`,
|
||||
limit && `limit=${limit}`,
|
||||
`with_relationships=true`
|
||||
'with_relationships=true'
|
||||
].filter(_ => _).join('&')
|
||||
|
||||
url += args ? '?' + args : ''
|
||||
|
|
@ -375,7 +386,7 @@ const fetchFollowRequests = ({ credentials }) => {
|
|||
}
|
||||
|
||||
const fetchConversation = ({ id, credentials }) => {
|
||||
let urlContext = MASTODON_STATUS_CONTEXT_URL(id)
|
||||
const urlContext = MASTODON_STATUS_CONTEXT_URL(id)
|
||||
return fetch(urlContext, { headers: authHeaders(credentials) })
|
||||
.then((data) => {
|
||||
if (data.ok) {
|
||||
|
|
@ -391,7 +402,7 @@ const fetchConversation = ({ id, credentials }) => {
|
|||
}
|
||||
|
||||
const fetchStatus = ({ id, credentials }) => {
|
||||
let url = MASTODON_STATUS_URL(id)
|
||||
const url = MASTODON_STATUS_URL(id)
|
||||
return fetch(url, { headers: authHeaders(credentials) })
|
||||
.then((data) => {
|
||||
if (data.ok) {
|
||||
|
|
@ -415,7 +426,7 @@ const tagUser = ({ tag, credentials, user }) => {
|
|||
|
||||
return fetch(TAG_USER_URL, {
|
||||
method: 'PUT',
|
||||
headers: headers,
|
||||
headers,
|
||||
body: JSON.stringify(form)
|
||||
})
|
||||
}
|
||||
|
|
@ -432,7 +443,7 @@ const untagUser = ({ tag, credentials, user }) => {
|
|||
|
||||
return fetch(TAG_USER_URL, {
|
||||
method: 'DELETE',
|
||||
headers: headers,
|
||||
headers,
|
||||
body: JSON.stringify(body)
|
||||
})
|
||||
}
|
||||
|
|
@ -485,7 +496,7 @@ const deleteUser = ({ credentials, user }) => {
|
|||
|
||||
return fetch(`${ADMIN_USERS_URL}?nickname=${screenName}`, {
|
||||
method: 'DELETE',
|
||||
headers: headers
|
||||
headers
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -504,7 +515,7 @@ const fetchTimeline = ({
|
|||
friends: MASTODON_USER_HOME_TIMELINE_URL,
|
||||
dms: MASTODON_DIRECT_MESSAGES_TIMELINE_URL,
|
||||
notifications: MASTODON_USER_NOTIFICATIONS_URL,
|
||||
'publicAndExternal': MASTODON_PUBLIC_TIMELINE,
|
||||
publicAndExternal: MASTODON_PUBLIC_TIMELINE,
|
||||
user: MASTODON_USER_TIMELINE_URL,
|
||||
media: MASTODON_USER_TIMELINE_URL,
|
||||
favorites: MASTODON_USER_FAVORITES_TIMELINE_URL,
|
||||
|
|
@ -678,7 +689,7 @@ const postStatus = ({
|
|||
form.append('preview', 'true')
|
||||
}
|
||||
|
||||
let postHeaders = authHeaders(credentials)
|
||||
const postHeaders = authHeaders(credentials)
|
||||
if (idempotencyKey) {
|
||||
postHeaders['idempotency-key'] = idempotencyKey
|
||||
}
|
||||
|
|
@ -782,6 +793,49 @@ const changeEmail = ({ credentials, email, password }) => {
|
|||
.then((response) => response.json())
|
||||
}
|
||||
|
||||
const moveAccount = ({ credentials, password, targetAccount }) => {
|
||||
const form = new FormData()
|
||||
|
||||
form.append('password', password)
|
||||
form.append('target_account', targetAccount)
|
||||
|
||||
return fetch(MOVE_ACCOUNT_URL, {
|
||||
body: form,
|
||||
method: 'POST',
|
||||
headers: authHeaders(credentials)
|
||||
})
|
||||
.then((response) => response.json())
|
||||
}
|
||||
|
||||
const addAlias = ({ credentials, alias }) => {
|
||||
return promisedRequest({
|
||||
url: ALIASES_URL,
|
||||
method: 'PUT',
|
||||
credentials,
|
||||
payload: { alias }
|
||||
})
|
||||
}
|
||||
|
||||
const deleteAlias = ({ credentials, alias }) => {
|
||||
return promisedRequest({
|
||||
url: ALIASES_URL,
|
||||
method: 'DELETE',
|
||||
credentials,
|
||||
payload: { alias }
|
||||
})
|
||||
}
|
||||
|
||||
const listAliases = ({ credentials }) => {
|
||||
return promisedRequest({
|
||||
url: ALIASES_URL,
|
||||
method: 'GET',
|
||||
credentials,
|
||||
params: {
|
||||
_cacheBooster: (new Date()).getTime()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const changePassword = ({ credentials, password, newPassword, newPasswordConfirmation }) => {
|
||||
const form = new FormData()
|
||||
|
||||
|
|
@ -868,6 +922,25 @@ const fetchBlocks = ({ credentials }) => {
|
|||
.then((users) => users.map(parseUser))
|
||||
}
|
||||
|
||||
const addBackup = ({ credentials }) => {
|
||||
return promisedRequest({
|
||||
url: PLEROMA_BACKUP_URL,
|
||||
method: 'POST',
|
||||
credentials
|
||||
})
|
||||
}
|
||||
|
||||
const listBackups = ({ credentials }) => {
|
||||
return promisedRequest({
|
||||
url: PLEROMA_BACKUP_URL,
|
||||
method: 'GET',
|
||||
credentials,
|
||||
params: {
|
||||
_cacheBooster: (new Date()).getTime()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const fetchOAuthTokens = ({ credentials }) => {
|
||||
const url = '/api/oauth_tokens.json'
|
||||
|
||||
|
|
@ -921,7 +994,7 @@ const vote = ({ pollId, choices, credentials }) => {
|
|||
method: 'POST',
|
||||
credentials,
|
||||
payload: {
|
||||
choices: choices
|
||||
choices
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -981,8 +1054,8 @@ const reportUser = ({ credentials, userId, statusIds, comment, forward }) => {
|
|||
url: MASTODON_REPORT_USER_URL,
|
||||
method: 'POST',
|
||||
payload: {
|
||||
'account_id': userId,
|
||||
'status_ids': statusIds,
|
||||
account_id: userId,
|
||||
status_ids: statusIds,
|
||||
comment,
|
||||
forward
|
||||
},
|
||||
|
|
@ -1004,7 +1077,7 @@ const searchUsers = ({ credentials, query }) => {
|
|||
|
||||
const search2 = ({ credentials, q, resolve, limit, offset, following }) => {
|
||||
let url = MASTODON_SEARCH_2
|
||||
let params = []
|
||||
const params = []
|
||||
|
||||
if (q) {
|
||||
params.push(['q', encodeURIComponent(q)])
|
||||
|
|
@ -1028,7 +1101,7 @@ const search2 = ({ credentials, q, resolve, limit, offset, following }) => {
|
|||
|
||||
params.push(['with_relationships', true])
|
||||
|
||||
let queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&')
|
||||
const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&')
|
||||
url += `?${queryString}`
|
||||
|
||||
return fetch(url, { headers: authHeaders(credentials) })
|
||||
|
|
@ -1182,12 +1255,12 @@ export const handleMastoWS = (wsEvent) => {
|
|||
}
|
||||
|
||||
export const WSConnectionStatus = Object.freeze({
|
||||
'JOINED': 1,
|
||||
'CLOSED': 2,
|
||||
'ERROR': 3,
|
||||
'DISABLED': 4,
|
||||
'STARTING': 5,
|
||||
'STARTING_INITIAL': 6
|
||||
JOINED: 1,
|
||||
CLOSED: 2,
|
||||
ERROR: 3,
|
||||
DISABLED: 4,
|
||||
STARTING: 5,
|
||||
STARTING_INITIAL: 6
|
||||
})
|
||||
|
||||
const chats = ({ credentials }) => {
|
||||
|
|
@ -1225,11 +1298,11 @@ const chatMessages = ({ id, credentials, maxId, sinceId, limit = 20 }) => {
|
|||
|
||||
const sendChatMessage = ({ id, content, mediaId = null, idempotencyKey, credentials }) => {
|
||||
const payload = {
|
||||
'content': content
|
||||
content
|
||||
}
|
||||
|
||||
if (mediaId) {
|
||||
payload['media_id'] = mediaId
|
||||
payload.media_id = mediaId
|
||||
}
|
||||
|
||||
const headers = {}
|
||||
|
|
@ -1241,7 +1314,7 @@ const sendChatMessage = ({ id, content, mediaId = null, idempotencyKey, credenti
|
|||
return promisedRequest({
|
||||
url: PLEROMA_CHAT_MESSAGES_URL(id),
|
||||
method: 'POST',
|
||||
payload: payload,
|
||||
payload,
|
||||
credentials,
|
||||
headers
|
||||
})
|
||||
|
|
@ -1252,7 +1325,7 @@ const readChat = ({ id, lastReadId, credentials }) => {
|
|||
url: PLEROMA_CHAT_READ_URL(id),
|
||||
method: 'POST',
|
||||
payload: {
|
||||
'last_read_id': lastReadId
|
||||
last_read_id: lastReadId
|
||||
},
|
||||
credentials
|
||||
})
|
||||
|
|
@ -1319,12 +1392,18 @@ const apiService = {
|
|||
importFollows,
|
||||
deleteAccount,
|
||||
changeEmail,
|
||||
moveAccount,
|
||||
addAlias,
|
||||
deleteAlias,
|
||||
listAliases,
|
||||
changePassword,
|
||||
settingsMFA,
|
||||
mfaDisableOTP,
|
||||
generateMfaBackupCodes,
|
||||
mfaSetupOTP,
|
||||
mfaConfirmOTP,
|
||||
addBackup,
|
||||
listBackups,
|
||||
fetchFollowRequests,
|
||||
approveUser,
|
||||
denyUser,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const empty = (chatId) => {
|
|||
messages: [],
|
||||
newMessageCount: 0,
|
||||
lastSeenMessageId: '0',
|
||||
chatId: chatId,
|
||||
chatId,
|
||||
minId: undefined,
|
||||
maxId: undefined
|
||||
}
|
||||
|
|
@ -101,7 +101,7 @@ const add = (storage, { messages: newMessages, updateMaxId = true }) => {
|
|||
storage.messages = storage.messages.filter(msg => msg.id !== message.id)
|
||||
}
|
||||
Object.assign(fakeMessage, message, { error: false })
|
||||
delete fakeMessage['fakeId']
|
||||
delete fakeMessage.fakeId
|
||||
storage.idIndex[fakeMessage.id] = fakeMessage
|
||||
delete storage.idIndex[message.fakeId]
|
||||
|
||||
|
|
@ -178,7 +178,7 @@ const getView = (storage) => {
|
|||
id: date.getTime().toString()
|
||||
})
|
||||
|
||||
previousMessage['isTail'] = true
|
||||
previousMessage.isTail = true
|
||||
currentMessageChainId = undefined
|
||||
afterDate = true
|
||||
}
|
||||
|
|
@ -193,15 +193,15 @@ const getView = (storage) => {
|
|||
|
||||
// end a message chian
|
||||
if ((nextMessage && nextMessage.account_id) !== message.account_id) {
|
||||
object['isTail'] = true
|
||||
object.isTail = true
|
||||
currentMessageChainId = undefined
|
||||
}
|
||||
|
||||
// start a new message chain
|
||||
if ((previousMessage && previousMessage.data && previousMessage.data.account_id) !== message.account_id || afterDate) {
|
||||
currentMessageChainId = _.uniqueId()
|
||||
object['isHead'] = true
|
||||
object['messageChainId'] = currentMessageChainId
|
||||
object.isHead = true
|
||||
object.messageChainId = currentMessageChainId
|
||||
}
|
||||
|
||||
result.push(object)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export const buildFakeMessage = ({ content, chatId, attachments, userId, idempot
|
|||
chat_id: chatId,
|
||||
created_at: new Date(),
|
||||
id: `${new Date().getTime()}`,
|
||||
attachments: attachments,
|
||||
attachments,
|
||||
account_id: userId,
|
||||
idempotency_key: idempotencyKey,
|
||||
emojis: [],
|
||||
|
|
|
|||
|
|
@ -144,11 +144,13 @@ export const invert = (rgb) => {
|
|||
*/
|
||||
export const hex2rgb = (hex) => {
|
||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
|
||||
return result ? {
|
||||
r: parseInt(result[1], 16),
|
||||
g: parseInt(result[2], 16),
|
||||
b: parseInt(result[3], 16)
|
||||
} : null
|
||||
return result
|
||||
? {
|
||||
r: parseInt(result[1], 16),
|
||||
g: parseInt(result[2], 16),
|
||||
b: parseInt(result[3], 16)
|
||||
}
|
||||
: null
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export const addPositionToWords = (words) => {
|
|||
}
|
||||
|
||||
export const splitByWhitespaceBoundary = (str) => {
|
||||
let result = []
|
||||
const result = []
|
||||
let currentWord = ''
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const currentChar = str[i]
|
||||
|
|
|
|||
|
|
@ -10,31 +10,29 @@ export const relativeTime = (date, nowThreshold = 1) => {
|
|||
if (typeof date === 'string') date = Date.parse(date)
|
||||
const round = Date.now() > date ? Math.floor : Math.ceil
|
||||
const d = Math.abs(Date.now() - date)
|
||||
let r = { num: round(d / YEAR), key: 'time.years' }
|
||||
const r = { num: round(d / YEAR), key: 'time.unit.years' }
|
||||
if (d < nowThreshold * SECOND) {
|
||||
r.num = 0
|
||||
r.key = 'time.now'
|
||||
} else if (d < MINUTE) {
|
||||
r.num = round(d / SECOND)
|
||||
r.key = 'time.seconds'
|
||||
r.key = 'time.unit.seconds'
|
||||
} else if (d < HOUR) {
|
||||
r.num = round(d / MINUTE)
|
||||
r.key = 'time.minutes'
|
||||
r.key = 'time.unit.minutes'
|
||||
} else if (d < DAY) {
|
||||
r.num = round(d / HOUR)
|
||||
r.key = 'time.hours'
|
||||
r.key = 'time.unit.hours'
|
||||
} else if (d < WEEK) {
|
||||
r.num = round(d / DAY)
|
||||
r.key = 'time.days'
|
||||
r.key = 'time.unit.days'
|
||||
} else if (d < MONTH) {
|
||||
r.num = round(d / WEEK)
|
||||
r.key = 'time.weeks'
|
||||
r.key = 'time.unit.weeks'
|
||||
} else if (d < YEAR) {
|
||||
r.num = round(d / MONTH)
|
||||
r.key = 'time.months'
|
||||
r.key = 'time.unit.months'
|
||||
}
|
||||
// Remove plural form when singular
|
||||
if (r.num === 1) r.key = r.key.slice(0, -1)
|
||||
return r
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,11 +39,12 @@ const qvitterStatusType = (status) => {
|
|||
|
||||
export const parseUser = (data) => {
|
||||
const output = {}
|
||||
const masto = data.hasOwnProperty('acct')
|
||||
const masto = Object.prototype.hasOwnProperty.call(data, 'acct')
|
||||
// case for users in "mentions" property for statuses in MastoAPI
|
||||
const mastoShort = masto && !data.hasOwnProperty('avatar')
|
||||
const mastoShort = masto && !Object.prototype.hasOwnProperty.call(data, 'avatar')
|
||||
|
||||
output.id = String(data.id)
|
||||
output._original = data // used for server-side settings
|
||||
|
||||
if (masto) {
|
||||
output.screen_name = data.acct
|
||||
|
|
@ -227,7 +228,7 @@ export const parseUser = (data) => {
|
|||
|
||||
export const parseAttachment = (data) => {
|
||||
const output = {}
|
||||
const masto = !data.hasOwnProperty('oembed')
|
||||
const masto = !Object.prototype.hasOwnProperty.call(data, 'oembed')
|
||||
|
||||
if (masto) {
|
||||
// Not exactly same...
|
||||
|
|
@ -248,7 +249,7 @@ export const parseAttachment = (data) => {
|
|||
|
||||
export const parseStatus = (data) => {
|
||||
const output = {}
|
||||
const masto = data.hasOwnProperty('account')
|
||||
const masto = Object.prototype.hasOwnProperty.call(data, 'account')
|
||||
|
||||
if (masto) {
|
||||
output.favorited = data.favourited
|
||||
|
|
@ -373,10 +374,10 @@ export const parseStatus = (data) => {
|
|||
|
||||
export const parseNotification = (data) => {
|
||||
const mastoDict = {
|
||||
'favourite': 'like',
|
||||
'reblog': 'repeat'
|
||||
favourite: 'like',
|
||||
reblog: 'repeat'
|
||||
}
|
||||
const masto = !data.hasOwnProperty('ntype')
|
||||
const masto = !Object.prototype.hasOwnProperty.call(data, 'ntype')
|
||||
const output = {}
|
||||
|
||||
if (masto) {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export class RegistrationError extends Error {
|
|||
// the error is probably a JSON object with a single key, "errors", whose value is another JSON object containing the real errors
|
||||
if (typeof error === 'string') {
|
||||
error = JSON.parse(error)
|
||||
// eslint-disable-next-line
|
||||
if (error.hasOwnProperty('error')) {
|
||||
error = JSON.parse(error.error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import utf8 from 'utf8'
|
||||
|
||||
export const newExporter = ({
|
||||
filename = 'data',
|
||||
getExportedObject
|
||||
}) => ({
|
||||
exportData () {
|
||||
const stringified = JSON.stringify(getExportedObject(), null, 2) // Pretty-print and indent with 2 spaces
|
||||
const stringified = utf8.encode(JSON.stringify(getExportedObject(), null, 2)) // Pretty-print and indent with 2 spaces
|
||||
|
||||
// Create an invisible link with a data url and simulate a click
|
||||
const e = document.createElement('a')
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
const fileSizeFormat = (num) => {
|
||||
var exponent
|
||||
var unit
|
||||
var units = ['B', 'KiB', 'MiB', 'GiB', 'TiB']
|
||||
const fileSizeFormat = (numArg) => {
|
||||
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB']
|
||||
let num = numArg
|
||||
if (num < 1) {
|
||||
return num + ' ' + units[0]
|
||||
}
|
||||
|
||||
exponent = Math.min(Math.floor(Math.log(num) / Math.log(1024)), units.length - 1)
|
||||
const exponent = Math.min(Math.floor(Math.log(num) / Math.log(1024)), units.length - 1)
|
||||
num = (num / Math.pow(1024, exponent)).toFixed(2) * 1
|
||||
unit = units[exponent]
|
||||
return { num: num, unit: unit }
|
||||
const unit = units[exponent]
|
||||
return { num, unit }
|
||||
}
|
||||
const fileSizeFormatService = {
|
||||
fileSizeFormat
|
||||
|
|
|
|||
|
|
@ -4,9 +4,15 @@ const DIRECTION_RIGHT = [1, 0]
|
|||
const DIRECTION_UP = [0, -1]
|
||||
const DIRECTION_DOWN = [0, 1]
|
||||
|
||||
const BUTTON_LEFT = 0
|
||||
|
||||
const deltaCoord = (oldCoord, newCoord) => [newCoord[0] - oldCoord[0], newCoord[1] - oldCoord[1]]
|
||||
|
||||
const touchEventCoord = e => ([e.touches[0].screenX, e.touches[0].screenY])
|
||||
const touchCoord = touch => [touch.screenX, touch.screenY]
|
||||
|
||||
const touchEventCoord = e => touchCoord(e.touches[0])
|
||||
|
||||
const pointerEventCoord = e => [e.clientX, e.clientY]
|
||||
|
||||
const vectorLength = v => Math.sqrt(v[0] * v[0] + v[1] * v[1])
|
||||
|
||||
|
|
@ -61,6 +67,132 @@ const updateSwipe = (event, gesture) => {
|
|||
gesture._swiping = false
|
||||
}
|
||||
|
||||
class SwipeAndClickGesture {
|
||||
// swipePreviewCallback(offsets: Array[Number])
|
||||
// offsets: the offset vector which the underlying component should move, from the starting position
|
||||
// swipeEndCallback(sign: 0|-1|1)
|
||||
// sign: if the swipe does not meet the threshold, 0
|
||||
// if the swipe meets the threshold in the positive direction, 1
|
||||
// if the swipe meets the threshold in the negative direction, -1
|
||||
constructor ({
|
||||
direction,
|
||||
// swipeStartCallback
|
||||
swipePreviewCallback,
|
||||
swipeEndCallback,
|
||||
swipeCancelCallback,
|
||||
swipelessClickCallback,
|
||||
threshold = 30,
|
||||
perpendicularTolerance = 1.0,
|
||||
disableClickThreshold = 1
|
||||
}) {
|
||||
const nop = () => {}
|
||||
this.direction = direction
|
||||
this.swipePreviewCallback = swipePreviewCallback || nop
|
||||
this.swipeEndCallback = swipeEndCallback || nop
|
||||
this.swipeCancelCallback = swipeCancelCallback || nop
|
||||
this.swipelessClickCallback = swipelessClickCallback || nop
|
||||
this.threshold = typeof threshold === 'function' ? threshold : () => threshold
|
||||
this.disableClickThreshold = typeof disableClickThreshold === 'function' ? disableClickThreshold : () => disableClickThreshold
|
||||
this.perpendicularTolerance = perpendicularTolerance
|
||||
this._reset()
|
||||
}
|
||||
|
||||
_reset () {
|
||||
this._startPos = [0, 0]
|
||||
this._pointerId = -1
|
||||
this._swiping = false
|
||||
this._swiped = false
|
||||
this._preventNextClick = false
|
||||
}
|
||||
|
||||
start (event) {
|
||||
// Only handle left click
|
||||
if (event.button !== BUTTON_LEFT) {
|
||||
return
|
||||
}
|
||||
|
||||
this._startPos = pointerEventCoord(event)
|
||||
this._pointerId = event.pointerId
|
||||
this._swiping = true
|
||||
this._swiped = false
|
||||
}
|
||||
|
||||
move (event) {
|
||||
if (this._swiping && this._pointerId === event.pointerId) {
|
||||
this._swiped = true
|
||||
|
||||
const coord = pointerEventCoord(event)
|
||||
const delta = deltaCoord(this._startPos, coord)
|
||||
|
||||
this.swipePreviewCallback(delta)
|
||||
}
|
||||
}
|
||||
|
||||
cancel (event) {
|
||||
if (!this._swiping || this._pointerId !== event.pointerId) {
|
||||
return
|
||||
}
|
||||
|
||||
this.swipeCancelCallback()
|
||||
}
|
||||
|
||||
end (event) {
|
||||
if (!this._swiping) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._pointerId !== event.pointerId) {
|
||||
return
|
||||
}
|
||||
|
||||
this._swiping = false
|
||||
|
||||
// movement too small
|
||||
const coord = pointerEventCoord(event)
|
||||
const delta = deltaCoord(this._startPos, coord)
|
||||
|
||||
const sign = (() => {
|
||||
if (vectorLength(delta) < this.threshold()) {
|
||||
return 0
|
||||
}
|
||||
// movement is opposite from direction
|
||||
const isPositive = dotProduct(delta, this.direction) > 0
|
||||
|
||||
// movement perpendicular to direction is too much
|
||||
const towardsDir = project(delta, this.direction)
|
||||
const perpendicularDir = perpendicular(this.direction)
|
||||
const towardsPerpendicular = project(delta, perpendicularDir)
|
||||
if (
|
||||
vectorLength(towardsDir) * this.perpendicularTolerance <
|
||||
vectorLength(towardsPerpendicular)
|
||||
) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return isPositive ? 1 : -1
|
||||
})()
|
||||
|
||||
if (this._swiped) {
|
||||
this.swipeEndCallback(sign)
|
||||
}
|
||||
this._reset()
|
||||
// Only a mouse will fire click event when
|
||||
// the end point is far from the starting point
|
||||
// so for other kinds of pointers do not check
|
||||
// whether we have swiped
|
||||
if (vectorLength(delta) >= this.disableClickThreshold() && event.pointerType === 'mouse') {
|
||||
this._preventNextClick = true
|
||||
}
|
||||
}
|
||||
|
||||
click (event) {
|
||||
if (!this._preventNextClick) {
|
||||
this.swipelessClickCallback()
|
||||
}
|
||||
this._reset()
|
||||
}
|
||||
}
|
||||
|
||||
const GestureService = {
|
||||
DIRECTION_LEFT,
|
||||
DIRECTION_RIGHT,
|
||||
|
|
@ -68,7 +200,8 @@ const GestureService = {
|
|||
DIRECTION_DOWN,
|
||||
swipeGesture,
|
||||
beginSwipe,
|
||||
updateSwipe
|
||||
updateSwipe,
|
||||
SwipeAndClickGesture
|
||||
}
|
||||
|
||||
export default GestureService
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export const convertHtmlToLines = (html = '') => {
|
|||
// All block-level elements that aren't empty elements, i.e. not <hr>
|
||||
const nonEmptyElements = new Set(visualLineElements)
|
||||
// Difference
|
||||
for (let elem of emptyElements) {
|
||||
for (const elem of emptyElements) {
|
||||
nonEmptyElements.delete(elem)
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ export const convertHtmlToLines = (html = '') => {
|
|||
...emptyElements.values()
|
||||
])
|
||||
|
||||
let buffer = [] // Current output buffer
|
||||
const buffer = [] // Current output buffer
|
||||
const level = [] // How deep we are in tags and which tags were there
|
||||
let textBuffer = '' // Current line content
|
||||
let tagBuffer = null // Current tag buffer, if null = we are not currently reading a tag
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { getTagName } from './utility.service.js'
|
||||
import { unescape } from 'lodash'
|
||||
|
||||
/**
|
||||
* This is a not-so-tiny purpose-built HTML parser/processor. This parses html
|
||||
|
|
@ -49,7 +50,7 @@ export const convertHtmlToTree = (html = '') => {
|
|||
|
||||
const handleOpen = (tag) => {
|
||||
const curBuf = getCurrentBuffer()
|
||||
const newLevel = [tag, []]
|
||||
const newLevel = [unescape(tag), []]
|
||||
levels.push(newLevel)
|
||||
curBuf.push(newLevel)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export const processTextForEmoji = (text, emojis, processor) => {
|
|||
if (char === ':') {
|
||||
const next = text.slice(i + 1)
|
||||
let found = false
|
||||
for (let emoji of emojis) {
|
||||
for (const emoji of emojis) {
|
||||
if (next.slice(0, emoji.shortcode.length + 1) === (emoji.shortcode + ':')) {
|
||||
found = emoji
|
||||
break
|
||||
|
|
|
|||
|
|
@ -1,12 +1,35 @@
|
|||
import languagesObject from '../../i18n/messages'
|
||||
import ISO6391 from 'iso-639-1'
|
||||
import _ from 'lodash'
|
||||
|
||||
const specialLanguageCodes = {
|
||||
'ja_easy': 'ja',
|
||||
'zh_Hant': 'zh-HANT'
|
||||
ja_easy: 'ja',
|
||||
zh_Hant: 'zh-HANT',
|
||||
zh: 'zh-Hans'
|
||||
}
|
||||
|
||||
const internalToBrowserLocale = code => specialLanguageCodes[code] || code
|
||||
|
||||
const internalToBackendLocale = code => internalToBrowserLocale(code).replace('_', '-')
|
||||
|
||||
const getLanguageName = (code) => {
|
||||
const specialLanguageNames = {
|
||||
ja_easy: 'やさしいにほんご',
|
||||
zh: '简体中文',
|
||||
zh_Hant: '繁體中文'
|
||||
}
|
||||
const languageName = specialLanguageNames[code] || ISO6391.getNativeName(code)
|
||||
const browserLocale = internalToBrowserLocale(code)
|
||||
return languageName.charAt(0).toLocaleUpperCase(browserLocale) + languageName.slice(1)
|
||||
}
|
||||
|
||||
const languages = _.map(languagesObject.languages, (code) => ({ code, name: getLanguageName(code) })).sort((a, b) => a.name.localeCompare(b.name))
|
||||
|
||||
const localeService = {
|
||||
internalToBrowserLocale
|
||||
internalToBrowserLocale,
|
||||
internalToBackendLocale,
|
||||
languages,
|
||||
getLanguageName
|
||||
}
|
||||
|
||||
export default localeService
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { reduce } from 'lodash'
|
||||
|
||||
const MASTODON_PASSWORD_RESET_URL = `/auth/password`
|
||||
const MASTODON_PASSWORD_RESET_URL = '/auth/password'
|
||||
|
||||
const resetPassword = ({ instance, email }) => {
|
||||
const params = { email }
|
||||
|
|
|
|||
|
|
@ -14,11 +14,12 @@ export const visibleTypes = store => {
|
|||
rootState.config.notificationVisibility.follows && 'follow',
|
||||
rootState.config.notificationVisibility.followRequest && 'follow_request',
|
||||
rootState.config.notificationVisibility.moves && 'move',
|
||||
rootState.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction'
|
||||
rootState.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction',
|
||||
rootState.config.notificationVisibility.polls && 'poll'
|
||||
].filter(_ => _))
|
||||
}
|
||||
|
||||
const statusNotifications = ['like', 'mention', 'repeat', 'pleroma:emoji_reaction']
|
||||
const statusNotifications = ['like', 'mention', 'repeat', 'pleroma:emoji_reaction', 'poll']
|
||||
|
||||
export const isStatusNotification = (type) => includes(statusNotifications, type)
|
||||
|
||||
|
|
@ -98,6 +99,9 @@ export const prepareNotificationObject = (notification, i18n) => {
|
|||
case 'follow_request':
|
||||
i18nString = 'follow_request'
|
||||
break
|
||||
case 'poll':
|
||||
i18nString = 'poll_ended'
|
||||
break
|
||||
}
|
||||
|
||||
if (notification.type === 'pleroma:emoji_reaction') {
|
||||
|
|
|
|||
|
|
@ -12,20 +12,20 @@ const fetchAndUpdate = ({ store, credentials, older = false, since }) => {
|
|||
const timelineData = rootState.statuses.notifications
|
||||
const hideMutedPosts = getters.mergedConfig.hideMutedPosts
|
||||
|
||||
args['withMuted'] = !hideMutedPosts
|
||||
args.withMuted = !hideMutedPosts
|
||||
|
||||
args['timeline'] = 'notifications'
|
||||
args.timeline = 'notifications'
|
||||
if (older) {
|
||||
if (timelineData.minId !== Number.POSITIVE_INFINITY) {
|
||||
args['until'] = timelineData.minId
|
||||
args.until = timelineData.minId
|
||||
}
|
||||
return fetchNotifications({ store, args, older })
|
||||
} else {
|
||||
// fetch new notifications
|
||||
if (since === undefined && timelineData.maxId !== Number.POSITIVE_INFINITY) {
|
||||
args['since'] = timelineData.maxId
|
||||
args.since = timelineData.maxId
|
||||
} else if (since !== null) {
|
||||
args['since'] = since
|
||||
args.since = since
|
||||
}
|
||||
const result = fetchNotifications({ store, args, older })
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ const fetchAndUpdate = ({ store, credentials, older = false, since }) => {
|
|||
const readNotifsIds = notifications.filter(n => n.seen).map(n => n.id)
|
||||
const numUnseenNotifs = notifications.length - readNotifsIds.length
|
||||
if (numUnseenNotifs > 0 && readNotifsIds.length > 0) {
|
||||
args['since'] = Math.max(...readNotifsIds)
|
||||
args.since = Math.max(...readNotifsIds)
|
||||
fetchNotifications({ store, args, older })
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export const findOffset = (child, parent, { top = 0, left = 0 } = {}, ignorePadd
|
|||
result.left += ignorePadding ? 0 : leftPadding
|
||||
}
|
||||
|
||||
if (child.offsetParent && (parent === window || parent.contains(child.offsetParent) || parent === child.offsetParent)) {
|
||||
if (child.offsetParent && window.getComputedStyle(child.offsetParent).position !== 'sticky' && (parent === window || parent.contains(child.offsetParent) || parent === child.offsetParent)) {
|
||||
return findOffset(child.offsetParent, parent, result, false)
|
||||
} else {
|
||||
if (parent !== window) {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ function deleteSubscriptionFromBackEnd (token) {
|
|||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}).then((response) => {
|
||||
if (!response.ok) throw new Error('Bad status code from server.')
|
||||
|
|
@ -56,7 +56,7 @@ function sendSubscriptionToBackEnd (subscription, token, notificationVisibility)
|
|||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
subscription,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import Vue from 'vue'
|
||||
import { defineAsyncComponent, shallowReactive, h } from 'vue'
|
||||
|
||||
/* By default async components don't have any way to recover, if component is
|
||||
* failed, it is failed forever. This helper tries to remedy that by recreating
|
||||
|
|
@ -8,23 +8,21 @@ import Vue from 'vue'
|
|||
* actual target component itself if needs to be.
|
||||
*/
|
||||
function getResettableAsyncComponent (asyncComponent, options) {
|
||||
const asyncComponentFactory = () => () => ({
|
||||
component: asyncComponent(),
|
||||
const asyncComponentFactory = () => () => defineAsyncComponent({
|
||||
loader: asyncComponent,
|
||||
...options
|
||||
})
|
||||
|
||||
const observe = Vue.observable({ c: asyncComponentFactory() })
|
||||
const observe = shallowReactive({ c: asyncComponentFactory() })
|
||||
|
||||
return {
|
||||
functional: true,
|
||||
render (createElement, { data, children }) {
|
||||
render () {
|
||||
// emit event resetAsyncComponent to reloading
|
||||
data.on = {}
|
||||
data.on.resetAsyncComponent = () => {
|
||||
observe.c = asyncComponentFactory()
|
||||
// parent.$forceUpdate()
|
||||
}
|
||||
return createElement(observe.c, data, children)
|
||||
return h(observe.c(), {
|
||||
onResetAsyncComponent () {
|
||||
observe.c = asyncComponentFactory()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ export const applyTheme = (input) => {
|
|||
const styleSheet = styleEl.sheet
|
||||
|
||||
styleSheet.toString()
|
||||
styleSheet.insertRule(`body { ${rules.radii} }`, 'index-max')
|
||||
styleSheet.insertRule(`body { ${rules.colors} }`, 'index-max')
|
||||
styleSheet.insertRule(`body { ${rules.shadows} }`, 'index-max')
|
||||
styleSheet.insertRule(`body { ${rules.fonts} }`, 'index-max')
|
||||
styleSheet.insertRule(`:root { ${rules.radii} }`, 'index-max')
|
||||
styleSheet.insertRule(`:root { ${rules.colors} }`, 'index-max')
|
||||
styleSheet.insertRule(`:root { ${rules.shadows} }`, 'index-max')
|
||||
styleSheet.insertRule(`:root { ${rules.fonts} }`, 'index-max')
|
||||
body.classList.remove('hidden')
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ import { LAYERS, DEFAULT_OPACITY, SLOT_INHERITANCE } from './pleromafe.js'
|
|||
export const CURRENT_VERSION = 3
|
||||
|
||||
export const getLayersArray = (layer, data = LAYERS) => {
|
||||
let array = [layer]
|
||||
const array = [layer]
|
||||
let parent = data[layer]
|
||||
while (parent) {
|
||||
array.unshift(parent)
|
||||
|
|
@ -138,6 +138,7 @@ export const topoSort = (
|
|||
if (depsA === depsB || (depsB !== 0 && depsA !== 0)) return ai - bi
|
||||
if (depsA === 0 && depsB !== 0) return -1
|
||||
if (depsB === 0 && depsA !== 0) return 1
|
||||
return 0 // failsafe, shouldn't happen?
|
||||
}).map(({ data }) => data)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,20 +34,20 @@ const fetchAndUpdate = ({
|
|||
const loggedIn = !!rootState.users.currentUser
|
||||
|
||||
if (older) {
|
||||
args['until'] = until || timelineData.minId
|
||||
args.until = until || timelineData.minId
|
||||
} else {
|
||||
if (since === undefined) {
|
||||
args['since'] = timelineData.maxId
|
||||
args.since = timelineData.maxId
|
||||
} else if (since !== null) {
|
||||
args['since'] = since
|
||||
args.since = since
|
||||
}
|
||||
}
|
||||
|
||||
args['userId'] = userId
|
||||
args['tag'] = tag
|
||||
args['withMuted'] = !hideMutedPosts
|
||||
args.userId = userId
|
||||
args.tag = tag
|
||||
args.withMuted = !hideMutedPosts
|
||||
if (loggedIn && ['friends', 'public', 'publicAndExternal'].includes(timeline)) {
|
||||
args['replyVisibility'] = replyVisibility
|
||||
args.replyVisibility = replyVisibility
|
||||
}
|
||||
|
||||
const numStatusesBeforeFetch = timelineData.statuses.length
|
||||
|
|
@ -60,7 +60,7 @@ const fetchAndUpdate = ({
|
|||
|
||||
const { data: statuses, pagination } = response
|
||||
if (!older && statuses.length >= 20 && !timelineData.loading && numStatusesBeforeFetch > 0) {
|
||||
store.dispatch('queueFlush', { timeline: timeline, id: timelineData.maxId })
|
||||
store.dispatch('queueFlush', { timeline, id: timelineData.maxId })
|
||||
}
|
||||
update({ store, statuses, timeline, showImmediately, userId, pagination })
|
||||
return { statuses, pagination }
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ const highlightStyle = (prefs) => {
|
|||
'linear-gradient(to right,',
|
||||
`${solidColor} ,`,
|
||||
`${solidColor} 2px,`,
|
||||
`transparent 6px`
|
||||
'transparent 6px'
|
||||
].join(' '),
|
||||
backgroundPosition: '0 0',
|
||||
...customProps
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue