diff --git a/src/main/account.js b/src/main/account.js index f8824e3e..e7fae4e5 100644 --- a/src/main/account.js +++ b/src/main/account.js @@ -75,6 +75,21 @@ export default class Account { ) }) } + + removeAccount (id) { + return new Promise((resolve, reject) => { + this.db.remove( + { + _id: id + }, + { multi: true }, + (err, numRemoved) => { + if (err) return reject(err) + resolve(numRemoved) + } + ) + }) + } } class EmptyRecordError { diff --git a/src/main/index.js b/src/main/index.js index 567c62dd..1e1146ed 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -78,6 +78,16 @@ function createWindow () { { type: 'separator' }, + { + label: 'Preferences...', + accelerator: 'CmdOrCtrl+,', + click: () => { + mainWindow.webContents.send('open-preferences') + } + }, + { + type: 'separator' + }, { label: 'Quit', accelerator: 'CmdOrCtrl+Q', @@ -288,6 +298,17 @@ ipcMain.on('update-account', (event, acct) => { }) }) +ipcMain.on('remove-account', (event, id) => { + const account = new Account(db) + account.removeAccount(id) + .then(() => { + event.sender.send('response-remove-account') + }) + .catch((err) => { + event.sender.send('error-remove-account', err) + }) +}) + // streaming let userStreaming = null diff --git a/src/renderer/App.vue b/src/renderer/App.vue index 7b405fa4..bee9045c 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -6,11 +6,28 @@ diff --git a/src/renderer/components/Preferences/Account.vue b/src/renderer/components/Preferences/Account.vue new file mode 100644 index 00000000..d9ff98be --- /dev/null +++ b/src/renderer/components/Preferences/Account.vue @@ -0,0 +1,90 @@ + + + Account + + Connected Accounts + + + + + + + + + + Remove association + + + + + + + + + + + + diff --git a/src/renderer/components/Preferences/General.vue b/src/renderer/components/Preferences/General.vue new file mode 100644 index 00000000..722837e9 --- /dev/null +++ b/src/renderer/components/Preferences/General.vue @@ -0,0 +1,14 @@ + + + general + + + + + + diff --git a/src/renderer/router/index.js b/src/renderer/router/index.js index de7a0a5b..c7ca63c4 100644 --- a/src/renderer/router/index.js +++ b/src/renderer/router/index.js @@ -15,6 +15,23 @@ export default new Router({ name: 'authorize', component: require('@/components/Authorize').default }, + { + path: '/preferences/', + name: 'preferences', + component: require('@/components/Preferences').default, + children: [ + { + path: 'general', + name: 'general', + component: require('@/components/Preferences/General').default + }, + { + path: 'account', + name: 'account', + component: require('@/components/Preferences/Account').default + } + ] + }, { path: '/', name: 'global-header', diff --git a/src/renderer/store/App.js b/src/renderer/store/App.js new file mode 100644 index 00000000..6a1b70c9 --- /dev/null +++ b/src/renderer/store/App.js @@ -0,0 +1,20 @@ +import { ipcRenderer } from 'electron' +import router from '../router' + +const App = { + namespaced: true, + state: {}, + mutations: {}, + actions: { + watchShortcutsEvents () { + ipcRenderer.on('open-preferences', (event) => { + router.push('/preferences/account') + }) + }, + removeShortcutsEvents () { + ipcRenderer.removeAllListeners('open-preferences') + } + } +} + +export default App diff --git a/src/renderer/store/Preferences.js b/src/renderer/store/Preferences.js new file mode 100644 index 00000000..c9936a7c --- /dev/null +++ b/src/renderer/store/Preferences.js @@ -0,0 +1,20 @@ +import General from './Preferences/General' +import Account from './Preferences/Account' + +const Preferences = { + namespaced: true, + modules: { + General, + Account + }, + state: { + defaultActive: '1' + }, + mutations: { + changeActive (state, value) { + state.defaultActive = value + } + } +} + +export default Preferences diff --git a/src/renderer/store/Preferences/Account.js b/src/renderer/store/Preferences/Account.js new file mode 100644 index 00000000..d8ec1a4b --- /dev/null +++ b/src/renderer/store/Preferences/Account.js @@ -0,0 +1,92 @@ +import { ipcRenderer } from 'electron' +import Mastodon from 'mastodon-api' + +const Account = { + namespaced: true, + state: { + accounts: [], + accountLoading: false + }, + mutations: { + updateAccounts (state, accounts) { + state.accounts = accounts + }, + mergeAccounts (state, accounts) { + // TODO: Save username in local db after authorize. + // This function can not support if user add multiple accounts which are exist in same domain. + // So when username is saved in local db, please compare with reference to username@domain. + state.accounts = state.accounts.map((a) => { + let account = a + accounts.map((acct) => { + if (acct.domain === a.domain) { + account = acct + } + }) + return account + }) + }, + updateAccountLoading (state, value) { + state.accountLoading = value + } + }, + actions: { + loadAccounts ({ commit }) { + return new Promise((resolve, reject) => { + ipcRenderer.send('list-accounts', 'list') + ipcRenderer.once('error-list-accounts', (event, err) => { + ipcRenderer.removeAllListeners('response-list-accounts') + reject(err) + }) + ipcRenderer.once('response-list-accounts', (event, accounts) => { + ipcRenderer.removeAllListeners('error-list-accounts') + commit('updateAccounts', accounts) + resolve(accounts) + }) + }) + }, + fetchUsername ({ dispatch, commit }, accounts) { + return new Promise((resolve, reject) => { + dispatch('fetchAllAccounts', accounts) + .then((accounts) => { + commit('mergeAccounts', accounts) + resolve(accounts) + }) + .catch((err) => { + reject(err) + }) + }) + }, + fetchAllAccounts ({ commit }, accounts) { + return Promise.all(accounts.map((account) => { + return new Promise((resolve, reject) => { + const client = new Mastodon( + { + access_token: account.accessToken, + api_url: account.baseURL + '/api/v1' + }) + client.get('/accounts/verify_credentials', (err, data, res) => { + if (err) return reject(err) + // The response doesn't have domain, so I cann't confirm that response and account is same. + // Therefore I merge account. + resolve(Object.assign(data, account)) + }) + }) + })) + }, + removeAccount ({ commit }, account) { + return new Promise((resolve, reject) => { + ipcRenderer.send('remove-account', account._id) + ipcRenderer.once('error-remove-account', (event, err) => { + ipcRenderer.removeAllListeners('response-remove-account') + reject(err) + }) + ipcRenderer.once('response-remove-account', (event) => { + ipcRenderer.removeAllListeners('error-remove-account') + resolve() + }) + }) + } + } +} + +export default Account diff --git a/src/renderer/store/Preferences/General.js b/src/renderer/store/Preferences/General.js new file mode 100644 index 00000000..6c55d116 --- /dev/null +++ b/src/renderer/store/Preferences/General.js @@ -0,0 +1,8 @@ +const General = { + namespaced: true, + state: {}, + mutations: {}, + actions: {} +} + +export default General diff --git a/src/renderer/store/index.js b/src/renderer/store/index.js index 45da4238..4d7c5d61 100644 --- a/src/renderer/store/index.js +++ b/src/renderer/store/index.js @@ -2,10 +2,12 @@ import Vue from 'vue' import Vuex from 'vuex' import createLogger from 'vuex/dist/logger' +import App from './App' import GlobalHeader from './GlobalHeader' import Login from './Login' import Authorize from './Authorize' import TimelineSpace from './TimelineSpace' +import Preferences from './Preferences' Vue.use(Vuex) @@ -15,9 +17,11 @@ export default new Vuex.Store({ ? [createLogger()] : [], modules: { + App, GlobalHeader, Login, Authorize, - TimelineSpace + TimelineSpace, + Preferences } })