mirror of
https://github.com/hotomoe/hotomoe
synced 2024-12-11 21:28:14 +09:00
Refactoring
This commit is contained in:
parent
10356b4041
commit
28f8933c3c
@ -20,6 +20,7 @@ import Menu from './components/menu.vue';
|
|||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
import { applyTheme, lightTheme, builtinThemes } from './theme';
|
import { applyTheme, lightTheme, builtinThemes } from './theme';
|
||||||
import { isDeviceDarkmode } from './scripts/is-device-darkmode';
|
import { isDeviceDarkmode } from './scripts/is-device-darkmode';
|
||||||
|
import createStore from './store';
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
Vue.use(VueHotkey);
|
Vue.use(VueHotkey);
|
||||||
@ -134,36 +135,38 @@ document.body.setAttribute('ontouchstart', '');
|
|||||||
// アプリ基底要素マウント
|
// アプリ基底要素マウント
|
||||||
document.body.innerHTML = '<div id="app"></div>';
|
document.body.innerHTML = '<div id="app"></div>';
|
||||||
|
|
||||||
const os = new MiOS();
|
const store = createStore();
|
||||||
|
|
||||||
|
const os = new MiOS(store);
|
||||||
|
|
||||||
os.init(async () => {
|
os.init(async () => {
|
||||||
window.addEventListener('storage', e => {
|
window.addEventListener('storage', e => {
|
||||||
if (e.key === 'vuex') {
|
if (e.key === 'vuex') {
|
||||||
os.store.replaceState(JSON.parse(localStorage['vuex']));
|
store.replaceState(JSON.parse(localStorage['vuex']));
|
||||||
} else if (e.key === 'i') {
|
} else if (e.key === 'i') {
|
||||||
location.reload();
|
location.reload();
|
||||||
}
|
}
|
||||||
}, false)
|
}, false)
|
||||||
|
|
||||||
os.store.watch(state => state.device.darkMode, darkMode => {
|
store.watch(state => state.device.darkMode, darkMode => {
|
||||||
// TODO: このファイルでbuiltinThemesを参照するとcode splittingが効かず、初回読み込み時に全てのテーマコードを読み込むことになってしまい無駄なので何とかする
|
// TODO: このファイルでbuiltinThemesを参照するとcode splittingが効かず、初回読み込み時に全てのテーマコードを読み込むことになってしまい無駄なので何とかする
|
||||||
const themes = builtinThemes.concat(os.store.state.device.themes);
|
const themes = builtinThemes.concat(store.state.device.themes);
|
||||||
applyTheme(themes.find(x => x.id === (darkMode ? os.store.state.device.darkTheme : os.store.state.device.lightTheme)));
|
applyTheme(themes.find(x => x.id === (darkMode ? store.state.device.darkTheme : store.state.device.lightTheme)));
|
||||||
});
|
});
|
||||||
|
|
||||||
//#region Sync dark mode
|
//#region Sync dark mode
|
||||||
if (os.store.state.device.syncDeviceDarkMode) {
|
if (store.state.device.syncDeviceDarkMode) {
|
||||||
os.store.commit('device/set', { key: 'darkMode', value: isDeviceDarkmode() });
|
store.commit('device/set', { key: 'darkMode', value: isDeviceDarkmode() });
|
||||||
}
|
}
|
||||||
|
|
||||||
window.matchMedia('(prefers-color-scheme: dark)').addListener(mql => {
|
window.matchMedia('(prefers-color-scheme: dark)').addListener(mql => {
|
||||||
if (os.store.state.device.syncDeviceDarkMode) {
|
if (store.state.device.syncDeviceDarkMode) {
|
||||||
os.store.commit('device/set', { key: 'darkMode', value: mql.matches });
|
store.commit('device/set', { key: 'darkMode', value: mql.matches });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
if ('Notification' in window && os.store.getters.isSignedIn) {
|
if ('Notification' in window && store.getters.isSignedIn) {
|
||||||
// 許可を得ていなかったらリクエスト
|
// 許可を得ていなかったらリクエスト
|
||||||
if (Notification.permission === 'default') {
|
if (Notification.permission === 'default') {
|
||||||
Notification.requestPermission();
|
Notification.requestPermission();
|
||||||
@ -171,7 +174,7 @@ os.init(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const app = new Vue({
|
const app = new Vue({
|
||||||
store: os.store,
|
store: store,
|
||||||
metaInfo: {
|
metaInfo: {
|
||||||
title: null,
|
title: null,
|
||||||
titleTemplate: title => title ? `${title} | ${(instanceName || 'Misskey')}` : (instanceName || 'Misskey')
|
titleTemplate: title => title ? `${title} | ${(instanceName || 'Misskey')}` : (instanceName || 'Misskey')
|
||||||
@ -183,7 +186,7 @@ os.init(async () => {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
api: os.api,
|
api: (endpoint: string, data: { [x: string]: any } = {}, token?) => store.dispatch('api', { endpoint, data, token }),
|
||||||
signout: os.signout,
|
signout: os.signout,
|
||||||
new(vm, props) {
|
new(vm, props) {
|
||||||
const x = new vm({
|
const x = new vm({
|
||||||
@ -234,58 +237,58 @@ os.init(async () => {
|
|||||||
// マウント
|
// マウント
|
||||||
app.$mount('#app');
|
app.$mount('#app');
|
||||||
|
|
||||||
if (app.$store.getters.isSignedIn) {
|
if (store.getters.isSignedIn) {
|
||||||
const main = os.stream.useSharedConnection('main');
|
const main = os.stream.useSharedConnection('main');
|
||||||
|
|
||||||
// 自分の情報が更新されたとき
|
// 自分の情報が更新されたとき
|
||||||
main.on('meUpdated', i => {
|
main.on('meUpdated', i => {
|
||||||
app.$store.dispatch('mergeMe', i);
|
store.dispatch('mergeMe', i);
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('readAllNotifications', () => {
|
main.on('readAllNotifications', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadNotification: false
|
hasUnreadNotification: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('unreadNotification', () => {
|
main.on('unreadNotification', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadNotification: true
|
hasUnreadNotification: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('unreadMention', () => {
|
main.on('unreadMention', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadMentions: true
|
hasUnreadMentions: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('readAllUnreadMentions', () => {
|
main.on('readAllUnreadMentions', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadMentions: false
|
hasUnreadMentions: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('unreadSpecifiedNote', () => {
|
main.on('unreadSpecifiedNote', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadSpecifiedNotes: true
|
hasUnreadSpecifiedNotes: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('readAllUnreadSpecifiedNotes', () => {
|
main.on('readAllUnreadSpecifiedNotes', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadSpecifiedNotes: false
|
hasUnreadSpecifiedNotes: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('readAllMessagingMessages', () => {
|
main.on('readAllMessagingMessages', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadMessagingMessage: false
|
hasUnreadMessagingMessage: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('unreadMessagingMessage', () => {
|
main.on('unreadMessagingMessage', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadMessagingMessage: true
|
hasUnreadMessagingMessage: true
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -293,13 +296,13 @@ os.init(async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
main.on('readAllAntennas', () => {
|
main.on('readAllAntennas', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadAntenna: false
|
hasUnreadAntenna: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('unreadAntenna', () => {
|
main.on('unreadAntenna', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadAntenna: true
|
hasUnreadAntenna: true
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -307,13 +310,13 @@ os.init(async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
main.on('readAllAnnouncements', () => {
|
main.on('readAllAnnouncements', () => {
|
||||||
app.$store.dispatch('mergeMe', {
|
store.dispatch('mergeMe', {
|
||||||
hasUnreadAnnouncement: false
|
hasUnreadAnnouncement: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('clientSettingUpdated', x => {
|
main.on('clientSettingUpdated', x => {
|
||||||
app.$store.commit('settings/set', {
|
store.commit('settings/set', {
|
||||||
key: x.key,
|
key: x.key,
|
||||||
value: x.value
|
value: x.value
|
||||||
});
|
});
|
||||||
|
@ -2,16 +2,11 @@ import autobind from 'autobind-decorator';
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { EventEmitter } from 'eventemitter3';
|
import { EventEmitter } from 'eventemitter3';
|
||||||
|
|
||||||
import initStore from './store';
|
|
||||||
import { apiUrl, version } from './config';
|
import { apiUrl, version } from './config';
|
||||||
import Progress from './scripts/loading';
|
import Progress from './scripts/loading';
|
||||||
|
|
||||||
import Stream from './scripts/stream';
|
import Stream from './scripts/stream';
|
||||||
|
import store from './store';
|
||||||
//#region api requests
|
|
||||||
let spinner = null;
|
|
||||||
let pending = 0;
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Misskey Operating System
|
* Misskey Operating System
|
||||||
@ -19,7 +14,7 @@ let pending = 0;
|
|||||||
export default class MiOS extends EventEmitter {
|
export default class MiOS extends EventEmitter {
|
||||||
public app: Vue;
|
public app: Vue;
|
||||||
|
|
||||||
public store: ReturnType<typeof initStore>;
|
public store: ReturnType<typeof store>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A connection manager of home stream
|
* A connection manager of home stream
|
||||||
@ -31,6 +26,11 @@ export default class MiOS extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
private swRegistration: ServiceWorkerRegistration = null;
|
private swRegistration: ServiceWorkerRegistration = null;
|
||||||
|
|
||||||
|
constructor(vuex: MiOS['store']) {
|
||||||
|
super();
|
||||||
|
this.store = vuex;
|
||||||
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public signout() {
|
public signout() {
|
||||||
this.store.dispatch('logout');
|
this.store.dispatch('logout');
|
||||||
@ -52,8 +52,6 @@ export default class MiOS extends EventEmitter {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.store = initStore(this);
|
|
||||||
|
|
||||||
// ユーザーをフェッチしてコールバックする
|
// ユーザーをフェッチしてコールバックする
|
||||||
const fetchme = (token, cb) => {
|
const fetchme = (token, cb) => {
|
||||||
let me = null;
|
let me = null;
|
||||||
@ -187,10 +185,13 @@ export default class MiOS extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register
|
// Register
|
||||||
this.api('sw/register', {
|
this.store.dispatch('api', {
|
||||||
endpoint: subscription.endpoint,
|
endpoint: 'sw/register',
|
||||||
auth: encode(subscription.getKey('auth')),
|
data: {
|
||||||
publickey: encode(subscription.getKey('p256dh'))
|
endpoint: subscription.endpoint,
|
||||||
|
auth: encode(subscription.getKey('auth')),
|
||||||
|
publickey: encode(subscription.getKey('p256dh'))
|
||||||
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
// When subscribe failed
|
// When subscribe failed
|
||||||
@ -214,52 +215,6 @@ export default class MiOS extends EventEmitter {
|
|||||||
// Register service worker
|
// Register service worker
|
||||||
navigator.serviceWorker.register(sw);
|
navigator.serviceWorker.register(sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Misskey APIにリクエストします
|
|
||||||
* @param endpoint エンドポイント名
|
|
||||||
* @param data パラメータ
|
|
||||||
*/
|
|
||||||
@autobind
|
|
||||||
public api(endpoint: string, data: { [x: string]: any } = {}, token?): Promise<{ [x: string]: any }> {
|
|
||||||
if (++pending === 1) {
|
|
||||||
spinner = document.createElement('div');
|
|
||||||
spinner.setAttribute('id', 'wait');
|
|
||||||
document.body.appendChild(spinner);
|
|
||||||
}
|
|
||||||
|
|
||||||
const onFinally = () => {
|
|
||||||
if (--pending === 0) spinner.parentNode.removeChild(spinner);
|
|
||||||
};
|
|
||||||
|
|
||||||
const promise = new Promise((resolve, reject) => {
|
|
||||||
// Append a credential
|
|
||||||
if (this.store.getters.isSignedIn) (data as any).i = this.store.state.i.token;
|
|
||||||
if (token) (data as any).i = token;
|
|
||||||
|
|
||||||
// Send request
|
|
||||||
fetch(endpoint.indexOf('://') > -1 ? endpoint : `${apiUrl}/${endpoint}`, {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
credentials: 'omit',
|
|
||||||
cache: 'no-cache'
|
|
||||||
}).then(async (res) => {
|
|
||||||
const body = res.status === 204 ? null : await res.json();
|
|
||||||
|
|
||||||
if (res.status === 200) {
|
|
||||||
resolve(body);
|
|
||||||
} else if (res.status === 204) {
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
reject(body.error);
|
|
||||||
}
|
|
||||||
}).catch(reject);
|
|
||||||
});
|
|
||||||
|
|
||||||
promise.then(onFinally, onFinally);
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import createPersistedState from 'vuex-persistedstate';
|
import createPersistedState from 'vuex-persistedstate';
|
||||||
import * as nestedProperty from 'nested-property';
|
import * as nestedProperty from 'nested-property';
|
||||||
|
import { apiUrl } from './config';
|
||||||
import MiOS from './mios';
|
|
||||||
|
|
||||||
const defaultSettings = {
|
const defaultSettings = {
|
||||||
tutorial: 0,
|
tutorial: 0,
|
||||||
@ -57,13 +56,15 @@ function copy<T>(data: T): T {
|
|||||||
return JSON.parse(JSON.stringify(data));
|
return JSON.parse(JSON.stringify(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (os: MiOS) => new Vuex.Store({
|
export default () => new Vuex.Store({
|
||||||
plugins: [createPersistedState({
|
plugins: [createPersistedState({
|
||||||
paths: ['i', 'device', 'deviceUser', 'settings', 'instance']
|
paths: ['i', 'device', 'deviceUser', 'settings', 'instance']
|
||||||
})],
|
})],
|
||||||
|
|
||||||
state: {
|
state: {
|
||||||
i: null,
|
i: null,
|
||||||
|
pendingApiRequestsCount: 0,
|
||||||
|
spinner: null
|
||||||
},
|
},
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
@ -121,6 +122,47 @@ export default (os: MiOS) => new Vuex.Store({
|
|||||||
ctx.commit('settings/init', me.clientData);
|
ctx.commit('settings/init', me.clientData);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
api(ctx, { endpoint, data, token }) {
|
||||||
|
if (++ctx.state.pendingApiRequestsCount === 1) {
|
||||||
|
// TODO: spinnerの表示はstoreでやらない
|
||||||
|
ctx.state.spinner = document.createElement('div');
|
||||||
|
ctx.state.spinner.setAttribute('id', 'wait');
|
||||||
|
document.body.appendChild(ctx.state.spinner);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onFinally = () => {
|
||||||
|
if (--ctx.state.pendingApiRequestsCount === 0) ctx.state.spinner.parentNode.removeChild(ctx.state.spinner);
|
||||||
|
};
|
||||||
|
|
||||||
|
const promise = new Promise((resolve, reject) => {
|
||||||
|
// Append a credential
|
||||||
|
if (ctx.getters.isSignedIn) (data as any).i = ctx.state.i.token;
|
||||||
|
if (token) (data as any).i = token;
|
||||||
|
|
||||||
|
// Send request
|
||||||
|
fetch(endpoint.indexOf('://') > -1 ? endpoint : `${apiUrl}/${endpoint}`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
credentials: 'omit',
|
||||||
|
cache: 'no-cache'
|
||||||
|
}).then(async (res) => {
|
||||||
|
const body = res.status === 204 ? null : await res.json();
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
resolve(body);
|
||||||
|
} else if (res.status === 204) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject(body.error);
|
||||||
|
}
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
|
||||||
|
promise.then(onFinally, onFinally);
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
modules: {
|
modules: {
|
||||||
@ -139,9 +181,12 @@ export default (os: MiOS) => new Vuex.Store({
|
|||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
async fetch(ctx) {
|
async fetch(ctx) {
|
||||||
const meta = await os.api('meta', {
|
const meta = await ctx.dispatch('api', {
|
||||||
detail: false
|
endpoint: 'meta',
|
||||||
});
|
data: {
|
||||||
|
detail: false
|
||||||
|
}
|
||||||
|
}, { root: true });
|
||||||
|
|
||||||
ctx.commit('set', meta);
|
ctx.commit('set', meta);
|
||||||
}
|
}
|
||||||
@ -246,10 +291,13 @@ export default (os: MiOS) => new Vuex.Store({
|
|||||||
ctx.commit('set', x);
|
ctx.commit('set', x);
|
||||||
|
|
||||||
if (ctx.rootGetters.isSignedIn) {
|
if (ctx.rootGetters.isSignedIn) {
|
||||||
os.api('i/update-client-setting', {
|
ctx.dispatch('api', {
|
||||||
name: x.key,
|
endpoint: 'i/update-client-setting',
|
||||||
value: x.value
|
data: {
|
||||||
});
|
name: x.key,
|
||||||
|
value: x.value
|
||||||
|
}
|
||||||
|
}, { root: true });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user