commit: cd48268c858f4a7f24767e41e81cb6524393c5e6
parent: 6f32ccf41717c2b59404c04ab0b0cb4b8c8171e3
Author: Henry <spam@hjkos.com>
Date: Fri, 21 Sep 2018 09:19:02 +0000
Merge branch 'betterStorage' into 'develop'
Better storage
See merge request pleroma/pleroma-fe!343
Diffstat:
19 files changed, 318 insertions(+), 172 deletions(-)
diff --git a/config/index.js b/config/index.js
@@ -27,6 +27,11 @@ module.exports = {
changeOrigin: true,
cookieDomainRewrite: 'localhost'
},
+ '/nodeinfo': {
+ target: 'http://localhost:4000/',
+ changeOrigin: true,
+ cookieDomainRewrite: 'localhost'
+ },
'/socket': {
target: 'http://localhost:4000/',
changeOrigin: true,
diff --git a/src/App.js b/src/App.js
@@ -36,9 +36,9 @@ export default {
computed: {
currentUser () { return this.$store.state.users.currentUser },
background () {
- return this.currentUser.background_image || this.$store.state.config.background
+ return this.currentUser.background_image || this.$store.state.instance.background
},
- enableMask () { return this.supportsMask && this.$store.state.config.logoMask },
+ enableMask () { return this.supportsMask && this.$store.state.instance.logoMask },
logoStyle () {
return {
'visibility': this.enableMask ? 'hidden' : 'visible'
@@ -46,24 +46,24 @@ export default {
},
logoMaskStyle () {
return this.enableMask ? {
- 'mask-image': `url(${this.$store.state.config.logo})`
+ 'mask-image': `url(${this.$store.state.instance.logo})`
} : {
'background-color': this.enableMask ? '' : 'transparent'
}
},
logoBgStyle () {
return Object.assign({
- 'margin': `${this.$store.state.config.logoMargin} 0`
+ 'margin': `${this.$store.state.instance.logoMargin} 0`
}, this.enableMask ? {} : {
'background-color': this.enableMask ? '' : 'transparent'
})
},
- logo () { return this.$store.state.config.logo },
+ logo () { return this.$store.state.instance.logo },
style () { return { 'background-image': `url(${this.background})` } },
- sitename () { return this.$store.state.config.name },
+ sitename () { return this.$store.state.instance.name },
chat () { return this.$store.state.chat.channel.state === 'joined' },
- suggestionsEnabled () { return this.$store.state.config.suggestionsEnabled },
- showInstanceSpecificPanel () { return this.$store.state.config.showInstanceSpecificPanel }
+ suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
+ showInstanceSpecificPanel () { return this.$store.state.instance.showInstanceSpecificPanel }
},
methods: {
activatePanel (panelName) {
diff --git a/src/components/features_panel/features_panel.js b/src/components/features_panel/features_panel.js
@@ -1,13 +1,13 @@
const FeaturesPanel = {
computed: {
chat: function () {
- return this.$store.state.config.chatAvailable && (!this.$store.state.chatDisabled)
+ return this.$store.state.instance.chatAvailable && (!this.$store.state.chatDisabled)
},
- gopher: function () { return this.$store.state.config.gopherAvailable },
- whoToFollow: function () { return this.$store.state.config.suggestionsEnabled },
- mediaProxy: function () { return this.$store.state.config.mediaProxyAvailable },
- scopeOptions: function () { return this.$store.state.config.scopeOptionsEnabled },
- textlimit: function () { return this.$store.state.config.textlimit }
+ gopher: function () { return this.$store.state.instance.gopherAvailable },
+ whoToFollow: function () { return this.$store.state.instance.suggestionsEnabled },
+ mediaProxy: function () { return this.$store.state.instance.mediaProxyAvailable },
+ scopeOptions: function () { return this.$store.state.instance.scopeOptionsEnabled },
+ textlimit: function () { return this.$store.state.instance.textlimit }
}
}
diff --git a/src/components/instance_specific_panel/instance_specific_panel.js b/src/components/instance_specific_panel/instance_specific_panel.js
@@ -1,7 +1,7 @@
const InstanceSpecificPanel = {
computed: {
instanceSpecificPanelContent () {
- return this.$store.state.config.instanceSpecificPanelContent
+ return this.$store.state.instance.instanceSpecificPanelContent
}
}
}
diff --git a/src/components/login_form/login_form.js b/src/components/login_form/login_form.js
@@ -5,7 +5,7 @@ const LoginForm = {
}),
computed: {
loggingIn () { return this.$store.state.users.loggingIn },
- registrationOpen () { return this.$store.state.config.registrationOpen }
+ registrationOpen () { return this.$store.state.instance.registrationOpen }
},
methods: {
submit () {
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
@@ -102,7 +102,7 @@ const PostStatusForm = {
name: '',
utf: utf || '',
// eslint-disable-next-line camelcase
- img: utf ? '' : this.$store.state.config.server + image_url,
+ img: utf ? '' : this.$store.state.instance.server + image_url,
highlighted: index === this.highlighted
}))
} else {
@@ -120,16 +120,16 @@ const PostStatusForm = {
return this.$store.state.users.users
},
emoji () {
- return this.$store.state.config.emoji || []
+ return this.$store.state.instance.emoji || []
},
customEmoji () {
- return this.$store.state.config.customEmoji || []
+ return this.$store.state.instance.customEmoji || []
},
statusLength () {
return this.newStatus.status.length
},
statusLengthLimit () {
- return this.$store.state.config.textlimit
+ return this.$store.state.instance.textlimit
},
hasStatusLengthLimit () {
return this.statusLengthLimit > 0
@@ -141,10 +141,10 @@ const PostStatusForm = {
return this.hasStatusLengthLimit && (this.statusLength > this.statusLengthLimit)
},
scopeOptionsEnabled () {
- return this.$store.state.config.scopeOptionsEnabled
+ return this.$store.state.instance.scopeOptionsEnabled
},
formattingOptionsEnabled () {
- return this.$store.state.config.formattingOptionsEnabled
+ return this.$store.state.instance.formattingOptionsEnabled
}
},
methods: {
diff --git a/src/components/registration/registration.js b/src/components/registration/registration.js
@@ -5,16 +5,16 @@ const registration = {
registering: false
}),
created () {
- if ((!this.$store.state.config.registrationOpen && !this.token) || !!this.$store.state.users.currentUser) {
+ if ((!this.$store.state.instance.registrationOpen && !this.token) || !!this.$store.state.users.currentUser) {
this.$router.push('/main/all')
}
// Seems like this doesn't work at first page open for some reason
- if (this.$store.state.config.registrationOpen && this.token) {
+ if (this.$store.state.instance.registrationOpen && this.token) {
this.$router.push('/registration')
}
},
computed: {
- termsofservice () { return this.$store.state.config.tos },
+ termsofservice () { return this.$store.state.instance.tos },
token () { return this.$route.params.token }
},
methods: {
diff --git a/src/components/settings/settings.js b/src/components/settings/settings.js
@@ -6,25 +6,27 @@ import { filter, trim } from 'lodash'
const settings = {
data () {
- const config = this.$store.state.config
+ const user = this.$store.state.config
+ const instance = this.$store.state.instance
return {
- hideAttachmentsLocal: config.hideAttachments,
- hideAttachmentsInConvLocal: config.hideAttachmentsInConv,
- hideNsfwLocal: config.hideNsfw,
- notificationVisibilityLocal: config.notificationVisibility,
- replyVisibilityLocal: config.replyVisibility,
- loopVideoLocal: config.loopVideo,
- loopVideoSilentOnlyLocal: config.loopVideoSilentOnly,
- muteWordsString: config.muteWords.join('\n'),
- autoLoadLocal: config.autoLoad,
- streamingLocal: config.streaming,
- pauseOnUnfocusedLocal: config.pauseOnUnfocused,
- hoverPreviewLocal: config.hoverPreview,
- collapseMessageWithSubjectLocal: typeof config.collapseMessageWithSubject === 'undefined'
- ? config.defaultCollapseMessageWithSubject
- : config.collapseMessageWithSubject,
- stopGifs: config.stopGifs,
+ hideAttachmentsLocal: user.hideAttachments,
+ hideAttachmentsInConvLocal: user.hideAttachmentsInConv,
+ hideNsfwLocal: user.hideNsfw,
+ notificationVisibilityLocal: user.notificationVisibility,
+ replyVisibilityLocal: user.replyVisibility,
+ loopVideoLocal: user.loopVideo,
+ loopVideoSilentOnlyLocal: user.loopVideoSilentOnly,
+ muteWordsString: user.muteWords.join('\n'),
+ autoLoadLocal: user.autoLoad,
+ streamingLocal: user.streaming,
+ pauseOnUnfocusedLocal: user.pauseOnUnfocused,
+ hoverPreviewLocal: user.hoverPreview,
+ collapseMessageWithSubjectLocal: typeof user.collapseMessageWithSubject === 'undefined'
+ ? instance.collapseMessageWithSubject
+ : user.collapseMessageWithSubject,
+ collapseMessageWithSubjectDefault: this.$t('settings.values.' + instance.collapseMessageWithSubject),
+ stopGifs: user.stopGifs,
loopSilentAvailable:
// Firefox
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
@@ -42,6 +44,9 @@ const settings = {
computed: {
user () {
return this.$store.state.users.currentUser
+ },
+ currentSaveStateNotice () {
+ return this.$store.state.interface.settings.currentSaveStateNotice
}
},
watch: {
diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue
@@ -1,7 +1,21 @@
<template>
<div class="settings panel panel-default">
<div class="panel-heading">
- {{$t('settings.settings')}}
+ <div class="title">
+ {{$t('settings.settings')}}
+ </div>
+
+ <transition name="fade">
+ <template v-if="currentSaveStateNotice">
+ <div @click.prevent class="alert error" v-if="currentSaveStateNotice.error">
+ {{ $t('settings.saving_err') }}
+ </div>
+
+ <div @click.prevent class="alert transparent" v-if="!currentSaveStateNotice.error">
+ {{ $t('settings.saving_ok') }}
+ </div>
+ </template>
+ </transition>
</div>
<div class="panel-body">
<tab-switcher>
@@ -15,7 +29,9 @@
<ul class="setting-list">
<li>
<input type="checkbox" id="collapseMessageWithSubject" v-model="collapseMessageWithSubjectLocal">
- <label for="collapseMessageWithSubject">{{$t('settings.collapse_subject')}}</label>
+ <label for="collapseMessageWithSubject">
+ {{$t('settings.collapse_subject')}} {{$t('settings.instance_default', { value: collapseMessageWithSubjectDefault })}}
+ </label>
</li>
<li>
<input type="checkbox" id="streaming" v-model="streamingLocal">
diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js
@@ -32,10 +32,10 @@ const UserSettings = {
return this.$store.state.users.currentUser
},
pleromaBackend () {
- return this.$store.state.config.pleromaBackend
+ return this.$store.state.instance.pleromaBackend
},
scopeOptionsEnabled () {
- return this.$store.state.config.scopeOptionsEnabled
+ return this.$store.state.instance.scopeOptionsEnabled
},
vis () {
return {
diff --git a/src/components/who_to_follow_panel/who_to_follow_panel.js b/src/components/who_to_follow_panel/who_to_follow_panel.js
@@ -83,14 +83,14 @@ const WhoToFollowPanel = {
moreUrl: function () {
var host = window.location.hostname
var user = this.user
- var suggestionsWeb = this.$store.state.config.suggestionsWeb
+ var suggestionsWeb = this.$store.state.instance.suggestionsWeb
var url
url = suggestionsWeb.replace(/{{host}}/g, encodeURIComponent(host))
url = url.replace(/{{user}}/g, encodeURIComponent(user))
return url
},
suggestionsEnabled () {
- return this.$store.state.config.suggestionsEnabled
+ return this.$store.state.instance.suggestionsEnabled
}
},
watch: {
diff --git a/src/components/who_to_follow_panel/who_to_follow_panel.vue b/src/components/who_to_follow_panel/who_to_follow_panel.vue
@@ -11,7 +11,7 @@
<img v-bind:src="img1"/> <router-link :to="{ name: 'user-profile', params: { id: id1 } }">{{ name1 }}</router-link><br>
<img v-bind:src="img2"/> <router-link :to="{ name: 'user-profile', params: { id: id2 } }">{{ name2 }}</router-link><br>
<img v-bind:src="img3"/> <router-link :to="{ name: 'user-profile', params: { id: id3 } }">{{ name3 }}</router-link><br>
- <img v-bind:src="$store.state.config.logo"> <a v-bind:href="moreUrl" target="_blank">{{$t('who_to_follow.more')}}</a>
+ <img v-bind:src="$store.state.instance.logo"> <a v-bind:href="moreUrl" target="_blank">{{$t('who_to_follow.more')}}</a>
</p>
</div>
</div>
diff --git a/src/i18n/en.json b/src/i18n/en.json
@@ -114,6 +114,7 @@
"import_followers_from_a_csv_file": "Import follows from a csv file",
"import_theme": "Load preset",
"inputRadius": "Input fields",
+ "instance_default: (default": "{value})",
"interfaceLanguage": "Interface language",
"invalid_theme_imported": "The selected file is not a supported Pleroma theme. No changes to your theme were made.",
"limited_availability": "Unavailable in your browser",
@@ -142,6 +143,8 @@
"reply_visibility_all": "Show all replies",
"reply_visibility_following": "Only show replies directed at me or users I'm following",
"reply_visibility_self": "Only show replies directed at me",
+ "saving_err": "Error saving settings",
+ "saving_ok": "Settings saved",
"security_tab": "Security",
"set_new_avatar": "Set new avatar",
"set_new_profile_background": "Set new profile background",
@@ -153,7 +156,11 @@
"theme": "Theme",
"theme_help": "Use hex color codes (#rrggbb) to customize your color theme.",
"tooltipRadius": "Tooltips/alerts",
- "user_settings": "User Settings"
+ "user_settings": "User Settings",
+ "values": {
+ "false": "no",
+ "true": "yes"
+ }
},
"timeline": {
"collapse": "Collapse",
diff --git a/src/lib/persisted_state.js b/src/lib/persisted_state.js
@@ -1,7 +1,7 @@
import merge from 'lodash.merge'
import objectPath from 'object-path'
import localforage from 'localforage'
-import { throttle, each } from 'lodash'
+import { each } from 'lodash'
let loaded = false
@@ -12,18 +12,18 @@ const defaultReducer = (state, paths) => (
}, {})
)
+const saveImmedeatelyActions = [
+ 'markNotificationsAsSeen',
+ 'clearCurrentUser',
+ 'setCurrentUser',
+ 'setHighlight',
+ 'setOption'
+]
+
const defaultStorage = (() => {
return localforage
})()
-const defaultSetState = (key, state, storage) => {
- if (!loaded) {
- console.log('waiting for old state to be loaded...')
- } else {
- return storage.setItem(key, state)
- }
-}
-
export default function createPersistedState ({
key = 'vuex-lz',
paths = [],
@@ -31,7 +31,14 @@ export default function createPersistedState ({
let value = storage.getItem(key)
return value
},
- setState = throttle(defaultSetState, 60000),
+ setState = (key, state, storage) => {
+ if (!loaded) {
+ console.log('waiting for old state to be loaded...')
+ return Promise.resolve()
+ } else {
+ return storage.setItem(key, state)
+ }
+ },
reducer = defaultReducer,
storage = defaultStorage,
subscriber = store => handler => store.subscribe(handler)
@@ -72,7 +79,20 @@ export default function createPersistedState ({
subscriber(store)((mutation, state) => {
try {
- setState(key, reducer(state, paths), storage)
+ if (saveImmedeatelyActions.includes(mutation.type)) {
+ setState(key, reducer(state, paths), storage)
+ .then(success => {
+ if (typeof success !== 'undefined') {
+ if (mutation.type === 'setOption') {
+ store.dispatch('settingsSaved', { success })
+ }
+ }
+ }, error => {
+ if (mutation.type === 'setOption') {
+ store.dispatch('settingsSaved', { error })
+ }
+ })
+ }
} catch (e) {
console.log("Couldn't persist state:")
console.log(e)
diff --git a/src/main.js b/src/main.js
@@ -14,6 +14,8 @@ import Registration from './components/registration/registration.vue'
import UserSettings from './components/user_settings/user_settings.vue'
import FollowRequests from './components/follow_requests/follow_requests.vue'
+import interfaceModule from './modules/interface.js'
+import instanceModule from './modules/instance.js'
import statusesModule from './modules/statuses.js'
import usersModule from './modules/users.js'
import apiModule from './modules/api.js'
@@ -45,23 +47,7 @@ Vue.use(VueChatScroll)
const persistedStateOptions = {
paths: [
- 'config.collapseMessageWithSubject',
- 'config.hideAttachments',
- 'config.hideAttachmentsInConv',
- 'config.hideNsfw',
- 'config.replyVisibility',
- 'config.notificationVisibility',
- 'config.autoLoad',
- 'config.hoverPreview',
- 'config.streaming',
- 'config.muteWords',
- 'config.customTheme',
- 'config.highlight',
- 'config.loopVideo',
- 'config.loopVideoSilentOnly',
- 'config.pauseOnUnfocused',
- 'config.stopGifs',
- 'config.interfaceLanguage',
+ 'config',
'users.lastLoginName',
'statuses.notifications.maxSavedId'
]
@@ -69,6 +55,8 @@ const persistedStateOptions = {
const store = new Vuex.Store({
modules: {
+ interface: interfaceModule,
+ instance: instanceModule,
statuses: statusesModule,
users: usersModule,
api: apiModule,
@@ -92,92 +80,100 @@ window.fetch('/api/statusnet/config.json')
.then((data) => {
const {name, closed: registrationClosed, textlimit, server} = data.site
- store.dispatch('setOption', { name: 'name', value: name })
- store.dispatch('setOption', { name: 'registrationOpen', value: (registrationClosed === '0') })
- store.dispatch('setOption', { name: 'textlimit', value: parseInt(textlimit) })
- store.dispatch('setOption', { name: 'server', value: server })
+ store.dispatch('setInstanceOption', { name: 'name', value: name })
+ store.dispatch('setInstanceOption', { name: 'registrationOpen', value: (registrationClosed === '0') })
+ store.dispatch('setInstanceOption', { name: 'textlimit', value: parseInt(textlimit) })
+ store.dispatch('setInstanceOption', { name: 'server', value: server })
var apiConfig = data.site.pleromafe
window.fetch('/static/config.json')
- .then((res) => res.json())
- .then((data) => {
- var staticConfig = data
- // This takes static config and overrides properties that are present in apiConfig
- var config = Object.assign({}, staticConfig, apiConfig)
-
- var theme = (config.theme)
- var background = (config.background)
- var logo = (config.logo)
- var logoMask = (typeof config.logoMask === 'undefined' ? true : config.logoMask)
- var logoMargin = (typeof config.logoMargin === 'undefined' ? 0 : config.logoMargin)
- var redirectRootNoLogin = (config.redirectRootNoLogin)
- var redirectRootLogin = (config.redirectRootLogin)
- var chatDisabled = (config.chatDisabled)
- var showInstanceSpecificPanel = (config.showInstanceSpecificPanel)
- var scopeOptionsEnabled = (config.scopeOptionsEnabled)
- var formattingOptionsEnabled = (config.formattingOptionsEnabled)
- var defaultCollapseMessageWithSubject = (config.collapseMessageWithSubject)
-
- store.dispatch('setOption', { name: 'theme', value: theme })
- store.dispatch('setOption', { name: 'background', value: background })
- store.dispatch('setOption', { name: 'logo', value: logo })
- store.dispatch('setOption', { name: 'logoMask', value: logoMask })
- store.dispatch('setOption', { name: 'logoMargin', value: logoMargin })
- store.dispatch('setOption', { name: 'showInstanceSpecificPanel', value: showInstanceSpecificPanel })
- store.dispatch('setOption', { name: 'scopeOptionsEnabled', value: scopeOptionsEnabled })
- store.dispatch('setOption', { name: 'formattingOptionsEnabled', value: formattingOptionsEnabled })
- store.dispatch('setOption', { name: 'defaultCollapseMessageWithSubject', value: defaultCollapseMessageWithSubject })
- if (chatDisabled) {
- store.dispatch('disableChat')
- }
-
- const routes = [
- { name: 'root',
- path: '/',
- redirect: to => {
- return (store.state.users.currentUser ? redirectRootLogin : redirectRootNoLogin) || '/main/all'
- }},
- { path: '/main/all', component: PublicAndExternalTimeline },
- { path: '/main/public', component: PublicTimeline },
- { path: '/main/friends', component: FriendsTimeline },
- { path: '/tag/:tag', component: TagTimeline },
- { name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } },
- { name: 'user-profile', path: '/users/:id', component: UserProfile },
- { name: 'mentions', path: '/:username/mentions', component: Mentions },
- { name: 'settings', path: '/settings', component: Settings },
- { name: 'registration', path: '/registration', component: Registration },
- { name: 'registration', path: '/registration/:token', component: Registration },
- { name: 'friend-requests', path: '/friend-requests', component: FollowRequests },
- { name: 'user-settings', path: '/user-settings', component: UserSettings }
- ]
-
- const router = new VueRouter({
- mode: 'history',
- routes,
- scrollBehavior: (to, from, savedPosition) => {
- if (to.matched.some(m => m.meta.dontScroll)) {
- return false
- }
- return savedPosition || { x: 0, y: 0 }
- }
+ .then((res) => res.json())
+ .catch((err) => {
+ console.warn('Failed to load static/config.json, continuing without it.')
+ console.warn(err)
+ return {}
})
+ .then((staticConfig) => {
+ // This takes static config and overrides properties that are present in apiConfig
+ var config = Object.assign({}, staticConfig, apiConfig)
+
+ var theme = (config.theme)
+ var background = (config.background)
+ var logo = (config.logo)
+ var logoMask = (typeof config.logoMask === 'undefined' ? true : config.logoMask)
+ var logoMargin = (typeof config.logoMargin === 'undefined' ? 0 : config.logoMargin)
+ var redirectRootNoLogin = (config.redirectRootNoLogin)
+ var redirectRootLogin = (config.redirectRootLogin)
+ var chatDisabled = (config.chatDisabled)
+ var showInstanceSpecificPanel = (config.showInstanceSpecificPanel)
+ var scopeOptionsEnabled = (config.scopeOptionsEnabled)
+ var formattingOptionsEnabled = (config.formattingOptionsEnabled)
+ var collapseMessageWithSubject = (config.collapseMessageWithSubject)
+
+ store.dispatch('setInstanceOption', { name: 'theme', value: theme })
+ store.dispatch('setInstanceOption', { name: 'background', value: background })
+ store.dispatch('setInstanceOption', { name: 'logo', value: logo })
+ store.dispatch('setInstanceOption', { name: 'logoMask', value: logoMask })
+ store.dispatch('setInstanceOption', { name: 'logoMargin', value: logoMargin })
+ store.dispatch('setInstanceOption', { name: 'redirectRootNoLogin', value: redirectRootNoLogin })
+ store.dispatch('setInstanceOption', { name: 'redirectRootLogin', value: redirectRootLogin })
+ store.dispatch('setInstanceOption', { name: 'showInstanceSpecificPanel', value: showInstanceSpecificPanel })
+ store.dispatch('setInstanceOption', { name: 'scopeOptionsEnabled', value: scopeOptionsEnabled })
+ store.dispatch('setInstanceOption', { name: 'formattingOptionsEnabled', value: formattingOptionsEnabled })
+ store.dispatch('setInstanceOption', { name: 'collapseMessageWithSubject', value: collapseMessageWithSubject })
+ if (chatDisabled) {
+ store.dispatch('disableChat')
+ }
- /* eslint-disable no-new */
- new Vue({
- router,
- store,
- i18n,
- el: '#app',
- render: h => h(App)
+ const routes = [
+ { name: 'root',
+ path: '/',
+ redirect: to => {
+ return (store.state.users.currentUser
+ ? store.state.instance.redirectRootLogin
+ : store.state.instance.redirectRootNoLogin) || '/main/all'
+ }},
+ { path: '/main/all', component: PublicAndExternalTimeline },
+ { path: '/main/public', component: PublicTimeline },
+ { path: '/main/friends', component: FriendsTimeline },
+ { path: '/tag/:tag', component: TagTimeline },
+ { name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } },
+ { name: 'user-profile', path: '/users/:id', component: UserProfile },
+ { name: 'mentions', path: '/:username/mentions', component: Mentions },
+ { name: 'settings', path: '/settings', component: Settings },
+ { name: 'registration', path: '/registration', component: Registration },
+ { name: 'registration', path: '/registration/:token', component: Registration },
+ { name: 'friend-requests', path: '/friend-requests', component: FollowRequests },
+ { name: 'user-settings', path: '/user-settings', component: UserSettings }
+ ]
+
+ const router = new VueRouter({
+ mode: 'history',
+ routes,
+ scrollBehavior: (to, from, savedPosition) => {
+ if (to.matched.some(m => m.meta.dontScroll)) {
+ return false
+ }
+ return savedPosition || { x: 0, y: 0 }
+ }
+ })
+
+ /* eslint-disable no-new */
+ new Vue({
+ router,
+ store,
+ i18n,
+ el: '#app',
+ render: h => h(App)
+ })
})
- })
})
window.fetch('/static/terms-of-service.html')
.then((res) => res.text())
.then((html) => {
- store.dispatch('setOption', { name: 'tos', value: html })
+ store.dispatch('setInstanceOption', { name: 'tos', value: html })
})
window.fetch('/api/pleroma/emoji.json')
@@ -188,11 +184,11 @@ window.fetch('/api/pleroma/emoji.json')
const emoji = Object.keys(values).map((key) => {
return { shortcode: key, image_url: values[key] }
})
- store.dispatch('setOption', { name: 'customEmoji', value: emoji })
- store.dispatch('setOption', { name: 'pleromaBackend', value: true })
+ store.dispatch('setInstanceOption', { name: 'customEmoji', value: emoji })
+ store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: true })
},
(failure) => {
- store.dispatch('setOption', { name: 'pleromaBackend', value: false })
+ store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: false })
}
),
(error) => console.log(error)
@@ -204,24 +200,24 @@ window.fetch('/static/emoji.json')
const emoji = Object.keys(values).map((key) => {
return { shortcode: key, image_url: false, 'utf': values[key] }
})
- store.dispatch('setOption', { name: 'emoji', value: emoji })
+ store.dispatch('setInstanceOption', { name: 'emoji', value: emoji })
})
window.fetch('/instance/panel.html')
.then((res) => res.text())
.then((html) => {
- store.dispatch('setOption', { name: 'instanceSpecificPanelContent', value: html })
+ store.dispatch('setInstanceOption', { name: 'instanceSpecificPanelContent', value: html })
})
window.fetch('/nodeinfo/2.0.json')
.then((res) => res.json())
.then((data) => {
const metadata = data.metadata
- store.dispatch('setOption', { name: 'mediaProxyAvailable', value: data.metadata.mediaProxy })
- store.dispatch('setOption', { name: 'chatAvailable', value: data.metadata.chat })
- store.dispatch('setOption', { name: 'gopherAvailable', value: data.metadata.gopher })
+ store.dispatch('setInstanceOption', { name: 'mediaProxyAvailable', value: data.metadata.mediaProxy })
+ store.dispatch('setInstanceOption', { name: 'chatAvailable', value: data.metadata.chat })
+ store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: data.metadata.gopher })
const suggestions = metadata.suggestions
- store.dispatch('setOption', { name: 'suggestionsEnabled', value: suggestions.enabled })
- store.dispatch('setOption', { name: 'suggestionsWeb', value: suggestions.web })
+ store.dispatch('setInstanceOption', { name: 'suggestionsEnabled', value: suggestions.enabled })
+ store.dispatch('setInstanceOption', { name: 'suggestionsWeb', value: suggestions.web })
})
diff --git a/src/modules/config.js b/src/modules/config.js
@@ -4,7 +4,6 @@ import StyleSetter from '../services/style_setter/style_setter.js'
const browserLocale = (window.navigator.language || 'en').split('-')[0]
const defaultState = {
- name: 'Pleroma FE',
colors: {},
collapseMessageWithSubject: false,
hideAttachments: false,
@@ -45,18 +44,12 @@ const config = {
}
},
actions: {
- setPageTitle ({state}, option = '') {
- document.title = `${option} ${state.name}`
- },
setHighlight ({ commit, dispatch }, { user, color, type }) {
commit('setHighlight', {user, color, type})
},
setOption ({ commit, dispatch }, { name, value }) {
commit('setOption', {name, value})
switch (name) {
- case 'name':
- dispatch('setPageTitle')
- break
case 'theme':
StyleSetter.setPreset(value, commit)
break
diff --git a/src/modules/instance.js b/src/modules/instance.js
@@ -0,0 +1,63 @@
+import { set } from 'vue'
+import StyleSetter from '../services/style_setter/style_setter.js'
+
+const defaultState = {
+ // Stuff from static/config.json and apiConfig
+ name: 'Pleroma FE',
+ registrationOpen: true,
+ textlimit: 5000,
+ server: 'http://localhost:4040/',
+ theme: 'pleroma-dark',
+ background: '/static/aurora_borealis.jpg',
+ logo: '/static/logo.png',
+ logoMask: true,
+ logoMargin: '.2em',
+ redirectRootNoLogin: '/main/all',
+ redirectRootLogin: '/main/friends',
+ showInstanceSpecificPanel: false,
+ scopeOptionsEnabled: true,
+ formattingOptionsEnabled: false,
+ collapseMessageWithSubject: false,
+ disableChat: false,
+
+ // Nasty stuff
+ pleromaBackend: true,
+ emoji: [],
+ customEmoji: [],
+
+ // Feature-set, apparently, not everything here is reported...
+ mediaProxyAvailable: false,
+ chatAvailable: false,
+ gopherAvailable: false,
+ suggestionsEnabled: false,
+ suggestionsWeb: '',
+
+ // Html stuff
+ instanceSpecificPanelContent: '',
+ tos: ''
+}
+
+const instance = {
+ state: defaultState,
+ mutations: {
+ setInstanceOption (state, { name, value }) {
+ if (typeof value !== 'undefined') {
+ set(state, name, value)
+ }
+ }
+ },
+ actions: {
+ setInstanceOption ({ commit, dispatch }, { name, value }) {
+ commit('setInstanceOption', {name, value})
+ switch (name) {
+ case 'name':
+ dispatch('setPageTitle')
+ break
+ case 'theme':
+ StyleSetter.setPreset(value, commit)
+ }
+ }
+ }
+}
+
+export default instance
diff --git a/src/modules/interface.js b/src/modules/interface.js
@@ -0,0 +1,36 @@
+import { set, delete as del } from 'vue'
+
+const defaultState = {
+ settings: {
+ currentSaveStateNotice: null,
+ noticeClearTimeout: null
+ }
+}
+
+const interfaceMod = {
+ state: defaultState,
+ mutations: {
+ settingsSaved (state, { success, error }) {
+ if (success) {
+ if (state.noticeClearTimeout) {
+ clearTimeout(state.noticeClearTimeout)
+ }
+ set(state.settings, 'currentSaveStateNotice', { error: false, data: success })
+ set(state.settings, 'noticeClearTimeout',
+ setTimeout(() => del(state.settings, 'currentSaveStateNotice'), 2000))
+ } else {
+ set(state.settings, 'currentSaveStateNotice', { error: true, errorData: error })
+ }
+ }
+ },
+ actions: {
+ setPageTitle ({ rootState }, option = '') {
+ document.title = `${option} ${rootState.instance.name}`
+ },
+ settingsSaved ({ commit, dispatch }, { success, error }) {
+ commit('settingsSaved', { success, error })
+ }
+ }
+}
+
+export default interfaceMod
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
@@ -133,8 +133,6 @@ const updateBanner = ({credentials, params}) => {
const updateProfile = ({credentials, params}) => {
let url = PROFILE_UPDATE_URL
- console.log(params)
-
const form = new FormData()
each(params, (value, key) => {
@@ -335,7 +333,14 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use
const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&')
url += `?${queryString}`
- return fetch(url, { headers: authHeaders(credentials) }).then((data) => data.json())
+ return fetch(url, { headers: authHeaders(credentials) })
+ .then((data) => {
+ if (data.ok) {
+ return data
+ }
+ throw new Error('Error fetching timeline')
+ })
+ .then((data) => data.json())
}
const verifyCredentials = (user) => {