refs #2500 Change settings database to sqlite3

This commit is contained in:
AkiraFukushima 2023-01-01 18:02:52 +09:00
parent 8feee0c4c7
commit 30cb45d967
No known key found for this signature in database
GPG Key ID: B6E51BAC4DE1A957
18 changed files with 296 additions and 418 deletions

View File

@ -1,22 +1,7 @@
import { Setting, Timeline, UnreadNotification, UseMarker } from '~/src/types/setting'
const unreadNotification: UnreadNotification = {
direct: false,
local: true,
public: false
}
const useMarker: UseMarker = {
home: false,
notifications: true
}
const timeline: Timeline = {
unreadNotification: unreadNotification,
useMarker: useMarker
}
import { Setting } from '~/src/types/setting'
export const DefaultSetting: Setting = {
accountID: '',
timeline: timeline
accountId: 0,
markerHome: false,
markerNotifications: true
}

View File

@ -23,7 +23,7 @@ export const insertAccount = (
}
let order = 1
if (row) {
order = row.order
order = row.sort + 1
}
db.run(
'INSERT INTO accounts(username, account_id, avatar, client_id, client_secret, access_token, refresh_token, sort) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
@ -161,3 +161,115 @@ FROM accounts INNER JOIN servers ON servers.account_id = accounts.id WHERE accou
)
})
}
export const removeAccount = (db: sqlite3.Database, id: number): Promise<null> => {
return new Promise((resolve, reject) => {
db.run('DELETE FROM accounts WHERE id = ?', id, err => {
if (err) {
reject(err)
}
resolve(null)
})
})
}
export const removeAllAccounts = (db: sqlite3.Database): Promise<null> => {
return new Promise((resolve, reject) => {
db.run('DELETE FROM accounts', err => {
if (err) {
reject(err)
}
resolve(null)
})
})
}
export const forwardAccount = (db: sqlite3.Database, account: LocalAccount): Promise<LocalAccount> => {
return new Promise((resolve, reject) => {
db.serialize(() => {
db.run('BEGIN TRANSACTION')
db.all('SELECT * FROM accounts ORDER BY sort', (err, rows) => {
if (err) {
reject(err)
}
const index = rows.findIndex(r => r.id === account.id)
if (index < 0 || index >= rows.length) {
db.run('ROLLBACK TRANSACTION')
return resolve(account)
}
const target = rows[index + 1]
const base = rows[index]
db.serialize(() => {
db.run('UPDATE accounts SET sort = ? WHERE id = ?', [-100, base.id], err => {
if (err) {
reject(err)
}
})
db.run('UPDATE accounts SET sort = ? WHERE id = ?', [base.sort, target.id], err => {
if (err) {
reject(err)
}
})
db.run('UPDATE accounts SET sort = ? WHERE id = ?', [target.sort, base.id], err => {
if (err) {
reject(err)
}
db.run('COMMIT')
resolve(
Object.assign(account, {
order: target.sort
})
)
})
})
})
})
})
}
export const backwardAccount = (db: sqlite3.Database, account: LocalAccount): Promise<LocalAccount> => {
return new Promise((resolve, reject) => {
db.serialize(() => {
db.run('BEGIN TRANSACTION')
db.all('SELECT * FROM accounts ORDER BY sort', (err, rows) => {
if (err) {
reject(err)
}
const index = rows.findIndex(r => r.id === account.id)
if (index < 1) {
return resolve(account)
}
const target = rows[index - 1]
const base = rows[index]
db.serialize(() => {
db.run('UPDATE accounts SET sort = ? WHERE id = ?', [-100, base.id], err => {
if (err) {
db.run('ROLLBACK TRANSACTION')
reject(err)
}
})
db.run('UPDATE accounts SET sort = ? WHERE id = ?', [base.sort, target.id], err => {
if (err) {
reject(err)
}
})
db.run('UPDATE accounts SET sort = ? WHERE id = ?', [target.sort, base.id], err => {
if (err) {
reject(err)
}
db.run('COMMIT')
resolve(
Object.assign(account, {
order: target.sort
})
)
})
})
})
})
})
}

View File

@ -1,48 +0,0 @@
import { isEmpty } from 'lodash'
import Datastore from 'nedb'
import fs from 'fs'
import { CachedAccount } from '~/src/types/cachedAccount'
export default class AccountCache {
private db: Datastore
constructor(path: string) {
this.db = new Datastore({
filename: path,
autoload: true,
onload: (err: Error) => {
if (err) {
fs.unlink(path, err => {
if (err) {
console.error(err)
}
})
}
}
})
}
listAccounts(ownerID: string): Promise<Array<CachedAccount>> {
return new Promise((resolve, reject) => {
this.db.find<CachedAccount>({ owner_id: ownerID }, (err, docs) => {
if (err) return reject(err)
resolve(docs)
})
})
}
insertAccount(ownerID: string, acct: string): Promise<CachedAccount> {
return new Promise((resolve, reject) => {
// At first confirm records for unique.
this.db.findOne<CachedAccount>({ owner_id: ownerID, acct: acct }, (err, doc) => {
if (err) return err
// Ignore error for unique constraints.
if (!isEmpty(doc)) return err
return this.db.insert<CachedAccount>({ owner_id: ownerID, acct: acct }, (err, doc) => {
if (err) return reject(err)
return resolve(doc)
})
})
})
}
}

View File

@ -1,45 +0,0 @@
import Datastore from 'nedb'
import fs from 'fs'
import { LocalTag } from '~/src/types/localTag'
export default class HashtagCache {
private db: Datastore
constructor(path: string) {
this.db = new Datastore({
filename: path,
autoload: true,
onload: (err: Error) => {
if (err) {
fs.unlink(path, err => {
if (err) {
console.error(err)
}
})
}
}
})
this.db.ensureIndex({ fieldName: 'tagName', unique: true, sparse: true }, err => {
if (err) console.error(err)
})
}
listTags(): Promise<Array<LocalTag>> {
return new Promise((resolve, reject) => {
this.db.find<LocalTag>({}, (err, docs) => {
if (err) return reject(err)
resolve(docs)
})
})
}
insertHashtag(tag: string): Promise<LocalTag> {
return new Promise((resolve, reject) => {
// Ignore error for unique constraints.
this.db.insert({ tagName: tag }, (err, doc) => {
if (err) return reject(err)
resolve(doc)
})
})
}
}

View File

@ -48,6 +48,19 @@ FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE)',
}
}
)
db.run(
'CREATE TABLE IF NOT EXISTS settings(\
id INTEGER PRIMARY KEY, \
account_id INTEGER UNIQUE NOT NULL, \
marker_home BOOLEAN NOT NULL DEFAULT false, \
marker_notifications BOOLEAN NOT NULL DEFAULT true, \
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE)',
err => {
if (err) {
console.error('failed to create settings: ', err)
}
}
)
})
return db

View File

@ -30,7 +30,7 @@ import sanitizeHtml from 'sanitize-html'
import AutoLaunch from 'auto-launch'
import minimist from 'minimist'
import { getAccount, insertAccount, listAccounts } from './account'
import { backwardAccount, forwardAccount, getAccount, insertAccount, listAccounts, removeAccount, removeAllAccounts } from './account'
// import { StreamingURL, UserStreaming, DirectStreaming, LocalStreaming, PublicStreaming, ListStreaming, TagStreaming } from './websocket'
import Preferences from './preferences'
import Fonts from './fonts'
@ -46,11 +46,11 @@ import ProxyConfiguration from './proxy'
import { Menu as MenuPreferences } from '~/src/types/preference'
import { General as GeneralPreferences } from '~/src/types/preference'
import newDB from './database'
import Settings from './settings'
import { BaseSettings, Setting } from '~/src/types/setting'
import { Setting } from '~/src/types/setting'
import { insertServer } from './server'
import { LocalServer } from '~src/types/localServer'
import { insertTag, listTags, removeTag } from './hashtags'
import { createOrUpdateSetting, getSetting } from './settings'
/**
* Context menu
@ -113,7 +113,6 @@ const splashURL =
? path.resolve(__dirname, '../../static/splash-screen.html')
: path.join(__dirname, '/static/splash-screen.html')
// https://github.com/louischatriot/nedb/issues/459
const userData = app.getPath('userData')
const appPath = app.getPath('exe')
@ -122,8 +121,6 @@ const db = newDB(databasePath)
const preferencesDBPath = process.env.NODE_ENV === 'production' ? userData + './db/preferences.json' : 'preferences.json'
const settingsDBPath = process.env.NODE_ENV === 'production' ? userData + './db/settings.json' : 'settings.json'
const soundBasePath =
process.env.NODE_ENV === 'development' ? path.join(__dirname, '../../build/sounds/') : path.join(process.resourcesPath!, 'build/sounds/')
const iconBasePath =
@ -145,7 +142,7 @@ if (process.platform !== 'darwin') {
})
}
async function changeAccount([account, _server]: [LocalAccount, LocalServer], index: number) {
async function changeAccount(account: LocalAccount, index: number) {
// Sometimes application is closed to tray.
// In this time, mainWindow in not exist, so we have to create window.
if (mainWindow === null) {
@ -229,7 +226,7 @@ async function createWindow() {
return {
label: s.domain,
accelerator: `CmdOrCtrl+${index + 1}`,
click: () => changeAccount([a, s], index)
click: () => changeAccount(a, index)
}
})
@ -497,66 +494,52 @@ ipcMain.handle('get-local-account', async (_: IpcMainInvokeEvent, id: number) =>
return account
})
// ipcMain.handle('update-account', async (_: IpcMainInvokeEvent, acct: LocalAccount) => {
// const proxy = await proxyConfiguration.forMastodon()
// const ac: LocalAccount = await accountRepo.refresh(acct, proxy)
// return ac
// })
ipcMain.handle('remove-account', async (_: IpcMainInvokeEvent, id: number) => {
await removeAccount(db, id)
// ipcMain.handle('remove-account', async (_: IpcMainInvokeEvent, id: string) => {
// const accountId = await accountRepo.removeAccount(id)
const accounts = await listAccounts(db)
const accountsChange: Array<MenuItemConstructorOptions> = accounts.map(([account, server], index) => {
return {
label: server.domain,
accelerator: `CmdOrCtrl+${index + 1}`,
click: () => changeAccount(account, index)
}
})
// const accounts = await listAccounts()
// const accountsChange: Array<MenuItemConstructorOptions> = accounts.map((a, index) => {
// return {
// label: a.domain,
// accelerator: `CmdOrCtrl+${index + 1}`,
// click: () => changeAccount(a, index)
// }
// })
await updateApplicationMenu(accountsChange)
await updateDockMenu(accountsChange)
if (process.platform !== 'darwin' && tray !== null) {
tray.setContextMenu(TrayMenu(accountsChange, i18next))
}
// await updateApplicationMenu(accountsChange)
// await updateDockMenu(accountsChange)
// if (process.platform !== 'darwin' && tray !== null) {
// tray.setContextMenu(TrayMenu(accountsChange, i18next))
// }
// TODO: stopUserStreaming(accountId)
})
// stopUserStreaming(accountId)
// })
ipcMain.handle('forward-account', async (_: IpcMainInvokeEvent, acct: LocalAccount) => {
await forwardAccount(db, acct)
})
// ipcMain.handle('forward-account', async (_: IpcMainInvokeEvent, acct: LocalAccount) => {
// await accountRepo.forwardAccount(acct)
// })
ipcMain.handle('backward-account', async (_: IpcMainInvokeEvent, acct: LocalAccount) => {
await backwardAccount(db, acct)
})
// ipcMain.handle('backward-account', async (_: IpcMainInvokeEvent, acct: LocalAccount) => {
// await accountRepo.backwardAccount(acct)
// })
ipcMain.handle('remove-all-accounts', async (_: IpcMainInvokeEvent) => {
await removeAllAccounts(db)
const accounts = await listAccounts(db)
const accountsChange: Array<MenuItemConstructorOptions> = accounts.map(([account, server], index) => {
return {
label: server.domain,
accelerator: `CmdOrCtrl+${index + 1}`,
click: () => changeAccount(account, index)
}
})
// ipcMain.handle('refresh-accounts', async (_: IpcMainInvokeEvent) => {
// const proxy = await proxyConfiguration.forMastodon()
// const accounts = await accountRepo.refreshAccounts(proxy)
// return accounts
// })
// ipcMain.handle('remove-all-accounts', async (_: IpcMainInvokeEvent) => {
// await accountRepo.removeAll()
// const accounts = await listAccounts()
// const accountsChange: Array<MenuItemConstructorOptions> = accounts.map((a, index) => {
// return {
// label: a.domain,
// accelerator: `CmdOrCtrl+${index + 1}`,
// click: () => changeAccount(a, index)
// }
// })
// await updateApplicationMenu(accountsChange)
// await updateDockMenu(accountsChange)
// if (process.platform !== 'darwin' && tray !== null) {
// tray.setContextMenu(TrayMenu(accountsChange, i18next))
// }
// })
await updateApplicationMenu(accountsChange)
await updateDockMenu(accountsChange)
if (process.platform !== 'darwin' && tray !== null) {
tray.setContextMenu(TrayMenu(accountsChange, i18next))
}
})
ipcMain.handle('change-auto-launch', async (_: IpcMainInvokeEvent, enable: boolean) => {
if (launcher) {
@ -1072,7 +1055,7 @@ ipcMain.handle('change-language', async (_: IpcMainInvokeEvent, value: string) =
return {
label: s.domain,
accelerator: `CmdOrCtrl+${index + 1}`,
click: () => changeAccount([a, s], index)
click: () => changeAccount(a, index)
}
})
@ -1139,18 +1122,17 @@ ipcMain.handle('list-fonts', async (_: IpcMainInvokeEvent) => {
// Settings
ipcMain.handle(
'get-account-setting',
async (_: IpcMainInvokeEvent, accountID: string): Promise<Setting> => {
const settings = new Settings(settingsDBPath)
const setting = await settings.get(accountID)
async (_: IpcMainInvokeEvent, accountId: number): Promise<Setting> => {
const setting = await getSetting(db, accountId)
return setting
}
)
ipcMain.handle(
'update-account-setting',
async (_: IpcMainInvokeEvent, setting: Setting): Promise<BaseSettings> => {
const settings = new Settings(settingsDBPath)
const res = await settings.update(setting)
async (_: IpcMainInvokeEvent, setting: Setting): Promise<Setting> => {
console.log(setting)
const res = await createOrUpdateSetting(db, setting)
return res
}
)

View File

@ -1,80 +1,55 @@
import storage from 'electron-json-storage'
import log from 'electron-log'
import objectAssignDeep from 'object-assign-deep'
import { BaseSettings, Setting } from '~/src/types/setting'
import sqlite3 from 'sqlite3'
import { Setting } from '~/src/types/setting'
import { DefaultSetting } from '~/src/constants/initializer/setting'
import { isEmpty } from 'lodash'
export default class Settings {
private path: string
constructor(path: string) {
this.path = path
}
public async _load(): Promise<BaseSettings> {
try {
const settings = await this._get()
if (isEmpty(settings)) {
return []
export const getSetting = (db: sqlite3.Database, accountId: number): Promise<Setting> => {
return new Promise((resolve, reject) => {
db.get('SELECT * FROM settings WHERE account_id = ?', accountId, (err, row) => {
if (err) {
reject(err)
}
return settings
} catch (err) {
log.error(err)
return []
}
}
public async get(accountID: string): Promise<Setting> {
const current = await this._load()
const find: Setting | undefined = current.find(d => {
return d.accountID === accountID
if (row) {
resolve({
accountId: row.account_id,
markerHome: Boolean(row.marker_home),
markerNotifications: Boolean(row.marker_notifications)
})
}
resolve(DefaultSetting)
})
if (find) {
return objectAssignDeep({}, DefaultSetting, find)
}
return objectAssignDeep({}, DefaultSetting, {
accountID: accountID
})
}
private _get(): Promise<BaseSettings> {
return new Promise((resolve, reject) => {
storage.get(this.path, (err, data) => {
if (err) return reject(err)
return resolve(data as BaseSettings)
})
})
}
private _save(data: BaseSettings): Promise<BaseSettings> {
return new Promise((resolve, reject) => {
storage.set(this.path, data, err => {
if (err) return reject(err)
return resolve(data)
})
})
}
public async update(obj: Setting): Promise<BaseSettings> {
const current = await this._load()
const find = current.find(d => {
return d.accountID === obj.accountID
})
if (find) {
const data = current.map(d => {
if (d.accountID !== obj.accountID) {
return d
}
const newData = objectAssignDeep({}, d, obj)
return newData
})
const result = await this._save(data)
return result
} else {
const data = current.concat([obj])
const result = await this._save(data)
return result
}
}
})
}
export const createOrUpdateSetting = (db: sqlite3.Database, setting: Setting): Promise<Setting> => {
return new Promise((resolve, reject) => {
db.get('SELECT * FROM settings WHERE account_id = ?', setting.accountId, (err, row) => {
if (err) {
reject(err)
}
if (row) {
db.run(
'UPDATE settings SET marker_home = ?, marker_notifications = ? WHERE account_id = ?',
[setting.markerHome, setting.markerNotifications, setting.accountId],
err => {
if (err) {
reject(err)
}
resolve(setting)
}
)
resolve(setting)
} else {
db.run(
'INSERT INTO settings(account_id, marker_home, marker_notifications) VALUES (?, ?, ?)',
[setting.accountId, setting.markerHome, setting.markerNotifications],
function (err) {
if (err) {
reject(err)
}
resolve(setting)
}
)
}
})
})
}

View File

@ -64,7 +64,7 @@ export default defineComponent({
const activeRoute = computed(() => route.path)
onMounted(() => {
store.commit(`${space}/${MUTATION_TYPES.CHANGE_ACCOUNT_ID}`, id.value)
store.commit(`${space}/${MUTATION_TYPES.CHANGE_ACCOUNT_ID}`, parseInt(id.value as string))
router.push(`/${id.value}/settings/general`)
})

View File

@ -1,23 +1,6 @@
<template>
<div id="timeline">
<h2>{{ $t('settings.timeline.title') }}</h2>
<el-form class="unread-notification section" size="default" label-position="right" label-width="250px">
<h3>{{ $t('settings.timeline.unread_notification.title') }}</h3>
<p class="description">
{{ $t('settings.timeline.unread_notification.description') }}
</p>
<el-form-item for="direct" :label="$t('settings.timeline.unread_notification.direct')">
<el-switch v-model="directNotify" id="direct" />
</el-form-item>
<el-form-item for="local" :label="$t('settings.timeline.unread_notification.local')">
<el-switch v-model="localNotify" id="local" />
</el-form-item>
<el-form-item for="public" :label="$t('settings.timeline.unread_notification.public')">
<el-switch v-model="publicNotify" id="public" />
</el-form-item>
</el-form>
<el-form class="use-marker section" size="default" label-position="right" label-width="250px">
<h3>{{ $t('settings.timeline.use_marker.title') }}</h3>
<el-form-item for="marker_home" :label="$t('settings.timeline.use_marker.home')">
@ -41,39 +24,18 @@ export default defineComponent({
const space = 'Settings/Timeline'
const store = useStore()
const directNotify = computed({
get: () => store.state.Settings.Timeline.setting.unreadNotification.direct,
set: value =>
store.dispatch(`${space}/${ACTION_TYPES.CHANGE_UNREAD_NOTIFICATION}`, {
direct: value
})
})
const localNotify = computed({
get: () => store.state.Settings.Timeline.setting.unreadNotification.local,
set: value =>
store.dispatch(`${space}/${ACTION_TYPES.CHANGE_UNREAD_NOTIFICATION}`, {
local: value
})
})
const publicNotify = computed({
get: () => store.state.Settings.Timeline.setting.unreadNotification.public,
set: value =>
store.dispatch(`${space}/${ACTION_TYPES.CHANGE_UNREAD_NOTIFICATION}`, {
public: value
})
})
const marker_home = computed({
get: () => store.state.Settings.Timeline.setting.useMarker.home,
get: () => store.state.Settings.Timeline.setting.markerHome,
set: value =>
store.dispatch(`${space}/${ACTION_TYPES.CHANGE_USER_MARKER}`, {
home: value
markerHome: value
})
})
const marker_notifications = computed({
get: () => store.state.Settings.Timeline.setting.useMarker.notifications,
get: () => store.state.Settings.Timeline.setting.markerNotifications,
set: value =>
store.dispatch(`${space}/${ACTION_TYPES.CHANGE_USER_MARKER}`, {
notifications: value
markerNotifications: value
})
})
@ -82,9 +44,6 @@ export default defineComponent({
})
return {
directNotify,
localNotify,
publicNotify,
marker_home,
marker_notifications
}

View File

@ -299,7 +299,7 @@ export default defineComponent({
onMounted(() => {
store.dispatch(`${space}/${ACTION_TYPES.READ_COLLAPSE}`)
store.dispatch(`${space}/${ACTION_TYPES.LIST_TAGS}`)
store.dispatch(`${space}/${ACTION_TYPES.LIST_TAGS}`, parseInt(id.value as string))
})
const handleProfile = (command: string) => {

View File

@ -149,7 +149,7 @@
<el-button v-if="quoteSupported" link class="quote-btn" @click="openQuote()">
<font-awesome-icon icon="quote-right" size="sm" />
</el-button>
<template v-if="sns !== 'mastodon'">
<template v-if="server!.sns !== 'mastodon'">
<el-popover
placement="bottom"
width="281"
@ -300,7 +300,7 @@ export default defineComponent({
default: false
}
},
emits: ['selectToot', 'focusRight', 'focusLeft'],
emits: ['selectToot', 'focusRight', 'focusLeft', 'update', 'delete', 'sizeChanged'],
setup(props, ctx) {
const space = 'organisms/Toot'
const store = useStore()
@ -320,7 +320,7 @@ export default defineComponent({
const displayNameStyle = computed(() => store.state.App.displayNameStyle)
const timeFormat = computed(() => store.state.App.timeFormat)
const language = computed(() => store.state.App.language)
const sns = computed(() => store.state.TimelineSpace.sns)
const server = computed(() => store.state.TimelineSpace.server)
const account = computed(() => store.state.TimelineSpace.account)
const bookmarkSupported = computed(() => store.state.TimelineSpace.SideMenu.enabledTimelines.bookmark)
const shortcutEnabled = computed(() => focused.value && !overlaid.value)
@ -352,7 +352,7 @@ export default defineComponent({
return null
})
const isMyMessage = computed(() => {
return store.state.TimelineSpace.account.accountId === originalMessage.value.account.id
return account.value!.accountId === originalMessage.value.account.id
})
const application = computed(() => {
const msg = originalMessage.value
@ -386,7 +386,7 @@ export default defineComponent({
return originalMessage.value.visibility === 'direct'
})
const quoteSupported = computed(() => {
return QuoteSupported(sns.value, account.value.domain)
return QuoteSupported(server.value!.sns, server.value!.domain)
})
whenever(logicAnd(l, shortcutEnabled), () => {
@ -677,7 +677,7 @@ export default defineComponent({
displayNameStyle,
timeFormat,
language,
sns,
server,
account,
bookmarkSupported,
originalMessage,

View File

@ -5,20 +5,20 @@ import { Module, MutationTree } from 'vuex'
import { RootState } from '@/store'
export type SettingsState = {
accountID: string | null
accountId: number | null
}
const state = (): SettingsState => ({
accountID: null
accountId: null
})
export const MUTATION_TYPES = {
CHANGE_ACCOUNT_ID: 'changeAccountID'
CHANGE_ACCOUNT_ID: 'changeAccountId'
}
const mutations: MutationTree<SettingsState> = {
[MUTATION_TYPES.CHANGE_ACCOUNT_ID]: (state, id: string) => {
state.accountID = id
[MUTATION_TYPES.CHANGE_ACCOUNT_ID]: (state, id: number) => {
state.accountId = id
}
}

View File

@ -1,18 +1,17 @@
import { Module, MutationTree, ActionTree } from 'vuex'
import { toRaw } from 'vue'
import { RootState } from '@/store'
import { MyWindow } from '~/src/types/global'
import { Setting, UnreadNotification, Timeline as TimelineSetting, UseMarker } from '~src/types/setting'
import { Setting } from '~src/types/setting'
import { DefaultSetting } from '~/src/constants/initializer/setting'
const win = window as any as MyWindow
const win = (window as any) as MyWindow
export type TimelineState = {
setting: TimelineSetting
setting: Setting
}
const state = (): TimelineState => ({
setting: DefaultSetting.timeline
setting: DefaultSetting
})
export const MUTATION_TYPES = {
@ -20,7 +19,7 @@ export const MUTATION_TYPES = {
}
const mutations: MutationTree<TimelineState> = {
[MUTATION_TYPES.UPDATE_TIMELINE_SETTING]: (state, setting: TimelineSetting) => {
[MUTATION_TYPES.UPDATE_TIMELINE_SETTING]: (state, setting: Setting) => {
state.setting = setting
}
}
@ -33,34 +32,16 @@ export const ACTION_TYPES = {
const actions: ActionTree<TimelineState, RootState> = {
[ACTION_TYPES.LOAD_TIMELINE_SETTING]: async ({ commit, rootState }): Promise<boolean> => {
const setting: Setting = await win.ipcRenderer.invoke('get-account-setting', rootState.Settings.accountID)
commit(MUTATION_TYPES.UPDATE_TIMELINE_SETTING, setting.timeline)
return true
},
[ACTION_TYPES.CHANGE_UNREAD_NOTIFICATION]: async ({ dispatch, state, rootState }, timeline: { key: boolean }): Promise<boolean> => {
const unread: UnreadNotification = Object.assign({}, state.setting.unreadNotification, timeline)
const tl: TimelineSetting = Object.assign({}, toRaw(state.setting), {
unreadNotification: unread
})
const setting: Setting = {
accountID: rootState.Settings.accountID!,
timeline: tl
}
await win.ipcRenderer.invoke('update-account-setting', setting)
dispatch('loadTimelineSetting')
const setting: Setting = await win.ipcRenderer.invoke('get-account-setting', rootState.Settings.accountId)
commit(MUTATION_TYPES.UPDATE_TIMELINE_SETTING, setting)
return true
},
[ACTION_TYPES.CHANGE_USER_MARKER]: async ({ dispatch, state, rootState }, timeline: { key: boolean }) => {
const marker: UseMarker = Object.assign({}, state.setting.useMarker, timeline)
const tl: TimelineSetting = Object.assign({}, toRaw(state.setting), {
useMarker: marker
})
const setting: Setting = {
accountID: rootState.Settings.accountID!,
timeline: tl
}
const setting: Setting = Object.assign({}, state.setting, timeline)
setting.accountId = rootState.Settings.accountId!
console.log(setting)
await win.ipcRenderer.invoke('update-account-setting', setting)
dispatch('loadTimelineSetting')
dispatch(ACTION_TYPES.LOAD_TIMELINE_SETTING)
return true
}
}

View File

@ -9,9 +9,9 @@ import { RootState } from '@/store'
import { AccountLoadError } from '@/errors/load'
import { TimelineFetchError } from '@/errors/fetch'
import { MyWindow } from '~/src/types/global'
import { Timeline, Setting } from '~src/types/setting'
import { DefaultSetting } from '~/src/constants/initializer/setting'
import { LocalServer } from '~src/types/localServer'
import { Setting } from '~/src/types/setting'
import { DefaultSetting } from '~/src/constants/initializer/setting'
const win = (window as any) as MyWindow
@ -21,8 +21,8 @@ export type TimelineSpaceState = {
loading: boolean
emojis: Array<Entity.Emoji>
tootMax: number
timelineSetting: Timeline
filters: Array<Entity.Filter>
setting: Setting
}
const state = (): TimelineSpaceState => ({
@ -31,8 +31,8 @@ const state = (): TimelineSpaceState => ({
loading: false,
emojis: [],
tootMax: 500,
timelineSetting: DefaultSetting.timeline,
filters: []
filters: [],
setting: DefaultSetting
})
export const MUTATION_TYPES = {
@ -41,8 +41,8 @@ export const MUTATION_TYPES = {
CHANGE_LOADING: 'changeLoading',
UPDATE_EMOJIS: 'updateEmojis',
UPDATE_TOOT_MAX: 'updateTootMax',
UPDATE_TIMELINE_SETTING: 'updateTimelineSetting',
UPDATE_FILTERS: 'updateFilters'
UPDATE_FILTERS: 'updateFilters',
UPDATE_SETTING: 'updateSetting'
}
const mutations: MutationTree<TimelineSpaceState> = {
@ -65,11 +65,11 @@ const mutations: MutationTree<TimelineSpaceState> = {
state.tootMax = 500
}
},
[MUTATION_TYPES.UPDATE_TIMELINE_SETTING]: (state, setting: Timeline) => {
state.timelineSetting = setting
},
[MUTATION_TYPES.UPDATE_FILTERS]: (state, filters: Array<Entity.Filter>) => {
state.filters = filters
},
[MUTATION_TYPES.UPDATE_SETTING]: (state, setting: Setting) => {
state.setting = setting
}
}
@ -84,7 +84,6 @@ export const ACTION_TYPES = {
FETCH_EMOJIS: 'fetchEmojis',
FETCH_FILTERS: 'fetchFilters',
FETCH_INSTANCE: 'fetchInstance',
LOAD_TIMELINE_SETTING: 'loadTimelineSetting',
FETCH_CONTENTS_TIMELINES: 'fetchContentsTimelines',
CLEAR_CONTENTS_TIMELINES: 'clearContentsTimelines',
BIND_STREAMINGS: 'bindStreamings',
@ -92,7 +91,8 @@ export const ACTION_TYPES = {
BIND_LOCAL_STREAMING: 'bindLocalStreaming',
BIND_PUBLIC_STREAMING: 'bindPublicStreaming',
BIND_DIRECT_MESSAGES_STREAMING: 'bindDirectMessagesStreaming',
UPDATE_TOOT_FOR_ALL_TIMELINES: 'updateTootForAllTimelines'
UPDATE_TOOT_FOR_ALL_TIMELINES: 'updateTootForAllTimelines',
LOAD_SETTING: 'loadSetting'
}
const actions: ActionTree<TimelineSpaceState, RootState> = {
@ -107,7 +107,7 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
dispatch('TimelineSpace/SideMenu/fetchLists', null, { root: true })
dispatch('TimelineSpace/SideMenu/fetchFollowRequests', null, { root: true })
dispatch('TimelineSpace/SideMenu/confirmTimelines', null, { root: true })
await dispatch(ACTION_TYPES.LOAD_TIMELINE_SETTING, accountId)
await dispatch(ACTION_TYPES.LOAD_SETTING)
await dispatch(ACTION_TYPES.FETCH_FILTERS)
commit(MUTATION_TYPES.CHANGE_LOADING, false)
await dispatch(ACTION_TYPES.FETCH_CONTENTS_TIMELINES).catch(_ => {
@ -170,6 +170,10 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
commit(MUTATION_TYPES.UPDATE_EMOJIS, res.data)
return res.data
},
[ACTION_TYPES.LOAD_SETTING]: async ({ commit, state }) => {
const setting: Setting = await win.ipcRenderer.invoke('get-account-setting', state.account!.id)
commit(MUTATION_TYPES.UPDATE_SETTING, setting)
},
/**
* fetchFilters
*/
@ -197,11 +201,7 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
}
return true
},
[ACTION_TYPES.LOAD_TIMELINE_SETTING]: async ({ commit }, accountID: string) => {
const setting: Setting = await win.ipcRenderer.invoke('get-account-setting', accountID)
commit(MUTATION_TYPES.UPDATE_TIMELINE_SETTING, setting.timeline)
},
[ACTION_TYPES.FETCH_CONTENTS_TIMELINES]: async ({ dispatch, state }) => {
[ACTION_TYPES.FETCH_CONTENTS_TIMELINES]: async ({ dispatch }) => {
dispatch('TimelineSpace/Contents/changeLoading', true, { root: true })
await dispatch('TimelineSpace/Contents/Home/fetchTimeline', {}, { root: true }).finally(() => {
dispatch('TimelineSpace/Contents/changeLoading', false, { root: true })
@ -209,15 +209,9 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
await dispatch('TimelineSpace/Contents/Notifications/fetchNotifications', {}, { root: true })
await dispatch('TimelineSpace/Contents/Mentions/fetchMentions', {}, { root: true })
if (state.timelineSetting.unreadNotification.direct) {
await dispatch('TimelineSpace/Contents/DirectMessages/fetchTimeline', {}, { root: true })
}
if (state.timelineSetting.unreadNotification.local) {
await dispatch('TimelineSpace/Contents/Local/fetchLocalTimeline', {}, { root: true })
}
if (state.timelineSetting.unreadNotification.public) {
await dispatch('TimelineSpace/Contents/Public/fetchPublicTimeline', {}, { root: true })
}
await dispatch('TimelineSpace/Contents/DirectMessages/fetchTimeline', {}, { root: true })
await dispatch('TimelineSpace/Contents/Local/fetchLocalTimeline', {}, { root: true })
await dispatch('TimelineSpace/Contents/Public/fetchPublicTimeline', {}, { root: true })
},
[ACTION_TYPES.CLEAR_CONTENTS_TIMELINES]: ({ commit }) => {
commit('TimelineSpace/Contents/Home/clearTimeline', {}, { root: true })
@ -227,17 +221,11 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
commit('TimelineSpace/Contents/Public/clearTimeline', {}, { root: true })
commit('TimelineSpace/Contents/Mentions/clearMentions', {}, { root: true })
},
[ACTION_TYPES.BIND_STREAMINGS]: ({ dispatch, state }) => {
[ACTION_TYPES.BIND_STREAMINGS]: ({ dispatch }) => {
dispatch('bindUserStreaming')
if (state.timelineSetting.unreadNotification.direct) {
dispatch('bindDirectMessagesStreaming')
}
if (state.timelineSetting.unreadNotification.local) {
dispatch('bindLocalStreaming')
}
if (state.timelineSetting.unreadNotification.public) {
dispatch('bindPublicStreaming')
}
dispatch('bindDirectMessagesStreaming')
dispatch('bindLocalStreaming')
dispatch('bindPublicStreaming')
},
// ------------------------------------------------
// Each streaming methods
@ -312,19 +300,13 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
})
},
[ACTION_TYPES.UPDATE_TOOT_FOR_ALL_TIMELINES]: ({ commit, state }, status: Entity.Status): boolean => {
[ACTION_TYPES.UPDATE_TOOT_FOR_ALL_TIMELINES]: ({ commit }, status: Entity.Status): boolean => {
commit('TimelineSpace/Contents/Home/updateToot', status, { root: true })
commit('TimelineSpace/Contents/Notifications/updateToot', status, { root: true })
commit('TimelineSpace/Contents/Mentions/updateToot', status, { root: true })
if (state.timelineSetting.unreadNotification.direct) {
commit('TimelineSpace/Contents/DirectMessages/updateToot', status, { root: true })
}
if (state.timelineSetting.unreadNotification.local) {
commit('TimelineSpace/Contents/Local/updateToot', status, { root: true })
}
if (state.timelineSetting.unreadNotification.public) {
commit('TimelineSpace/Contents/Public/updateToot', status, { root: true })
}
commit('TimelineSpace/Contents/DirectMessages/updateToot', status, { root: true })
commit('TimelineSpace/Contents/Local/updateToot', status, { root: true })
commit('TimelineSpace/Contents/Public/updateToot', status, { root: true })
return true
}
}

View File

@ -144,7 +144,7 @@ const actions: ActionTree<HomeState, RootState> = {
console.error(err)
})
if (rootState.TimelineSpace.timelineSetting.useMarker.home && marker !== null && marker.home) {
if (rootState.TimelineSpace.setting.markerHome && marker !== null && marker.home) {
const last = await client.getStatus(marker.home.last_read_id)
const lastReadStatus = last.data
@ -255,7 +255,7 @@ const actions: ActionTree<HomeState, RootState> = {
return res.data
},
[ACTION_TYPES.GET_MARKER]: async ({ rootState }): Promise<Entity.Marker | null> => {
if (!rootState.TimelineSpace.timelineSetting.useMarker.home) {
if (!rootState.TimelineSpace.setting.markerHome) {
return null
}
const client = generator(

View File

@ -122,7 +122,7 @@ const actions: ActionTree<NotificationsState, RootState> = {
console.error(err)
})
if (rootState.TimelineSpace.timelineSetting.useMarker.notifications && marker !== null && marker.notifications) {
if (rootState.TimelineSpace.setting.markerNotifications && marker !== null && marker.notifications) {
// The result does not contain max_id's notification, when we specify max_id parameter in get notifications.
// So we need to get max_id's notification.
const nextResponse = await client.getNotifications({ limit: 1, min_id: marker.notifications.last_read_id })
@ -224,7 +224,7 @@ const actions: ActionTree<NotificationsState, RootState> = {
win.ipcRenderer.send('reset-badge')
},
[ACTION_TYPES.GET_MARKER]: async ({ rootState }): Promise<Entity.Marker | null> => {
if (!rootState.TimelineSpace.timelineSetting.useMarker.notifications) {
if (!rootState.TimelineSpace.setting.markerNotifications) {
return null
}
const client = generator(

View File

@ -223,10 +223,8 @@ const actions: ActionTree<SideMenuState, RootState> = {
commit(MUTATION_TYPES.CHANGE_COLLAPSE, value)
return value
},
[ACTION_TYPES.LIST_TAGS]: async ({ rootState, commit }) => {
// TODO: Can not get account because too early.
// It should be executed after TimelineSpace obtain local account.
const tags: Array<LocalTag> = await win.ipcRenderer.invoke('list-hashtags', rootState.TimelineSpace.account!.id)
[ACTION_TYPES.LIST_TAGS]: async ({ commit }, accountId: number) => {
const tags: Array<LocalTag> = await win.ipcRenderer.invoke('list-hashtags', accountId)
commit(MUTATION_TYPES.UPDATE_TAGS, tags)
return tags
}

View File

@ -1,21 +1,5 @@
export type UnreadNotification = {
direct: boolean
local: boolean
public: boolean
}
export type UseMarker = {
home: boolean
notifications: boolean
}
export type Timeline = {
unreadNotification: UnreadNotification
useMarker: UseMarker
}
export type Setting = {
accountID: string
timeline: Timeline
accountId: number
markerHome: boolean
markerNotifications: boolean
}
export type BaseSettings = Array<Setting>