Merge remote-tracking branch 'upstream/develop' into develop
This commit is contained in:
commit
f6cf509a04
229 changed files with 9798 additions and 5400 deletions
|
@ -152,9 +152,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, {
|
||||
|
@ -192,6 +198,7 @@ const updateProfile = ({ credentials, params }) => {
|
|||
// homepage
|
||||
// location
|
||||
// token
|
||||
// language
|
||||
const register = ({ params, credentials }) => {
|
||||
const { nickname, ...rest } = params
|
||||
return fetch(MASTODON_REGISTRATION_URL, {
|
||||
|
|
|
@ -44,6 +44,7 @@ export const parseUser = (data) => {
|
|||
const mastoShort = masto && !data.hasOwnProperty('avatar')
|
||||
|
||||
output.id = String(data.id)
|
||||
output._original = data // used for server-side settings
|
||||
|
||||
if (masto) {
|
||||
output.screen_name = data.acct
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
'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: code, name: getLanguageName(code) })).sort((a, b) => a.name.localeCompare(b.name))
|
||||
|
||||
const localeService = {
|
||||
internalToBrowserLocale
|
||||
internalToBrowserLocale,
|
||||
internalToBackendLocale,
|
||||
languages,
|
||||
getLanguageName
|
||||
}
|
||||
|
||||
export default localeService
|
||||
|
|
|
@ -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') {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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')
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue