forked from cybrespace/pinafore
refactor store
This commit is contained in:
parent
88d59678f2
commit
90af9eedde
|
@ -1,4 +1,4 @@
|
|||
import { store } from '../_utils/store'
|
||||
import { store } from '../_store/store'
|
||||
import { database } from '../_utils/database/database'
|
||||
import { getTimeline } from '../_utils/mastodon/timelines'
|
||||
import { toast } from '../_utils/toast'
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<script>
|
||||
import Nav from './Nav.html';
|
||||
import VirtualListContainer from './virtualList/VirtualListContainer.html'
|
||||
import { store } from '../_utils/store'
|
||||
import { store } from '../_store/store'
|
||||
|
||||
export default {
|
||||
oncreate() {
|
||||
|
|
|
@ -297,7 +297,7 @@
|
|||
import { mark, stop } from '../../_utils/marks'
|
||||
import IntlRelativeFormat from 'intl-relativeformat'
|
||||
import { replaceAll } from '../../_utils/replaceAll'
|
||||
import { store } from '../../_utils/store'
|
||||
import { store } from '../../_store/store'
|
||||
|
||||
const relativeFormat = new IntlRelativeFormat('en-US');
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
import LoadingSpinner from '../LoadingSpinner.html'
|
||||
// TODO: the transition seems to occasionally cause an error in Svelte, transition_run is undefined
|
||||
//import { fade } from 'svelte-transitions'
|
||||
import { store } from '../../_utils/store'
|
||||
import { store } from '../../_store/store'
|
||||
|
||||
export default {
|
||||
oncreate() {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
}
|
||||
</style>
|
||||
<script>
|
||||
import { store } from '../../_utils/store'
|
||||
import { store } from '../../_store/store'
|
||||
import StatusListItem from './StatusListItem.html'
|
||||
import LoadingFooter from './LoadingFooter.html'
|
||||
import VirtualList from '../virtualList/VirtualList.html'
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
import { Store } from 'svelte/store'
|
||||
|
||||
const LS = process.browser && localStorage
|
||||
|
||||
export class LocalStorageStore extends Store {
|
||||
constructor(state, keysToWatch) {
|
||||
super(state)
|
||||
if (!process.browser) {
|
||||
return
|
||||
}
|
||||
this._keysToWatch = keysToWatch
|
||||
this._keysToSave = {}
|
||||
let newState = {}
|
||||
for (let i = 0, len = LS.length; i < len; i++) {
|
||||
let key = LS.key(i)
|
||||
if (key.startsWith('store_')) {
|
||||
let item = LS.getItem(key)
|
||||
newState[key.substring(6)] = item === 'undefined' ? undefined : JSON.parse(item)
|
||||
}
|
||||
}
|
||||
this.set(newState)
|
||||
this.onchange((state, changed) => {
|
||||
Object.keys(changed).forEach(change => {
|
||||
if (this._keysToWatch.has(change)) {
|
||||
this._keysToSave[change] = true
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
save() {
|
||||
if (!process.browser) {
|
||||
return
|
||||
}
|
||||
Object.keys(this._keysToSave).forEach(key => {
|
||||
LS.setItem(`store_${key}`, JSON.stringify(this.get(key)))
|
||||
})
|
||||
this._keysToSave = {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { instanceComputations } from './instanceComputations'
|
||||
import { timelineComputations } from './timelineComputations'
|
||||
|
||||
export function computations(store) {
|
||||
instanceComputations(store)
|
||||
timelineComputations(store)
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
export function instanceComputations(store) {
|
||||
store.compute(
|
||||
'isUserLoggedIn',
|
||||
['currentInstance', 'loggedInInstances'],
|
||||
(currentInstance, loggedInInstances) => !!(currentInstance && Object.keys(loggedInInstances).includes(currentInstance))
|
||||
)
|
||||
|
||||
store.compute(
|
||||
'loggedInInstancesAsList',
|
||||
['currentInstance', 'loggedInInstances', 'loggedInInstancesInOrder'],
|
||||
(currentInstance, loggedInInstances, loggedInInstancesInOrder) => {
|
||||
return loggedInInstancesInOrder.map(instanceName => {
|
||||
return Object.assign({
|
||||
current: currentInstance === instanceName,
|
||||
name: instanceName
|
||||
}, loggedInInstances[instanceName])
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
store.compute(
|
||||
'currentInstanceData',
|
||||
['currentInstance', 'loggedInInstances'],
|
||||
(currentInstance, loggedInInstances) => {
|
||||
return Object.assign({
|
||||
name: currentInstance
|
||||
}, loggedInInstances[currentInstance])
|
||||
})
|
||||
|
||||
store.compute(
|
||||
'accessToken',
|
||||
['currentInstanceData'],
|
||||
(currentInstanceData) => currentInstanceData.access_token
|
||||
)
|
||||
|
||||
store.compute(
|
||||
'currentTheme',
|
||||
['currentInstance', 'instanceThemes'],
|
||||
(currentInstance, instanceThemes) => {
|
||||
return instanceThemes[currentInstance] || 'default'
|
||||
}
|
||||
)
|
||||
|
||||
store.compute(
|
||||
'currentVerifyCredentials',
|
||||
['currentInstance', 'verifyCredentials'],
|
||||
(currentInstance, verifyCredentials) => verifyCredentials && verifyCredentials[currentInstance]
|
||||
)
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
function timelineMixins(Store) {
|
||||
Store.prototype.setForTimeline = function (instanceName, timelineName, obj) {
|
||||
let timelines = this.get('timelines') || {}
|
||||
let timelineData = timelines[instanceName] || {}
|
||||
timelineData[timelineName] = Object.assign(timelineData[timelineName] || {}, obj)
|
||||
timelines[instanceName] = timelineData
|
||||
this.set({timelines: timelines})
|
||||
}
|
||||
|
||||
Store.prototype.getForTimeline = function (instanceName, timelineName, key) {
|
||||
let timelines = this.get('timelines') || {}
|
||||
let timelineData = timelines[instanceName] || {}
|
||||
return (timelineData[timelineName] || {})[key]
|
||||
}
|
||||
}
|
||||
|
||||
export function mixins(Store) {
|
||||
timelineMixins(Store)
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { updateVerifyCredentialsForInstance } from '../settings/instances/_actions/[instanceName]'
|
||||
|
||||
export function storeObservers(store) {
|
||||
export function observers(store) {
|
||||
store.observe('currentInstance', (currentInstance) => {
|
||||
updateVerifyCredentialsForInstance(currentInstance)
|
||||
})
|
|
@ -0,0 +1,38 @@
|
|||
import { observers } from './obsevers'
|
||||
import { computations } from './computations'
|
||||
import { mixins } from './mixins'
|
||||
import { LocalStorageStore } from './LocalStorageStore'
|
||||
|
||||
const KEYS_TO_STORE_IN_LOCAL_STORAGE = new Set([
|
||||
"currentInstance",
|
||||
"currentRegisteredInstance",
|
||||
"currentRegisteredInstanceName",
|
||||
"instanceNameInSearch",
|
||||
"instanceThemes",
|
||||
"loggedInInstances",
|
||||
"loggedInInstancesInOrder"
|
||||
])
|
||||
|
||||
class PinaforeStore extends LocalStorageStore {
|
||||
constructor(state) {
|
||||
super(state, KEYS_TO_STORE_IN_LOCAL_STORAGE)
|
||||
}
|
||||
}
|
||||
|
||||
const store = new PinaforeStore({
|
||||
instanceNameInSearch: '',
|
||||
currentInstance: null,
|
||||
loggedInInstances: {},
|
||||
loggedInInstancesInOrder: [],
|
||||
instanceThemes: {}
|
||||
})
|
||||
|
||||
mixins(PinaforeStore)
|
||||
computations(store)
|
||||
observers(store)
|
||||
|
||||
if (process.browser && process.env.NODE_ENV !== 'production') {
|
||||
window.store = store // for debugging
|
||||
}
|
||||
|
||||
export { store }
|
|
@ -0,0 +1,11 @@
|
|||
export function timelineComputations(store) {
|
||||
store.compute('currentTimelineData', ['currentInstance', 'currentTimeline', 'timelines'],
|
||||
(currentInstance, currentTimeline, timelines) => {
|
||||
return ((timelines && timelines[currentInstance]) || {})[currentTimeline] || {}
|
||||
})
|
||||
|
||||
store.compute('statusIds', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.statusIds || [])
|
||||
store.compute('runningUpdate', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.runningUpdate)
|
||||
store.compute('initialized', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.initialized)
|
||||
store.compute('lastStatusId', ['statusIds'], (statusIds) => statusIds.length && statusIds[statusIds.length - 1])
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// from blob-util
|
||||
function blobToBinaryString(blob) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var reader = new FileReader();
|
||||
var hasBinaryString = typeof reader.readAsBinaryString === 'function';
|
||||
reader.onloadend = function (e) {
|
||||
var result = e.target.result || '';
|
||||
if (hasBinaryString) {
|
||||
return resolve(result);
|
||||
}
|
||||
resolve(arrayBufferToBinaryString(result));
|
||||
};
|
||||
reader.onerror = reject;
|
||||
if (hasBinaryString) {
|
||||
reader.readAsBinaryString(blob);
|
||||
} else {
|
||||
reader.readAsArrayBuffer(blob);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function blobToBase64(blob) {
|
||||
return blobToBinaryString(blob).then(function (binary) {
|
||||
// web-safe variant
|
||||
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||
});
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
import { Store } from 'svelte/store.js'
|
||||
import { storeObservers } from './storeObservers'
|
||||
|
||||
const LOCAL_STORAGE_KEYS = new Set([
|
||||
"currentInstance",
|
||||
"currentRegisteredInstance",
|
||||
"currentRegisteredInstanceName",
|
||||
"instanceNameInSearch",
|
||||
"instanceThemes",
|
||||
"loggedInInstances",
|
||||
"loggedInInstancesInOrder"
|
||||
])
|
||||
|
||||
const LS = process.browser && localStorage
|
||||
class PinaforeStore extends Store {
|
||||
|
||||
constructor(state) {
|
||||
super(state)
|
||||
if (process.browser) {
|
||||
this.keysToStore = {}
|
||||
let newState = {}
|
||||
for (let i = 0, len = LS.length; i < len; i++) {
|
||||
let key = LS.key(i)
|
||||
if (key.startsWith('store_')) {
|
||||
let item = LS.getItem(key)
|
||||
newState[key.substring(6)] = item === 'undefined' ? undefined : JSON.parse(item)
|
||||
}
|
||||
}
|
||||
this.set(newState)
|
||||
this.onchange((state, changed) => {
|
||||
Object.keys(changed).forEach(change => {
|
||||
if (LOCAL_STORAGE_KEYS.has(change)) {
|
||||
this.keysToStore[change] = true
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
save() {
|
||||
if (process.browser) {
|
||||
Object.keys(this.keysToStore).forEach(key => {
|
||||
LS.setItem(`store_${key}`, JSON.stringify(this.get(key)))
|
||||
})
|
||||
this.keysToStore = {}
|
||||
}
|
||||
}
|
||||
|
||||
setForTimeline(instanceName, timelineName, obj) {
|
||||
let timelines = this.get('timelines') || {}
|
||||
let timelineData = timelines[instanceName] || {}
|
||||
timelineData[timelineName] = Object.assign(timelineData[timelineName] || {}, obj)
|
||||
timelines[instanceName] = timelineData
|
||||
this.set({timelines: timelines})
|
||||
}
|
||||
|
||||
getForTimeline(instanceName, timelineName, key) {
|
||||
let timelines = this.get('timelines') || {}
|
||||
let timelineData = timelines[instanceName] || {}
|
||||
return (timelineData[timelineName] || {})[key]
|
||||
}
|
||||
}
|
||||
|
||||
const store = new PinaforeStore({
|
||||
instanceNameInSearch: '',
|
||||
currentRegisteredInstance: null,
|
||||
currentRegisteredInstanceName: '',
|
||||
currentInstance: null,
|
||||
loggedInInstances: {},
|
||||
loggedInInstancesInOrder: [],
|
||||
instanceThemes: {}
|
||||
})
|
||||
|
||||
store.compute(
|
||||
'isUserLoggedIn',
|
||||
['currentInstance', 'loggedInInstances'],
|
||||
(currentInstance, loggedInInstances) => !!(currentInstance && Object.keys(loggedInInstances).includes(currentInstance))
|
||||
)
|
||||
|
||||
store.compute(
|
||||
'loggedInInstancesAsList',
|
||||
['currentInstance', 'loggedInInstances', 'loggedInInstancesInOrder'],
|
||||
(currentInstance, loggedInInstances, loggedInInstancesInOrder) => {
|
||||
return loggedInInstancesInOrder.map(instanceName => {
|
||||
return Object.assign({
|
||||
current: currentInstance === instanceName,
|
||||
name: instanceName
|
||||
}, loggedInInstances[instanceName])
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
store.compute(
|
||||
'currentInstanceData',
|
||||
['currentInstance', 'loggedInInstances'],
|
||||
(currentInstance, loggedInInstances) => {
|
||||
return Object.assign({
|
||||
name: currentInstance
|
||||
}, loggedInInstances[currentInstance])
|
||||
})
|
||||
|
||||
store.compute(
|
||||
'accessToken',
|
||||
['currentInstanceData'],
|
||||
(currentInstanceData) => currentInstanceData.access_token
|
||||
)
|
||||
|
||||
store.compute(
|
||||
'currentTheme',
|
||||
['currentInstance', 'instanceThemes'],
|
||||
(currentInstance, instanceThemes) => {
|
||||
return instanceThemes[currentInstance] || 'default'
|
||||
}
|
||||
)
|
||||
|
||||
store.compute(
|
||||
'currentVerifyCredentials',
|
||||
['currentInstance', 'verifyCredentials'],
|
||||
(currentInstance, verifyCredentials) => verifyCredentials && verifyCredentials[currentInstance]
|
||||
)
|
||||
|
||||
store.compute('currentTimelineData', ['currentInstance', 'currentTimeline', 'timelines'],
|
||||
(currentInstance, currentTimeline, timelines) => {
|
||||
return ((timelines && timelines[currentInstance]) || {})[currentTimeline] || {}
|
||||
})
|
||||
|
||||
store.compute('statusIds', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.statusIds || [])
|
||||
store.compute('runningUpdate', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.runningUpdate)
|
||||
store.compute('initialized', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.initialized)
|
||||
store.compute('lastStatusId', ['statusIds'], (statusIds) => statusIds.length && statusIds[statusIds.length - 1])
|
||||
|
||||
storeObservers(store)
|
||||
|
||||
if (process.browser && process.env.NODE_ENV !== 'production') {
|
||||
window.store = store // for debugging
|
||||
}
|
||||
|
||||
export { store }
|
|
@ -32,7 +32,7 @@
|
|||
import Layout from '../_components/Layout.html'
|
||||
import LazyTimeline from '../_components/timeline/LazyTimeline.html'
|
||||
import FreeTextLayout from '../_components/FreeTextLayout.html'
|
||||
import { store } from '../_utils/store.js'
|
||||
import { store } from '../_store/store.js'
|
||||
import HiddenFromSSR from '../_components/HiddenFromSSR'
|
||||
import DynamicPageBanner from '../_components/DynamicPageBanner.html'
|
||||
import { updateProfileAndRelationship } from './_actions/[accountId]'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { getAccount, getRelationship } from '../../_utils/mastodon/user'
|
||||
import { database } from '../../_utils/database/database'
|
||||
import { store } from '../../_utils/store'
|
||||
import { store } from '../../_store/store'
|
||||
|
||||
async function updateAccount(accountId, instanceName, accessToken) {
|
||||
let localPromise = database.getAccount(instanceName, accountId)
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import Layout from './_components/Layout.html'
|
||||
import LazyTimeline from './_components/timeline/LazyTimeline.html'
|
||||
import FreeTextLayout from './_components/FreeTextLayout.html'
|
||||
import { store } from './_utils/store.js'
|
||||
import { store } from './_store/store.js'
|
||||
import HiddenFromSSR from './_components/HiddenFromSSR'
|
||||
|
||||
export default {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
import Layout from './_components/Layout.html'
|
||||
import NotLoggedInHome from './_components/NotLoggedInHome.html'
|
||||
import LazyTimeline from './_components/timeline/LazyTimeline.html'
|
||||
import { store } from './_utils/store.js'
|
||||
import { store } from './_store/store.js'
|
||||
|
||||
export default {
|
||||
store: () => store,
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import Layout from './_components/Layout.html'
|
||||
import LazyTimeline from './_components/timeline/LazyTimeline.html'
|
||||
import FreeTextLayout from './_components/FreeTextLayout.html'
|
||||
import { store } from './_utils/store.js'
|
||||
import { store } from './_store/store.js'
|
||||
import HiddenFromSSR from './_components/HiddenFromSSR'
|
||||
|
||||
export default {
|
||||
|
|
|
@ -94,7 +94,7 @@
|
|||
}
|
||||
</style>
|
||||
<script>
|
||||
import { store } from '../../_utils/store'
|
||||
import { store } from '../../_store/store'
|
||||
import Layout from '../../_components/Layout.html'
|
||||
import SettingsLayout from '../_components/SettingsLayout.html'
|
||||
import {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { getVerifyCredentials } from '../../../_utils/mastodon/user'
|
||||
import { store } from '../../../_utils/store'
|
||||
import { store } from '../../../_store/store'
|
||||
import { switchToTheme } from '../../../_utils/themeEngine'
|
||||
import { toast } from '../../../_utils/toast'
|
||||
import { database } from '../../../_utils/database/database'
|
||||
|
|
|
@ -3,7 +3,7 @@ import { getInstanceInfo } from '../../../_utils/mastodon/instance'
|
|||
import { goto } from 'sapper/runtime.js'
|
||||
import { switchToTheme } from '../../../_utils/themeEngine'
|
||||
import { database } from '../../../_utils/database/database'
|
||||
import { store } from '../../../_utils/store'
|
||||
import { store } from '../../../_store/store'
|
||||
import { updateVerifyCredentialsForInstance } from './[instanceName]'
|
||||
|
||||
const REDIRECT_URI = (typeof location !== 'undefined' ?
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
<script>
|
||||
import Layout from '../../_components/Layout.html';
|
||||
import SettingsLayout from '../_components/SettingsLayout.html'
|
||||
import { store } from '../../_utils/store'
|
||||
import { store } from '../../_store/store'
|
||||
import LoadingMask from '../../_components/LoadingMask'
|
||||
import { logInToInstance, handleOauthCode } from './_actions/add'
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
</SettingsLayout>
|
||||
</Layout>
|
||||
<script>
|
||||
import { store } from '../../_utils/store'
|
||||
import { store } from '../../_store/store'
|
||||
import Layout from '../../_components/Layout.html'
|
||||
import SettingsLayout from '../_components/SettingsLayout.html'
|
||||
import SettingsList from '../_components/SettingsList.html'
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
import Layout from '../_components/Layout.html'
|
||||
import LazyTimeline from '../_components/timeline/LazyTimeline.html'
|
||||
import FreeTextLayout from '../_components/FreeTextLayout.html'
|
||||
import { store } from '../_utils/store.js'
|
||||
import { store } from '../_store/store.js'
|
||||
import HiddenFromSSR from '../_components/HiddenFromSSR'
|
||||
import DynamicPageBanner from '../_components/DynamicPageBanner.html'
|
||||
|
||||
|
|
Loading…
Reference in New Issue