Merge branch 'develop' into 'fix-move-type-notification'
# Conflicts: # static/fontello.json
This commit is contained in:
commit
215662afde
4
.babelrc
4
.babelrc
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"presets": ["es2015", "stage-2", "env"],
|
"presets": ["@babel/preset-env"],
|
||||||
"plugins": ["transform-runtime", "lodash", "transform-vue-jsx"],
|
"plugins": ["@babel/plugin-transform-runtime", "lodash", "@vue/babel-plugin-transform-vue-jsx"],
|
||||||
"comments": false
|
"comments": false
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,9 @@ Use custom image for NSFW'd images
|
||||||
### `showFeaturesPanel`
|
### `showFeaturesPanel`
|
||||||
Show panel showcasing instance features/settings to logged-out visitors
|
Show panel showcasing instance features/settings to logged-out visitors
|
||||||
|
|
||||||
|
### `hideSitename`
|
||||||
|
Hide instance name in header
|
||||||
|
|
||||||
## Indirect configuration
|
## Indirect configuration
|
||||||
Some features are configured depending on how backend is configured. In general the approach is "if backend allows it there's no need to hide it, if backend doesn't allow it there's no need to show it.
|
Some features are configured depending on how backend is configured. In general the approach is "if backend allows it there's no need to hide it, if backend doesn't allow it there's no need to show it.
|
||||||
|
|
||||||
|
@ -96,3 +99,6 @@ Setting this will change the warning text that is displayed for direct messages.
|
||||||
ATTENTION: If you actually want the behavior to change. You will need to set the appropriate option at the backend. See the backend documentation for information about that.
|
ATTENTION: If you actually want the behavior to change. You will need to set the appropriate option at the backend. See the backend documentation for information about that.
|
||||||
|
|
||||||
DO NOT activate this without checking the backend configuration first!
|
DO NOT activate this without checking the backend configuration first!
|
||||||
|
|
||||||
|
### Private Mode
|
||||||
|
If the `private` instance setting is enabled in the backend, features that are not accessible without authentication, such as the timelines and search will be disabled for unauthenticated users.
|
||||||
|
|
22
package.json
22
package.json
|
@ -15,9 +15,8 @@
|
||||||
"lint-fix": "eslint --fix --ext .js,.vue src test/unit/specs test/e2e/specs"
|
"lint-fix": "eslint --fix --ext .js,.vue src test/unit/specs test/e2e/specs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.7.6",
|
||||||
"@chenfengyuan/vue-qrcode": "^1.0.0",
|
"@chenfengyuan/vue-qrcode": "^1.0.0",
|
||||||
"babel-plugin-add-module-exports": "^0.2.1",
|
|
||||||
"babel-plugin-lodash": "^3.2.11",
|
|
||||||
"body-scroll-lock": "^2.6.4",
|
"body-scroll-lock": "^2.6.4",
|
||||||
"chromatism": "^3.0.0",
|
"chromatism": "^3.0.0",
|
||||||
"cropperjs": "^1.4.3",
|
"cropperjs": "^1.4.3",
|
||||||
|
@ -40,20 +39,17 @@
|
||||||
"whatwg-fetch": "^2.0.3"
|
"whatwg-fetch": "^2.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/polyfill": "^7.0.0",
|
"@babel/core": "^7.7.5",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.7.6",
|
||||||
|
"@babel/preset-env": "^7.7.6",
|
||||||
|
"@babel/register": "^7.7.4",
|
||||||
|
"@vue/babel-helper-vue-jsx-merge-props": "^1.0.0",
|
||||||
|
"@vue/babel-plugin-transform-vue-jsx": "^1.1.2",
|
||||||
"@vue/test-utils": "^1.0.0-beta.26",
|
"@vue/test-utils": "^1.0.0-beta.26",
|
||||||
"autoprefixer": "^6.4.0",
|
"autoprefixer": "^6.4.0",
|
||||||
"babel-core": "^6.0.0",
|
|
||||||
"babel-eslint": "^7.0.0",
|
"babel-eslint": "^7.0.0",
|
||||||
"babel-helper-vue-jsx-merge-props": "^2.0.3",
|
"babel-loader": "^8.0.6",
|
||||||
"babel-loader": "^7.0.0",
|
"babel-plugin-lodash": "^3.3.4",
|
||||||
"babel-plugin-syntax-jsx": "^6.18.0",
|
|
||||||
"babel-plugin-transform-runtime": "^6.0.0",
|
|
||||||
"babel-plugin-transform-vue-jsx": "3",
|
|
||||||
"babel-preset-env": "^1.7.0",
|
|
||||||
"babel-preset-es2015": "^6.0.0",
|
|
||||||
"babel-preset-stage-2": "^6.0.0",
|
|
||||||
"babel-register": "^6.0.0",
|
|
||||||
"chai": "^3.5.0",
|
"chai": "^3.5.0",
|
||||||
"chalk": "^1.1.3",
|
"chalk": "^1.1.3",
|
||||||
"chromedriver": "^2.21.2",
|
"chromedriver": "^2.21.2",
|
||||||
|
|
|
@ -90,6 +90,7 @@ export default {
|
||||||
},
|
},
|
||||||
sitename () { return this.$store.state.instance.name },
|
sitename () { return this.$store.state.instance.name },
|
||||||
chat () { return this.$store.state.chat.channel.state === 'joined' },
|
chat () { return this.$store.state.chat.channel.state === 'joined' },
|
||||||
|
hideSitename () { return this.$store.state.instance.hideSitename },
|
||||||
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
|
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
|
||||||
showInstanceSpecificPanel () {
|
showInstanceSpecificPanel () {
|
||||||
return this.$store.state.instance.showInstanceSpecificPanel &&
|
return this.$store.state.instance.showInstanceSpecificPanel &&
|
||||||
|
@ -97,7 +98,8 @@ export default {
|
||||||
this.$store.state.instance.instanceSpecificPanelContent
|
this.$store.state.instance.instanceSpecificPanelContent
|
||||||
},
|
},
|
||||||
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
|
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
|
||||||
isMobileLayout () { return this.$store.state.interface.mobileLayout }
|
isMobileLayout () { return this.$store.state.interface.mobileLayout },
|
||||||
|
privateMode () { return this.$store.state.instance.private }
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
scrollToTop () {
|
scrollToTop () {
|
||||||
|
|
13
src/App.scss
13
src/App.scss
|
@ -870,3 +870,16 @@ nav {
|
||||||
transform: rotate(359deg);
|
transform: rotate(359deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.new-status-notification {
|
||||||
|
position:relative;
|
||||||
|
margin-top: -1px;
|
||||||
|
font-size: 1.1em;
|
||||||
|
border-width: 1px 0 0 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: var(--border, $fallback--border);
|
||||||
|
padding: 10px;
|
||||||
|
z-index: 1;
|
||||||
|
background-color: $fallback--fg;
|
||||||
|
background-color: var(--panel, $fallback--fg);
|
||||||
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<router-link
|
<router-link
|
||||||
|
v-if="!hideSitename"
|
||||||
class="site-name"
|
class="site-name"
|
||||||
:to="{ name: 'root' }"
|
:to="{ name: 'root' }"
|
||||||
active-class="home"
|
active-class="home"
|
||||||
|
@ -40,6 +41,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="item right">
|
<div class="item right">
|
||||||
<search-bar
|
<search-bar
|
||||||
|
v-if="currentUser || !privateMode"
|
||||||
class="nav-icon mobile-hidden"
|
class="nav-icon mobile-hidden"
|
||||||
@toggled="onSearchBarToggled"
|
@toggled="onSearchBarToggled"
|
||||||
@click.stop.native
|
@click.stop.native
|
||||||
|
|
|
@ -108,6 +108,7 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => {
|
||||||
copyInstanceOption('alwaysShowSubjectInput')
|
copyInstanceOption('alwaysShowSubjectInput')
|
||||||
copyInstanceOption('noAttachmentLinks')
|
copyInstanceOption('noAttachmentLinks')
|
||||||
copyInstanceOption('showFeaturesPanel')
|
copyInstanceOption('showFeaturesPanel')
|
||||||
|
copyInstanceOption('hideSitename')
|
||||||
|
|
||||||
return store.dispatch('setTheme', config['theme'])
|
return store.dispatch('setTheme', config['theme'])
|
||||||
}
|
}
|
||||||
|
@ -218,12 +219,21 @@ const getNodeInfo = async ({ store }) => {
|
||||||
store.dispatch('setInstanceOption', { name: 'backendVersion', value: software.version })
|
store.dispatch('setInstanceOption', { name: 'backendVersion', value: software.version })
|
||||||
store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: software.name === 'pleroma' })
|
store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: software.name === 'pleroma' })
|
||||||
|
|
||||||
|
const priv = metadata.private
|
||||||
|
store.dispatch('setInstanceOption', { name: 'private', value: priv })
|
||||||
|
|
||||||
const frontendVersion = window.___pleromafe_commit_hash
|
const frontendVersion = window.___pleromafe_commit_hash
|
||||||
store.dispatch('setInstanceOption', { name: 'frontendVersion', value: frontendVersion })
|
store.dispatch('setInstanceOption', { name: 'frontendVersion', value: frontendVersion })
|
||||||
store.dispatch('setInstanceOption', { name: 'tagPolicyAvailable', value: metadata.federation.mrf_policies.includes('TagPolicy') })
|
store.dispatch('setInstanceOption', { name: 'tagPolicyAvailable', value: metadata.federation.mrf_policies.includes('TagPolicy') })
|
||||||
|
|
||||||
const federation = metadata.federation
|
const federation = metadata.federation
|
||||||
store.dispatch('setInstanceOption', { name: 'federationPolicy', value: federation })
|
store.dispatch('setInstanceOption', { name: 'federationPolicy', value: federation })
|
||||||
|
store.dispatch('setInstanceOption', {
|
||||||
|
name: 'federating',
|
||||||
|
value: typeof federation.enabled === 'undefined'
|
||||||
|
? true
|
||||||
|
: federation.enabled
|
||||||
|
})
|
||||||
|
|
||||||
const accounts = metadata.staffAccounts
|
const accounts = metadata.staffAccounts
|
||||||
await resolveStaffAccounts({ store, accounts })
|
await resolveStaffAccounts({ store, accounts })
|
||||||
|
|
|
@ -7,11 +7,11 @@ const FollowRequestCard = {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
approveUser () {
|
approveUser () {
|
||||||
this.$store.state.api.backendInteractor.approveUser(this.user.id)
|
this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
|
||||||
this.$store.dispatch('removeFollowRequest', this.user)
|
this.$store.dispatch('removeFollowRequest', this.user)
|
||||||
},
|
},
|
||||||
denyUser () {
|
denyUser () {
|
||||||
this.$store.state.api.backendInteractor.denyUser(this.user.id)
|
this.$store.state.api.backendInteractor.denyUser({ id: this.user.id })
|
||||||
this.$store.dispatch('removeFollowRequest', this.user)
|
this.$store.dispatch('removeFollowRequest', this.user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ const LoginForm = {
|
||||||
).then((result) => {
|
).then((result) => {
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
if (result.error === 'mfa_required') {
|
if (result.error === 'mfa_required') {
|
||||||
this.requireMFA({ app: app, settings: result })
|
this.requireMFA({ settings: result })
|
||||||
} else if (result.identifier === 'password_reset_required') {
|
} else if (result.identifier === 'password_reset_required') {
|
||||||
this.$router.push({ name: 'password-reset', params: { passwordResetRequested: true } })
|
this.$router.push({ name: 'password-reset', params: { passwordResetRequested: true } })
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -8,18 +8,23 @@ export default {
|
||||||
}),
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
authApp: 'authFlow/app',
|
|
||||||
authSettings: 'authFlow/settings'
|
authSettings: 'authFlow/settings'
|
||||||
}),
|
}),
|
||||||
...mapState({ instance: 'instance' })
|
...mapState({
|
||||||
|
instance: 'instance',
|
||||||
|
oauth: 'oauth'
|
||||||
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations('authFlow', ['requireTOTP', 'abortMFA']),
|
...mapMutations('authFlow', ['requireTOTP', 'abortMFA']),
|
||||||
...mapActions({ login: 'authFlow/login' }),
|
...mapActions({ login: 'authFlow/login' }),
|
||||||
clearError () { this.error = false },
|
clearError () { this.error = false },
|
||||||
submit () {
|
submit () {
|
||||||
|
const { clientId, clientSecret } = this.oauth
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
app: this.authApp,
|
clientId,
|
||||||
|
clientSecret,
|
||||||
instance: this.instance.server,
|
instance: this.instance.server,
|
||||||
mfaToken: this.authSettings.mfa_token,
|
mfaToken: this.authSettings.mfa_token,
|
||||||
code: this.code
|
code: this.code
|
||||||
|
|
|
@ -7,18 +7,23 @@ export default {
|
||||||
}),
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
authApp: 'authFlow/app',
|
|
||||||
authSettings: 'authFlow/settings'
|
authSettings: 'authFlow/settings'
|
||||||
}),
|
}),
|
||||||
...mapState({ instance: 'instance' })
|
...mapState({
|
||||||
|
instance: 'instance',
|
||||||
|
oauth: 'oauth'
|
||||||
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations('authFlow', ['requireRecovery', 'abortMFA']),
|
...mapMutations('authFlow', ['requireRecovery', 'abortMFA']),
|
||||||
...mapActions({ login: 'authFlow/login' }),
|
...mapActions({ login: 'authFlow/login' }),
|
||||||
clearError () { this.error = false },
|
clearError () { this.error = false },
|
||||||
submit () {
|
submit () {
|
||||||
|
const { clientId, clientSecret } = this.oauth
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
app: this.authApp,
|
clientId,
|
||||||
|
clientSecret,
|
||||||
instance: this.instance.server,
|
instance: this.instance.server,
|
||||||
mfaToken: this.authSettings.mfa_token,
|
mfaToken: this.authSettings.mfa_token,
|
||||||
code: this.code
|
code: this.code
|
||||||
|
|
|
@ -29,6 +29,7 @@ const MobileNav = {
|
||||||
unseenNotificationsCount () {
|
unseenNotificationsCount () {
|
||||||
return this.unseenNotifications.length
|
return this.unseenNotifications.length
|
||||||
},
|
},
|
||||||
|
hideSitename () { return this.$store.state.instance.hideSitename },
|
||||||
sitename () { return this.$store.state.instance.name }
|
sitename () { return this.$store.state.instance.name }
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
<i class="button-icon icon-menu" />
|
<i class="button-icon icon-menu" />
|
||||||
</a>
|
</a>
|
||||||
<router-link
|
<router-link
|
||||||
|
v-if="!hideSitename"
|
||||||
class="site-name"
|
class="site-name"
|
||||||
:to="{ name: 'root' }"
|
:to="{ name: 'root' }"
|
||||||
active-class="home"
|
active-class="home"
|
||||||
|
|
|
@ -45,12 +45,12 @@ const ModerationTools = {
|
||||||
toggleTag (tag) {
|
toggleTag (tag) {
|
||||||
const store = this.$store
|
const store = this.$store
|
||||||
if (this.tagsSet.has(tag)) {
|
if (this.tagsSet.has(tag)) {
|
||||||
store.state.api.backendInteractor.untagUser(this.user, tag).then(response => {
|
store.state.api.backendInteractor.untagUser({ user: this.user, tag }).then(response => {
|
||||||
if (!response.ok) { return }
|
if (!response.ok) { return }
|
||||||
store.commit('untagUser', { user: this.user, tag })
|
store.commit('untagUser', { user: this.user, tag })
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
store.state.api.backendInteractor.tagUser(this.user, tag).then(response => {
|
store.state.api.backendInteractor.tagUser({ user: this.user, tag }).then(response => {
|
||||||
if (!response.ok) { return }
|
if (!response.ok) { return }
|
||||||
store.commit('tagUser', { user: this.user, tag })
|
store.commit('tagUser', { user: this.user, tag })
|
||||||
})
|
})
|
||||||
|
@ -59,24 +59,19 @@ const ModerationTools = {
|
||||||
toggleRight (right) {
|
toggleRight (right) {
|
||||||
const store = this.$store
|
const store = this.$store
|
||||||
if (this.user.rights[right]) {
|
if (this.user.rights[right]) {
|
||||||
store.state.api.backendInteractor.deleteRight(this.user, right).then(response => {
|
store.state.api.backendInteractor.deleteRight({ user: this.user, right }).then(response => {
|
||||||
if (!response.ok) { return }
|
if (!response.ok) { return }
|
||||||
store.commit('updateRight', { user: this.user, right: right, value: false })
|
store.commit('updateRight', { user: this.user, right, value: false })
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
store.state.api.backendInteractor.addRight(this.user, right).then(response => {
|
store.state.api.backendInteractor.addRight({ user: this.user, right }).then(response => {
|
||||||
if (!response.ok) { return }
|
if (!response.ok) { return }
|
||||||
store.commit('updateRight', { user: this.user, right: right, value: true })
|
store.commit('updateRight', { user: this.user, right, value: true })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggleActivationStatus () {
|
toggleActivationStatus () {
|
||||||
const store = this.$store
|
this.$store.dispatch('toggleActivationStatus', { user: this.user })
|
||||||
const status = !!this.user.deactivated
|
|
||||||
store.state.api.backendInteractor.setActivationStatus(this.user, status).then(response => {
|
|
||||||
if (!response.ok) { return }
|
|
||||||
store.commit('updateActivationStatus', { user: this.user, status: status })
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
deleteUserDialog (show) {
|
deleteUserDialog (show) {
|
||||||
this.showDeleteUserDialog = show
|
this.showDeleteUserDialog = show
|
||||||
|
@ -85,7 +80,7 @@ const ModerationTools = {
|
||||||
const store = this.$store
|
const store = this.$store
|
||||||
const user = this.user
|
const user = this.user
|
||||||
const { id, name } = user
|
const { id, name } = user
|
||||||
store.state.api.backendInteractor.deleteUser(user)
|
store.state.api.backendInteractor.deleteUser({ user })
|
||||||
.then(e => {
|
.then(e => {
|
||||||
this.$store.dispatch('markStatusesAsDeleted', status => user.id === status.user.id)
|
this.$store.dispatch('markStatusesAsDeleted', status => user.id === status.user.id)
|
||||||
const isProfile = this.$route.name === 'external-user-profile' || this.$route.name === 'user-profile'
|
const isProfile = this.$route.name === 'external-user-profile' || this.$route.name === 'user-profile'
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
|
import { mapState } from 'vuex'
|
||||||
|
|
||||||
const NavPanel = {
|
const NavPanel = {
|
||||||
created () {
|
created () {
|
||||||
if (this.currentUser && this.currentUser.locked) {
|
if (this.currentUser && this.currentUser.locked) {
|
||||||
this.$store.dispatch('startFetchingFollowRequest')
|
this.$store.dispatch('startFetchingFollowRequest')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: mapState({
|
||||||
currentUser () {
|
currentUser: state => state.users.currentUser,
|
||||||
return this.$store.state.users.currentUser
|
chat: state => state.chat.channel,
|
||||||
},
|
followRequestCount: state => state.api.followRequests.length,
|
||||||
chat () {
|
privateMode: state => state.instance.private,
|
||||||
return this.$store.state.chat.channel
|
federating: state => state.instance.federating
|
||||||
},
|
})
|
||||||
followRequestCount () {
|
|
||||||
return this.$store.state.api.followRequests.length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NavPanel
|
export default NavPanel
|
||||||
|
|
|
@ -4,22 +4,22 @@
|
||||||
<ul>
|
<ul>
|
||||||
<li v-if="currentUser">
|
<li v-if="currentUser">
|
||||||
<router-link :to="{ name: 'friends' }">
|
<router-link :to="{ name: 'friends' }">
|
||||||
{{ $t("nav.timeline") }}
|
<i class="button-icon icon-home-2" /> {{ $t("nav.timeline") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="currentUser">
|
<li v-if="currentUser">
|
||||||
<router-link :to="{ name: 'interactions', params: { username: currentUser.screen_name } }">
|
<router-link :to="{ name: 'interactions', params: { username: currentUser.screen_name } }">
|
||||||
{{ $t("nav.interactions") }}
|
<i class="button-icon icon-bell-alt" /> {{ $t("nav.interactions") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="currentUser">
|
<li v-if="currentUser">
|
||||||
<router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
|
<router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
|
||||||
{{ $t("nav.dms") }}
|
<i class="button-icon icon-mail-alt" /> {{ $t("nav.dms") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="currentUser && currentUser.locked">
|
<li v-if="currentUser && currentUser.locked">
|
||||||
<router-link :to="{ name: 'friend-requests' }">
|
<router-link :to="{ name: 'friend-requests' }">
|
||||||
{{ $t("nav.friend_requests") }}
|
<i class="button-icon icon-user-plus" /> {{ $t("nav.friend_requests") }}
|
||||||
<span
|
<span
|
||||||
v-if="followRequestCount > 0"
|
v-if="followRequestCount > 0"
|
||||||
class="badge follow-request-count"
|
class="badge follow-request-count"
|
||||||
|
@ -28,19 +28,19 @@
|
||||||
</span>
|
</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li v-if="currentUser || !privateMode">
|
||||||
<router-link :to="{ name: 'public-timeline' }">
|
<router-link :to="{ name: 'public-timeline' }">
|
||||||
{{ $t("nav.public_tl") }}
|
<i class="button-icon icon-users" /> {{ $t("nav.public_tl") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li v-if="federating && !privateMode">
|
||||||
<router-link :to="{ name: 'public-external-timeline' }">
|
<router-link :to="{ name: 'public-external-timeline' }">
|
||||||
{{ $t("nav.twkn") }}
|
<i class="button-icon icon-globe" /> {{ $t("nav.twkn") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<router-link :to="{ name: 'about' }">
|
<router-link :to="{ name: 'about' }">
|
||||||
{{ $t("nav.about") }}
|
<i class="button-icon icon-info-circled" /> {{ $t("nav.about") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -113,4 +113,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav-panel .button-icon:before {
|
||||||
|
width: 1.1em;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -47,6 +47,11 @@ const Notifications = {
|
||||||
components: {
|
components: {
|
||||||
Notification
|
Notification
|
||||||
},
|
},
|
||||||
|
created () {
|
||||||
|
const { dispatch } = this.$store
|
||||||
|
|
||||||
|
dispatch('fetchAndUpdateNotifications')
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
unseenCount (count) {
|
unseenCount (count) {
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
|
|
|
@ -10,7 +10,7 @@ const PublicAndExternalTimeline = {
|
||||||
this.$store.dispatch('startFetchingTimeline', { timeline: 'publicAndExternal' })
|
this.$store.dispatch('startFetchingTimeline', { timeline: 'publicAndExternal' })
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed () {
|
||||||
this.$store.dispatch('stopFetching', 'publicAndExternal')
|
this.$store.dispatch('stopFetchingTimeline', 'publicAndExternal')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ const PublicTimeline = {
|
||||||
this.$store.dispatch('startFetchingTimeline', { timeline: 'public' })
|
this.$store.dispatch('startFetchingTimeline', { timeline: 'public' })
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed () {
|
||||||
this.$store.dispatch('stopFetching', 'public')
|
this.$store.dispatch('stopFetchingTimeline', 'public')
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,7 @@
|
||||||
for="captcha-label"
|
for="captcha-label"
|
||||||
>{{ $t('captcha') }}</label>
|
>{{ $t('captcha') }}</label>
|
||||||
|
|
||||||
<template v-if="captcha.type == 'kocaptcha'">
|
<template v-if="['kocaptcha', 'native'].includes(captcha.type)">
|
||||||
<img
|
<img
|
||||||
:src="captcha.url"
|
:src="captcha.url"
|
||||||
@click="setCaptcha"
|
@click="setCaptcha"
|
||||||
|
|
|
@ -84,7 +84,7 @@ const settings = {
|
||||||
}
|
}
|
||||||
}])
|
}])
|
||||||
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}),
|
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}),
|
||||||
// Special cases (need to transform values)
|
// Special cases (need to transform values or perform actions first)
|
||||||
muteWordsString: {
|
muteWordsString: {
|
||||||
get () { return this.$store.getters.mergedConfig.muteWords.join('\n') },
|
get () { return this.$store.getters.mergedConfig.muteWords.join('\n') },
|
||||||
set (value) {
|
set (value) {
|
||||||
|
@ -93,6 +93,22 @@ const settings = {
|
||||||
value: filter(value.split('\n'), (word) => trim(word).length > 0)
|
value: filter(value.split('\n'), (word) => trim(word).length > 0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
useStreamingApi: {
|
||||||
|
get () { return this.$store.getters.mergedConfig.useStreamingApi },
|
||||||
|
set (value) {
|
||||||
|
const promise = value
|
||||||
|
? this.$store.dispatch('enableMastoSockets')
|
||||||
|
: this.$store.dispatch('disableMastoSockets')
|
||||||
|
|
||||||
|
promise.then(() => {
|
||||||
|
this.$store.dispatch('setOption', { name: 'useStreamingApi', value })
|
||||||
|
}).catch((e) => {
|
||||||
|
console.error('Failed starting MastoAPI Streaming socket', e)
|
||||||
|
this.$store.dispatch('disableMastoSockets')
|
||||||
|
this.$store.dispatch('setOption', { name: 'useStreamingApi', value: false })
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Updating nested properties
|
// Updating nested properties
|
||||||
|
|
|
@ -73,6 +73,15 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<Checkbox v-model="useStreamingApi">
|
||||||
|
{{ $t('settings.useStreamingApi') }}
|
||||||
|
<br/>
|
||||||
|
<small>
|
||||||
|
{{ $t('settings.useStreamingApiWarning') }}
|
||||||
|
</small>
|
||||||
|
</Checkbox>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Checkbox v-model="autoLoad">
|
<Checkbox v-model="autoLoad">
|
||||||
{{ $t('settings.autoload') }}
|
{{ $t('settings.autoload') }}
|
||||||
|
|
|
@ -33,11 +33,20 @@ const SideDrawer = {
|
||||||
logo () {
|
logo () {
|
||||||
return this.$store.state.instance.logo
|
return this.$store.state.instance.logo
|
||||||
},
|
},
|
||||||
|
hideSitename () {
|
||||||
|
return this.$store.state.instance.hideSitename
|
||||||
|
},
|
||||||
sitename () {
|
sitename () {
|
||||||
return this.$store.state.instance.name
|
return this.$store.state.instance.name
|
||||||
},
|
},
|
||||||
followRequestCount () {
|
followRequestCount () {
|
||||||
return this.$store.state.api.followRequests.length
|
return this.$store.state.api.followRequests.length
|
||||||
|
},
|
||||||
|
privateMode () {
|
||||||
|
return this.$store.state.instance.private
|
||||||
|
},
|
||||||
|
federating () {
|
||||||
|
return this.$store.state.instance.federating
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
class="side-drawer-logo-wrapper"
|
class="side-drawer-logo-wrapper"
|
||||||
>
|
>
|
||||||
<img :src="logo">
|
<img :src="logo">
|
||||||
<span>{{ sitename }}</span>
|
<span v-if="!hideSitename">{{ sitename }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
@click="toggleDrawer"
|
@click="toggleDrawer"
|
||||||
>
|
>
|
||||||
<router-link :to="{ name: 'login' }">
|
<router-link :to="{ name: 'login' }">
|
||||||
{{ $t("login.login") }}
|
<i class="button-icon icon-login" /> {{ $t("login.login") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
@click="toggleDrawer"
|
@click="toggleDrawer"
|
||||||
>
|
>
|
||||||
<router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
|
<router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
|
||||||
{{ $t("nav.dms") }}
|
<i class="button-icon icon-mail-alt" /> {{ $t("nav.dms") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
@click="toggleDrawer"
|
@click="toggleDrawer"
|
||||||
>
|
>
|
||||||
<router-link :to="{ name: 'interactions', params: { username: currentUser.screen_name } }">
|
<router-link :to="{ name: 'interactions', params: { username: currentUser.screen_name } }">
|
||||||
{{ $t("nav.interactions") }}
|
<i class="button-icon icon-bell-alt" /> {{ $t("nav.interactions") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
@click="toggleDrawer"
|
@click="toggleDrawer"
|
||||||
>
|
>
|
||||||
<router-link :to="{ name: 'friends' }">
|
<router-link :to="{ name: 'friends' }">
|
||||||
{{ $t("nav.timeline") }}
|
<i class="button-icon icon-home-2" /> {{ $t("nav.timeline") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
|
@ -70,7 +70,7 @@
|
||||||
@click="toggleDrawer"
|
@click="toggleDrawer"
|
||||||
>
|
>
|
||||||
<router-link to="/friend-requests">
|
<router-link to="/friend-requests">
|
||||||
{{ $t("nav.friend_requests") }}
|
<i class="button-icon icon-user-plus" /> {{ $t("nav.friend_requests") }}
|
||||||
<span
|
<span
|
||||||
v-if="followRequestCount > 0"
|
v-if="followRequestCount > 0"
|
||||||
class="badge follow-request-count"
|
class="badge follow-request-count"
|
||||||
|
@ -79,14 +79,20 @@
|
||||||
</span>
|
</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li @click="toggleDrawer">
|
<li
|
||||||
|
v-if="currentUser || !privateMode"
|
||||||
|
@click="toggleDrawer"
|
||||||
|
>
|
||||||
<router-link to="/main/public">
|
<router-link to="/main/public">
|
||||||
{{ $t("nav.public_tl") }}
|
<i class="button-icon icon-users" /> {{ $t("nav.public_tl") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li @click="toggleDrawer">
|
<li
|
||||||
|
v-if="federating && !privateMode"
|
||||||
|
@click="toggleDrawer"
|
||||||
|
>
|
||||||
<router-link to="/main/all">
|
<router-link to="/main/all">
|
||||||
{{ $t("nav.twkn") }}
|
<i class="button-icon icon-globe" /> {{ $t("nav.twkn") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
|
@ -94,14 +100,17 @@
|
||||||
@click="toggleDrawer"
|
@click="toggleDrawer"
|
||||||
>
|
>
|
||||||
<router-link :to="{ name: 'chat' }">
|
<router-link :to="{ name: 'chat' }">
|
||||||
{{ $t("nav.chat") }}
|
<i class="button-icon icon-chat" /> {{ $t("nav.chat") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<li @click="toggleDrawer">
|
<li
|
||||||
|
v-if="currentUser || !privateMode"
|
||||||
|
@click="toggleDrawer"
|
||||||
|
>
|
||||||
<router-link :to="{ name: 'search' }">
|
<router-link :to="{ name: 'search' }">
|
||||||
{{ $t("nav.search") }}
|
<i class="button-icon icon-search" /> {{ $t("nav.search") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
|
@ -109,17 +118,17 @@
|
||||||
@click="toggleDrawer"
|
@click="toggleDrawer"
|
||||||
>
|
>
|
||||||
<router-link :to="{ name: 'who-to-follow' }">
|
<router-link :to="{ name: 'who-to-follow' }">
|
||||||
{{ $t("nav.who_to_follow") }}
|
<i class="button-icon icon-user-plus" /> {{ $t("nav.who_to_follow") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li @click="toggleDrawer">
|
<li @click="toggleDrawer">
|
||||||
<router-link :to="{ name: 'settings' }">
|
<router-link :to="{ name: 'settings' }">
|
||||||
{{ $t("settings.settings") }}
|
<i class="button-icon icon-cog" /> {{ $t("settings.settings") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li @click="toggleDrawer">
|
<li @click="toggleDrawer">
|
||||||
<router-link :to="{ name: 'about'}">
|
<router-link :to="{ name: 'about'}">
|
||||||
{{ $t("nav.about") }}
|
<i class="button-icon icon-info-circled" /> {{ $t("nav.about") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
|
@ -130,7 +139,7 @@
|
||||||
href="/pleroma/admin/#/login-pleroma"
|
href="/pleroma/admin/#/login-pleroma"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
{{ $t("nav.administration") }}
|
<i class="button-icon icon-gauge" /> {{ $t("nav.administration") }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
|
@ -141,7 +150,7 @@
|
||||||
href="#"
|
href="#"
|
||||||
@click="doLogout"
|
@click="doLogout"
|
||||||
>
|
>
|
||||||
{{ $t("login.logout") }}
|
<i class="button-icon icon-logout" /> {{ $t("login.logout") }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -215,6 +224,10 @@
|
||||||
box-shadow: var(--panelShadow);
|
box-shadow: var(--panelShadow);
|
||||||
background-color: $fallback--bg;
|
background-color: $fallback--bg;
|
||||||
background-color: var(--bg, $fallback--bg);
|
background-color: var(--bg, $fallback--bg);
|
||||||
|
|
||||||
|
.button-icon:before {
|
||||||
|
width: 1.1em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-drawer-logo-wrapper {
|
.side-drawer-logo-wrapper {
|
||||||
|
|
|
@ -36,23 +36,23 @@
|
||||||
|
|
||||||
.sticker-picker {
|
.sticker-picker {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
.contents {
|
||||||
.tab-switcher {
|
min-height: 250px;
|
||||||
position: absolute;
|
.sticker-picker-content {
|
||||||
top: 0;
|
display: flex;
|
||||||
bottom: 0;
|
flex-wrap: wrap;
|
||||||
left: 0;
|
padding: 0 4px;
|
||||||
right: 0;
|
.sticker {
|
||||||
}
|
display: flex;
|
||||||
.sticker-picker-content {
|
flex: 1 1 auto;
|
||||||
.sticker {
|
margin: 4px;
|
||||||
display: inline-block;
|
width: 56px;
|
||||||
width: 20%;
|
height: 56px;
|
||||||
height: 20%;
|
img {
|
||||||
img {
|
height: 100%;
|
||||||
width: 100%;
|
&:hover {
|
||||||
&:hover {
|
filter: drop-shadow(0 0 5px var(--link, $fallback--link));
|
||||||
filter: drop-shadow(0 0 5px var(--link, $fallback--link));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ const TagTimeline = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed () {
|
||||||
this.$store.dispatch('stopFetching', 'tag')
|
this.$store.dispatch('stopFetchingTimeline', 'tag')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,12 @@ const Timeline = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
timelineError () { return this.$store.state.statuses.error },
|
timelineError () {
|
||||||
|
return this.$store.state.statuses.error
|
||||||
|
},
|
||||||
|
errorData () {
|
||||||
|
return this.$store.state.statuses.errorData
|
||||||
|
},
|
||||||
newStatusCount () {
|
newStatusCount () {
|
||||||
return this.timeline.newStatusCount
|
return this.timeline.newStatusCount
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,15 +11,22 @@
|
||||||
>
|
>
|
||||||
{{ $t('timeline.error_fetching') }}
|
{{ $t('timeline.error_fetching') }}
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-else-if="errorData"
|
||||||
|
class="loadmore-error alert error"
|
||||||
|
@click.prevent
|
||||||
|
>
|
||||||
|
{{ errorData.statusText }}
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
v-if="timeline.newStatusCount > 0 && !timelineError"
|
v-if="timeline.newStatusCount > 0 && !timelineError && !errorData"
|
||||||
class="loadmore-button"
|
class="loadmore-button"
|
||||||
@click.prevent="showNewStatuses"
|
@click.prevent="showNewStatuses"
|
||||||
>
|
>
|
||||||
{{ $t('timeline.show_new') }}{{ newStatusCountStr }}
|
{{ $t('timeline.show_new') }}{{ newStatusCountStr }}
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
v-if="!timeline.newStatusCount > 0 && !timelineError"
|
v-if="!timeline.newStatusCount > 0 && !timelineError && !errorData"
|
||||||
class="loadmore-text faint"
|
class="loadmore-text faint"
|
||||||
@click.prevent
|
@click.prevent
|
||||||
>
|
>
|
||||||
|
@ -67,12 +74,18 @@
|
||||||
{{ $t('timeline.no_more_statuses') }}
|
{{ $t('timeline.no_more_statuses') }}
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
v-else-if="!timeline.loading"
|
v-else-if="!timeline.loading && !errorData"
|
||||||
href="#"
|
href="#"
|
||||||
@click.prevent="fetchOlderStatuses()"
|
@click.prevent="fetchOlderStatuses()"
|
||||||
>
|
>
|
||||||
<div class="new-status-notification text-center panel-footer">{{ $t('timeline.load_older') }}</div>
|
<div class="new-status-notification text-center panel-footer">{{ $t('timeline.load_older') }}</div>
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
v-else-if="errorData"
|
||||||
|
href="#"
|
||||||
|
>
|
||||||
|
<div class="new-status-notification text-center panel-footer">{{ errorData.error }}</div>
|
||||||
|
</a>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="new-status-notification text-center panel-footer"
|
class="new-status-notification text-center panel-footer"
|
||||||
|
@ -93,17 +106,4 @@
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.new-status-notification {
|
|
||||||
position:relative;
|
|
||||||
margin-top: -1px;
|
|
||||||
font-size: 1.1em;
|
|
||||||
border-width: 1px 0 0 0;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: var(--border, $fallback--border);
|
|
||||||
padding: 10px;
|
|
||||||
z-index: 1;
|
|
||||||
background-color: $fallback--fg;
|
|
||||||
background-color: var(--panel, $fallback--fg);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -112,9 +112,9 @@ const UserProfile = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
stopFetching () {
|
stopFetching () {
|
||||||
this.$store.dispatch('stopFetching', 'user')
|
this.$store.dispatch('stopFetchingTimeline', 'user')
|
||||||
this.$store.dispatch('stopFetching', 'favorites')
|
this.$store.dispatch('stopFetchingTimeline', 'favorites')
|
||||||
this.$store.dispatch('stopFetching', 'media')
|
this.$store.dispatch('stopFetchingTimeline', 'media')
|
||||||
},
|
},
|
||||||
switchUser (userNameOrId) {
|
switchUser (userNameOrId) {
|
||||||
this.stopFetching()
|
this.stopFetching()
|
||||||
|
|
|
@ -64,7 +64,7 @@ const UserReportingModal = {
|
||||||
forward: this.forward,
|
forward: this.forward,
|
||||||
statusIds: this.statusIdsToReport
|
statusIds: this.statusIdsToReport
|
||||||
}
|
}
|
||||||
this.$store.state.api.backendInteractor.reportUser(params)
|
this.$store.state.api.backendInteractor.reportUser({ ...params })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.processing = false
|
this.processing = false
|
||||||
this.resetState()
|
this.resetState()
|
||||||
|
|
|
@ -139,7 +139,7 @@ const Mfa = {
|
||||||
|
|
||||||
// fetch settings from server
|
// fetch settings from server
|
||||||
async fetchSettings () {
|
async fetchSettings () {
|
||||||
let result = await this.backendInteractor.fetchSettingsMFA()
|
let result = await this.backendInteractor.settingsMFA()
|
||||||
if (result.error) return
|
if (result.error) return
|
||||||
this.settings = result.settings
|
this.settings = result.settings
|
||||||
this.settings.available = true
|
this.settings.available = true
|
||||||
|
|
|
@ -242,7 +242,7 @@ const UserSettings = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
importFollows (file) {
|
importFollows (file) {
|
||||||
return this.$store.state.api.backendInteractor.importFollows(file)
|
return this.$store.state.api.backendInteractor.importFollows({ file })
|
||||||
.then((status) => {
|
.then((status) => {
|
||||||
if (!status) {
|
if (!status) {
|
||||||
throw new Error('failed')
|
throw new Error('failed')
|
||||||
|
@ -250,7 +250,7 @@ const UserSettings = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
importBlocks (file) {
|
importBlocks (file) {
|
||||||
return this.$store.state.api.backendInteractor.importBlocks(file)
|
return this.$store.state.api.backendInteractor.importBlocks({ file })
|
||||||
.then((status) => {
|
.then((status) => {
|
||||||
if (!status) {
|
if (!status) {
|
||||||
throw new Error('failed')
|
throw new Error('failed')
|
||||||
|
@ -297,7 +297,7 @@ const UserSettings = {
|
||||||
newPassword: this.changePasswordInputs[1],
|
newPassword: this.changePasswordInputs[1],
|
||||||
newPasswordConfirmation: this.changePasswordInputs[2]
|
newPasswordConfirmation: this.changePasswordInputs[2]
|
||||||
}
|
}
|
||||||
this.$store.state.api.backendInteractor.changePassword(params)
|
this.$store.state.api.backendInteractor.changePassword({ params })
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === 'success') {
|
if (res.status === 'success') {
|
||||||
this.changedPassword = true
|
this.changedPassword = true
|
||||||
|
@ -314,7 +314,7 @@ const UserSettings = {
|
||||||
email: this.newEmail,
|
email: this.newEmail,
|
||||||
password: this.changeEmailPassword
|
password: this.changeEmailPassword
|
||||||
}
|
}
|
||||||
this.$store.state.api.backendInteractor.changeEmail(params)
|
this.$store.state.api.backendInteractor.changeEmail({ params })
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === 'success') {
|
if (res.status === 'success') {
|
||||||
this.changedEmail = true
|
this.changedEmail = true
|
||||||
|
|
|
@ -65,7 +65,7 @@ const withLoadMore = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render (createElement) {
|
render (h) {
|
||||||
const props = {
|
const props = {
|
||||||
props: {
|
props: {
|
||||||
...this.$props,
|
...this.$props,
|
||||||
|
@ -74,7 +74,7 @@ const withLoadMore = ({
|
||||||
on: this.$listeners,
|
on: this.$listeners,
|
||||||
scopedSlots: this.$scopedSlots
|
scopedSlots: this.$scopedSlots
|
||||||
}
|
}
|
||||||
const children = Object.entries(this.$slots).map(([key, value]) => createElement('template', { slot: key }, value))
|
const children = Object.entries(this.$slots).map(([key, value]) => h('template', { slot: key }, value))
|
||||||
return (
|
return (
|
||||||
<div class="with-load-more">
|
<div class="with-load-more">
|
||||||
<WrappedComponent {...props}>
|
<WrappedComponent {...props}>
|
||||||
|
|
|
@ -49,7 +49,7 @@ const withSubscription = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render (createElement) {
|
render (h) {
|
||||||
if (!this.error && !this.loading) {
|
if (!this.error && !this.loading) {
|
||||||
const props = {
|
const props = {
|
||||||
props: {
|
props: {
|
||||||
|
@ -59,7 +59,7 @@ const withSubscription = ({
|
||||||
on: this.$listeners,
|
on: this.$listeners,
|
||||||
scopedSlots: this.$scopedSlots
|
scopedSlots: this.$scopedSlots
|
||||||
}
|
}
|
||||||
const children = Object.entries(this.$slots).map(([key, value]) => createElement('template', { slot: key }, value))
|
const children = Object.entries(this.$slots).map(([key, value]) => h('template', { slot: key }, value))
|
||||||
return (
|
return (
|
||||||
<div class="with-subscription">
|
<div class="with-subscription">
|
||||||
<WrappedComponent {...props}>
|
<WrappedComponent {...props}>
|
||||||
|
|
|
@ -361,6 +361,8 @@
|
||||||
"post_status_content_type": "Post status content type",
|
"post_status_content_type": "Post status content type",
|
||||||
"stop_gifs": "Play-on-hover GIFs",
|
"stop_gifs": "Play-on-hover GIFs",
|
||||||
"streaming": "Enable automatic streaming of new posts when scrolled to the top",
|
"streaming": "Enable automatic streaming of new posts when scrolled to the top",
|
||||||
|
"useStreamingApi": "Receive posts and notifications real-time",
|
||||||
|
"useStreamingApiWarning": "(Not recommended, experimental, known to skip posts)",
|
||||||
"text": "Text",
|
"text": "Text",
|
||||||
"theme": "Theme",
|
"theme": "Theme",
|
||||||
"theme_help": "Use hex color codes (#rrggbb) to customize your color theme.",
|
"theme_help": "Use hex color codes (#rrggbb) to customize your color theme.",
|
||||||
|
|
|
@ -1,4 +1,23 @@
|
||||||
{
|
{
|
||||||
|
"about": {
|
||||||
|
"staff": "スタッフ",
|
||||||
|
"federation": "フェデレーション",
|
||||||
|
"mrf_policies": "ゆうこうなMRFポリシー",
|
||||||
|
"mrf_policies_desc": "MRFポリシーは、このインスタンスのフェデレーションのふるまいを、いじります。これらのMRFポリシーがゆうこうになっています:",
|
||||||
|
"mrf_policy_simple": "インスタンスのポリシー",
|
||||||
|
"mrf_policy_simple_accept": "うけいれ",
|
||||||
|
"mrf_policy_simple_accept_desc": "このインスンスは、これらのインスタンスからのメッセージのみをうけいれます:",
|
||||||
|
"mrf_policy_simple_reject": "おことわり",
|
||||||
|
"mrf_policy_simple_reject_desc": "このインスタンスは、これらのインスタンスからのメッセージをうけいれません:",
|
||||||
|
"mrf_policy_simple_quarantine": "けんえき",
|
||||||
|
"mrf_policy_simple_quarantine_desc": "このインスタンスは、これらのインスタンスに、パブリックなとうこうのみを、おくります:",
|
||||||
|
"mrf_policy_simple_ftl_removal": "「つながっているすべてのネットワーク」タイムラインからのぞく",
|
||||||
|
"mrf_policy_simple_ftl_removal_desc": "このインスタンスは、つながっているすべてのネットワーク」タイムラインから、これらのインスタンスを、とりのぞきます:",
|
||||||
|
"mrf_policy_simple_media_removal": "メディアをのぞく",
|
||||||
|
"mrf_policy_simple_media_removal_desc": "このインスタンスは、これらのインスタンスからおくられてきたメディアを、とりのぞきます:",
|
||||||
|
"mrf_policy_simple_media_nsfw": "メディアをすべてセンシティブにする",
|
||||||
|
"mrf_policy_simple_media_nsfw_desc": "このインスタンスは、これらのインスタンスからおくられてきたメディアを、すべて、センシティブにマークします:"
|
||||||
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"title": "チャット"
|
"title": "チャット"
|
||||||
},
|
},
|
||||||
|
@ -68,6 +87,7 @@
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
"about": "これはなに?",
|
"about": "これはなに?",
|
||||||
|
"administration": "アドミニストレーション",
|
||||||
"back": "もどる",
|
"back": "もどる",
|
||||||
"chat": "ローカルチャット",
|
"chat": "ローカルチャット",
|
||||||
"friend_requests": "フォローリクエスト",
|
"friend_requests": "フォローリクエスト",
|
||||||
|
@ -113,7 +133,9 @@
|
||||||
"search_emoji": "えもじをさがす",
|
"search_emoji": "えもじをさがす",
|
||||||
"add_emoji": "えもじをうちこむ",
|
"add_emoji": "えもじをうちこむ",
|
||||||
"custom": "カスタムえもじ",
|
"custom": "カスタムえもじ",
|
||||||
"unicode": "ユニコードえもじ"
|
"unicode": "ユニコードえもじ",
|
||||||
|
"load_all_hint": "はじめの {saneAmount} このえもじだけがロードされています。すべてのえもじをロードすると、パフォーマンスがわるくなるかもしれません。",
|
||||||
|
"load_all": "すべてのえもじをロード ({emojiAmount} こあります)"
|
||||||
},
|
},
|
||||||
"stickers": {
|
"stickers": {
|
||||||
"add_sticker": "ステッカーをふやす"
|
"add_sticker": "ステッカーをふやす"
|
||||||
|
@ -173,6 +195,11 @@
|
||||||
"password_confirmation_match": "パスワードがちがいます"
|
"password_confirmation_match": "パスワードがちがいます"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"remote_user_resolver": {
|
||||||
|
"remote_user_resolver": "リモートユーザーリゾルバー",
|
||||||
|
"searching_for": "さがしています:",
|
||||||
|
"error": "みつかりませんでした。"
|
||||||
|
},
|
||||||
"selectable_list": {
|
"selectable_list": {
|
||||||
"select_all": "すべてえらぶ"
|
"select_all": "すべてえらぶ"
|
||||||
},
|
},
|
||||||
|
@ -220,6 +247,9 @@
|
||||||
"cGreen": "リピート",
|
"cGreen": "リピート",
|
||||||
"cOrange": "おきにいり",
|
"cOrange": "おきにいり",
|
||||||
"cRed": "キャンセル",
|
"cRed": "キャンセル",
|
||||||
|
"change_email": "メールアドレスをかえる",
|
||||||
|
"change_email_error": "メールアドレスをかえようとしましたが、なにかがおかしいです。",
|
||||||
|
"changed_email": "メールアドレスをかえることができました!",
|
||||||
"change_password": "パスワードをかえる",
|
"change_password": "パスワードをかえる",
|
||||||
"change_password_error": "パスワードをかえることが、できなかったかもしれません。",
|
"change_password_error": "パスワードをかえることが、できなかったかもしれません。",
|
||||||
"changed_password": "パスワードが、かわりました!",
|
"changed_password": "パスワードが、かわりました!",
|
||||||
|
@ -279,6 +309,7 @@
|
||||||
"use_contain_fit": "がぞうのサムネイルを、きりぬかない",
|
"use_contain_fit": "がぞうのサムネイルを、きりぬかない",
|
||||||
"name": "なまえ",
|
"name": "なまえ",
|
||||||
"name_bio": "なまえとプロフィール",
|
"name_bio": "なまえとプロフィール",
|
||||||
|
"new_email": "あたらしいメールアドレス",
|
||||||
"new_password": "あたらしいパスワード",
|
"new_password": "あたらしいパスワード",
|
||||||
"notification_visibility": "ひょうじするつうち",
|
"notification_visibility": "ひょうじするつうち",
|
||||||
"notification_visibility_follows": "フォロー",
|
"notification_visibility_follows": "フォロー",
|
||||||
|
@ -344,6 +375,8 @@
|
||||||
"false": "いいえ",
|
"false": "いいえ",
|
||||||
"true": "はい"
|
"true": "はい"
|
||||||
},
|
},
|
||||||
|
"fun": "おたのしみ",
|
||||||
|
"greentext": "ミームやじるし",
|
||||||
"notifications": "つうち",
|
"notifications": "つうち",
|
||||||
"notification_setting": "つうちをうけとる:",
|
"notification_setting": "つうちをうけとる:",
|
||||||
"notification_setting_follows": "あなたがフォローしているひとから",
|
"notification_setting_follows": "あなたがフォローしているひとから",
|
||||||
|
@ -391,6 +424,7 @@
|
||||||
"_tab_label": "くわしく",
|
"_tab_label": "くわしく",
|
||||||
"alert": "アラートのバックグラウンド",
|
"alert": "アラートのバックグラウンド",
|
||||||
"alert_error": "エラー",
|
"alert_error": "エラー",
|
||||||
|
"alert_warning": "けいこく",
|
||||||
"badge": "バッジのバックグラウンド",
|
"badge": "バッジのバックグラウンド",
|
||||||
"badge_notification": "つうち",
|
"badge_notification": "つうち",
|
||||||
"panel_header": "パネルヘッダー",
|
"panel_header": "パネルヘッダー",
|
||||||
|
@ -542,6 +576,7 @@
|
||||||
"followers": "フォロワー",
|
"followers": "フォロワー",
|
||||||
"following": "フォローしています!",
|
"following": "フォローしています!",
|
||||||
"follows_you": "フォローされました!",
|
"follows_you": "フォローされました!",
|
||||||
|
"hidden": "かくされています",
|
||||||
"its_you": "これはあなたです!",
|
"its_you": "これはあなたです!",
|
||||||
"media": "メディア",
|
"media": "メディア",
|
||||||
"mention": "メンション",
|
"mention": "メンション",
|
||||||
|
@ -559,6 +594,8 @@
|
||||||
"unmute": "ミュートをやめる",
|
"unmute": "ミュートをやめる",
|
||||||
"unmute_progress": "ミュートをとりけしています...",
|
"unmute_progress": "ミュートをとりけしています...",
|
||||||
"mute_progress": "ミュートしています...",
|
"mute_progress": "ミュートしています...",
|
||||||
|
"hide_repeats": "リピートをかくす",
|
||||||
|
"show_repeats": "リピートをみる",
|
||||||
"admin_menu": {
|
"admin_menu": {
|
||||||
"moderation": "モデレーション",
|
"moderation": "モデレーション",
|
||||||
"grant_admin": "アドミンにする",
|
"grant_admin": "アドミンにする",
|
||||||
|
@ -634,6 +671,8 @@
|
||||||
"return_home": "ホームページにもどる",
|
"return_home": "ホームページにもどる",
|
||||||
"not_found": "そのメールアドレスまたはユーザーめいを、みつけることができませんでした。",
|
"not_found": "そのメールアドレスまたはユーザーめいを、みつけることができませんでした。",
|
||||||
"too_many_requests": "パスワードリセットを、ためすことが、おおすぎます。しばらくしてから、ためしてください。",
|
"too_many_requests": "パスワードリセットを、ためすことが、おおすぎます。しばらくしてから、ためしてください。",
|
||||||
"password_reset_disabled": "このインスタンスでは、パスワードリセットは、できません。インスタンスのアドミニストレーターに、おといあわせください。"
|
"password_reset_disabled": "このインスタンスでは、パスワードリセットは、できません。インスタンスのアドミニストレーターに、おといあわせください。",
|
||||||
|
"password_reset_required": "ログインするには、パスワードをリセットしてください。",
|
||||||
|
"password_reset_required_but_mailer_is_disabled": "あなたはパスワードのリセットがひつようです。しかし、まずいことに、このインスタンスでは、パスワードのリセットができなくなっています。このインスタンスのアドミニストレーターに、おといあわせください。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,6 +219,8 @@
|
||||||
"subject_input_always_show": "Всегда показывать поле ввода темы",
|
"subject_input_always_show": "Всегда показывать поле ввода темы",
|
||||||
"stop_gifs": "Проигрывать GIF анимации только при наведении",
|
"stop_gifs": "Проигрывать GIF анимации только при наведении",
|
||||||
"streaming": "Включить автоматическую загрузку новых сообщений при прокрутке вверх",
|
"streaming": "Включить автоматическую загрузку новых сообщений при прокрутке вверх",
|
||||||
|
"useStreamingApi": "Получать сообщения и уведомления в реальном времени",
|
||||||
|
"useStreamingApiWarning": "(Не рекомендуется, экспериментально, сообщения могут пропадать)",
|
||||||
"text": "Текст",
|
"text": "Текст",
|
||||||
"theme": "Тема",
|
"theme": "Тема",
|
||||||
"theme_help": "Используйте шестнадцатеричные коды цветов (#rrggbb) для настройки темы.",
|
"theme_help": "Используйте шестнадцатеричные коды цветов (#rrggbb) для настройки темы.",
|
||||||
|
|
|
@ -6,6 +6,7 @@ const api = {
|
||||||
backendInteractor: backendInteractorService(),
|
backendInteractor: backendInteractorService(),
|
||||||
fetchers: {},
|
fetchers: {},
|
||||||
socket: null,
|
socket: null,
|
||||||
|
mastoUserSocket: null,
|
||||||
followRequests: []
|
followRequests: []
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
@ -15,7 +16,8 @@ const api = {
|
||||||
addFetcher (state, { fetcherName, fetcher }) {
|
addFetcher (state, { fetcherName, fetcher }) {
|
||||||
state.fetchers[fetcherName] = fetcher
|
state.fetchers[fetcherName] = fetcher
|
||||||
},
|
},
|
||||||
removeFetcher (state, { fetcherName }) {
|
removeFetcher (state, { fetcherName, fetcher }) {
|
||||||
|
window.clearInterval(fetcher)
|
||||||
delete state.fetchers[fetcherName]
|
delete state.fetchers[fetcherName]
|
||||||
},
|
},
|
||||||
setWsToken (state, token) {
|
setWsToken (state, token) {
|
||||||
|
@ -29,32 +31,134 @@ const api = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
startFetchingTimeline (store, { timeline = 'friends', tag = false, userId = false }) {
|
// Global MastoAPI socket control, in future should disable ALL sockets/(re)start relevant sockets
|
||||||
// Don't start fetching if we already are.
|
enableMastoSockets (store) {
|
||||||
|
const { state, dispatch } = store
|
||||||
|
if (state.mastoUserSocket) return
|
||||||
|
return dispatch('startMastoUserSocket')
|
||||||
|
},
|
||||||
|
disableMastoSockets (store) {
|
||||||
|
const { state, dispatch } = store
|
||||||
|
if (!state.mastoUserSocket) return
|
||||||
|
return dispatch('stopMastoUserSocket')
|
||||||
|
},
|
||||||
|
|
||||||
|
// MastoAPI 'User' sockets
|
||||||
|
startMastoUserSocket (store) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const { state, dispatch, rootState } = store
|
||||||
|
const timelineData = rootState.statuses.timelines.friends
|
||||||
|
state.mastoUserSocket = state.backendInteractor.startUserSocket({ store })
|
||||||
|
state.mastoUserSocket.addEventListener(
|
||||||
|
'message',
|
||||||
|
({ detail: message }) => {
|
||||||
|
if (!message) return // pings
|
||||||
|
if (message.event === 'notification') {
|
||||||
|
dispatch('addNewNotifications', {
|
||||||
|
notifications: [message.notification],
|
||||||
|
older: false
|
||||||
|
})
|
||||||
|
} else if (message.event === 'update') {
|
||||||
|
dispatch('addNewStatuses', {
|
||||||
|
statuses: [message.status],
|
||||||
|
userId: false,
|
||||||
|
showImmediately: timelineData.visibleStatuses.length === 0,
|
||||||
|
timeline: 'friends'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
state.mastoUserSocket.addEventListener('error', ({ detail: error }) => {
|
||||||
|
console.error('Error in MastoAPI websocket:', error)
|
||||||
|
})
|
||||||
|
state.mastoUserSocket.addEventListener('close', ({ detail: closeEvent }) => {
|
||||||
|
const ignoreCodes = new Set([
|
||||||
|
1000, // Normal (intended) closure
|
||||||
|
1001 // Going away
|
||||||
|
])
|
||||||
|
const { code } = closeEvent
|
||||||
|
if (ignoreCodes.has(code)) {
|
||||||
|
console.debug(`Not restarting socket becasue of closure code ${code} is in ignore list`)
|
||||||
|
} else {
|
||||||
|
console.warn(`MastoAPI websocket disconnected, restarting. CloseEvent code: ${code}`)
|
||||||
|
dispatch('startFetchingTimeline', { timeline: 'friends' })
|
||||||
|
dispatch('startFetchingNotifications')
|
||||||
|
dispatch('restartMastoUserSocket')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
resolve()
|
||||||
|
} catch (e) {
|
||||||
|
reject(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
restartMastoUserSocket ({ dispatch }) {
|
||||||
|
// This basically starts MastoAPI user socket and stops conventional
|
||||||
|
// fetchers when connection reestablished
|
||||||
|
return dispatch('startMastoUserSocket').then(() => {
|
||||||
|
dispatch('stopFetchingTimeline', { timeline: 'friends' })
|
||||||
|
dispatch('stopFetchingNotifications')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
stopMastoUserSocket ({ state, dispatch }) {
|
||||||
|
dispatch('startFetchingTimeline', { timeline: 'friends' })
|
||||||
|
dispatch('startFetchingNotifications')
|
||||||
|
console.log(state.mastoUserSocket)
|
||||||
|
state.mastoUserSocket.close()
|
||||||
|
},
|
||||||
|
|
||||||
|
// Timelines
|
||||||
|
startFetchingTimeline (store, {
|
||||||
|
timeline = 'friends',
|
||||||
|
tag = false,
|
||||||
|
userId = false
|
||||||
|
}) {
|
||||||
if (store.state.fetchers[timeline]) return
|
if (store.state.fetchers[timeline]) return
|
||||||
|
|
||||||
const fetcher = store.state.backendInteractor.startFetchingTimeline({ timeline, store, userId, tag })
|
const fetcher = store.state.backendInteractor.startFetchingTimeline({
|
||||||
|
timeline, store, userId, tag
|
||||||
|
})
|
||||||
store.commit('addFetcher', { fetcherName: timeline, fetcher })
|
store.commit('addFetcher', { fetcherName: timeline, fetcher })
|
||||||
},
|
},
|
||||||
startFetchingNotifications (store) {
|
stopFetchingTimeline (store, timeline) {
|
||||||
// Don't start fetching if we already are.
|
const fetcher = store.state.fetchers[timeline]
|
||||||
if (store.state.fetchers['notifications']) return
|
if (!fetcher) return
|
||||||
|
store.commit('removeFetcher', { fetcherName: timeline, fetcher })
|
||||||
|
},
|
||||||
|
|
||||||
|
// Notifications
|
||||||
|
startFetchingNotifications (store) {
|
||||||
|
if (store.state.fetchers.notifications) return
|
||||||
const fetcher = store.state.backendInteractor.startFetchingNotifications({ store })
|
const fetcher = store.state.backendInteractor.startFetchingNotifications({ store })
|
||||||
store.commit('addFetcher', { fetcherName: 'notifications', fetcher })
|
store.commit('addFetcher', { fetcherName: 'notifications', fetcher })
|
||||||
},
|
},
|
||||||
startFetchingFollowRequest (store) {
|
stopFetchingNotifications (store) {
|
||||||
// Don't start fetching if we already are.
|
const fetcher = store.state.fetchers.notifications
|
||||||
if (store.state.fetchers['followRequest']) return
|
if (!fetcher) return
|
||||||
|
store.commit('removeFetcher', { fetcherName: 'notifications', fetcher })
|
||||||
|
},
|
||||||
|
fetchAndUpdateNotifications (store) {
|
||||||
|
store.state.backendInteractor.fetchAndUpdateNotifications({ store })
|
||||||
|
},
|
||||||
|
|
||||||
const fetcher = store.state.backendInteractor.startFetchingFollowRequest({ store })
|
// Follow requests
|
||||||
store.commit('addFetcher', { fetcherName: 'followRequest', fetcher })
|
startFetchingFollowRequests (store) {
|
||||||
|
if (store.state.fetchers['followRequests']) return
|
||||||
|
const fetcher = store.state.backendInteractor.startFetchingFollowRequests({ store })
|
||||||
|
store.commit('addFetcher', { fetcherName: 'followRequests', fetcher })
|
||||||
},
|
},
|
||||||
stopFetching (store, fetcherName) {
|
stopFetchingFollowRequests (store) {
|
||||||
const fetcher = store.state.fetchers[fetcherName]
|
const fetcher = store.state.fetchers.followRequests
|
||||||
window.clearInterval(fetcher)
|
if (!fetcher) return
|
||||||
store.commit('removeFetcher', { fetcherName })
|
store.commit('removeFetcher', { fetcherName: 'followRequests', fetcher })
|
||||||
},
|
},
|
||||||
|
removeFollowRequest (store, request) {
|
||||||
|
let requests = store.state.followRequests.filter((it) => it !== request)
|
||||||
|
store.commit('setFollowRequests', requests)
|
||||||
|
},
|
||||||
|
|
||||||
|
// Pleroma websocket
|
||||||
setWsToken (store, token) {
|
setWsToken (store, token) {
|
||||||
store.commit('setWsToken', token)
|
store.commit('setWsToken', token)
|
||||||
},
|
},
|
||||||
|
@ -72,10 +176,6 @@ const api = {
|
||||||
disconnectFromSocket ({ commit, state }) {
|
disconnectFromSocket ({ commit, state }) {
|
||||||
state.socket && state.socket.disconnect()
|
state.socket && state.socket.disconnect()
|
||||||
commit('setSocket', null)
|
commit('setSocket', null)
|
||||||
},
|
|
||||||
removeFollowRequest (store, request) {
|
|
||||||
let requests = store.state.followRequests.filter((it) => it !== request)
|
|
||||||
store.commit('setFollowRequests', requests)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ const RECOVERY_STRATEGY = 'recovery'
|
||||||
|
|
||||||
// initial state
|
// initial state
|
||||||
const state = {
|
const state = {
|
||||||
app: null,
|
|
||||||
settings: {},
|
settings: {},
|
||||||
strategy: PASSWORD_STRATEGY,
|
strategy: PASSWORD_STRATEGY,
|
||||||
initStrategy: PASSWORD_STRATEGY // default strategy from config
|
initStrategy: PASSWORD_STRATEGY // default strategy from config
|
||||||
|
@ -16,14 +15,10 @@ const state = {
|
||||||
const resetState = (state) => {
|
const resetState = (state) => {
|
||||||
state.strategy = state.initStrategy
|
state.strategy = state.initStrategy
|
||||||
state.settings = {}
|
state.settings = {}
|
||||||
state.app = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
const getters = {
|
const getters = {
|
||||||
app: (state, getters) => {
|
|
||||||
return state.app
|
|
||||||
},
|
|
||||||
settings: (state, getters) => {
|
settings: (state, getters) => {
|
||||||
return state.settings
|
return state.settings
|
||||||
},
|
},
|
||||||
|
@ -55,9 +50,8 @@ const mutations = {
|
||||||
requireToken (state) {
|
requireToken (state) {
|
||||||
state.strategy = TOKEN_STRATEGY
|
state.strategy = TOKEN_STRATEGY
|
||||||
},
|
},
|
||||||
requireMFA (state, { app, settings }) {
|
requireMFA (state, { settings }) {
|
||||||
state.settings = settings
|
state.settings = settings
|
||||||
state.app = app
|
|
||||||
state.strategy = TOTP_STRATEGY // default strategy of MFA
|
state.strategy = TOTP_STRATEGY // default strategy of MFA
|
||||||
},
|
},
|
||||||
requireRecovery (state) {
|
requireRecovery (state) {
|
||||||
|
|
|
@ -36,6 +36,7 @@ export const defaultState = {
|
||||||
highlight: {},
|
highlight: {},
|
||||||
interfaceLanguage: browserLocale,
|
interfaceLanguage: browserLocale,
|
||||||
hideScopeNotice: false,
|
hideScopeNotice: false,
|
||||||
|
useStreamingApi: false,
|
||||||
scopeCopy: undefined, // instance default
|
scopeCopy: undefined, // instance default
|
||||||
subjectLineBehavior: undefined, // instance default
|
subjectLineBehavior: undefined, // instance default
|
||||||
alwaysShowSubjectInput: undefined, // instance default
|
alwaysShowSubjectInput: undefined, // instance default
|
||||||
|
|
|
@ -27,6 +27,7 @@ const defaultState = {
|
||||||
scopeCopy: true,
|
scopeCopy: true,
|
||||||
subjectLineBehavior: 'email',
|
subjectLineBehavior: 'email',
|
||||||
postContentType: 'text/plain',
|
postContentType: 'text/plain',
|
||||||
|
hideSitename: false,
|
||||||
nsfwCensorImage: undefined,
|
nsfwCensorImage: undefined,
|
||||||
vapidPublicKey: undefined,
|
vapidPublicKey: undefined,
|
||||||
noAttachmentLinks: false,
|
noAttachmentLinks: false,
|
||||||
|
|
|
@ -9,7 +9,7 @@ const oauthTokens = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
revokeToken ({ rootState, commit, state }, id) {
|
revokeToken ({ rootState, commit, state }, id) {
|
||||||
rootState.api.backendInteractor.revokeOAuthToken(id).then((response) => {
|
rootState.api.backendInteractor.revokeOAuthToken({ id }).then((response) => {
|
||||||
if (response.status === 201) {
|
if (response.status === 201) {
|
||||||
commit('swapTokens', state.tokens.filter(token => token.id !== id))
|
commit('swapTokens', state.tokens.filter(token => token.id !== id))
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ const polls = {
|
||||||
commit('mergeOrAddPoll', poll)
|
commit('mergeOrAddPoll', poll)
|
||||||
},
|
},
|
||||||
updateTrackedPoll ({ rootState, dispatch, commit }, pollId) {
|
updateTrackedPoll ({ rootState, dispatch, commit }, pollId) {
|
||||||
rootState.api.backendInteractor.fetchPoll(pollId).then(poll => {
|
rootState.api.backendInteractor.fetchPoll({ pollId }).then(poll => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (rootState.polls.trackedPolls[pollId]) {
|
if (rootState.polls.trackedPolls[pollId]) {
|
||||||
dispatch('updateTrackedPoll', pollId)
|
dispatch('updateTrackedPoll', pollId)
|
||||||
|
@ -59,7 +59,7 @@ const polls = {
|
||||||
commit('untrackPoll', pollId)
|
commit('untrackPoll', pollId)
|
||||||
},
|
},
|
||||||
votePoll ({ rootState, commit }, { id, pollId, choices }) {
|
votePoll ({ rootState, commit }, { id, pollId, choices }) {
|
||||||
return rootState.api.backendInteractor.vote(pollId, choices).then(poll => {
|
return rootState.api.backendInteractor.vote({ pollId, choices }).then(poll => {
|
||||||
commit('mergeOrAddPoll', poll)
|
commit('mergeOrAddPoll', poll)
|
||||||
return poll
|
return poll
|
||||||
})
|
})
|
||||||
|
|
|
@ -38,6 +38,7 @@ export const defaultState = () => ({
|
||||||
notifications: emptyNotifications(),
|
notifications: emptyNotifications(),
|
||||||
favorites: new Set(),
|
favorites: new Set(),
|
||||||
error: false,
|
error: false,
|
||||||
|
errorData: null,
|
||||||
timelines: {
|
timelines: {
|
||||||
mentions: emptyTl(),
|
mentions: emptyTl(),
|
||||||
public: emptyTl(),
|
public: emptyTl(),
|
||||||
|
@ -483,6 +484,9 @@ export const mutations = {
|
||||||
setError (state, { value }) {
|
setError (state, { value }) {
|
||||||
state.error = value
|
state.error = value
|
||||||
},
|
},
|
||||||
|
setErrorData (state, { value }) {
|
||||||
|
state.errorData = value
|
||||||
|
},
|
||||||
setNotificationsLoading (state, { value }) {
|
setNotificationsLoading (state, { value }) {
|
||||||
state.notifications.loading = value
|
state.notifications.loading = value
|
||||||
},
|
},
|
||||||
|
@ -532,6 +536,9 @@ const statuses = {
|
||||||
setError ({ rootState, commit }, { value }) {
|
setError ({ rootState, commit }, { value }) {
|
||||||
commit('setError', { value })
|
commit('setError', { value })
|
||||||
},
|
},
|
||||||
|
setErrorData ({ rootState, commit }, { value }) {
|
||||||
|
commit('setErrorData', { value })
|
||||||
|
},
|
||||||
setNotificationsLoading ({ rootState, commit }, { value }) {
|
setNotificationsLoading ({ rootState, commit }, { value }) {
|
||||||
commit('setNotificationsLoading', { value })
|
commit('setNotificationsLoading', { value })
|
||||||
},
|
},
|
||||||
|
@ -555,45 +562,45 @@ const statuses = {
|
||||||
favorite ({ rootState, commit }, status) {
|
favorite ({ rootState, commit }, status) {
|
||||||
// Optimistic favoriting...
|
// Optimistic favoriting...
|
||||||
commit('setFavorited', { status, value: true })
|
commit('setFavorited', { status, value: true })
|
||||||
rootState.api.backendInteractor.favorite(status.id)
|
rootState.api.backendInteractor.favorite({ id: status.id })
|
||||||
.then(status => commit('setFavoritedConfirm', { status, user: rootState.users.currentUser }))
|
.then(status => commit('setFavoritedConfirm', { status, user: rootState.users.currentUser }))
|
||||||
},
|
},
|
||||||
unfavorite ({ rootState, commit }, status) {
|
unfavorite ({ rootState, commit }, status) {
|
||||||
// Optimistic unfavoriting...
|
// Optimistic unfavoriting...
|
||||||
commit('setFavorited', { status, value: false })
|
commit('setFavorited', { status, value: false })
|
||||||
rootState.api.backendInteractor.unfavorite(status.id)
|
rootState.api.backendInteractor.unfavorite({ id: status.id })
|
||||||
.then(status => commit('setFavoritedConfirm', { status, user: rootState.users.currentUser }))
|
.then(status => commit('setFavoritedConfirm', { status, user: rootState.users.currentUser }))
|
||||||
},
|
},
|
||||||
fetchPinnedStatuses ({ rootState, dispatch }, userId) {
|
fetchPinnedStatuses ({ rootState, dispatch }, userId) {
|
||||||
rootState.api.backendInteractor.fetchPinnedStatuses(userId)
|
rootState.api.backendInteractor.fetchPinnedStatuses({ id: userId })
|
||||||
.then(statuses => dispatch('addNewStatuses', { statuses, timeline: 'user', userId, showImmediately: true, noIdUpdate: true }))
|
.then(statuses => dispatch('addNewStatuses', { statuses, timeline: 'user', userId, showImmediately: true, noIdUpdate: true }))
|
||||||
},
|
},
|
||||||
pinStatus ({ rootState, dispatch }, statusId) {
|
pinStatus ({ rootState, dispatch }, statusId) {
|
||||||
return rootState.api.backendInteractor.pinOwnStatus(statusId)
|
return rootState.api.backendInteractor.pinOwnStatus({ id: statusId })
|
||||||
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
|
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
|
||||||
},
|
},
|
||||||
unpinStatus ({ rootState, dispatch }, statusId) {
|
unpinStatus ({ rootState, dispatch }, statusId) {
|
||||||
rootState.api.backendInteractor.unpinOwnStatus(statusId)
|
rootState.api.backendInteractor.unpinOwnStatus({ id: statusId })
|
||||||
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
|
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
|
||||||
},
|
},
|
||||||
muteConversation ({ rootState, commit }, statusId) {
|
muteConversation ({ rootState, commit }, statusId) {
|
||||||
return rootState.api.backendInteractor.muteConversation(statusId)
|
return rootState.api.backendInteractor.muteConversation({ id: statusId })
|
||||||
.then((status) => commit('setMutedStatus', status))
|
.then((status) => commit('setMutedStatus', status))
|
||||||
},
|
},
|
||||||
unmuteConversation ({ rootState, commit }, statusId) {
|
unmuteConversation ({ rootState, commit }, statusId) {
|
||||||
return rootState.api.backendInteractor.unmuteConversation(statusId)
|
return rootState.api.backendInteractor.unmuteConversation({ id: statusId })
|
||||||
.then((status) => commit('setMutedStatus', status))
|
.then((status) => commit('setMutedStatus', status))
|
||||||
},
|
},
|
||||||
retweet ({ rootState, commit }, status) {
|
retweet ({ rootState, commit }, status) {
|
||||||
// Optimistic retweeting...
|
// Optimistic retweeting...
|
||||||
commit('setRetweeted', { status, value: true })
|
commit('setRetweeted', { status, value: true })
|
||||||
rootState.api.backendInteractor.retweet(status.id)
|
rootState.api.backendInteractor.retweet({ id: status.id })
|
||||||
.then(status => commit('setRetweetedConfirm', { status: status.retweeted_status, user: rootState.users.currentUser }))
|
.then(status => commit('setRetweetedConfirm', { status: status.retweeted_status, user: rootState.users.currentUser }))
|
||||||
},
|
},
|
||||||
unretweet ({ rootState, commit }, status) {
|
unretweet ({ rootState, commit }, status) {
|
||||||
// Optimistic unretweeting...
|
// Optimistic unretweeting...
|
||||||
commit('setRetweeted', { status, value: false })
|
commit('setRetweeted', { status, value: false })
|
||||||
rootState.api.backendInteractor.unretweet(status.id)
|
rootState.api.backendInteractor.unretweet({ id: status.id })
|
||||||
.then(status => commit('setRetweetedConfirm', { status, user: rootState.users.currentUser }))
|
.then(status => commit('setRetweetedConfirm', { status, user: rootState.users.currentUser }))
|
||||||
},
|
},
|
||||||
queueFlush ({ rootState, commit }, { timeline, id }) {
|
queueFlush ({ rootState, commit }, { timeline, id }) {
|
||||||
|
@ -608,19 +615,19 @@ const statuses = {
|
||||||
},
|
},
|
||||||
fetchFavsAndRepeats ({ rootState, commit }, id) {
|
fetchFavsAndRepeats ({ rootState, commit }, id) {
|
||||||
Promise.all([
|
Promise.all([
|
||||||
rootState.api.backendInteractor.fetchFavoritedByUsers(id),
|
rootState.api.backendInteractor.fetchFavoritedByUsers({ id }),
|
||||||
rootState.api.backendInteractor.fetchRebloggedByUsers(id)
|
rootState.api.backendInteractor.fetchRebloggedByUsers({ id })
|
||||||
]).then(([favoritedByUsers, rebloggedByUsers]) => {
|
]).then(([favoritedByUsers, rebloggedByUsers]) => {
|
||||||
commit('addFavs', { id, favoritedByUsers, currentUser: rootState.users.currentUser })
|
commit('addFavs', { id, favoritedByUsers, currentUser: rootState.users.currentUser })
|
||||||
commit('addRepeats', { id, rebloggedByUsers, currentUser: rootState.users.currentUser })
|
commit('addRepeats', { id, rebloggedByUsers, currentUser: rootState.users.currentUser })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fetchFavs ({ rootState, commit }, id) {
|
fetchFavs ({ rootState, commit }, id) {
|
||||||
rootState.api.backendInteractor.fetchFavoritedByUsers(id)
|
rootState.api.backendInteractor.fetchFavoritedByUsers({ id })
|
||||||
.then(favoritedByUsers => commit('addFavs', { id, favoritedByUsers, currentUser: rootState.users.currentUser }))
|
.then(favoritedByUsers => commit('addFavs', { id, favoritedByUsers, currentUser: rootState.users.currentUser }))
|
||||||
},
|
},
|
||||||
fetchRepeats ({ rootState, commit }, id) {
|
fetchRepeats ({ rootState, commit }, id) {
|
||||||
rootState.api.backendInteractor.fetchRebloggedByUsers(id)
|
rootState.api.backendInteractor.fetchRebloggedByUsers({ id })
|
||||||
.then(rebloggedByUsers => commit('addRepeats', { id, rebloggedByUsers, currentUser: rootState.users.currentUser }))
|
.then(rebloggedByUsers => commit('addRepeats', { id, rebloggedByUsers, currentUser: rootState.users.currentUser }))
|
||||||
},
|
},
|
||||||
search (store, { q, resolve, limit, offset, following }) {
|
search (store, { q, resolve, limit, offset, following }) {
|
||||||
|
|
|
@ -32,7 +32,7 @@ const getNotificationPermission = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const blockUser = (store, id) => {
|
const blockUser = (store, id) => {
|
||||||
return store.rootState.api.backendInteractor.blockUser(id)
|
return store.rootState.api.backendInteractor.blockUser({ id })
|
||||||
.then((relationship) => {
|
.then((relationship) => {
|
||||||
store.commit('updateUserRelationship', [relationship])
|
store.commit('updateUserRelationship', [relationship])
|
||||||
store.commit('addBlockId', id)
|
store.commit('addBlockId', id)
|
||||||
|
@ -43,12 +43,12 @@ const blockUser = (store, id) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const unblockUser = (store, id) => {
|
const unblockUser = (store, id) => {
|
||||||
return store.rootState.api.backendInteractor.unblockUser(id)
|
return store.rootState.api.backendInteractor.unblockUser({ id })
|
||||||
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
||||||
}
|
}
|
||||||
|
|
||||||
const muteUser = (store, id) => {
|
const muteUser = (store, id) => {
|
||||||
return store.rootState.api.backendInteractor.muteUser(id)
|
return store.rootState.api.backendInteractor.muteUser({ id })
|
||||||
.then((relationship) => {
|
.then((relationship) => {
|
||||||
store.commit('updateUserRelationship', [relationship])
|
store.commit('updateUserRelationship', [relationship])
|
||||||
store.commit('addMuteId', id)
|
store.commit('addMuteId', id)
|
||||||
|
@ -56,7 +56,7 @@ const muteUser = (store, id) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const unmuteUser = (store, id) => {
|
const unmuteUser = (store, id) => {
|
||||||
return store.rootState.api.backendInteractor.unmuteUser(id)
|
return store.rootState.api.backendInteractor.unmuteUser({ id })
|
||||||
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,9 +95,9 @@ export const mutations = {
|
||||||
newRights[right] = value
|
newRights[right] = value
|
||||||
set(user, 'rights', newRights)
|
set(user, 'rights', newRights)
|
||||||
},
|
},
|
||||||
updateActivationStatus (state, { user: { id }, status }) {
|
updateActivationStatus (state, { user: { id }, deactivated }) {
|
||||||
const user = state.usersObject[id]
|
const user = state.usersObject[id]
|
||||||
set(user, 'deactivated', !status)
|
set(user, 'deactivated', deactivated)
|
||||||
},
|
},
|
||||||
setCurrentUser (state, user) {
|
setCurrentUser (state, user) {
|
||||||
state.lastLoginName = user.screen_name
|
state.lastLoginName = user.screen_name
|
||||||
|
@ -324,13 +324,18 @@ const users = {
|
||||||
commit('clearFollowers', userId)
|
commit('clearFollowers', userId)
|
||||||
},
|
},
|
||||||
subscribeUser ({ rootState, commit }, id) {
|
subscribeUser ({ rootState, commit }, id) {
|
||||||
return rootState.api.backendInteractor.subscribeUser(id)
|
return rootState.api.backendInteractor.subscribeUser({ id })
|
||||||
.then((relationship) => commit('updateUserRelationship', [relationship]))
|
.then((relationship) => commit('updateUserRelationship', [relationship]))
|
||||||
},
|
},
|
||||||
unsubscribeUser ({ rootState, commit }, id) {
|
unsubscribeUser ({ rootState, commit }, id) {
|
||||||
return rootState.api.backendInteractor.unsubscribeUser(id)
|
return rootState.api.backendInteractor.unsubscribeUser({ id })
|
||||||
.then((relationship) => commit('updateUserRelationship', [relationship]))
|
.then((relationship) => commit('updateUserRelationship', [relationship]))
|
||||||
},
|
},
|
||||||
|
toggleActivationStatus ({ rootState, commit }, user) {
|
||||||
|
const api = user.deactivated ? rootState.api.backendInteractor.activateUser : rootState.api.backendInteractor.deactivateUser
|
||||||
|
api(user)
|
||||||
|
.then(({ deactivated }) => commit('updateActivationStatus', { user, deactivated }))
|
||||||
|
},
|
||||||
registerPushNotifications (store) {
|
registerPushNotifications (store) {
|
||||||
const token = store.state.currentUser.credentials
|
const token = store.state.currentUser.credentials
|
||||||
const vapidPublicKey = store.rootState.instance.vapidPublicKey
|
const vapidPublicKey = store.rootState.instance.vapidPublicKey
|
||||||
|
@ -384,7 +389,7 @@ const users = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
searchUsers (store, query) {
|
searchUsers (store, query) {
|
||||||
return store.rootState.api.backendInteractor.searchUsers(query)
|
return store.rootState.api.backendInteractor.searchUsers({ query })
|
||||||
.then((users) => {
|
.then((users) => {
|
||||||
store.commit('addNewUsers', users)
|
store.commit('addNewUsers', users)
|
||||||
return users
|
return users
|
||||||
|
@ -396,7 +401,7 @@ const users = {
|
||||||
let rootState = store.rootState
|
let rootState = store.rootState
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let data = await rootState.api.backendInteractor.register(userInfo)
|
let data = await rootState.api.backendInteractor.register({ ...userInfo })
|
||||||
store.commit('signUpSuccess')
|
store.commit('signUpSuccess')
|
||||||
store.commit('setToken', data.access_token)
|
store.commit('setToken', data.access_token)
|
||||||
store.dispatch('loginUser', data.access_token)
|
store.dispatch('loginUser', data.access_token)
|
||||||
|
@ -433,10 +438,10 @@ const users = {
|
||||||
store.commit('clearCurrentUser')
|
store.commit('clearCurrentUser')
|
||||||
store.dispatch('disconnectFromSocket')
|
store.dispatch('disconnectFromSocket')
|
||||||
store.commit('clearToken')
|
store.commit('clearToken')
|
||||||
store.dispatch('stopFetching', 'friends')
|
store.dispatch('stopFetchingTimeline', 'friends')
|
||||||
store.commit('setBackendInteractor', backendInteractorService(store.getters.getToken()))
|
store.commit('setBackendInteractor', backendInteractorService(store.getters.getToken()))
|
||||||
store.dispatch('stopFetching', 'notifications')
|
store.dispatch('stopFetchingNotifications')
|
||||||
store.dispatch('stopFetching', 'followRequest')
|
store.dispatch('stopFetchingFollowRequests')
|
||||||
store.commit('clearNotifications')
|
store.commit('clearNotifications')
|
||||||
store.commit('resetStatuses')
|
store.commit('resetStatuses')
|
||||||
})
|
})
|
||||||
|
@ -471,11 +476,24 @@ const users = {
|
||||||
store.dispatch('initializeSocket')
|
store.dispatch('initializeSocket')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start getting fresh posts.
|
const startPolling = () => {
|
||||||
store.dispatch('startFetchingTimeline', { timeline: 'friends' })
|
// Start getting fresh posts.
|
||||||
|
store.dispatch('startFetchingTimeline', { timeline: 'friends' })
|
||||||
|
|
||||||
// Start fetching notifications
|
// Start fetching notifications
|
||||||
store.dispatch('startFetchingNotifications')
|
store.dispatch('startFetchingNotifications')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (store.getters.mergedConfig.useStreamingApi) {
|
||||||
|
store.dispatch('enableMastoSockets').catch((error) => {
|
||||||
|
console.error('Failed initializing MastoAPI Streaming socket', error)
|
||||||
|
startPolling()
|
||||||
|
}).then(() => {
|
||||||
|
setTimeout(() => store.dispatch('setNotificationsSilence', false), 10000)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
startPolling()
|
||||||
|
}
|
||||||
|
|
||||||
// Get user mutes
|
// Get user mutes
|
||||||
store.dispatch('fetchMutes')
|
store.dispatch('fetchMutes')
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { each, map, concat, last } from 'lodash'
|
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 } from '../entity_normalizer/entity_normalizer.service.js'
|
||||||
import 'whatwg-fetch'
|
import 'whatwg-fetch'
|
||||||
import { RegistrationError, StatusCodeError } from '../errors/errors'
|
import { RegistrationError, StatusCodeError } from '../errors/errors'
|
||||||
|
@ -12,7 +12,8 @@ const CHANGE_EMAIL_URL = '/api/pleroma/change_email'
|
||||||
const CHANGE_PASSWORD_URL = '/api/pleroma/change_password'
|
const CHANGE_PASSWORD_URL = '/api/pleroma/change_password'
|
||||||
const TAG_USER_URL = '/api/pleroma/admin/users/tag'
|
const TAG_USER_URL = '/api/pleroma/admin/users/tag'
|
||||||
const PERMISSION_GROUP_URL = (screenName, right) => `/api/pleroma/admin/users/${screenName}/permission_group/${right}`
|
const PERMISSION_GROUP_URL = (screenName, right) => `/api/pleroma/admin/users/${screenName}/permission_group/${right}`
|
||||||
const ACTIVATION_STATUS_URL = screenName => `/api/pleroma/admin/users/${screenName}/activation_status`
|
const ACTIVATE_USER_URL = '/api/pleroma/admin/users/activate'
|
||||||
|
const DEACTIVATE_USER_URL = '/api/pleroma/admin/users/deactivate'
|
||||||
const ADMIN_USERS_URL = '/api/pleroma/admin/users'
|
const ADMIN_USERS_URL = '/api/pleroma/admin/users'
|
||||||
const SUGGESTIONS_URL = '/api/v1/suggestions'
|
const SUGGESTIONS_URL = '/api/v1/suggestions'
|
||||||
const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings'
|
const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings'
|
||||||
|
@ -22,7 +23,7 @@ const MFA_BACKUP_CODES_URL = '/api/pleroma/accounts/mfa/backup_codes'
|
||||||
|
|
||||||
const MFA_SETUP_OTP_URL = '/api/pleroma/accounts/mfa/setup/totp'
|
const MFA_SETUP_OTP_URL = '/api/pleroma/accounts/mfa/setup/totp'
|
||||||
const MFA_CONFIRM_OTP_URL = '/api/pleroma/accounts/mfa/confirm/totp'
|
const MFA_CONFIRM_OTP_URL = '/api/pleroma/accounts/mfa/confirm/totp'
|
||||||
const MFA_DISABLE_OTP_URL = '/api/pleroma/account/mfa/totp'
|
const MFA_DISABLE_OTP_URL = '/api/pleroma/accounts/mfa/totp'
|
||||||
|
|
||||||
const MASTODON_LOGIN_URL = '/api/v1/accounts/verify_credentials'
|
const MASTODON_LOGIN_URL = '/api/v1/accounts/verify_credentials'
|
||||||
const MASTODON_REGISTRATION_URL = '/api/v1/accounts'
|
const MASTODON_REGISTRATION_URL = '/api/v1/accounts'
|
||||||
|
@ -71,6 +72,7 @@ const MASTODON_MUTE_CONVERSATION = id => `/api/v1/statuses/${id}/mute`
|
||||||
const MASTODON_UNMUTE_CONVERSATION = id => `/api/v1/statuses/${id}/unmute`
|
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_USER_SEARCH_URL = '/api/v1/accounts/search'
|
||||||
|
const MASTODON_STREAMING = '/api/v1/streaming'
|
||||||
|
|
||||||
const oldfetch = window.fetch
|
const oldfetch = window.fetch
|
||||||
|
|
||||||
|
@ -450,20 +452,26 @@ const deleteRight = ({ right, credentials, ...user }) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const setActivationStatus = ({ status, credentials, ...user }) => {
|
const activateUser = ({ credentials, user: { screen_name: nickname } }) => {
|
||||||
const screenName = user.screen_name
|
return promisedRequest({
|
||||||
const body = {
|
url: ACTIVATE_USER_URL,
|
||||||
status: status
|
method: 'PATCH',
|
||||||
}
|
credentials,
|
||||||
|
payload: {
|
||||||
|
nicknames: [nickname]
|
||||||
|
}
|
||||||
|
}).then(response => get(response, 'users.0'))
|
||||||
|
}
|
||||||
|
|
||||||
const headers = authHeaders(credentials)
|
const deactivateUser = ({ credentials, user: { screen_name: nickname } }) => {
|
||||||
headers['Content-Type'] = 'application/json'
|
return promisedRequest({
|
||||||
|
url: DEACTIVATE_USER_URL,
|
||||||
return fetch(ACTIVATION_STATUS_URL(screenName), {
|
method: 'PATCH',
|
||||||
method: 'PUT',
|
credentials,
|
||||||
headers: headers,
|
payload: {
|
||||||
body: JSON.stringify(body)
|
nicknames: [nickname]
|
||||||
})
|
}
|
||||||
|
}).then(response => get(response, 'users.0'))
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteUser = ({ credentials, ...user }) => {
|
const deleteUser = ({ credentials, ...user }) => {
|
||||||
|
@ -529,16 +537,24 @@ const fetchTimeline = ({
|
||||||
|
|
||||||
const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&')
|
const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&')
|
||||||
url += `?${queryString}`
|
url += `?${queryString}`
|
||||||
|
let status = ''
|
||||||
|
let statusText = ''
|
||||||
return fetch(url, { headers: authHeaders(credentials) })
|
return fetch(url, { headers: authHeaders(credentials) })
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.ok) {
|
status = data.status
|
||||||
return data
|
statusText = data.statusText
|
||||||
}
|
return data
|
||||||
throw new Error('Error fetching timeline', data)
|
|
||||||
})
|
})
|
||||||
.then((data) => data.json())
|
.then((data) => data.json())
|
||||||
.then((data) => data.map(isNotifications ? parseNotification : parseStatus))
|
.then((data) => {
|
||||||
|
if (!data.error) {
|
||||||
|
return data.map(isNotifications ? parseNotification : parseStatus)
|
||||||
|
} else {
|
||||||
|
data.status = status
|
||||||
|
data.statusText = statusText
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchPinnedStatuses = ({ id, credentials }) => {
|
const fetchPinnedStatuses = ({ id, credentials }) => {
|
||||||
|
@ -932,6 +948,99 @@ const search2 = ({ credentials, q, resolve, limit, offset, following }) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getMastodonSocketURI = ({ credentials, stream, args = {} }) => {
|
||||||
|
return Object.entries({
|
||||||
|
...(credentials
|
||||||
|
? { access_token: credentials }
|
||||||
|
: {}
|
||||||
|
),
|
||||||
|
stream,
|
||||||
|
...args
|
||||||
|
}).reduce((acc, [key, val]) => {
|
||||||
|
return acc + `${key}=${val}&`
|
||||||
|
}, MASTODON_STREAMING + '?')
|
||||||
|
}
|
||||||
|
|
||||||
|
const MASTODON_STREAMING_EVENTS = new Set([
|
||||||
|
'update',
|
||||||
|
'notification',
|
||||||
|
'delete',
|
||||||
|
'filters_changed'
|
||||||
|
])
|
||||||
|
|
||||||
|
// A thin wrapper around WebSocket API that allows adding a pre-processor to it
|
||||||
|
// Uses EventTarget and a CustomEvent to proxy events
|
||||||
|
export const ProcessedWS = ({
|
||||||
|
url,
|
||||||
|
preprocessor = handleMastoWS,
|
||||||
|
id = 'Unknown'
|
||||||
|
}) => {
|
||||||
|
const eventTarget = new EventTarget()
|
||||||
|
const socket = new WebSocket(url)
|
||||||
|
if (!socket) throw new Error(`Failed to create socket ${id}`)
|
||||||
|
const proxy = (original, eventName, processor = a => a) => {
|
||||||
|
original.addEventListener(eventName, (eventData) => {
|
||||||
|
eventTarget.dispatchEvent(new CustomEvent(
|
||||||
|
eventName,
|
||||||
|
{ detail: processor(eventData) }
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
socket.addEventListener('open', (wsEvent) => {
|
||||||
|
console.debug(`[WS][${id}] Socket connected`, wsEvent)
|
||||||
|
})
|
||||||
|
socket.addEventListener('error', (wsEvent) => {
|
||||||
|
console.debug(`[WS][${id}] Socket errored`, wsEvent)
|
||||||
|
})
|
||||||
|
socket.addEventListener('close', (wsEvent) => {
|
||||||
|
console.debug(
|
||||||
|
`[WS][${id}] Socket disconnected with code ${wsEvent.code}`,
|
||||||
|
wsEvent
|
||||||
|
)
|
||||||
|
})
|
||||||
|
// Commented code reason: very spammy, uncomment to enable message debug logging
|
||||||
|
/*
|
||||||
|
socket.addEventListener('message', (wsEvent) => {
|
||||||
|
console.debug(
|
||||||
|
`[WS][${id}] Message received`,
|
||||||
|
wsEvent
|
||||||
|
)
|
||||||
|
})
|
||||||
|
/**/
|
||||||
|
|
||||||
|
proxy(socket, 'open')
|
||||||
|
proxy(socket, 'close')
|
||||||
|
proxy(socket, 'message', preprocessor)
|
||||||
|
proxy(socket, 'error')
|
||||||
|
|
||||||
|
// 1000 = Normal Closure
|
||||||
|
eventTarget.close = () => { socket.close(1000, 'Shutting down socket') }
|
||||||
|
|
||||||
|
return eventTarget
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleMastoWS = (wsEvent) => {
|
||||||
|
const { data } = wsEvent
|
||||||
|
if (!data) return
|
||||||
|
const parsedEvent = JSON.parse(data)
|
||||||
|
const { event, payload } = parsedEvent
|
||||||
|
if (MASTODON_STREAMING_EVENTS.has(event)) {
|
||||||
|
// MastoBE and PleromaBE both send payload for delete as a PLAIN string
|
||||||
|
if (event === 'delete') {
|
||||||
|
return { event, id: payload }
|
||||||
|
}
|
||||||
|
const data = payload ? JSON.parse(payload) : null
|
||||||
|
if (event === 'update') {
|
||||||
|
return { event, status: parseStatus(data) }
|
||||||
|
} else if (event === 'notification') {
|
||||||
|
return { event, notification: parseNotification(data) }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn('Unknown event', wsEvent)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const apiService = {
|
const apiService = {
|
||||||
verifyCredentials,
|
verifyCredentials,
|
||||||
fetchTimeline,
|
fetchTimeline,
|
||||||
|
@ -971,7 +1080,8 @@ const apiService = {
|
||||||
deleteUser,
|
deleteUser,
|
||||||
addRight,
|
addRight,
|
||||||
deleteRight,
|
deleteRight,
|
||||||
setActivationStatus,
|
activateUser,
|
||||||
|
deactivateUser,
|
||||||
register,
|
register,
|
||||||
getCaptcha,
|
getCaptcha,
|
||||||
updateAvatar,
|
updateAvatar,
|
||||||
|
|
|
@ -1,230 +1,39 @@
|
||||||
import apiService from '../api/api.service.js'
|
import apiService, { getMastodonSocketURI, ProcessedWS } from '../api/api.service.js'
|
||||||
import timelineFetcherService from '../timeline_fetcher/timeline_fetcher.service.js'
|
import timelineFetcherService from '../timeline_fetcher/timeline_fetcher.service.js'
|
||||||
import notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js'
|
import notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js'
|
||||||
import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service'
|
import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service'
|
||||||
|
|
||||||
const backendInteractorService = credentials => {
|
const backendInteractorService = credentials => ({
|
||||||
const fetchStatus = ({ id }) => {
|
startFetchingTimeline ({ timeline, store, userId = false, tag }) {
|
||||||
return apiService.fetchStatus({ id, credentials })
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchConversation = ({ id }) => {
|
|
||||||
return apiService.fetchConversation({ id, credentials })
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchFriends = ({ id, maxId, sinceId, limit }) => {
|
|
||||||
return apiService.fetchFriends({ id, maxId, sinceId, limit, credentials })
|
|
||||||
}
|
|
||||||
|
|
||||||
const exportFriends = ({ id }) => {
|
|
||||||
return apiService.exportFriends({ id, credentials })
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchFollowers = ({ id, maxId, sinceId, limit }) => {
|
|
||||||
return apiService.fetchFollowers({ id, maxId, sinceId, limit, credentials })
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchUser = ({ id }) => {
|
|
||||||
return apiService.fetchUser({ id, credentials })
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchUserRelationship = ({ id }) => {
|
|
||||||
return apiService.fetchUserRelationship({ id, credentials })
|
|
||||||
}
|
|
||||||
|
|
||||||
const followUser = ({ id, reblogs }) => {
|
|
||||||
return apiService.followUser({ credentials, id, reblogs })
|
|
||||||
}
|
|
||||||
|
|
||||||
const unfollowUser = (id) => {
|
|
||||||
return apiService.unfollowUser({ credentials, id })
|
|
||||||
}
|
|
||||||
|
|
||||||
const blockUser = (id) => {
|
|
||||||
return apiService.blockUser({ credentials, id })
|
|
||||||
}
|
|
||||||
|
|
||||||
const unblockUser = (id) => {
|
|
||||||
return apiService.unblockUser({ credentials, id })
|
|
||||||
}
|
|
||||||
|
|
||||||
const approveUser = (id) => {
|
|
||||||
return apiService.approveUser({ credentials, id })
|
|
||||||
}
|
|
||||||
|
|
||||||
const denyUser = (id) => {
|
|
||||||
return apiService.denyUser({ credentials, id })
|
|
||||||
}
|
|
||||||
|
|
||||||
const startFetchingTimeline = ({ timeline, store, userId = false, tag }) => {
|
|
||||||
return timelineFetcherService.startFetching({ timeline, store, credentials, userId, tag })
|
return timelineFetcherService.startFetching({ timeline, store, credentials, userId, tag })
|
||||||
}
|
},
|
||||||
|
|
||||||
const startFetchingNotifications = ({ store }) => {
|
startFetchingNotifications ({ store }) {
|
||||||
return notificationsFetcher.startFetching({ store, credentials })
|
return notificationsFetcher.startFetching({ store, credentials })
|
||||||
}
|
},
|
||||||
|
|
||||||
const startFetchingFollowRequest = ({ store }) => {
|
fetchAndUpdateNotifications ({ store }) {
|
||||||
|
return notificationsFetcher.fetchAndUpdate({ store, credentials })
|
||||||
|
},
|
||||||
|
|
||||||
|
startFetchingFollowRequest ({ store }) {
|
||||||
return followRequestFetcher.startFetching({ store, credentials })
|
return followRequestFetcher.startFetching({ store, credentials })
|
||||||
}
|
},
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
startUserSocket ({ store }) {
|
||||||
const tagUser = ({ screen_name }, tag) => {
|
const serv = store.rootState.instance.server.replace('http', 'ws')
|
||||||
return apiService.tagUser({ screen_name, tag, credentials })
|
const url = serv + getMastodonSocketURI({ credentials, stream: 'user' })
|
||||||
}
|
return ProcessedWS({ url, id: 'User' })
|
||||||
|
},
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
...Object.entries(apiService).reduce((acc, [key, func]) => {
|
||||||
const untagUser = ({ screen_name }, tag) => {
|
return {
|
||||||
return apiService.untagUser({ screen_name, tag, credentials })
|
...acc,
|
||||||
}
|
[key]: (args) => func({ credentials, ...args })
|
||||||
|
}
|
||||||
|
}, {}),
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
verifyCredentials: apiService.verifyCredentials
|
||||||
const addRight = ({ screen_name }, right) => {
|
})
|
||||||
return apiService.addRight({ screen_name, right, credentials })
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
|
||||||
const deleteRight = ({ screen_name }, right) => {
|
|
||||||
return apiService.deleteRight({ screen_name, right, credentials })
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
|
||||||
const setActivationStatus = ({ screen_name }, status) => {
|
|
||||||
return apiService.setActivationStatus({ screen_name, status, credentials })
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
|
||||||
const deleteUser = ({ screen_name }) => {
|
|
||||||
return apiService.deleteUser({ screen_name, credentials })
|
|
||||||
}
|
|
||||||
|
|
||||||
const vote = (pollId, choices) => {
|
|
||||||
return apiService.vote({ credentials, pollId, choices })
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchPoll = (pollId) => {
|
|
||||||
return apiService.fetchPoll({ credentials, pollId })
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateNotificationSettings = ({ settings }) => {
|
|
||||||
return apiService.updateNotificationSettings({ credentials, settings })
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchMutes = () => apiService.fetchMutes({ credentials })
|
|
||||||
const muteUser = (id) => apiService.muteUser({ credentials, id })
|
|
||||||
const unmuteUser = (id) => apiService.unmuteUser({ credentials, id })
|
|
||||||
const subscribeUser = (id) => apiService.subscribeUser({ credentials, id })
|
|
||||||
const unsubscribeUser = (id) => apiService.unsubscribeUser({ credentials, id })
|
|
||||||
const fetchBlocks = () => apiService.fetchBlocks({ credentials })
|
|
||||||
const fetchOAuthTokens = () => apiService.fetchOAuthTokens({ credentials })
|
|
||||||
const revokeOAuthToken = (id) => apiService.revokeOAuthToken({ id, credentials })
|
|
||||||
const fetchPinnedStatuses = (id) => apiService.fetchPinnedStatuses({ credentials, id })
|
|
||||||
const pinOwnStatus = (id) => apiService.pinOwnStatus({ credentials, id })
|
|
||||||
const unpinOwnStatus = (id) => apiService.unpinOwnStatus({ credentials, id })
|
|
||||||
const muteConversation = (id) => apiService.muteConversation({ credentials, id })
|
|
||||||
const unmuteConversation = (id) => apiService.unmuteConversation({ credentials, id })
|
|
||||||
|
|
||||||
const getCaptcha = () => apiService.getCaptcha()
|
|
||||||
const register = (params) => apiService.register({ credentials, params })
|
|
||||||
const updateAvatar = ({ avatar }) => apiService.updateAvatar({ credentials, avatar })
|
|
||||||
const updateBg = ({ background }) => apiService.updateBg({ credentials, background })
|
|
||||||
const updateBanner = ({ banner }) => apiService.updateBanner({ credentials, banner })
|
|
||||||
const updateProfile = ({ params }) => apiService.updateProfile({ credentials, params })
|
|
||||||
|
|
||||||
const importBlocks = (file) => apiService.importBlocks({ file, credentials })
|
|
||||||
const importFollows = (file) => apiService.importFollows({ file, credentials })
|
|
||||||
|
|
||||||
const deleteAccount = ({ password }) => apiService.deleteAccount({ credentials, password })
|
|
||||||
const changeEmail = ({ email, password }) => apiService.changeEmail({ credentials, email, password })
|
|
||||||
const changePassword = ({ password, newPassword, newPasswordConfirmation }) =>
|
|
||||||
apiService.changePassword({ credentials, password, newPassword, newPasswordConfirmation })
|
|
||||||
|
|
||||||
const fetchSettingsMFA = () => apiService.settingsMFA({ credentials })
|
|
||||||
const generateMfaBackupCodes = () => apiService.generateMfaBackupCodes({ credentials })
|
|
||||||
const mfaSetupOTP = () => apiService.mfaSetupOTP({ credentials })
|
|
||||||
const mfaConfirmOTP = ({ password, token }) => apiService.mfaConfirmOTP({ credentials, password, token })
|
|
||||||
const mfaDisableOTP = ({ password }) => apiService.mfaDisableOTP({ credentials, password })
|
|
||||||
|
|
||||||
const fetchFavoritedByUsers = (id) => apiService.fetchFavoritedByUsers({ id })
|
|
||||||
const fetchRebloggedByUsers = (id) => apiService.fetchRebloggedByUsers({ id })
|
|
||||||
const reportUser = (params) => apiService.reportUser({ credentials, ...params })
|
|
||||||
|
|
||||||
const favorite = (id) => apiService.favorite({ id, credentials })
|
|
||||||
const unfavorite = (id) => apiService.unfavorite({ id, credentials })
|
|
||||||
const retweet = (id) => apiService.retweet({ id, credentials })
|
|
||||||
const unretweet = (id) => apiService.unretweet({ id, credentials })
|
|
||||||
const search2 = ({ q, resolve, limit, offset, following }) =>
|
|
||||||
apiService.search2({ credentials, q, resolve, limit, offset, following })
|
|
||||||
const searchUsers = (query) => apiService.searchUsers({ query, credentials })
|
|
||||||
|
|
||||||
const backendInteractorServiceInstance = {
|
|
||||||
fetchStatus,
|
|
||||||
fetchConversation,
|
|
||||||
fetchFriends,
|
|
||||||
exportFriends,
|
|
||||||
fetchFollowers,
|
|
||||||
followUser,
|
|
||||||
unfollowUser,
|
|
||||||
blockUser,
|
|
||||||
unblockUser,
|
|
||||||
fetchUser,
|
|
||||||
fetchUserRelationship,
|
|
||||||
verifyCredentials: apiService.verifyCredentials,
|
|
||||||
startFetchingTimeline,
|
|
||||||
startFetchingNotifications,
|
|
||||||
startFetchingFollowRequest,
|
|
||||||
fetchMutes,
|
|
||||||
muteUser,
|
|
||||||
unmuteUser,
|
|
||||||
subscribeUser,
|
|
||||||
unsubscribeUser,
|
|
||||||
fetchBlocks,
|
|
||||||
fetchOAuthTokens,
|
|
||||||
revokeOAuthToken,
|
|
||||||
fetchPinnedStatuses,
|
|
||||||
pinOwnStatus,
|
|
||||||
unpinOwnStatus,
|
|
||||||
muteConversation,
|
|
||||||
unmuteConversation,
|
|
||||||
tagUser,
|
|
||||||
untagUser,
|
|
||||||
addRight,
|
|
||||||
deleteRight,
|
|
||||||
deleteUser,
|
|
||||||
setActivationStatus,
|
|
||||||
register,
|
|
||||||
getCaptcha,
|
|
||||||
updateAvatar,
|
|
||||||
updateBg,
|
|
||||||
updateBanner,
|
|
||||||
updateProfile,
|
|
||||||
importBlocks,
|
|
||||||
importFollows,
|
|
||||||
deleteAccount,
|
|
||||||
changeEmail,
|
|
||||||
changePassword,
|
|
||||||
fetchSettingsMFA,
|
|
||||||
generateMfaBackupCodes,
|
|
||||||
mfaSetupOTP,
|
|
||||||
mfaConfirmOTP,
|
|
||||||
mfaDisableOTP,
|
|
||||||
approveUser,
|
|
||||||
denyUser,
|
|
||||||
vote,
|
|
||||||
fetchPoll,
|
|
||||||
fetchFavoritedByUsers,
|
|
||||||
fetchRebloggedByUsers,
|
|
||||||
reportUser,
|
|
||||||
favorite,
|
|
||||||
unfavorite,
|
|
||||||
retweet,
|
|
||||||
unretweet,
|
|
||||||
updateNotificationSettings,
|
|
||||||
search2,
|
|
||||||
searchUsers
|
|
||||||
}
|
|
||||||
|
|
||||||
return backendInteractorServiceInstance
|
|
||||||
}
|
|
||||||
|
|
||||||
export default backendInteractorService
|
export default backendInteractorService
|
||||||
|
|
|
@ -39,7 +39,7 @@ export const requestFollow = (user, store) => new Promise((resolve, reject) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
export const requestUnfollow = (user, store) => new Promise((resolve, reject) => {
|
export const requestUnfollow = (user, store) => new Promise((resolve, reject) => {
|
||||||
store.state.api.backendInteractor.unfollowUser(user.id)
|
store.state.api.backendInteractor.unfollowUser({ id: user.id })
|
||||||
.then((updated) => {
|
.then((updated) => {
|
||||||
store.commit('updateUserRelationship', [updated])
|
store.commit('updateUserRelationship', [updated])
|
||||||
resolve({
|
resolve({
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
const verifyOTPCode = ({ app, instance, mfaToken, code }) => {
|
const verifyOTPCode = ({ clientId, clientSecret, instance, mfaToken, code }) => {
|
||||||
const url = `${instance}/oauth/mfa/challenge`
|
const url = `${instance}/oauth/mfa/challenge`
|
||||||
const form = new window.FormData()
|
const form = new window.FormData()
|
||||||
|
|
||||||
form.append('client_id', app.client_id)
|
form.append('client_id', clientId)
|
||||||
form.append('client_secret', app.client_secret)
|
form.append('client_secret', clientSecret)
|
||||||
form.append('mfa_token', mfaToken)
|
form.append('mfa_token', mfaToken)
|
||||||
form.append('code', code)
|
form.append('code', code)
|
||||||
form.append('challenge_type', 'totp')
|
form.append('challenge_type', 'totp')
|
||||||
|
@ -14,12 +14,12 @@ const verifyOTPCode = ({ app, instance, mfaToken, code }) => {
|
||||||
}).then((data) => data.json())
|
}).then((data) => data.json())
|
||||||
}
|
}
|
||||||
|
|
||||||
const verifyRecoveryCode = ({ app, instance, mfaToken, code }) => {
|
const verifyRecoveryCode = ({ clientId, clientSecret, instance, mfaToken, code }) => {
|
||||||
const url = `${instance}/oauth/mfa/challenge`
|
const url = `${instance}/oauth/mfa/challenge`
|
||||||
const form = new window.FormData()
|
const form = new window.FormData()
|
||||||
|
|
||||||
form.append('client_id', app.client_id)
|
form.append('client_id', clientId)
|
||||||
form.append('client_secret', app.client_secret)
|
form.append('client_secret', clientSecret)
|
||||||
form.append('mfa_token', mfaToken)
|
form.append('mfa_token', mfaToken)
|
||||||
form.append('code', code)
|
form.append('code', code)
|
||||||
form.append('challenge_type', 'recovery')
|
form.append('challenge_type', 'recovery')
|
||||||
|
|
|
@ -12,7 +12,7 @@ export const getOrCreateApp = ({ clientId, clientSecret, instance, commit }) =>
|
||||||
|
|
||||||
form.append('client_name', `PleromaFE_${window.___pleromafe_commit_hash}_${(new Date()).toISOString()}`)
|
form.append('client_name', `PleromaFE_${window.___pleromafe_commit_hash}_${(new Date()).toISOString()}`)
|
||||||
form.append('redirect_uris', REDIRECT_URI)
|
form.append('redirect_uris', REDIRECT_URI)
|
||||||
form.append('scopes', 'read write follow')
|
form.append('scopes', 'read write follow push admin')
|
||||||
|
|
||||||
return window.fetch(url, {
|
return window.fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -28,7 +28,7 @@ const login = ({ instance, clientId }) => {
|
||||||
response_type: 'code',
|
response_type: 'code',
|
||||||
client_id: clientId,
|
client_id: clientId,
|
||||||
redirect_uri: REDIRECT_URI,
|
redirect_uri: REDIRECT_URI,
|
||||||
scope: 'read write follow'
|
scope: 'read write follow push admin'
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataString = reduce(data, (acc, v, k) => {
|
const dataString = reduce(data, (acc, v, k) => {
|
||||||
|
|
|
@ -6,6 +6,7 @@ const update = ({ store, statuses, timeline, showImmediately, userId }) => {
|
||||||
const ccTimeline = camelCase(timeline)
|
const ccTimeline = camelCase(timeline)
|
||||||
|
|
||||||
store.dispatch('setError', { value: false })
|
store.dispatch('setError', { value: false })
|
||||||
|
store.dispatch('setErrorData', { value: null })
|
||||||
|
|
||||||
store.dispatch('addNewStatuses', {
|
store.dispatch('addNewStatuses', {
|
||||||
timeline: ccTimeline,
|
timeline: ccTimeline,
|
||||||
|
@ -45,6 +46,10 @@ const fetchAndUpdate = ({
|
||||||
|
|
||||||
return apiService.fetchTimeline(args)
|
return apiService.fetchTimeline(args)
|
||||||
.then((statuses) => {
|
.then((statuses) => {
|
||||||
|
if (statuses.error) {
|
||||||
|
store.dispatch('setErrorData', { value: statuses })
|
||||||
|
return
|
||||||
|
}
|
||||||
if (!older && statuses.length >= 20 && !timelineData.loading && numStatusesBeforeFetch > 0) {
|
if (!older && statuses.length >= 20 && !timelineData.loading && numStatusesBeforeFetch > 0) {
|
||||||
store.dispatch('queueFlush', { timeline: timeline, id: timelineData.maxId })
|
store.dispatch('queueFlush', { timeline: timeline, id: timelineData.maxId })
|
||||||
}
|
}
|
||||||
|
|
|
@ -304,10 +304,40 @@
|
||||||
"code": 61668,
|
"code": 61668,
|
||||||
"src": "fontawesome"
|
"src": "fontawesome"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"uid": "31972e4e9d080eaa796290349ae6c1fd",
|
||||||
|
"css": "users",
|
||||||
|
"code": 59421,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "e82cedfa1d5f15b00c5a81c9bd731ea2",
|
||||||
|
"css": "info-circled",
|
||||||
|
"code": 59423,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "w3nzesrlbezu6f30q7ytyq919p6gdlb6",
|
||||||
|
"css": "home-2",
|
||||||
|
"code": 59425,
|
||||||
|
"src": "typicons"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "dcedf50ab1ede3283d7a6c70e2fe32f3",
|
||||||
|
"css": "chat",
|
||||||
|
"code": 59422,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3a00327e61b997b58518bd43ed83c3df",
|
||||||
|
"css": "login",
|
||||||
|
"code": 59424,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"uid": "f3ebd6751c15a280af5cc5f4a764187d",
|
"uid": "f3ebd6751c15a280af5cc5f4a764187d",
|
||||||
"css": "arrow-curved",
|
"css": "arrow-curved",
|
||||||
"code": 59421,
|
"code": 59426,
|
||||||
"src": "iconic"
|
"src": "iconic"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
require('babel-register')
|
require('@babel/register')
|
||||||
var config = require('../../config')
|
var config = require('../../config')
|
||||||
|
|
||||||
// http://nightwatchjs.org/guide#settings-file
|
// http://nightwatchjs.org/guide#settings-file
|
||||||
|
|
Loading…
Reference in New Issue