Merge branch 'betterStorage' into 'develop'

Better storage

See merge request pleroma/pleroma-fe!343
This commit is contained in:
Henry 2018-09-21 09:19:02 +00:00
commit cd48268c85
19 changed files with 316 additions and 170 deletions

View file

@ -27,6 +27,11 @@ module.exports = {
changeOrigin: true, changeOrigin: true,
cookieDomainRewrite: 'localhost' cookieDomainRewrite: 'localhost'
}, },
'/nodeinfo': {
target: 'http://localhost:4000/',
changeOrigin: true,
cookieDomainRewrite: 'localhost'
},
'/socket': { '/socket': {
target: 'http://localhost:4000/', target: 'http://localhost:4000/',
changeOrigin: true, changeOrigin: true,

View file

@ -36,9 +36,9 @@ export default {
computed: { computed: {
currentUser () { return this.$store.state.users.currentUser }, currentUser () { return this.$store.state.users.currentUser },
background () { 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 () { logoStyle () {
return { return {
'visibility': this.enableMask ? 'hidden' : 'visible' 'visibility': this.enableMask ? 'hidden' : 'visible'
@ -46,24 +46,24 @@ export default {
}, },
logoMaskStyle () { logoMaskStyle () {
return this.enableMask ? { return this.enableMask ? {
'mask-image': `url(${this.$store.state.config.logo})` 'mask-image': `url(${this.$store.state.instance.logo})`
} : { } : {
'background-color': this.enableMask ? '' : 'transparent' 'background-color': this.enableMask ? '' : 'transparent'
} }
}, },
logoBgStyle () { logoBgStyle () {
return Object.assign({ return Object.assign({
'margin': `${this.$store.state.config.logoMargin} 0` 'margin': `${this.$store.state.instance.logoMargin} 0`
}, this.enableMask ? {} : { }, this.enableMask ? {} : {
'background-color': this.enableMask ? '' : 'transparent' '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})` } }, 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' }, chat () { return this.$store.state.chat.channel.state === 'joined' },
suggestionsEnabled () { return this.$store.state.config.suggestionsEnabled }, suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
showInstanceSpecificPanel () { return this.$store.state.config.showInstanceSpecificPanel } showInstanceSpecificPanel () { return this.$store.state.instance.showInstanceSpecificPanel }
}, },
methods: { methods: {
activatePanel (panelName) { activatePanel (panelName) {

View file

@ -1,13 +1,13 @@
const FeaturesPanel = { const FeaturesPanel = {
computed: { computed: {
chat: function () { 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 }, gopher: function () { return this.$store.state.instance.gopherAvailable },
whoToFollow: function () { return this.$store.state.config.suggestionsEnabled }, whoToFollow: function () { return this.$store.state.instance.suggestionsEnabled },
mediaProxy: function () { return this.$store.state.config.mediaProxyAvailable }, mediaProxy: function () { return this.$store.state.instance.mediaProxyAvailable },
scopeOptions: function () { return this.$store.state.config.scopeOptionsEnabled }, scopeOptions: function () { return this.$store.state.instance.scopeOptionsEnabled },
textlimit: function () { return this.$store.state.config.textlimit } textlimit: function () { return this.$store.state.instance.textlimit }
} }
} }

View file

@ -1,7 +1,7 @@
const InstanceSpecificPanel = { const InstanceSpecificPanel = {
computed: { computed: {
instanceSpecificPanelContent () { instanceSpecificPanelContent () {
return this.$store.state.config.instanceSpecificPanelContent return this.$store.state.instance.instanceSpecificPanelContent
} }
} }
} }

View file

@ -5,7 +5,7 @@ const LoginForm = {
}), }),
computed: { computed: {
loggingIn () { return this.$store.state.users.loggingIn }, loggingIn () { return this.$store.state.users.loggingIn },
registrationOpen () { return this.$store.state.config.registrationOpen } registrationOpen () { return this.$store.state.instance.registrationOpen }
}, },
methods: { methods: {
submit () { submit () {

View file

@ -102,7 +102,7 @@ const PostStatusForm = {
name: '', name: '',
utf: utf || '', utf: utf || '',
// eslint-disable-next-line camelcase // 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 highlighted: index === this.highlighted
})) }))
} else { } else {
@ -120,16 +120,16 @@ const PostStatusForm = {
return this.$store.state.users.users return this.$store.state.users.users
}, },
emoji () { emoji () {
return this.$store.state.config.emoji || [] return this.$store.state.instance.emoji || []
}, },
customEmoji () { customEmoji () {
return this.$store.state.config.customEmoji || [] return this.$store.state.instance.customEmoji || []
}, },
statusLength () { statusLength () {
return this.newStatus.status.length return this.newStatus.status.length
}, },
statusLengthLimit () { statusLengthLimit () {
return this.$store.state.config.textlimit return this.$store.state.instance.textlimit
}, },
hasStatusLengthLimit () { hasStatusLengthLimit () {
return this.statusLengthLimit > 0 return this.statusLengthLimit > 0
@ -141,10 +141,10 @@ const PostStatusForm = {
return this.hasStatusLengthLimit && (this.statusLength > this.statusLengthLimit) return this.hasStatusLengthLimit && (this.statusLength > this.statusLengthLimit)
}, },
scopeOptionsEnabled () { scopeOptionsEnabled () {
return this.$store.state.config.scopeOptionsEnabled return this.$store.state.instance.scopeOptionsEnabled
}, },
formattingOptionsEnabled () { formattingOptionsEnabled () {
return this.$store.state.config.formattingOptionsEnabled return this.$store.state.instance.formattingOptionsEnabled
} }
}, },
methods: { methods: {

View file

@ -5,16 +5,16 @@ const registration = {
registering: false registering: false
}), }),
created () { 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') this.$router.push('/main/all')
} }
// Seems like this doesn't work at first page open for some reason // 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') this.$router.push('/registration')
} }
}, },
computed: { computed: {
termsofservice () { return this.$store.state.config.tos }, termsofservice () { return this.$store.state.instance.tos },
token () { return this.$route.params.token } token () { return this.$route.params.token }
}, },
methods: { methods: {

View file

@ -6,25 +6,27 @@ import { filter, trim } from 'lodash'
const settings = { const settings = {
data () { data () {
const config = this.$store.state.config const user = this.$store.state.config
const instance = this.$store.state.instance
return { return {
hideAttachmentsLocal: config.hideAttachments, hideAttachmentsLocal: user.hideAttachments,
hideAttachmentsInConvLocal: config.hideAttachmentsInConv, hideAttachmentsInConvLocal: user.hideAttachmentsInConv,
hideNsfwLocal: config.hideNsfw, hideNsfwLocal: user.hideNsfw,
notificationVisibilityLocal: config.notificationVisibility, notificationVisibilityLocal: user.notificationVisibility,
replyVisibilityLocal: config.replyVisibility, replyVisibilityLocal: user.replyVisibility,
loopVideoLocal: config.loopVideo, loopVideoLocal: user.loopVideo,
loopVideoSilentOnlyLocal: config.loopVideoSilentOnly, loopVideoSilentOnlyLocal: user.loopVideoSilentOnly,
muteWordsString: config.muteWords.join('\n'), muteWordsString: user.muteWords.join('\n'),
autoLoadLocal: config.autoLoad, autoLoadLocal: user.autoLoad,
streamingLocal: config.streaming, streamingLocal: user.streaming,
pauseOnUnfocusedLocal: config.pauseOnUnfocused, pauseOnUnfocusedLocal: user.pauseOnUnfocused,
hoverPreviewLocal: config.hoverPreview, hoverPreviewLocal: user.hoverPreview,
collapseMessageWithSubjectLocal: typeof config.collapseMessageWithSubject === 'undefined' collapseMessageWithSubjectLocal: typeof user.collapseMessageWithSubject === 'undefined'
? config.defaultCollapseMessageWithSubject ? instance.collapseMessageWithSubject
: config.collapseMessageWithSubject, : user.collapseMessageWithSubject,
stopGifs: config.stopGifs, collapseMessageWithSubjectDefault: this.$t('settings.values.' + instance.collapseMessageWithSubject),
stopGifs: user.stopGifs,
loopSilentAvailable: loopSilentAvailable:
// Firefox // Firefox
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') || Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
@ -42,6 +44,9 @@ const settings = {
computed: { computed: {
user () { user () {
return this.$store.state.users.currentUser return this.$store.state.users.currentUser
},
currentSaveStateNotice () {
return this.$store.state.interface.settings.currentSaveStateNotice
} }
}, },
watch: { watch: {

View file

@ -1,7 +1,21 @@
<template> <template>
<div class="settings panel panel-default"> <div class="settings panel panel-default">
<div class="panel-heading"> <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>
<div class="panel-body"> <div class="panel-body">
<tab-switcher> <tab-switcher>
@ -15,7 +29,9 @@
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
<input type="checkbox" id="collapseMessageWithSubject" v-model="collapseMessageWithSubjectLocal"> <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>
<li> <li>
<input type="checkbox" id="streaming" v-model="streamingLocal"> <input type="checkbox" id="streaming" v-model="streamingLocal">

View file

@ -32,10 +32,10 @@ const UserSettings = {
return this.$store.state.users.currentUser return this.$store.state.users.currentUser
}, },
pleromaBackend () { pleromaBackend () {
return this.$store.state.config.pleromaBackend return this.$store.state.instance.pleromaBackend
}, },
scopeOptionsEnabled () { scopeOptionsEnabled () {
return this.$store.state.config.scopeOptionsEnabled return this.$store.state.instance.scopeOptionsEnabled
}, },
vis () { vis () {
return { return {

View file

@ -83,14 +83,14 @@ const WhoToFollowPanel = {
moreUrl: function () { moreUrl: function () {
var host = window.location.hostname var host = window.location.hostname
var user = this.user var user = this.user
var suggestionsWeb = this.$store.state.config.suggestionsWeb var suggestionsWeb = this.$store.state.instance.suggestionsWeb
var url var url
url = suggestionsWeb.replace(/{{host}}/g, encodeURIComponent(host)) url = suggestionsWeb.replace(/{{host}}/g, encodeURIComponent(host))
url = url.replace(/{{user}}/g, encodeURIComponent(user)) url = url.replace(/{{user}}/g, encodeURIComponent(user))
return url return url
}, },
suggestionsEnabled () { suggestionsEnabled () {
return this.$store.state.config.suggestionsEnabled return this.$store.state.instance.suggestionsEnabled
} }
}, },
watch: { watch: {

View file

@ -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="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="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="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> </p>
</div> </div>
</div> </div>

View file

@ -114,6 +114,7 @@
"import_followers_from_a_csv_file": "Import follows from a csv file", "import_followers_from_a_csv_file": "Import follows from a csv file",
"import_theme": "Load preset", "import_theme": "Load preset",
"inputRadius": "Input fields", "inputRadius": "Input fields",
"instance_default: (default": "{value})",
"interfaceLanguage": "Interface language", "interfaceLanguage": "Interface language",
"invalid_theme_imported": "The selected file is not a supported Pleroma theme. No changes to your theme were made.", "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", "limited_availability": "Unavailable in your browser",
@ -142,6 +143,8 @@
"reply_visibility_all": "Show all replies", "reply_visibility_all": "Show all replies",
"reply_visibility_following": "Only show replies directed at me or users I'm following", "reply_visibility_following": "Only show replies directed at me or users I'm following",
"reply_visibility_self": "Only show replies directed at me", "reply_visibility_self": "Only show replies directed at me",
"saving_err": "Error saving settings",
"saving_ok": "Settings saved",
"security_tab": "Security", "security_tab": "Security",
"set_new_avatar": "Set new avatar", "set_new_avatar": "Set new avatar",
"set_new_profile_background": "Set new profile background", "set_new_profile_background": "Set new profile background",
@ -153,7 +156,11 @@
"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.",
"tooltipRadius": "Tooltips/alerts", "tooltipRadius": "Tooltips/alerts",
"user_settings": "User Settings" "user_settings": "User Settings",
"values": {
"false": "no",
"true": "yes"
}
}, },
"timeline": { "timeline": {
"collapse": "Collapse", "collapse": "Collapse",

View file

@ -1,7 +1,7 @@
import merge from 'lodash.merge' import merge from 'lodash.merge'
import objectPath from 'object-path' import objectPath from 'object-path'
import localforage from 'localforage' import localforage from 'localforage'
import { throttle, each } from 'lodash' import { each } from 'lodash'
let loaded = false let loaded = false
@ -12,18 +12,18 @@ const defaultReducer = (state, paths) => (
}, {}) }, {})
) )
const saveImmedeatelyActions = [
'markNotificationsAsSeen',
'clearCurrentUser',
'setCurrentUser',
'setHighlight',
'setOption'
]
const defaultStorage = (() => { const defaultStorage = (() => {
return localforage 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 ({ export default function createPersistedState ({
key = 'vuex-lz', key = 'vuex-lz',
paths = [], paths = [],
@ -31,7 +31,14 @@ export default function createPersistedState ({
let value = storage.getItem(key) let value = storage.getItem(key)
return value 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, reducer = defaultReducer,
storage = defaultStorage, storage = defaultStorage,
subscriber = store => handler => store.subscribe(handler) subscriber = store => handler => store.subscribe(handler)
@ -72,7 +79,20 @@ export default function createPersistedState ({
subscriber(store)((mutation, state) => { subscriber(store)((mutation, state) => {
try { 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) { } catch (e) {
console.log("Couldn't persist state:") console.log("Couldn't persist state:")
console.log(e) console.log(e)

View file

@ -14,6 +14,8 @@ import Registration from './components/registration/registration.vue'
import UserSettings from './components/user_settings/user_settings.vue' import UserSettings from './components/user_settings/user_settings.vue'
import FollowRequests from './components/follow_requests/follow_requests.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 statusesModule from './modules/statuses.js'
import usersModule from './modules/users.js' import usersModule from './modules/users.js'
import apiModule from './modules/api.js' import apiModule from './modules/api.js'
@ -45,23 +47,7 @@ Vue.use(VueChatScroll)
const persistedStateOptions = { const persistedStateOptions = {
paths: [ paths: [
'config.collapseMessageWithSubject', 'config',
'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',
'users.lastLoginName', 'users.lastLoginName',
'statuses.notifications.maxSavedId' 'statuses.notifications.maxSavedId'
] ]
@ -69,6 +55,8 @@ const persistedStateOptions = {
const store = new Vuex.Store({ const store = new Vuex.Store({
modules: { modules: {
interface: interfaceModule,
instance: instanceModule,
statuses: statusesModule, statuses: statusesModule,
users: usersModule, users: usersModule,
api: apiModule, api: apiModule,
@ -92,92 +80,100 @@ window.fetch('/api/statusnet/config.json')
.then((data) => { .then((data) => {
const {name, closed: registrationClosed, textlimit, server} = data.site const {name, closed: registrationClosed, textlimit, server} = data.site
store.dispatch('setOption', { name: 'name', value: name }) store.dispatch('setInstanceOption', { name: 'name', value: name })
store.dispatch('setOption', { name: 'registrationOpen', value: (registrationClosed === '0') }) store.dispatch('setInstanceOption', { name: 'registrationOpen', value: (registrationClosed === '0') })
store.dispatch('setOption', { name: 'textlimit', value: parseInt(textlimit) }) store.dispatch('setInstanceOption', { name: 'textlimit', value: parseInt(textlimit) })
store.dispatch('setOption', { name: 'server', value: server }) store.dispatch('setInstanceOption', { name: 'server', value: server })
var apiConfig = data.site.pleromafe var apiConfig = data.site.pleromafe
window.fetch('/static/config.json') window.fetch('/static/config.json')
.then((res) => res.json()) .then((res) => res.json())
.then((data) => { .catch((err) => {
var staticConfig = data console.warn('Failed to load static/config.json, continuing without it.')
// This takes static config and overrides properties that are present in apiConfig console.warn(err)
var config = Object.assign({}, staticConfig, apiConfig) 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 theme = (config.theme)
var background = (config.background) var background = (config.background)
var logo = (config.logo) var logo = (config.logo)
var logoMask = (typeof config.logoMask === 'undefined' ? true : config.logoMask) var logoMask = (typeof config.logoMask === 'undefined' ? true : config.logoMask)
var logoMargin = (typeof config.logoMargin === 'undefined' ? 0 : config.logoMargin) var logoMargin = (typeof config.logoMargin === 'undefined' ? 0 : config.logoMargin)
var redirectRootNoLogin = (config.redirectRootNoLogin) var redirectRootNoLogin = (config.redirectRootNoLogin)
var redirectRootLogin = (config.redirectRootLogin) var redirectRootLogin = (config.redirectRootLogin)
var chatDisabled = (config.chatDisabled) var chatDisabled = (config.chatDisabled)
var showInstanceSpecificPanel = (config.showInstanceSpecificPanel) var showInstanceSpecificPanel = (config.showInstanceSpecificPanel)
var scopeOptionsEnabled = (config.scopeOptionsEnabled) var scopeOptionsEnabled = (config.scopeOptionsEnabled)
var formattingOptionsEnabled = (config.formattingOptionsEnabled) var formattingOptionsEnabled = (config.formattingOptionsEnabled)
var defaultCollapseMessageWithSubject = (config.collapseMessageWithSubject) var collapseMessageWithSubject = (config.collapseMessageWithSubject)
store.dispatch('setOption', { name: 'theme', value: theme }) store.dispatch('setInstanceOption', { name: 'theme', value: theme })
store.dispatch('setOption', { name: 'background', value: background }) store.dispatch('setInstanceOption', { name: 'background', value: background })
store.dispatch('setOption', { name: 'logo', value: logo }) store.dispatch('setInstanceOption', { name: 'logo', value: logo })
store.dispatch('setOption', { name: 'logoMask', value: logoMask }) store.dispatch('setInstanceOption', { name: 'logoMask', value: logoMask })
store.dispatch('setOption', { name: 'logoMargin', value: logoMargin }) store.dispatch('setInstanceOption', { name: 'logoMargin', value: logoMargin })
store.dispatch('setOption', { name: 'showInstanceSpecificPanel', value: showInstanceSpecificPanel }) store.dispatch('setInstanceOption', { name: 'redirectRootNoLogin', value: redirectRootNoLogin })
store.dispatch('setOption', { name: 'scopeOptionsEnabled', value: scopeOptionsEnabled }) store.dispatch('setInstanceOption', { name: 'redirectRootLogin', value: redirectRootLogin })
store.dispatch('setOption', { name: 'formattingOptionsEnabled', value: formattingOptionsEnabled }) store.dispatch('setInstanceOption', { name: 'showInstanceSpecificPanel', value: showInstanceSpecificPanel })
store.dispatch('setOption', { name: 'defaultCollapseMessageWithSubject', value: defaultCollapseMessageWithSubject }) store.dispatch('setInstanceOption', { name: 'scopeOptionsEnabled', value: scopeOptionsEnabled })
if (chatDisabled) { store.dispatch('setInstanceOption', { name: 'formattingOptionsEnabled', value: formattingOptionsEnabled })
store.dispatch('disableChat') store.dispatch('setInstanceOption', { name: 'collapseMessageWithSubject', value: collapseMessageWithSubject })
} 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 }
} }
})
/* eslint-disable no-new */ const routes = [
new Vue({ { name: 'root',
router, path: '/',
store, redirect: to => {
i18n, return (store.state.users.currentUser
el: '#app', ? store.state.instance.redirectRootLogin
render: h => h(App) : 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') window.fetch('/static/terms-of-service.html')
.then((res) => res.text()) .then((res) => res.text())
.then((html) => { .then((html) => {
store.dispatch('setOption', { name: 'tos', value: html }) store.dispatch('setInstanceOption', { name: 'tos', value: html })
}) })
window.fetch('/api/pleroma/emoji.json') window.fetch('/api/pleroma/emoji.json')
@ -188,11 +184,11 @@ window.fetch('/api/pleroma/emoji.json')
const emoji = Object.keys(values).map((key) => { const emoji = Object.keys(values).map((key) => {
return { shortcode: key, image_url: values[key] } return { shortcode: key, image_url: values[key] }
}) })
store.dispatch('setOption', { name: 'customEmoji', value: emoji }) store.dispatch('setInstanceOption', { name: 'customEmoji', value: emoji })
store.dispatch('setOption', { name: 'pleromaBackend', value: true }) store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: true })
}, },
(failure) => { (failure) => {
store.dispatch('setOption', { name: 'pleromaBackend', value: false }) store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: false })
} }
), ),
(error) => console.log(error) (error) => console.log(error)
@ -204,24 +200,24 @@ window.fetch('/static/emoji.json')
const emoji = Object.keys(values).map((key) => { const emoji = Object.keys(values).map((key) => {
return { shortcode: key, image_url: false, 'utf': values[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') window.fetch('/instance/panel.html')
.then((res) => res.text()) .then((res) => res.text())
.then((html) => { .then((html) => {
store.dispatch('setOption', { name: 'instanceSpecificPanelContent', value: html }) store.dispatch('setInstanceOption', { name: 'instanceSpecificPanelContent', value: html })
}) })
window.fetch('/nodeinfo/2.0.json') window.fetch('/nodeinfo/2.0.json')
.then((res) => res.json()) .then((res) => res.json())
.then((data) => { .then((data) => {
const metadata = data.metadata const metadata = data.metadata
store.dispatch('setOption', { name: 'mediaProxyAvailable', value: data.metadata.mediaProxy }) store.dispatch('setInstanceOption', { name: 'mediaProxyAvailable', value: data.metadata.mediaProxy })
store.dispatch('setOption', { name: 'chatAvailable', value: data.metadata.chat }) store.dispatch('setInstanceOption', { name: 'chatAvailable', value: data.metadata.chat })
store.dispatch('setOption', { name: 'gopherAvailable', value: data.metadata.gopher }) store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: data.metadata.gopher })
const suggestions = metadata.suggestions const suggestions = metadata.suggestions
store.dispatch('setOption', { name: 'suggestionsEnabled', value: suggestions.enabled }) store.dispatch('setInstanceOption', { name: 'suggestionsEnabled', value: suggestions.enabled })
store.dispatch('setOption', { name: 'suggestionsWeb', value: suggestions.web }) store.dispatch('setInstanceOption', { name: 'suggestionsWeb', value: suggestions.web })
}) })

View file

@ -4,7 +4,6 @@ import StyleSetter from '../services/style_setter/style_setter.js'
const browserLocale = (window.navigator.language || 'en').split('-')[0] const browserLocale = (window.navigator.language || 'en').split('-')[0]
const defaultState = { const defaultState = {
name: 'Pleroma FE',
colors: {}, colors: {},
collapseMessageWithSubject: false, collapseMessageWithSubject: false,
hideAttachments: false, hideAttachments: false,
@ -45,18 +44,12 @@ const config = {
} }
}, },
actions: { actions: {
setPageTitle ({state}, option = '') {
document.title = `${option} ${state.name}`
},
setHighlight ({ commit, dispatch }, { user, color, type }) { setHighlight ({ commit, dispatch }, { user, color, type }) {
commit('setHighlight', {user, color, type}) commit('setHighlight', {user, color, type})
}, },
setOption ({ commit, dispatch }, { name, value }) { setOption ({ commit, dispatch }, { name, value }) {
commit('setOption', {name, value}) commit('setOption', {name, value})
switch (name) { switch (name) {
case 'name':
dispatch('setPageTitle')
break
case 'theme': case 'theme':
StyleSetter.setPreset(value, commit) StyleSetter.setPreset(value, commit)
break break

63
src/modules/instance.js Normal file
View file

@ -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

36
src/modules/interface.js Normal file
View file

@ -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

View file

@ -133,8 +133,6 @@ const updateBanner = ({credentials, params}) => {
const updateProfile = ({credentials, params}) => { const updateProfile = ({credentials, params}) => {
let url = PROFILE_UPDATE_URL let url = PROFILE_UPDATE_URL
console.log(params)
const form = new FormData() const form = new FormData()
each(params, (value, key) => { 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('&') const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&')
url += `?${queryString}` 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) => { const verifyCredentials = (user) => {