diff --git a/spec/renderer/integration/store/GlobalHeader.spec.ts b/spec/renderer/integration/store/GlobalHeader.spec.ts index 8898608c7f..a7e64b5b54 100644 --- a/spec/renderer/integration/store/GlobalHeader.spec.ts +++ b/spec/renderer/integration/store/GlobalHeader.spec.ts @@ -3,7 +3,7 @@ import { createStore, Store } from 'vuex' import { ipcMain, ipcRenderer } from '~/spec/mock/electron' import GlobalHeader, { GlobalHeaderState } from '~/src/renderer/store/GlobalHeader' import { MyWindow } from '~/src/types/global' -;(window as any as MyWindow).ipcRenderer = ipcRenderer +;((window as any) as MyWindow).ipcRenderer = ipcRenderer const state = (): GlobalHeaderState => { return { @@ -58,21 +58,6 @@ describe('GlobalHeader', () => { }) }) - describe('refreshAccounts', () => { - beforeEach(() => { - ipcMain.handle('refresh-accounts', () => { - return ['accounts'] - }) - }) - afterEach(() => { - ipcMain.removeHandler('refresh-accounts') - }) - it('should be refreshed', async () => { - await store.dispatch('GlobalHeader/refreshAccounts') - expect(store.state.GlobalHeader.accounts).toEqual(['accounts']) - }) - }) - describe('removeShortcutEvents', () => { it('should be removed', async () => { const removed = await store.dispatch('GlobalHeader/removeShortcutEvents') diff --git a/spec/renderer/integration/store/Login.spec.ts b/spec/renderer/integration/store/Login.spec.ts index cb3ece5b72..7b508954f8 100644 --- a/spec/renderer/integration/store/Login.spec.ts +++ b/spec/renderer/integration/store/Login.spec.ts @@ -1,9 +1,9 @@ import { createStore, Store } from 'vuex' -import { ipcMain, ipcRenderer } from '~/spec/mock/electron' +import { ipcRenderer } from '~/spec/mock/electron' import Login, { LoginState } from '@/store/Login' import { MyWindow } from '~/src/types/global' import { RootState } from '@/store' -;(window as any as MyWindow).ipcRenderer = ipcRenderer +;((window as any) as MyWindow).ipcRenderer = ipcRenderer jest.mock('megalodon', () => ({ ...jest.requireActual('megalodon'), @@ -13,8 +13,10 @@ jest.mock('megalodon', () => ({ const state = (): LoginState => { return { - selectedInstance: null, + domain: null, searching: false, + server: null, + appData: null, sns: 'mastodon' } } @@ -47,34 +49,10 @@ describe('Login', () => { }) }) - describe('fetchLogin', () => { - describe('error', () => { - it('should return error', async () => { - ipcMain.handle('get-auth-url', () => { - throw new Error() - }) - await store.dispatch('Login/fetchLogin', 'pleroma.io').catch((err: Error) => { - expect(err instanceof Error).toEqual(true) - }) - ipcMain.removeHandler('get-auth-url') - }) - }) - describe('success', () => { - it('should return url', async () => { - ipcMain.handle('get-auth-url', () => { - return 'http://example.com/auth' - }) - const url = await store.dispatch('Login/fetchLogin', 'pleroma.io') - expect(url).toEqual('http://example.com/auth') - ipcMain.removeHandler('get-auth-url') - }) - }) - }) - describe('pageBack', () => { it('should reset instance', () => { store.dispatch('Login/pageBack') - expect(store.state.Login.selectedInstance).toEqual(null) + expect(store.state.Login.domain).toEqual(null) }) }) @@ -82,7 +60,7 @@ describe('Login', () => { it('should change instance', async () => { const result = await store.dispatch('Login/confirmInstance', 'pleroma.io') expect(result).toEqual(true) - expect(store.state.Login.selectedInstance).toEqual('pleroma.io') + expect(store.state.Login.domain).toEqual('pleroma.io') }) }) }) diff --git a/spec/renderer/integration/store/Preferences/Account.spec.ts b/spec/renderer/integration/store/Preferences/Account.spec.ts deleted file mode 100644 index 7f74c18ba9..0000000000 --- a/spec/renderer/integration/store/Preferences/Account.spec.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { createStore, Store } from 'vuex' -import { ipcMain, ipcRenderer } from '~/spec/mock/electron' -import Account, { AccountState } from '@/store/Preferences/Account' -import { LocalAccount } from '~/src/types/localAccount' -import { MyWindow } from '~/src/types/global' -import { RootState } from '@/store' -;(window as any as MyWindow).ipcRenderer = ipcRenderer - -const account: LocalAccount = { - _id: 'sample', - baseURL: 'http://example.com', - domain: 'example.com', - clientId: 'hoge', - clientSecret: 'hogehoge', - accessToken: null, - refreshToken: null, - username: null, - accountId: null, - avatar: null, - order: 1 -} - -const state = (): AccountState => { - return { - accounts: [], - accountLoading: false - } -} - -const initStore = () => { - return { - namespaced: true, - state: state(), - actions: Account.actions, - mutations: Account.mutations - } -} - -const preferencesStore = () => ({ - namespaced: true, - modules: { - Account: initStore() - } -}) - -describe('Account', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - Preferences: preferencesStore() - } - }) - }) - - describe('loadAccounts', () => { - it('error', async () => { - ipcMain.handle('list-accounts', async () => { - throw new Error() - }) - - await store.dispatch('Preferences/Account/loadAccounts').catch((err: Error) => { - expect(err instanceof Error).toEqual(true) - }) - ipcMain.removeHandler('list-accounts') - }) - it('success', async () => { - ipcMain.handle('list-accounts', () => { - return [account] - }) - await store.dispatch('Preferences/Account/loadAccounts') - expect(store.state.Preferences.Account.accounts).toEqual([account]) - ipcMain.removeHandler('list-accounts') - }) - }) - - describe('removeAccount', () => { - it('error', async () => { - ipcMain.handle('remove-account', async () => { - throw new Error() - }) - await store.dispatch('Preferences/Account/removeAccount', account).catch((err: Error) => { - expect(err instanceof Error).toEqual(true) - }) - ipcMain.removeHandler('remove-account') - }) - it('success', async () => { - ipcMain.handle('remove-account', () => { - return true - }) - const res = await store.dispatch('Preferences/Account/removeAccount', account) - expect(res).toEqual(undefined) - ipcMain.removeHandler('remove-account') - }) - }) - - describe('forwardAccount', () => { - it('error', async () => { - ipcMain.handle('forward-account', async () => { - throw new Error() - }) - await store.dispatch('Preferences/Account/forwardAccount', account).catch((err: Error) => { - expect(err instanceof Error).toEqual(true) - }) - ipcMain.removeHandler('forward-account') - }) - it('success', async () => { - ipcMain.handle('forward-account', () => { - return {} - }) - const res = await store.dispatch('Preferences/Account/forwardAccount', account) - expect(res).toEqual(undefined) - ipcMain.removeHandler('forward-account') - }) - }) - - describe('backwardAccount', () => { - it('error', async () => { - ipcMain.handle('backward-account', () => { - throw new Error() - }) - await store.dispatch('Preferences/Account/backwardAccount', account).catch((err: Error) => { - expect(err instanceof Error).toEqual(true) - }) - ipcMain.removeHandler('backward-account') - }) - it('success', async () => { - ipcMain.handle('backward-account', () => { - return {} - }) - const res = await store.dispatch('Preferences/Account/backwardAccount', account) - expect(res).toEqual(undefined) - ipcMain.removeHandler('backward-account') - }) - }) - - describe('removeAllAccounts', () => { - it('error', async () => { - ipcMain.handle('remove-all-accounts', () => { - throw new Error() - }) - await store.dispatch('Preferences/Account/removeAllAccounts', account).catch((err: Error) => { - expect(err instanceof Error).toEqual(true) - }) - ipcMain.removeHandler('remove-all-accounts') - }) - it('success', async () => { - ipcMain.handle('remove-all-accounts', () => { - return {} - }) - const res = await store.dispatch('Preferences/Account/removeAllAccounts', account) - expect(res).toEqual(undefined) - ipcMain.removeHandler('remove-all-accounts') - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace.spec.ts b/spec/renderer/integration/store/TimelineSpace.spec.ts deleted file mode 100644 index b66ab8bd9a..0000000000 --- a/spec/renderer/integration/store/TimelineSpace.spec.ts +++ /dev/null @@ -1,286 +0,0 @@ -import { RootState } from '@/store' -import { Entity, Response } from 'megalodon' -import { createStore, Store } from 'vuex' -import { ipcMain, ipcRenderer } from '~/spec/mock/electron' -import TimelineSpace, { TimelineSpaceState, blankAccount } from '~/src/renderer/store/TimelineSpace' -import { MyWindow } from '~/src/types/global' -;((window as any) as MyWindow).ipcRenderer = ipcRenderer - -const emacsEmoji: Entity.Emoji = { - shortcode: 'emacs', - url: 'http://example.com/emacs', - static_url: 'http://example.com/emacs', - visible_in_picker: true -} -const rubyEmoji: Entity.Emoji = { - shortcode: 'ruby', - url: 'http://example.com/ruby', - static_url: 'http://example.com/ruby', - visible_in_picker: true -} - -const mockedInstance: Entity.Instance = { - uri: 'http://pleroma.io', - title: 'pleroma', - description: '', - email: 'test@example.com', - version: '2.5.0 (compatible; Pleroma 0.9.0-3363-g7c5d2dc7)', - thumbnail: null, - urls: { - streaming_api: 'wss://pleroma.io' - }, - stats: { - user_count: 10, - status_count: 1000, - domain_count: 100 - }, - languages: ['en'], - contact_account: null, - max_toot_chars: 5000 -} - -const mockClient = { - getInstance: () => { - return new Promise>(resolve => { - const res: Response = { - data: mockedInstance, - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - }, - getInstanceCustomEmojis: () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [emacsEmoji, rubyEmoji], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } -} - -jest.mock('megalodon', () => ({ - ...jest.requireActual('megalodon'), - detector: jest.fn(() => 'pleroma'), - default: jest.fn(() => mockClient), - __esModule: true -})) - -const state = (): TimelineSpaceState => { - return { - account: blankAccount, - bindingAccount: null, - loading: false, - emojis: [], - tootMax: 500, - timelineSetting: { - unreadNotification: { - direct: true, - local: true, - public: true - }, - useMarker: { - home: false, - notifications: true - } - }, - sns: 'mastodon', - filters: [] - } -} - -const homeStore = { - namespaced: true, - actions: { - fetchTimeline: jest.fn() - } -} - -const notificationStore = { - namespaced: true, - actions: { - fetchNotifications: jest.fn() - } -} - -const DMStore = { - namespaced: true, - actions: { - fetchTimeline: jest.fn() - } -} - -const LocalStore = { - namespaced: true, - actions: { - fetchLocalTimeline: jest.fn() - } -} - -const PublicStore = { - namespaced: true, - actions: { - fetchPublicTimeline: jest.fn() - } -} - -const MentionStore = { - namespaced: true, - actions: { - fetchMentions: jest.fn() - } -} - -const contentsStore = { - namespaced: true, - modules: { - Home: homeStore, - Notifications: notificationStore, - DirectMessages: DMStore, - Local: LocalStore, - Public: PublicStore, - Mentions: MentionStore - }, - actions: { - changeLoading: jest.fn() - } -} - -const initStore = () => { - return { - namespaced: true, - modules: { - Contents: contentsStore - }, - state: state(), - actions: TimelineSpace.actions, - mutations: TimelineSpace.mutations - } -} - -const appState = { - namespaced: true, - state: { - proxyConfiguration: false - } -} - -describe('TimelineSpace', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: initStore(), - App: appState - } - }) - }) - - describe('localAccount', () => { - describe('account already exists', () => { - beforeEach(() => { - ipcMain.handle('get-local-account', () => { - return { - username: 'test' - } - }) - }) - afterEach(() => { - ipcMain.removeHandler('get-local-account') - }) - it('should be updated', async () => { - await store.dispatch('TimelineSpace/localAccount', 1) - expect(store.state.TimelineSpace.account.username).toEqual('test') - }) - }) - - describe('account does not exist', () => { - beforeEach(() => { - ipcMain.handle('get-local-account', () => { - return {} - }) - ipcMain.handle('update-account', () => { - return { - username: 'fetched' - } - }) - }) - afterEach(() => { - ipcMain.removeHandler('get-local-account') - ipcMain.removeHandler('update-account') - }) - it('should be fetched', async () => { - await store.dispatch('TimelineSpace/localAccount', 1) - expect(store.state.TimelineSpace.account.username).toEqual('fetched') - }) - }) - }) - - describe('detectSNS', () => { - describe('API is pleroma', () => { - it('should be detected', async () => { - await store.dispatch('TimelineSpace/detectSNS') - expect(store.state.TimelineSpace.sns).toEqual('pleroma') - }) - }) - }) - - describe('fetchEmojis', () => { - it('should be updated', async () => { - await store.dispatch('TimelineSpace/fetchEmojis', {}) - expect(store.state.TimelineSpace.emojis).toEqual([emacsEmoji, rubyEmoji]) - }) - }) - - describe('fetchInstance', () => { - it('should be updated', async () => { - await store.dispatch('TimelineSpace/fetchInstance', {}) - expect(store.state.TimelineSpace.tootMax).toEqual(mockedInstance.max_toot_chars) - }) - }) - - describe('loadUnreadNotification', () => { - describe('success', () => { - it('should be updated', async () => { - ipcMain.handle('get-account-setting', () => { - return { - accountID: 'sample', - timeline: { - unreadNotification: { - direct: false, - local: false, - public: false - } - } - } - }) - await store.dispatch('TimelineSpace/loadTimelineSetting') - expect(store.state.TimelineSpace.timelineSetting).toEqual({ - unreadNotification: { - direct: false, - local: false, - public: false - } - }) - ipcMain.removeHandler('get-account-setting') - }) - }) - }) - - describe('fetchContentsTimelines', () => { - it('should be called', async () => { - await store.dispatch('TimelineSpace/fetchContentsTimelines', {}) - expect(homeStore.actions.fetchTimeline).toHaveBeenCalled() - expect(notificationStore.actions.fetchNotifications).toHaveBeenCalled() - expect(DMStore.actions.fetchTimeline).toHaveBeenCalled() - expect(LocalStore.actions.fetchLocalTimeline).toHaveBeenCalled() - expect(PublicStore.actions.fetchPublicTimeline).toHaveBeenCalled() - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/DirectMessages.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/DirectMessages.spec.ts deleted file mode 100644 index 3ee8091fd5..0000000000 --- a/spec/renderer/integration/store/TimelineSpace/Contents/DirectMessages.spec.ts +++ /dev/null @@ -1,228 +0,0 @@ -import { Response, Entity } from 'megalodon' -import { createStore, Store } from 'vuex' -import DirectMessages, { DirectMessagesState } from '@/store/TimelineSpace/Contents/DirectMessages' -import { RootState } from '@/store' - -const mockClient = { - getConversationTimeline: () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [conversation1], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } -} - -jest.mock('megalodon', () => ({ - ...jest.requireActual('megalodon'), - default: jest.fn(() => mockClient), - __esModule: true -})) - -const account: Entity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: null, - bot: false -} - -const status1: Entity.Status = { - id: '1', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'hoge', - plain_content: 'hoge', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} - -const status2: Entity.Status = { - id: '2', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'fuga', - plain_content: 'fuga', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} - -const conversation1: Entity.Conversation = { - id: '1', - accounts: [account], - last_status: status1, - unread: false -} - -const conversation2: Entity.Conversation = { - id: '2', - accounts: [account], - last_status: status2, - unread: false -} - -let state = (): DirectMessagesState => { - return { - lazyLoading: false, - heading: true, - timeline: [] - } -} - -const initStore = () => { - return { - namespaced: true, - state: state(), - actions: DirectMessages.actions, - mutations: DirectMessages.mutations - } -} - -const contentsStore = () => ({ - namespaced: true, - modules: { - DirectMessages: initStore() - } -}) - -const timelineStore = () => ({ - namespaced: true, - state: { - account: { - accessToken: 'token', - baseURL: 'http://localhost' - }, - sns: 'mastodon' - }, - modules: { - Contents: contentsStore() - } -}) - -const appState = { - namespaced: true, - state: { - proxyConfiguration: false - } -} - -describe('Home', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: timelineStore(), - App: appState - } - }) - }) - - describe('fetchTimeline', () => { - it('should be updated', async () => { - const statuses = await store.dispatch('TimelineSpace/Contents/DirectMessages/fetchTimeline') - expect(statuses).toEqual([status1]) - expect(store.state.TimelineSpace.Contents.DirectMessages.timeline).toEqual([status1]) - }) - }) - - describe('lazyFetchTimeline', () => { - describe('success', () => { - beforeAll(() => { - state = () => { - return { - lazyLoading: false, - heading: true, - timeline: [status1] - } - } - }) - it('should be updated', async () => { - mockClient.getConversationTimeline = () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [conversation2], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } - await store.dispatch('TimelineSpace/Contents/DirectMessages/lazyFetchTimeline', status1) - expect(store.state.TimelineSpace.Contents.DirectMessages.lazyLoading).toEqual(false) - expect(store.state.TimelineSpace.Contents.DirectMessages.timeline).toEqual([status1, status2]) - }) - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/Favourites.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/Favourites.spec.ts deleted file mode 100644 index 116b2a3819..0000000000 --- a/spec/renderer/integration/store/TimelineSpace/Contents/Favourites.spec.ts +++ /dev/null @@ -1,321 +0,0 @@ -import { Response, Entity } from 'megalodon' -import { createStore, Store } from 'vuex' -import Favourites, { FavouritesState } from '@/store/TimelineSpace/Contents/Favourites' -import { LocalAccount } from '~/src/types/localAccount' -import { RootState } from '@/store' - -const mockClient = { - getFavourites: () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [status1], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } -} - -jest.mock('megalodon', () => ({ - ...jest.requireActual('megalodon'), - default: jest.fn(() => mockClient), - __esModule: true -})) - -const localAccount: LocalAccount = { - _id: '1', - baseURL: 'http://localhost', - domain: 'localhost', - clientId: 'id', - clientSecret: 'secret', - accessToken: 'token', - refreshToken: null, - username: 'hoge', - accountId: '1', - avatar: null, - order: 1 -} - -const account: Entity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: null, - bot: false -} -const status1: Entity.Status = { - id: '1', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'hoge', - plain_content: 'hoge', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} -const status2: Entity.Status = { - id: '2', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'fuga', - plain_content: 'fuga', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} - -let state = (): FavouritesState => { - return { - favourites: [], - lazyLoading: false, - maxId: null - } -} - -const initStore = () => { - return { - namespaced: true, - state: state(), - actions: Favourites.actions, - mutations: Favourites.mutations - } -} - -const contentsStore = () => ({ - namespaced: true, - modules: { - Favourites: initStore() - } -}) - -const timelineStore = () => ({ - namespaced: true, - state: { - account: { - accessToken: 'token', - baseURL: 'http://localhost' - }, - sns: 'mastodon' - }, - modules: { - Contents: contentsStore() - } -}) - -const appState = { - namespaced: true, - state: { - proxyConfiguration: false - } -} - -describe('Favourites', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: timelineStore(), - App: appState - } - }) - }) - - describe('fetchFavourites', () => { - it('does not exist header', async () => { - await store.dispatch('TimelineSpace/Contents/Favourites/fetchFavourites', localAccount) - expect(store.state.TimelineSpace.Contents.Favourites.favourites).toEqual([status1]) - expect(store.state.TimelineSpace.Contents.Favourites.maxId).toEqual(null) - }) - - it('link is null', async () => { - mockClient.getFavourites = () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [status1], - status: 200, - statusText: 'OK', - headers: { - link: null - } - } - resolve(res) - }) - } - await store.dispatch('TimelineSpace/Contents/Favourites/fetchFavourites', localAccount) - expect(store.state.TimelineSpace.Contents.Favourites.favourites).toEqual([status1]) - expect(store.state.TimelineSpace.Contents.Favourites.maxId).toEqual(null) - }) - - it('link exists in header', async () => { - mockClient.getFavourites = () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [status1], - status: 200, - statusText: 'OK', - headers: { - link: '; rel="next"' - } - } - resolve(res) - }) - } - - await store.dispatch('TimelineSpace/Contents/Favourites/fetchFavourites', localAccount) - expect(store.state.TimelineSpace.Contents.Favourites.favourites).toEqual([status1]) - expect(store.state.TimelineSpace.Contents.Favourites.maxId).toEqual('2') - }) - }) - - describe('lazyFetchFavourites', () => { - describe('lazyLoading', () => { - beforeAll(() => { - state = () => { - return { - favourites: [], - lazyLoading: true, - maxId: null - } - } - }) - it('should not be updated', async () => { - const res = await store.dispatch('TimelineSpace/Contents/Favourites/lazyFetchFavourites') - expect(res).toEqual(null) - }) - }) - - describe('does not exist maxId', () => { - beforeAll(() => { - state = () => { - return { - favourites: [], - lazyLoading: false, - maxId: null - } - } - }) - it('should not be updated', async () => { - const res = await store.dispatch('TimelineSpace/Contents/Favourites/lazyFetchFavourites') - expect(res).toEqual(null) - }) - }) - - describe('fetch', () => { - beforeAll(() => { - state = () => { - return { - favourites: [status1], - lazyLoading: false, - maxId: '2' - } - } - }) - it('link is null', async () => { - mockClient.getFavourites = () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [status2], - status: 200, - statusText: 'OK', - headers: { - link: null - } - } - resolve(res) - }) - } - - await store.dispatch('TimelineSpace/Contents/Favourites/lazyFetchFavourites') - expect(store.state.TimelineSpace.Contents.Favourites.favourites).toEqual([status1, status2]) - expect(store.state.TimelineSpace.Contents.Favourites.maxId).toEqual(null) - }) - - it('link exists in header', async () => { - mockClient.getFavourites = () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [status2], - status: 200, - statusText: 'OK', - headers: { - link: '; rel="next"' - } - } - resolve(res) - }) - } - - await store.dispatch('TimelineSpace/Contents/Favourites/lazyFetchFavourites') - expect(store.state.TimelineSpace.Contents.Favourites.favourites).toEqual([status1, status2]) - expect(store.state.TimelineSpace.Contents.Favourites.maxId).toEqual('3') - }) - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/FollowRequests.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/FollowRequests.spec.ts deleted file mode 100644 index 6198ffbdcb..0000000000 --- a/spec/renderer/integration/store/TimelineSpace/Contents/FollowRequests.spec.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { Entity, Response } from 'megalodon' -import { createStore, Store } from 'vuex' -import FollowRequests, { FollowRequestsState } from '@/store/TimelineSpace/Contents/FollowRequests' -import { SideMenuState } from '@/store/TimelineSpace/SideMenu' -import { RootState } from '@/store' - -const mockClient = { - getFollowRequests: () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [account], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - }, - acceptFollowRequest: () => { - return new Promise>(resolve => { - const res: Response<{}> = { - data: {}, - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - }, - rejectFollowRequest: () => { - return new Promise>(resolve => { - const res: Response<{}> = { - data: {}, - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } -} - -jest.mock('megalodon', () => ({ - ...jest.requireActual('megalodon'), - default: jest.fn(() => mockClient), - __esModule: true -})) - -const account: Entity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: null, - bot: false -} - -let state = (): FollowRequestsState => { - return { - requests: [] - } -} - -const initStore = () => { - return { - namespaced: true, - state: state(), - actions: FollowRequests.actions, - mutations: FollowRequests.mutations - } -} - -const sideMenuState = (): SideMenuState => { - return { - unreadHomeTimeline: false, - unreadNotifications: false, - unreadMentions: false, - unreadLocalTimeline: false, - unreadDirectMessagesTimeline: false, - unreadPublicTimeline: false, - unreadFollowRequests: false, - lists: [], - tags: [], - collapse: false, - enabledTimelines: { - home: true, - notification: true, - mention: true, - direct: true, - favourite: true, - bookmark: true, - local: true, - public: true, - tag: true, - list: true - } - } -} - -const sideMenuStore = () => ({ - namespaced: true, - state: sideMenuState(), - actions: { - fetchFollowRequests: jest.fn() - }, - mutations: {} -}) - -const contentsStore = () => ({ - namespaced: true, - modules: { - FollowRequests: initStore() - } -}) - -const timelineStore = () => ({ - namespaced: true, - modules: { - SideMenu: sideMenuStore(), - Contents: contentsStore() - }, - state: { - account: { - accessToken: 'token', - baseURL: 'http://localhost' - }, - sns: 'mastodon' - } -}) - -const appState = { - namespaced: true, - state: { - proxyConfiguration: false - } -} - -describe('Home', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: timelineStore(), - App: appState - } - }) - }) - - describe('fetchRequests', () => { - it('should be updated', async () => { - await store.dispatch('TimelineSpace/Contents/FollowRequests/fetchRequests') - expect(store.state.TimelineSpace.Contents.FollowRequests.requests).toEqual([account]) - }) - }) - - describe('acceptRequest', () => { - beforeAll(() => { - state = () => { - return { - requests: [account] - } - } - }) - it('should be succeed', async () => { - mockClient.getFollowRequests = () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } - - await store.dispatch('TimelineSpace/Contents/FollowRequests/acceptRequest', account) - expect(store.state.TimelineSpace.Contents.FollowRequests.requests).toEqual([]) - }) - }) - - describe('rejectRequest', () => { - beforeAll(() => { - state = () => { - return { - requests: [account] - } - } - }) - it('should be succeed', async () => { - mockClient.getFollowRequests = () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } - - await store.dispatch('TimelineSpace/Contents/FollowRequests/rejectRequest', account) - expect(store.state.TimelineSpace.Contents.FollowRequests.requests).toEqual([]) - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/Hashtag/List.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/Hashtag/List.spec.ts deleted file mode 100644 index 0dde038de3..0000000000 --- a/spec/renderer/integration/store/TimelineSpace/Contents/Hashtag/List.spec.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { IpcMainInvokeEvent } from 'electron' -import { createStore, Store } from 'vuex' -import { ipcMain, ipcRenderer } from '~/spec/mock/electron' -import { LocalTag } from '~/src/types/localTag' -import List, { ListState } from '@/store/TimelineSpace/Contents/Hashtag/List' -import { MyWindow } from '~/src/types/global' -import { RootState } from '@/store' -;(window as any as MyWindow).ipcRenderer = ipcRenderer - -const tag1: LocalTag = { - tagName: 'tag1', - _id: '1' -} - -const tag2: LocalTag = { - tagName: 'tag2', - _id: '2' -} - -const state = (): ListState => { - return { - tags: [] - } -} - -const initStore = () => { - return { - namespaced: true, - state: state(), - actions: List.actions, - mutations: List.mutations - } -} - -const hashtagStore = () => ({ - namespaced: true, - modules: { - List: initStore() - } -}) - -const contentsStore = () => ({ - namespaced: true, - modules: { - Hashtag: hashtagStore() - } -}) - -const sideMenuStore = { - namespaced: true, - actions: { - listTags: jest.fn() - } -} - -const timelineStore = () => ({ - namespaced: true, - modules: { - SideMenu: sideMenuStore, - Contents: contentsStore() - } -}) - -describe('Hashtag/List', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: timelineStore() - } - }) - }) - - describe('listTags', () => { - it('should be updated', async () => { - ipcMain.handle('list-hashtags', () => { - return [tag1, tag2] - }) - afterEach(() => { - ipcMain.removeHandler('list-hashtags') - }) - await store.dispatch('TimelineSpace/Contents/Hashtag/List/listTags') - expect(store.state.TimelineSpace.Contents.Hashtag.List.tags).toEqual([tag1, tag2]) - }) - }) - - describe('removeTag', () => { - it('should be updated', async () => { - ipcMain.handle('remove-hashtag', (_: IpcMainInvokeEvent, tag: LocalTag) => { - expect(tag).toEqual(tag1) - }) - await store.dispatch('TimelineSpace/Contents/Hashtag/List/removeTag', tag1) - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/Hashtag/Tag.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/Hashtag/Tag.spec.ts deleted file mode 100644 index eeff2afe97..0000000000 --- a/spec/renderer/integration/store/TimelineSpace/Contents/Hashtag/Tag.spec.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { Response, Entity } from 'megalodon' -import { createStore, Store } from 'vuex' -import Tag, { TagState } from '@/store/TimelineSpace/Contents/Hashtag/Tag' -import { LoadPositionWithTag } from '@/types/loadPosition' -import { RootState } from '@/store' - -const mockClient = { - getTagTimeline: () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [status1], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } -} - -jest.mock('megalodon', () => ({ - ...jest.requireActual('megalodon'), - default: jest.fn(() => mockClient), - __esModule: true -})) - -const account: Entity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: null, - bot: false -} -const status1: Entity.Status = { - id: '1', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'hoge', - plain_content: 'hoge', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} -const status2: Entity.Status = { - id: '2', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'fuga', - plain_content: 'fuga', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} - -let state = (): TagState => { - return { - lazyLoading: false, - heading: true, - timeline: [], - unreads: [] - } -} - -const initStore = () => { - return { - namespaced: true, - state: state(), - actions: Tag.actions, - mutations: Tag.mutations - } -} - -const hashtagStore = () => ({ - namespaced: true, - modules: { - Tag: initStore() - } -}) - -const contentsStore = () => ({ - namespaced: true, - modules: { - Hashtag: hashtagStore() - } -}) - -const timelineStore = () => ({ - namespaced: true, - state: { - account: { - accessToken: 'token', - baseURL: 'http://localhost' - } - }, - modules: { - Contents: contentsStore() - } -}) - -const appState = { - namespaced: true, - state: { - proxyConfiguration: false - } -} - -describe('Home', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: timelineStore(), - App: appState - } - }) - }) - - describe('fetch', () => { - it('should be updated', async () => { - const statuses = await store.dispatch('TimelineSpace/Contents/Hashtag/Tag/fetch', 'tag') - expect(statuses).toEqual([status1]) - expect(store.state.TimelineSpace.Contents.Hashtag.Tag.timeline).toEqual([status1]) - }) - }) - - describe('lazyFetchTimeline', () => { - describe('success', () => { - beforeAll(() => { - state = () => { - return { - lazyLoading: false, - heading: true, - timeline: [status1], - unreads: [] - } - } - }) - it('should be updated', async () => { - mockClient.getTagTimeline = () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [status2], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } - const loadPositionWithTag: LoadPositionWithTag = { - status: status1, - tag: 'tag' - } - await store.dispatch('TimelineSpace/Contents/Hashtag/Tag/lazyFetchTimeline', loadPositionWithTag) - expect(store.state.TimelineSpace.Contents.Hashtag.Tag.lazyLoading).toEqual(false) - expect(store.state.TimelineSpace.Contents.Hashtag.Tag.timeline).toEqual([status1, status2]) - }) - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/Home.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/Home.spec.ts deleted file mode 100644 index fde3aafc97..0000000000 --- a/spec/renderer/integration/store/TimelineSpace/Contents/Home.spec.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { Response, Entity } from 'megalodon' -import { createStore, Store } from 'vuex' -import Home, { HomeState } from '@/store/TimelineSpace/Contents/Home' -import { RootState } from '@/store' - -const mockClient = { - getHomeTimeline: () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [status1], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } -} - -jest.mock('megalodon', () => ({ - ...jest.requireActual('megalodon'), - default: jest.fn(() => mockClient), - __esModule: true -})) - -const account: Entity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: null, - bot: false -} -const status1: Entity.Status = { - id: '1', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'hoge', - plain_content: 'hoge', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} -const status2: Entity.Status = { - id: '2', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'fuga', - plain_content: 'fuga', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} - -let state = (): HomeState => { - return { - lazyLoading: false, - heading: true, - timeline: [], - unreads: [], - showReblogs: true, - showReplies: true - } -} - -const homeStore = () => { - return { - namespaced: true, - state: state(), - actions: Home.actions, - mutations: Home.mutations - } -} - -const contentsStore = () => ({ - namespaced: true, - modules: { - Home: homeStore() - } -}) - -const timelineStore = () => ({ - namespaced: true, - state: { - account: { - accessToken: 'token', - baseURL: 'http://localhost' - }, - timelineSetting: { - useMarker: { - home: false, - notifications: false - } - } - }, - modules: { - Contents: contentsStore() - } -}) - -const appState = { - namespaced: true, - state: { - proxyConfiguration: false - } -} - -describe('Home', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: timelineStore(), - App: appState - } - }) - }) - - describe('fetchTimeline', () => { - it('should be updated', async () => { - const statuses = await store.dispatch('TimelineSpace/Contents/Home/fetchTimeline') - expect(statuses).toEqual([status1]) - expect(store.state.TimelineSpace.Contents.Home.timeline).toEqual([status1]) - }) - }) - - describe('lazyFetchTimeline', () => { - describe('success', () => { - beforeAll(() => { - state = () => { - return { - lazyLoading: false, - heading: true, - timeline: [status1], - unreads: [], - showReblogs: true, - showReplies: true - } - } - }) - it('should be updated', async () => { - mockClient.getHomeTimeline = () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [status2], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } - - await store.dispatch('TimelineSpace/Contents/Home/lazyFetchTimeline', status1) - expect(store.state.TimelineSpace.Contents.Home.lazyLoading).toEqual(false) - expect(store.state.TimelineSpace.Contents.Home.timeline).toEqual([status1, status2]) - }) - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/Lists/Edit.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/Lists/Edit.spec.ts deleted file mode 100644 index ce44bc450b..0000000000 --- a/spec/renderer/integration/store/TimelineSpace/Contents/Lists/Edit.spec.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { Response, Entity } from 'megalodon' -import { createStore, Store } from 'vuex' -import Edit, { EditState } from '@/store/TimelineSpace/Contents/Lists/Edit' -import { RemoveAccountFromList } from '@/types/removeAccountFromList' -import { RootState } from '@/store' - -const mockClient = { - getAccountsInList: () => { - return new Promise>(resolve => { - const res: Response = { - data: [account], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - }, - deleteAccountsFromList: () => { - return new Promise>(resolve => { - const res: Response<{}> = { - data: {}, - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } -} - -jest.mock('megalodon', () => ({ - ...jest.requireActual('megalodon'), - default: jest.fn(() => mockClient), - __esModule: true -})) - -const account: Entity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: null, - bot: false -} - -const state = (): EditState => { - return { - members: [] - } -} - -const initStore = () => { - return { - namespaced: true, - state: state(), - actions: Edit.actions, - mutations: Edit.mutations - } -} - -const listsStore = () => ({ - namespaced: true, - modules: { - Edit: initStore() - } -}) - -const contentsStore = () => ({ - namespaced: true, - modules: { - Lists: listsStore() - } -}) - -const timelineStore = () => ({ - namespaced: true, - state: { - account: { - accessToken: 'token', - baseURL: 'http://localhost' - }, - sns: 'mastodon' - }, - modules: { - Contents: contentsStore() - } -}) - -const appState = { - namespaced: true, - state: { - proxyConfiguration: false - } -} - -describe('Lists/Edit', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: timelineStore(), - App: appState - } - }) - }) - - describe('fetchMembers', () => { - it('should get', async () => { - await store.dispatch('TimelineSpace/Contents/Lists/Edit/fetchMembers', 'id') - expect(store.state.TimelineSpace.Contents.Lists.Edit.members).toEqual([account]) - }) - }) - - describe('removeAccount', () => { - it('should be removed', async () => { - const removeFromList: RemoveAccountFromList = { - account: account, - listId: 'id' - } - const res = await store.dispatch('TimelineSpace/Contents/Lists/Edit/removeAccount', removeFromList) - expect(res.data).toEqual({}) - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/Lists/Index.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/Lists/Index.spec.ts deleted file mode 100644 index 0b10ac582d..0000000000 --- a/spec/renderer/integration/store/TimelineSpace/Contents/Lists/Index.spec.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { Response, Entity } from 'megalodon' -import { createStore, Store } from 'vuex' -import Index, { IndexState } from '@/store/TimelineSpace/Contents/Lists/Index' -import { RootState } from '@/store' - -const mockClient = { - getLists: () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [list], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - }, - createList: () => { - return new Promise>(resolve => { - const res: Response = { - data: list, - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } -} - -jest.mock('megalodon', () => ({ - ...jest.requireActual('megalodon'), - default: jest.fn(() => mockClient), - __esModule: true -})) - -const list: Entity.List = { - id: '1', - title: 'list1' -} - -const state = (): IndexState => { - return { - lists: [] - } -} - -const initStore = () => { - return { - namespaced: true, - state: state(), - actions: Index.actions, - mutations: Index.mutations - } -} - -const listsStore = () => ({ - namespaced: true, - modules: { - Index: initStore() - } -}) - -const contentsStore = () => ({ - namespaced: true, - modules: { - Lists: listsStore() - } -}) - -const timelineStore = () => ({ - namespaced: true, - state: { - account: { - accessToken: 'token', - baseURL: 'http://localhost' - }, - sns: 'mastodon' - }, - modules: { - Contents: contentsStore() - } -}) - -const appState = { - namespaced: true, - state: { - proxyConfiguration: false - } -} - -describe('Lists/Index', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: timelineStore(), - App: appState - } - }) - }) - - describe('fetchLists', () => { - it('should be updated', async () => { - await store.dispatch('TimelineSpace/Contents/Lists/Index/fetchLists') - expect(store.state.TimelineSpace.Contents.Lists.Index.lists).toEqual([list]) - }) - }) - - describe('createList', () => { - it('should be created', async () => { - const res: Entity.List = await store.dispatch('TimelineSpace/Contents/Lists/Index/createList', 'list1') - expect(res.title).toEqual('list1') - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/Lists/Show.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/Lists/Show.spec.ts deleted file mode 100644 index 6b692ebbb2..0000000000 --- a/spec/renderer/integration/store/TimelineSpace/Contents/Lists/Show.spec.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { Response, Entity } from 'megalodon' -import { createStore, Store } from 'vuex' -import Show, { ShowState } from '@/store/TimelineSpace/Contents/Lists/Show' -import { LoadPositionWithList } from '@/types/loadPosition' -import { RootState } from '@/store' - -const mockClient = { - getListTimeline: () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [status1], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } -} - -jest.mock('megalodon', () => ({ - ...jest.requireActual('megalodon'), - default: jest.fn(() => mockClient), - __esModule: true -})) - -const account: Entity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: null, - bot: false -} - -const status1: Entity.Status = { - id: '1', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'hoge', - plain_content: 'hoge', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} - -const status2: Entity.Status = { - id: '2', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'fuga', - plain_content: 'fuga', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} - -let state = (): ShowState => { - return { - lazyLoading: false, - heading: true, - timeline: [], - unreads: [] - } -} - -const initStore = () => { - return { - namespaced: true, - state: state(), - actions: Show.actions, - mutations: Show.mutations - } -} - -const listsStore = () => ({ - namespaced: true, - modules: { - Show: initStore() - } -}) - -const contentsStore = () => ({ - namespaced: true, - modules: { - Lists: listsStore() - } -}) - -const timelineStore = () => ({ - namespaced: true, - state: { - account: { - accessToken: 'token', - baseURL: 'http://localhost' - } - }, - modules: { - Contents: contentsStore() - } -}) - -const appState = { - namespaced: true, - state: { - proxyConfiguration: false - } -} - -describe('Lists/Show', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: timelineStore(), - App: appState - } - }) - }) - - describe('fetchTimeline', () => { - it('should be updated', async () => { - await store.dispatch('TimelineSpace/Contents/Lists/Show/fetchTimeline', '1') - expect(store.state.TimelineSpace.Contents.Lists.Show.timeline).toEqual([status1]) - }) - }) - - describe('lazyFetchTimeline', () => { - beforeAll(() => { - state = () => { - return { - lazyLoading: false, - heading: true, - timeline: [status1], - unreads: [] - } - } - }) - it('should be updated', async () => { - mockClient.getListTimeline = () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [status2], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } - - const loadPosition: LoadPositionWithList = { - status: status1, - list_id: '1' - } - await store.dispatch('TimelineSpace/Contents/Lists/Show/lazyFetchTimeline', loadPosition) - expect(store.state.TimelineSpace.Contents.Lists.Show.timeline).toEqual([status1, status2]) - expect(store.state.TimelineSpace.Contents.Lists.Show.lazyLoading).toEqual(false) - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/Local.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/Local.spec.ts deleted file mode 100644 index 34c7287ffd..0000000000 --- a/spec/renderer/integration/store/TimelineSpace/Contents/Local.spec.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { Response, Entity } from 'megalodon' -import { createStore, Store } from 'vuex' -import Local, { LocalState } from '@/store/TimelineSpace/Contents/Local' -import { RootState } from '@/store' - -const mockClient = { - getLocalTimeline: () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [status1], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } -} - -jest.mock('megalodon', () => ({ - ...jest.requireActual('megalodon'), - default: jest.fn(() => mockClient), - __esModule: true -})) - -const account: Entity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: null, - bot: false -} -const status1: Entity.Status = { - id: '1', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'hoge', - plain_content: 'hoge', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} -const status2: Entity.Status = { - id: '2', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'fuga', - plain_content: 'fuga', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} - -let state = (): LocalState => { - return { - lazyLoading: false, - heading: true, - timeline: [], - unreads: [] - } -} - -const initStore = () => { - return { - namespaced: true, - state: state(), - actions: Local.actions, - mutations: Local.mutations - } -} - -const contentsStore = () => ({ - namespaced: true, - modules: { - Local: initStore() - } -}) - -const timelineStore = () => ({ - namespaced: true, - state: { - account: { - accessToken: 'token', - baseURL: 'http://localhost' - } - }, - modules: { - Contents: contentsStore() - } -}) - -const appState = { - namespaced: true, - state: { - proxyConfiguration: false - } -} - -describe('Home', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: timelineStore(), - App: appState - } - }) - }) - - describe('fetchLocalTimeline', () => { - it('should be updated', async () => { - const statuses = await store.dispatch('TimelineSpace/Contents/Local/fetchLocalTimeline') - expect(statuses).toEqual([status1]) - expect(store.state.TimelineSpace.Contents.Local.timeline).toEqual([status1]) - }) - }) - - describe('lazyFetchTimeline', () => { - describe('success', () => { - beforeAll(() => { - state = () => { - return { - lazyLoading: false, - heading: true, - timeline: [status1], - unreads: [] - } - } - }) - it('should be updated', async () => { - mockClient.getLocalTimeline = () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [status2], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } - - await store.dispatch('TimelineSpace/Contents/Local/lazyFetchTimeline', status1) - expect(store.state.TimelineSpace.Contents.Local.lazyLoading).toEqual(false) - expect(store.state.TimelineSpace.Contents.Local.timeline).toEqual([status1, status2]) - }) - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/Mentions.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/Mentions.spec.ts deleted file mode 100644 index ce000e540b..0000000000 --- a/spec/renderer/integration/store/TimelineSpace/Contents/Mentions.spec.ts +++ /dev/null @@ -1,248 +0,0 @@ -import { RootState } from '@/store' -import { Response, Entity } from 'megalodon' -import { createStore, Store } from 'vuex' -import Mentions from '~/src/renderer/store/TimelineSpace/Contents/Mentions' - -const mockClient = { - getNotifications: () => { - return new Promise>(resolve => { - const res: Response = { - data: [mention, reblog, favourite, follow], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } -} - -jest.mock('megalodon', () => ({ - ...jest.requireActual('megalodon'), - default: jest.fn(() => mockClient), - __esModule: true -})) - -const account: Entity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: null, - bot: false -} - -const status: Entity.Status = { - id: '1', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'hoge', - plain_content: 'hoge', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} - -const mention: Entity.Notification = { - account: account, - created_at: '2019-03-26T21:40:32', - id: '1', - status: status, - type: 'mention' -} - -const reblog: Entity.Notification = { - account: account, - created_at: '2019-03-26T21:40:32', - id: '2', - status: status, - type: 'reblog' -} - -const favourite: Entity.Notification = { - account: account, - created_at: '2019-03-26T21:40:32', - id: '3', - status: status, - type: 'favourite' -} - -const follow: Entity.Notification = { - account: account, - created_at: '2019-03-26T21:40:32', - id: '4', - type: 'follow' -} - -let state: Function = () => { - return { - lazyLoading: false, - heading: true, - mentions: [] - } -} - -const initStore = () => { - return { - namespaced: true, - state: state(), - actions: Mentions.actions, - mutations: Mentions.mutations, - getters: Mentions.getters - } -} - -const contentsStore = () => ({ - namespaced: true, - modules: { - Mentions: initStore() - } -}) - -const timelineStore = () => ({ - namespaced: true, - state: { - account: { - accessToken: 'token', - baseURL: 'http://localhost' - }, - timelineSetting: { - useMarker: { - home: false, - notifications: false, - mentions: false - } - } - }, - modules: { - Contents: contentsStore() - } -}) - -const appState = { - namespaced: true, - state: { - proxyConfiguration: false - } -} - -describe('Mentions', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: timelineStore(), - App: appState - } - }) - }) - - describe('fetchMentions', () => { - it('should be updated', async () => { - await store.dispatch('TimelineSpace/Contents/Mentions/fetchMentions') - expect(store.state.TimelineSpace.Contents.Mentions.mentions).toEqual([mention, reblog, favourite, follow]) - }) - }) - - describe('lazyFetchMentions', () => { - describe('loading', () => { - beforeAll(() => { - state = () => { - return { - lazyLoading: true, - heading: true, - mentions: [] - } - } - }) - it('should not be updated', async () => { - const result = await store.dispatch('TimelineSpace/Contents/Mentions/lazyFetchMentions', {}) - expect(result).toEqual(null) - }) - }) - - describe('success', () => { - beforeAll(() => { - state = () => { - return { - lazyLoading: false, - heading: true, - mentions: [mention, reblog] - } - } - }) - it('should be updated', async () => { - mockClient.getNotifications = () => { - return new Promise>(resolve => { - const res: Response = { - data: [favourite, follow], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } - - await store.dispatch('TimelineSpace/Contents/Mentions/lazyFetchMentions', { id: 1 }) - expect(store.state.TimelineSpace.Contents.Mentions.mentions).toEqual([mention, reblog, favourite, follow]) - expect(store.state.TimelineSpace.Contents.Mentions.lazyLoading).toEqual(false) - }) - }) - }) - - describe('mentions', () => { - beforeAll(() => { - state = () => { - return { - lazyLoading: false, - heading: true, - mentions: [mention, favourite, reblog, follow] - } - } - }) - it('should return only mentions', () => { - const mentions = store.getters['TimelineSpace/Contents/Mentions/mentions'] - expect(mentions).toEqual([mention]) - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/Notifications.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/Notifications.spec.ts deleted file mode 100644 index f52f437040..0000000000 --- a/spec/renderer/integration/store/TimelineSpace/Contents/Notifications.spec.ts +++ /dev/null @@ -1,293 +0,0 @@ -import { Response, Entity } from 'megalodon' -import { createStore, Store } from 'vuex' -import Notifications, { NotificationsState } from '@/store/TimelineSpace/Contents/Notifications' -import { RootState } from '@/store' - -const mockClient = { - getNotifications: () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [notification1], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } -} - -jest.mock('megalodon', () => ({ - ...jest.requireActual('megalodon'), - default: jest.fn(() => mockClient), - __esModule: true -})) - -const account1: Entity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: null, - bot: false -} - -const account2: Entity.Account = { - id: '2', - username: 'h3poteto', - acct: 'h3poteto@mstdn.io', - display_name: 'h3poteto', - locked: false, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://mstdn.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: null, - bot: false -} - -const status1: Entity.Status = { - id: '1', - uri: 'http://example.com', - url: 'http://example.com', - account: account1, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'hoge', - plain_content: 'hoge', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} - -const status2: Entity.Status = { - id: '2', - uri: 'http://example.com', - url: 'http://example.com', - account: account1, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'hoge', - plain_content: 'hoge', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} - -const rebloggedStatus: Entity.Status = { - id: '3', - uri: 'http://example.com', - url: 'http://example.com', - account: account1, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: status2, - content: 'hoge', - plain_content: 'hoge', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} - -const notification1: Entity.Notification = { - id: '1', - account: account2, - status: status1, - type: 'favourite', - created_at: '2019-04-01T17:01:32' -} - -const notification2: Entity.Notification = { - id: '2', - account: account2, - status: rebloggedStatus, - type: 'mention', - created_at: '2019-04-01T17:01:32' -} - -let state = (): NotificationsState => { - return { - lazyLoading: false, - heading: true, - notifications: [] - } -} - -const initStore = () => { - return { - namespaced: true, - state: state(), - actions: Notifications.actions, - mutations: Notifications.mutations - } -} - -const contentsStore = () => ({ - namespaced: true, - modules: { - Notifications: initStore() - } -}) - -const timelineStore = () => ({ - namespaced: true, - state: { - account: { - accessToken: 'token', - baseURL: 'http://localhost' - }, - timelineSetting: { - useMarker: { - home: false, - notifications: false, - mentions: false - } - } - }, - modules: { - Contents: contentsStore() - } -}) - -const appState = { - namespaced: true, - state: { - proxyConfiguration: false, - useMarkerTimeline: [] - } -} - -describe('Notifications', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: timelineStore(), - App: appState - } - }) - }) - - describe('fetchNotifications', () => { - it('should be updated', async () => { - const response = await store.dispatch('TimelineSpace/Contents/Notifications/fetchNotifications') - expect(response).toEqual([notification1]) - expect(store.state.TimelineSpace.Contents.Notifications.notifications).toEqual([notification1]) - }) - }) - - describe('lazyFetchNotifications', () => { - beforeAll(() => { - state = () => { - return { - lazyLoading: false, - heading: true, - notifications: [notification1] - } - } - }) - it('should be updated', async () => { - mockClient.getNotifications = () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [notification2], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } - await store.dispatch('TimelineSpace/Contents/Notifications/lazyFetchNotifications', notification1) - expect(store.state.TimelineSpace.Contents.Notifications.lazyLoading).toEqual(false) - expect(store.state.TimelineSpace.Contents.Notifications.notifications).toEqual([notification1, notification2]) - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/Public.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/Public.spec.ts deleted file mode 100644 index 1386e81bdb..0000000000 --- a/spec/renderer/integration/store/TimelineSpace/Contents/Public.spec.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { Response, Entity } from 'megalodon' -import { createStore, Store } from 'vuex' -import Public, { PublicState } from '@/store/TimelineSpace/Contents/Public' -import { RootState } from '@/store' - -const mockClient = { - getPublicTimeline: () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [status1], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } -} - -jest.mock('megalodon', () => ({ - ...jest.requireActual('megalodon'), - default: jest.fn(() => mockClient), - __esModule: true -})) - -const account: Entity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: null, - bot: false -} -const status1: Entity.Status = { - id: '1', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'hoge', - plain_content: 'hoge', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} -const status2: Entity.Status = { - id: '2', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'fuga', - plain_content: 'fuga', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} - -let state = (): PublicState => { - return { - lazyLoading: false, - heading: true, - timeline: [], - unreads: [] - } -} - -const initStore = () => { - return { - namespaced: true, - state: state(), - actions: Public.actions, - mutations: Public.mutations - } -} - -const contentsStore = () => ({ - namespaced: true, - modules: { - Public: initStore() - } -}) - -const timelineStore = () => ({ - namespaced: true, - state: { - account: { - accessToken: 'token', - baseURL: 'http://localhost' - } - }, - modules: { - Contents: contentsStore() - } -}) - -const appState = { - namespaced: true, - state: { - proxyConfiguration: false - } -} - -describe('Home', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: timelineStore(), - App: appState - } - }) - }) - - describe('fetchPublicTimeline', () => { - it('should be updated', async () => { - const statuses = await store.dispatch('TimelineSpace/Contents/Public/fetchPublicTimeline') - expect(statuses).toEqual([status1]) - expect(store.state.TimelineSpace.Contents.Public.timeline).toEqual([status1]) - }) - }) - - describe('lazyFetchTimeline', () => { - describe('success', () => { - beforeAll(() => { - state = () => { - return { - lazyLoading: false, - heading: true, - timeline: [status1], - unreads: [] - } - } - }) - it('should be updated', async () => { - mockClient.getPublicTimeline = () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [status2], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } - await store.dispatch('TimelineSpace/Contents/Public/lazyFetchTimeline', status1) - expect(store.state.TimelineSpace.Contents.Public.lazyLoading).toEqual(false) - expect(store.state.TimelineSpace.Contents.Public.timeline).toEqual([status1, status2]) - }) - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/Search/Account.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/Search/Account.spec.ts deleted file mode 100644 index 643834e4c6..0000000000 --- a/spec/renderer/integration/store/TimelineSpace/Contents/Search/Account.spec.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { Response, Entity } from 'megalodon' -import { createStore, Store } from 'vuex' -import AccountStore, { AccountState } from '@/store/TimelineSpace/Contents/Search/Account' -import { RootState } from '@/store' - -const account: Entity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: null, - bot: false -} - -const mockClient = { - searchAccount: () => { - return new Promise>>(resolve => { - const res: Response> = { - data: [account], - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } -} - -jest.mock('megalodon', () => ({ - ...jest.requireActual('megalodon'), - default: jest.fn(() => mockClient), - __esModule: true -})) - -const state = (): AccountState => { - return { - results: [] - } -} - -const initStore = () => { - return { - namespaced: true, - state: state(), - actions: AccountStore.actions, - mutations: AccountStore.mutations - } -} - -const searchStore = () => ({ - namespaced: true, - modules: { - Account: initStore() - } -}) - -const contentsStore = () => ({ - namespaced: true, - state: {}, - mutations: { - changeLoading: jest.fn() - }, - actions: {}, - modules: { - Search: searchStore() - } -}) - -const timelineStore = () => ({ - namespaced: true, - modules: { - Contents: contentsStore() - }, - state: { - account: { - accessToken: 'token', - baseURL: 'http://localhost' - }, - sns: 'mastodon' - } -}) - -const appState = { - namespaced: true, - state: { - proxyConfiguration: false - } -} - -describe('Search/Account', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: timelineStore(), - App: appState - } - }) - }) - - describe('search', () => { - it('should be updated', async () => { - await store.dispatch('TimelineSpace/Contents/Search/Account/search', 'query') - expect(store.state.TimelineSpace.Contents.Search.Account.results).toEqual([account]) - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/Search/Tag.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/Search/Tag.spec.ts deleted file mode 100644 index 6a47f1a773..0000000000 --- a/spec/renderer/integration/store/TimelineSpace/Contents/Search/Tag.spec.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { Response, Entity } from 'megalodon' -import { createStore, Store } from 'vuex' -import TagStore, { TagState } from '@/store/TimelineSpace/Contents/Search/Tag' -import { RootState } from '@/store' - -const tag1: Entity.Tag = { - name: 'tag1', - url: 'http://example.com/tag1', - history: null -} - -const mockClient = { - search: () => { - return new Promise>(resolve => { - const res: Response = { - data: { - accounts: [], - statuses: [], - hashtags: [tag1] - }, - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } -} - -jest.mock('megalodon', () => ({ - ...jest.requireActual('megalodon'), - default: jest.fn(() => mockClient), - __esModule: true -})) - -const state = (): TagState => { - return { - results: [] - } -} - -const initStore = () => { - return { - namespaced: true, - state: state(), - actions: TagStore.actions, - mutations: TagStore.mutations - } -} - -const searchStore = () => ({ - namespaced: true, - modules: { - Tag: initStore() - } -}) - -const contentsStore = () => ({ - namespaced: true, - state: {}, - mutations: { - changeLoading: jest.fn() - }, - actions: {}, - modules: { - Search: searchStore() - } -}) - -const timelineStore = () => ({ - namespaced: true, - modules: { - Contents: contentsStore() - }, - state: { - account: { - accessToken: 'token', - baseURL: 'http://localhost' - }, - sns: 'mastodon' - } -}) - -const appState = { - namespaced: true, - state: { - proxyConfiguration: false - } -} - -describe('Search/Tag', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: timelineStore(), - App: appState - } - }) - }) - - describe('search', () => { - it('should be updated', async () => { - await store.dispatch('TimelineSpace/Contents/Search/Tag/search', 'query') - expect(store.state.TimelineSpace.Contents.Search.Tag.results).toEqual([tag1]) - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/Search/Toots.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/Search/Toots.spec.ts deleted file mode 100644 index 5a29929218..0000000000 --- a/spec/renderer/integration/store/TimelineSpace/Contents/Search/Toots.spec.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { Response, Entity } from 'megalodon' -import { createStore, Store } from 'vuex' -import Toots, { TootsState } from '@/store/TimelineSpace/Contents/Search/Toots' -import { RootState } from '@/store' - -const mockClient = { - search: () => { - return new Promise>(resolve => { - const res: Response = { - data: { - accounts: [], - statuses: [status], - hashtags: [] - }, - status: 200, - statusText: 'OK', - headers: {} - } - resolve(res) - }) - } -} - -jest.mock('megalodon', () => ({ - ...jest.requireActual('megalodon'), - default: jest.fn(() => mockClient), - __esModule: true -})) - -const account: Entity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: null, - bot: false -} - -const status: Entity.Status = { - id: '1', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'hoge', - plain_content: 'hoge', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} - -const state = (): TootsState => { - return { - results: [] - } -} - -const initStore = () => { - return { - namespaced: true, - state: state(), - actions: Toots.actions, - mutations: Toots.mutations - } -} - -const searchStore = () => ({ - namespaced: true, - modules: { - Toots: initStore() - } -}) - -const contentsStore = () => ({ - namespaced: true, - state: {}, - mutations: { - changeLoading: jest.fn() - }, - actions: {}, - modules: { - Search: searchStore() - } -}) - -const timelineStore = () => ({ - namespaced: true, - modules: { - Contents: contentsStore() - }, - state: { - account: { - accessToken: 'token', - baseURL: 'http://localhost' - }, - sns: 'mastodon' - } -}) - -const appState = { - namespaced: true, - state: { - proxyConfiguration: false - } -} - -describe('Search/Account', () => { - let store: Store - - beforeEach(() => { - store = createStore({ - modules: { - TimelineSpace: timelineStore(), - App: appState - } - }) - }) - - describe('search', () => { - it('should be updated', async () => { - await store.dispatch('TimelineSpace/Contents/Search/Toots/search', 'query') - expect(store.state.TimelineSpace.Contents.Search.Toots.results).toEqual([status]) - }) - }) -}) diff --git a/spec/renderer/integration/store/TimelineSpace/Contents/SideBar/AccountProfile.spec.ts b/spec/renderer/integration/store/TimelineSpace/Contents/SideBar/AccountProfile.spec.ts index a6633d6f14..8417dd4dc1 100644 --- a/spec/renderer/integration/store/TimelineSpace/Contents/SideBar/AccountProfile.spec.ts +++ b/spec/renderer/integration/store/TimelineSpace/Contents/SideBar/AccountProfile.spec.ts @@ -40,16 +40,13 @@ const timelineStore = (account: Entity.Account | null) => ({ namespaced: true, state: { account: { - baseURL: 'https://example.com', - domain: 'example.com', - clientId: 'sampleId', - clientSecret: 'sampleSecret', accessToken: 'sampleAccessToken', - refreshToken: null, - username: 'h3poteto', - accountID: '1', - avatar: null, - order: 1 + id: 1, + username: 'h3poteto' + }, + server: { + sns: 'mastodon', + baseURL: 'https://example.com' } }, modules: { diff --git a/spec/renderer/integration/store/TimelineSpace/HeaderMenu.spec.ts b/spec/renderer/integration/store/TimelineSpace/HeaderMenu.spec.ts index 4ffd9561ee..1c9eb97e81 100644 --- a/spec/renderer/integration/store/TimelineSpace/HeaderMenu.spec.ts +++ b/spec/renderer/integration/store/TimelineSpace/HeaderMenu.spec.ts @@ -49,10 +49,12 @@ const timelineStore = () => ({ namespaced: true, state: { account: { - accessToken: 'token', - baseURL: 'http://localhost' + accessToken: 'token' }, - sns: 'mastodon' + server: { + sns: 'mastodon', + baseURL: 'http://localhost' + } }, modules: { HeaderMenu: initStore() diff --git a/spec/renderer/integration/store/TimelineSpace/Modals/AddListMember.spec.ts b/spec/renderer/integration/store/TimelineSpace/Modals/AddListMember.spec.ts index 918ed50aa6..0fc1dbf936 100644 --- a/spec/renderer/integration/store/TimelineSpace/Modals/AddListMember.spec.ts +++ b/spec/renderer/integration/store/TimelineSpace/Modals/AddListMember.spec.ts @@ -84,9 +84,12 @@ const timelineStore = () => ({ namespaced: true, state: { account: { - _id: '0' + id: 0, + accessToken: 'token' }, - sns: 'mastodon' + server: { + sns: 'mastodon' + } }, modules: { Modals: modalsStore() diff --git a/spec/renderer/integration/store/TimelineSpace/Modals/Jump.spec.ts b/spec/renderer/integration/store/TimelineSpace/Modals/Jump.spec.ts index 0237ad7607..f686fef9af 100644 --- a/spec/renderer/integration/store/TimelineSpace/Modals/Jump.spec.ts +++ b/spec/renderer/integration/store/TimelineSpace/Modals/Jump.spec.ts @@ -70,7 +70,7 @@ const timelineStore = () => ({ namespaced: true, state: { account: { - _id: '0' + id: 0 } }, modules: { diff --git a/spec/renderer/integration/store/TimelineSpace/Modals/ListMembership.spec.ts b/spec/renderer/integration/store/TimelineSpace/Modals/ListMembership.spec.ts index e937cc31fb..ae686bff4f 100644 --- a/spec/renderer/integration/store/TimelineSpace/Modals/ListMembership.spec.ts +++ b/spec/renderer/integration/store/TimelineSpace/Modals/ListMembership.spec.ts @@ -130,7 +130,12 @@ const timelineStore = () => ({ namespaced: true, state: { account: { - _id: '0' + id: 0, + accessToken: 'token' + }, + server: { + sns: 'mastodon', + baseURL: 'http://localhost' } }, modules: { diff --git a/spec/renderer/integration/store/TimelineSpace/SideMenu.spec.ts b/spec/renderer/integration/store/TimelineSpace/SideMenu.spec.ts index f24274dd9a..7a8fb87f15 100644 --- a/spec/renderer/integration/store/TimelineSpace/SideMenu.spec.ts +++ b/spec/renderer/integration/store/TimelineSpace/SideMenu.spec.ts @@ -2,10 +2,9 @@ import { Entity, Response } from 'megalodon' import { createStore, Store } from 'vuex' import { ipcMain, ipcRenderer } from '~/spec/mock/electron' import SideMenu, { SideMenuState } from '~/src/renderer/store/TimelineSpace/SideMenu' -import { LocalTag } from '~/src/types/localTag' import { MyWindow } from '~/src/types/global' import { RootState } from '@/store' -;(window as any as MyWindow).ipcRenderer = ipcRenderer +;((window as any) as MyWindow).ipcRenderer = ipcRenderer const mockClient = { getLists: () => { @@ -85,7 +84,14 @@ const appStore = { const timelineStore = () => ({ namespaced: true, state: { - sns: 'mastodon' + account: { + id: 0, + accessToken: 'token' + }, + server: { + sns: 'mastodon', + baseURL: 'http://localhost' + } }, modules: { SideMenu: initStore() @@ -145,20 +151,4 @@ describe('SideMenu', () => { expect(store.state.TimelineSpace.SideMenu.collapse).toEqual(true) }) }) - - describe('listTags', () => { - it('should be listed', async () => { - const tag1: LocalTag = { - tagName: 'tag1' - } - const tag2: LocalTag = { - tagName: 'tag2' - } - ipcMain.handle('list-hashtags', () => { - return [tag1, tag2] - }) - await store.dispatch('TimelineSpace/SideMenu/listTags') - expect(store.state.TimelineSpace.SideMenu.tags).toEqual([tag1, tag2]) - }) - }) }) diff --git a/spec/renderer/unit/store/Login.spec.ts b/spec/renderer/unit/store/Login.spec.ts index e6861b3b7e..9a19c2e1ce 100644 --- a/spec/renderer/unit/store/Login.spec.ts +++ b/spec/renderer/unit/store/Login.spec.ts @@ -5,15 +5,17 @@ describe('Login', () => { let state: LoginState beforeEach(() => { state = { - selectedInstance: null, + domain: null, searching: false, + server: null, + appData: null, sns: 'mastodon' } }) describe('changeInstance', () => { it('should be changed', () => { - Login.mutations![MUTATION_TYPES.CHANGE_INSTANCE](state, 'pleroma.io') - expect(state.selectedInstance).toEqual('pleroma.io') + Login.mutations![MUTATION_TYPES.CHANGE_DOMAIN](state, 'pleroma.io') + expect(state.domain).toEqual('pleroma.io') }) }) describe('changeSearching', () => { diff --git a/spec/renderer/unit/store/Preferences/Account.spec.ts b/spec/renderer/unit/store/Preferences/Account.spec.ts deleted file mode 100644 index 9fc23fe135..0000000000 --- a/spec/renderer/unit/store/Preferences/Account.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -import Account, { AccountState, MUTATION_TYPES } from '@/store/Preferences/Account' -import { LocalAccount } from '~/src/types/localAccount' - -const account: LocalAccount = { - _id: 'sample', - baseURL: 'http://example.com', - domain: 'example.com', - clientId: 'hoge', - clientSecret: 'hogehoge', - accessToken: null, - refreshToken: null, - username: null, - accountId: null, - avatar: null, - order: 1 -} - -describe('Preferences/Account', () => { - describe('mutations', () => { - let state: AccountState - beforeEach(() => { - state = { - accounts: [], - accountLoading: false - } - }) - describe('updateAccounts', () => { - it('should be updated', () => { - Account.mutations![MUTATION_TYPES.UPDATE_ACCOUNTS](state, [account]) - expect(state.accounts).toEqual([account]) - }) - }) - describe('updateAccountLoading', () => { - it('should be update', () => { - Account.mutations![MUTATION_TYPES.UPDATE_ACCOUNT_LOADING](state, true) - expect(state.accountLoading).toEqual(true) - }) - }) - }) -}) diff --git a/spec/renderer/unit/store/Preferences/Appearance.spec.ts b/spec/renderer/unit/store/Preferences/Appearance.spec.ts deleted file mode 100644 index 1c54f34885..0000000000 --- a/spec/renderer/unit/store/Preferences/Appearance.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -import Theme from '~/src/constants/theme' -import DisplayStyle from '~/src/constants/displayStyle' -import TimeFormat from '~/src/constants/timeFormat' -import { LightTheme } from '~/src/constants/themeColor' -import DefaultFonts from '@/utils/fonts' -import Appearance, { AppearanceState, MUTATION_TYPES } from '@/store/Preferences/Appearance' - -describe('Preferences/Appearance', () => { - let state: AppearanceState - beforeEach(() => { - state = { - appearance: { - theme: Theme.Light.key, - fontSize: 14, - displayNameStyle: DisplayStyle.DisplayNameAndUsername.value, - timeFormat: TimeFormat.Absolute.value, - customThemeColor: LightTheme, - font: DefaultFonts[0], - tootPadding: 8 - }, - fonts: [] - } - }) - describe('mutations', () => { - describe('updateAppearance', () => { - it('should be changed', () => { - Appearance.mutations![MUTATION_TYPES.UPDATE_APPEARANCE](state, { - theme: Theme.Dark.key - }) - expect(state.appearance.theme).toEqual(Theme.Dark.key) - }) - }) - describe('updateFonts', () => { - it('should be changed', () => { - Appearance.mutations![MUTATION_TYPES.UPDATE_FONTS](state, ['font']) - expect(state.fonts).toEqual(['font']) - }) - }) - }) -}) diff --git a/spec/renderer/unit/store/Preferences/General.spec.ts b/spec/renderer/unit/store/Preferences/General.spec.ts deleted file mode 100644 index d4db7079ca..0000000000 --- a/spec/renderer/unit/store/Preferences/General.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import General, { GeneralState, MUTATION_TYPES } from '@/store/Preferences/General' - -describe('Preferences/General', () => { - let state: GeneralState - beforeEach(() => { - state = { - general: { - sound: { - fav_rb: true, - toot: true - }, - timeline: { - cw: false, - nsfw: false, - hideAllAttachments: false - }, - other: { - launch: false - } - }, - loading: false - } - }) - - describe('mutations', () => { - it('updateGeneral', () => { - General.mutations![MUTATION_TYPES.UPDATE_GENERAL](state, { - sound: { - fav_rb: false, - toot: false - } - }) - expect(state.general.sound.fav_rb).toEqual(false) - expect(state.general.sound.toot).toEqual(false) - }) - }) -}) diff --git a/spec/renderer/unit/store/Preferences/Language.spec.ts b/spec/renderer/unit/store/Preferences/Language.spec.ts deleted file mode 100644 index 05fcd89513..0000000000 --- a/spec/renderer/unit/store/Preferences/Language.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import Language, { LanguageState, MUTATION_TYPES } from '@/store/Preferences/Language' -import DefaultLanguage from '~/src/constants/language' - -describe('Preferences/Language', () => { - let state: LanguageState - beforeEach(() => { - state = { - language: { - language: DefaultLanguage.en.key, - spellchecker: { - enabled: true, - languages: [] - } - } - } - }) - describe('mutations', () => { - describe('updateLanguage', () => { - it('should be updated', () => { - Language.mutations![MUTATION_TYPES.UPDATE_LANGUAGE](state, { - language: DefaultLanguage.ja.key - }) - expect(state.language.language).toEqual(DefaultLanguage.ja.key) - }) - }) - describe('changeLanguage', () => { - it('should be changed', () => { - Language.mutations![MUTATION_TYPES.CHANGE_LANGUAGE](state, DefaultLanguage.ja.key) - expect(state.language.language).toEqual(DefaultLanguage.ja.key) - }) - }) - }) -}) diff --git a/spec/renderer/unit/store/Preferences/Notification.spec.ts b/spec/renderer/unit/store/Preferences/Notification.spec.ts deleted file mode 100644 index 93c6670425..0000000000 --- a/spec/renderer/unit/store/Preferences/Notification.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import Notification, { NotificationState, MUTATION_TYPES } from '@/store/Preferences/Notification' - -describe('Preferences/Notification', () => { - let state: NotificationState - beforeEach(() => { - state = { - notification: { - notify: { - reply: true, - reblog: true, - favourite: true, - follow: true, - follow_request: true, - reaction: true, - status: true, - poll_vote: true, - poll_expired: true - } - } - } - }) - describe('mutations', () => { - it('updateNotification', () => { - Notification.mutations![MUTATION_TYPES.UPDATE_NOTIFICATION](state, { - notify: { - reply: false, - reblog: false, - favourite: false, - follow: false, - follow_request: false, - reaction: false, - status: false, - poll_vote: false, - poll_expired: false - } - }) - expect(state.notification.notify).toEqual({ - reply: false, - reblog: false, - favourite: false, - follow: false, - follow_request: false, - reaction: false, - status: false, - poll_vote: false, - poll_expired: false - }) - }) - }) -}) diff --git a/spec/renderer/unit/store/TimelineSpace.spec.ts b/spec/renderer/unit/store/TimelineSpace.spec.ts index e016dc5ff3..f73ce71f1a 100644 --- a/spec/renderer/unit/store/TimelineSpace.spec.ts +++ b/spec/renderer/unit/store/TimelineSpace.spec.ts @@ -1,4 +1,4 @@ -import TimelineSpace, { TimelineSpaceState, blankAccount, MUTATION_TYPES } from '~/src/renderer/store/TimelineSpace' +import TimelineSpace, { TimelineSpaceState, MUTATION_TYPES } from '~/src/renderer/store/TimelineSpace' import { DefaultSetting } from '~/src/constants/initializer/setting' describe('TimelineSpace', () => { @@ -6,13 +6,12 @@ describe('TimelineSpace', () => { let state: TimelineSpaceState beforeEach(() => { state = { - account: blankAccount, - bindingAccount: null, + account: null, + server: null, loading: false, emojis: [], tootMax: 500, - timelineSetting: DefaultSetting.timeline, - sns: 'mastodon', + setting: DefaultSetting, filters: [] } }) diff --git a/spec/renderer/unit/store/TimelineSpace/Modals/Jump.spec.ts b/spec/renderer/unit/store/TimelineSpace/Modals/Jump.spec.ts index 48e436effb..f6d3b7ce19 100644 --- a/spec/renderer/unit/store/TimelineSpace/Modals/Jump.spec.ts +++ b/spec/renderer/unit/store/TimelineSpace/Modals/Jump.spec.ts @@ -88,10 +88,14 @@ describe('TimelineSpace/Modals/Jump', () => { describe('updateTagChannel', () => { it('should be updated', () => { const whalebird: LocalTag = { - tagName: 'whalebird' + tagName: 'whalebird', + id: 0, + accountId: 1 } const tqrk: LocalTag = { - tagName: 'tqrk' + tagName: 'tqrk', + id: 0, + accountId: 1 } const channelList = [whalebird, tqrk] Jump.mutations![MUTATION_TYPES.UPDATE_TAG_CHANNEL](state, channelList) diff --git a/src/constants/initializer/setting.ts b/src/constants/initializer/setting.ts index 563f803cea..298dcfd926 100644 --- a/src/constants/initializer/setting.ts +++ b/src/constants/initializer/setting.ts @@ -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 } diff --git a/src/main/cache/account.ts b/src/main/cache/account.ts deleted file mode 100644 index 9f031758b0..0000000000 --- a/src/main/cache/account.ts +++ /dev/null @@ -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> { - return new Promise((resolve, reject) => { - this.db.find({ owner_id: ownerID }, (err, docs) => { - if (err) return reject(err) - resolve(docs) - }) - }) - } - - insertAccount(ownerID: string, acct: string): Promise { - return new Promise((resolve, reject) => { - // At first confirm records for unique. - this.db.findOne({ 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({ owner_id: ownerID, acct: acct }, (err, doc) => { - if (err) return reject(err) - return resolve(doc) - }) - }) - }) - } -} diff --git a/src/main/cache/hashtag.ts b/src/main/cache/hashtag.ts deleted file mode 100644 index 821a2d60cd..0000000000 --- a/src/main/cache/hashtag.ts +++ /dev/null @@ -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> { - return new Promise((resolve, reject) => { - this.db.find({}, (err, docs) => { - if (err) return reject(err) - resolve(docs) - }) - }) - } - - insertHashtag(tag: string): Promise { - return new Promise((resolve, reject) => { - // Ignore error for unique constraints. - this.db.insert({ tagName: tag }, (err, doc) => { - if (err) return reject(err) - resolve(doc) - }) - }) - } -} diff --git a/src/main/database.ts b/src/main/database.ts index ad8976b9d3..3d9ca44e6c 100644 --- a/src/main/database.ts +++ b/src/main/database.ts @@ -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 diff --git a/src/main/account.ts b/src/main/db/account.ts similarity index 58% rename from src/main/account.ts rename to src/main/db/account.ts index bba9fd346c..1789cf6fe6 100644 --- a/src/main/account.ts +++ b/src/main/db/account.ts @@ -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 => { + 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 => { + 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 => { + 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 => { + 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 + }) + ) + }) + }) + }) + }) + }) +} diff --git a/src/main/hashtags.ts b/src/main/db/hashtags.ts similarity index 100% rename from src/main/hashtags.ts rename to src/main/db/hashtags.ts diff --git a/src/main/server.ts b/src/main/db/server.ts similarity index 100% rename from src/main/server.ts rename to src/main/db/server.ts diff --git a/src/main/db/setting.ts b/src/main/db/setting.ts new file mode 100644 index 0000000000..38bd0f4dfa --- /dev/null +++ b/src/main/db/setting.ts @@ -0,0 +1,55 @@ +import sqlite3 from 'sqlite3' +import { Setting } from '~/src/types/setting' +import { DefaultSetting } from '~/src/constants/initializer/setting' + +export const getSetting = (db: sqlite3.Database, accountId: number): Promise => { + return new Promise((resolve, reject) => { + db.get('SELECT * FROM settings WHERE account_id = ?', accountId, (err, row) => { + if (err) { + reject(err) + } + if (row) { + resolve({ + accountId: row.account_id, + markerHome: Boolean(row.marker_home), + markerNotifications: Boolean(row.marker_notifications) + }) + } + resolve(DefaultSetting) + }) + }) +} + +export const createOrUpdateSetting = (db: sqlite3.Database, setting: Setting): Promise => { + 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) + } + ) + } + }) + }) +} diff --git a/src/main/index.ts b/src/main/index.ts index 0d9994cf18..b82a29e9b4 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -11,10 +11,10 @@ import { BrowserWindowConstructorOptions, MenuItemConstructorOptions, IpcMainEvent, - Notification, - NotificationConstructorOptions, nativeTheme, - IpcMainInvokeEvent + IpcMainInvokeEvent, + Notification, + NotificationConstructorOptions } from 'electron' import crypto from 'crypto' @@ -25,13 +25,18 @@ import path from 'path' import ContextMenu from 'electron-context-menu' import { initSplashScreen, Config } from '@trodi/electron-splashscreen' import openAboutWindow from 'about-window' -import generator, { Entity, detector, NotificationType, OAuth } from 'megalodon' -import sanitizeHtml from 'sanitize-html' +import generator, { detector, OAuth, NotificationType, Entity } from 'megalodon' import AutoLaunch from 'auto-launch' import minimist from 'minimist' +import sanitizeHtml from 'sanitize-html' -import { getAccount, insertAccount, listAccounts } from './account' -// import { StreamingURL, UserStreaming, DirectStreaming, LocalStreaming, PublicStreaming, ListStreaming, TagStreaming } from './websocket' +// db +import { backwardAccount, forwardAccount, getAccount, insertAccount, listAccounts, removeAccount, removeAllAccounts } from './db/account' +import { insertTag, listTags, removeTag } from './db/hashtags' +import { createOrUpdateSetting, getSetting } from './db/setting' +import { insertServer } from './db/server' + +import { DirectStreaming, ListStreaming, LocalStreaming, PublicStreaming, StreamingURL, TagStreaming, UserStreaming } from './websocket' import Preferences from './preferences' import Fonts from './fonts' import i18next from '~/src/config/i18n' @@ -39,17 +44,13 @@ import { i18n as I18n } from 'i18next' import Language, { LanguageType } from '../constants/language' import { LocalAccount } from '~/src/types/localAccount' import { LocalTag } from '~/src/types/localTag' -import { Notify } from '~/src/types/notify' -// import { StreamingError } from '~/src/errors/streamingError' import { Proxy } from '~/src/types/proxy' import ProxyConfiguration from './proxy' import { Menu as MenuPreferences } from '~/src/types/preference' import newDB from './database' -import Settings from './settings' -import { BaseSettings, Setting } from '~/src/types/setting' -import { insertServer } from './server' -import { LocalServer } from '~src/types/localServer' -import { insertTag, listTags, removeTag } from './hashtags' +import { Setting } from '~/src/types/setting' +import { LocalServer } from '~/src/types/localServer' +import { Notify } from '~/src/types/notify' /** * Context menu @@ -112,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') @@ -121,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 = @@ -144,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) { @@ -222,7 +220,7 @@ async function createWindow() { return { label: s.domain, accelerator: `CmdOrCtrl+${index + 1}`, - click: () => changeAccount([a, s], index) + click: () => changeAccount(a, index) } }) @@ -372,7 +370,15 @@ Options // Do not lower the rendering priority of Chromium when background app.commandLine.appendSwitch('disable-renderer-backgrounding') -app.on('ready', createWindow) +app.on('ready', async () => { + createWindow() + const accounts = await listAccounts(db) + const preferences = new Preferences(preferencesDBPath) + startUserStreamings(accounts, proxyConfiguration, preferences) + startDirectStreamings(accounts, proxyConfiguration) + startLocalStreamings(accounts, proxyConfiguration) + startPublicStreamings(accounts, proxyConfiguration) +}) app.on('window-all-closed', () => { // this action is called when user click the close button. @@ -474,66 +480,50 @@ 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: string) => { -// const accountId = await accountRepo.removeAccount(id) - -// const accounts = await listAccounts() -// const accountsChange: Array = 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)) -// } - -// stopUserStreaming(accountId) -// }) - -// ipcMain.handle('forward-account', async (_: IpcMainInvokeEvent, acct: LocalAccount) => { -// await accountRepo.forwardAccount(acct) -// }) - -// ipcMain.handle('backward-account', async (_: IpcMainInvokeEvent, acct: LocalAccount) => { -// await accountRepo.backwardAccount(acct) -// }) - -// 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 = 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)) -// } -// }) +ipcMain.handle('remove-account', async (_: IpcMainInvokeEvent, id: number) => { + await removeAccount(db, id) + + const accounts = await listAccounts(db) + const accountsChange: Array = accounts.map(([account, server], index) => { + return { + label: server.domain, + accelerator: `CmdOrCtrl+${index + 1}`, + click: () => changeAccount(account, index) + } + }) + + await updateApplicationMenu(accountsChange) + await updateDockMenu(accountsChange) + if (process.platform !== 'darwin' && tray !== null) { + tray.setContextMenu(TrayMenu(accountsChange, i18next)) + } +}) + +ipcMain.handle('forward-account', async (_: IpcMainInvokeEvent, acct: LocalAccount) => { + await forwardAccount(db, acct) +}) + +ipcMain.handle('backward-account', async (_: IpcMainInvokeEvent, acct: LocalAccount) => { + await backwardAccount(db, acct) +}) + +ipcMain.handle('remove-all-accounts', async (_: IpcMainInvokeEvent) => { + await removeAllAccounts(db) + const accounts = await listAccounts(db) + const accountsChange: Array = accounts.map(([account, server], index) => { + return { + label: server.domain, + accelerator: `CmdOrCtrl+${index + 1}`, + click: () => changeAccount(account, index) + } + }) + + 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) { @@ -556,362 +546,6 @@ ipcMain.on('reset-badge', () => { } }) -// // user streaming -// const userStreamings: { [key: string]: UserStreaming | null } = {} - -// ipcMain.on('start-all-user-streamings', (event: IpcMainEvent, accounts: Array) => { -// accounts.map(async id => { -// const acct = await accountRepo.getAccount(id) -// try { -// // Stop old user streaming -// if (userStreamings[id]) { -// userStreamings[id]!.stop() -// userStreamings[id] = null -// } -// const proxy = await proxyConfiguration.forMastodon() -// const sns = await detector(acct.baseURL, proxy) -// const url = await StreamingURL(sns, acct, proxy) -// userStreamings[id] = new UserStreaming(sns, acct, url, proxy) -// userStreamings[id]!.start( -// async (update: Entity.Status) => { -// if (!event.sender.isDestroyed()) { -// event.sender.send(`update-start-all-user-streamings-${id}`, update) -// } -// // Cache hashtag -// update.tags.map(async tag => { -// await hashtagCache.insertHashtag(tag.name).catch(err => console.error(err)) -// }) -// // Cache account -// await accountCache.insertAccount(id, update.account.acct).catch(err => console.error(err)) -// }, -// async (notification: Entity.Notification) => { -// await publishNotification(notification, event, id) - -// // In macOS and Windows, sometimes window is closed (not quit). -// // But streamings are always running. -// // When window is closed, we can not send event to webContents; because it is already destroyed. -// // So we have to guard it. -// if (!event.sender.isDestroyed()) { -// // To update notification timeline -// event.sender.send(`notification-start-all-user-streamings-${id}`, notification) - -// // Does not exist a endpoint for only mention. And mention is a part of notification. -// // So we have to get mention from notification. -// if (notification.type === 'mention') { -// event.sender.send(`mention-start-all-user-streamings-${id}`, notification) -// } -// } -// }, -// (statusId: string) => { -// if (!event.sender.isDestroyed()) { -// event.sender.send(`delete-start-all-user-streamings-${id}`, statusId) -// } -// }, -// (err: Error) => { -// log.error(err) -// // In macOS, sometimes window is closed (not quit). -// // When window is closed, we can not send event to webContents; because it is destroyed. -// // So we have to guard it. -// if (!event.sender.isDestroyed()) { -// event.sender.send('error-start-all-user-streamings', err) -// } -// } -// ) -// // Generate notifications received while the app was not running -// const client = generator(sns, acct.baseURL, acct.accessToken, 'Whalebird', proxy) -// const marker = await getMarker(client, id) -// if (marker !== null) { -// const unreadResponse = await client.getNotifications({ min_id: marker.last_read_id }) -// unreadResponse.data.map(async notification => { -// await publishNotification(notification, event, id) -// }) -// } -// } catch (err: any) { -// log.error(err) -// const streamingError = new StreamingError(err.message, acct.domain) -// if (!event.sender.isDestroyed()) { -// event.sender.send('error-start-all-user-streamings', streamingError) -// } -// } -// }) -// }) - -// ipcMain.on('stop-all-user-streamings', () => { -// Object.keys(userStreamings).forEach((key: string) => { -// if (userStreamings[key]) { -// userStreamings[key]!.stop() -// userStreamings[key] = null -// } -// }) -// }) - -// /** -// * Stop an user streaming in all user streamings. -// * @param id specified user id in nedb. -// */ -// const stopUserStreaming = (id: string) => { -// Object.keys(userStreamings).forEach((key: string) => { -// if (key === id && userStreamings[id]) { -// userStreamings[id]!.stop() -// userStreamings[id] = null -// } -// }) -// } - -// let directMessagesStreaming: DirectStreaming | null = null - -// ipcMain.on('start-directmessages-streaming', async (event: IpcMainEvent, id: string) => { -// try { -// const acct = await accountRepo.getAccount(id) - -// // Stop old directmessages streaming -// if (directMessagesStreaming !== null) { -// directMessagesStreaming.stop() -// directMessagesStreaming = null -// } -// const proxy = await proxyConfiguration.forMastodon() -// const sns = await detector(acct.baseURL, proxy) -// const url = await StreamingURL(sns, acct, proxy) -// directMessagesStreaming = new DirectStreaming(sns, acct, url, proxy) -// directMessagesStreaming.start( -// (update: Entity.Status) => { -// if (!event.sender.isDestroyed()) { -// event.sender.send('update-start-directmessages-streaming', update) -// } -// }, -// (id: string) => { -// if (!event.sender.isDestroyed()) { -// event.sender.send('delete-start-directmessages-streaming', id) -// } -// }, -// (err: Error) => { -// log.error(err) -// if (!event.sender.isDestroyed()) { -// event.sender.send('error-start-directmessages-streaming', err) -// } -// } -// ) -// } catch (err) { -// log.error(err) -// if (!event.sender.isDestroyed()) { -// event.sender.send('error-start-directmessages-streaming', err) -// } -// } -// }) - -// ipcMain.on('stop-directmessages-streaming', () => { -// if (directMessagesStreaming !== null) { -// directMessagesStreaming.stop() -// directMessagesStreaming = null -// } -// }) - -// let localStreaming: LocalStreaming | null = null - -// ipcMain.on('start-local-streaming', async (event: IpcMainEvent, id: string) => { -// try { -// const acct = await accountRepo.getAccount(id) - -// // Stop old local streaming -// if (localStreaming !== null) { -// localStreaming.stop() -// localStreaming = null -// } -// const proxy = await proxyConfiguration.forMastodon() -// const sns = await detector(acct.baseURL, proxy) -// const url = await StreamingURL(sns, acct, proxy) -// localStreaming = new LocalStreaming(sns, acct, url, proxy) -// localStreaming.start( -// (update: Entity.Status) => { -// if (!event.sender.isDestroyed()) { -// event.sender.send('update-start-local-streaming', update) -// } -// }, -// (id: string) => { -// if (!event.sender.isDestroyed()) { -// event.sender.send('delete-start-local-streaming', id) -// } -// }, -// (err: Error) => { -// log.error(err) -// if (!event.sender.isDestroyed()) { -// event.sender.send('error-start-local-streaming', err) -// } -// } -// ) -// } catch (err) { -// log.error(err) -// if (!event.sender.isDestroyed()) { -// event.sender.send('error-start-local-streaming', err) -// } -// } -// }) - -// ipcMain.on('stop-local-streaming', () => { -// if (localStreaming !== null) { -// localStreaming.stop() -// localStreaming = null -// } -// }) - -// let publicStreaming: PublicStreaming | null = null - -// ipcMain.on('start-public-streaming', async (event: IpcMainEvent, id: string) => { -// try { -// const acct = await accountRepo.getAccount(id) - -// // Stop old public streaming -// if (publicStreaming !== null) { -// publicStreaming.stop() -// publicStreaming = null -// } -// const proxy = await proxyConfiguration.forMastodon() -// const sns = await detector(acct.baseURL, proxy) -// const url = await StreamingURL(sns, acct, proxy) -// publicStreaming = new PublicStreaming(sns, acct, url, proxy) -// publicStreaming.start( -// (update: Entity.Status) => { -// if (!event.sender.isDestroyed()) { -// event.sender.send('update-start-public-streaming', update) -// } -// }, -// (id: string) => { -// if (!event.sender.isDestroyed()) { -// event.sender.send('delete-start-public-streaming', id) -// } -// }, -// (err: Error) => { -// log.error(err) -// if (!event.sender.isDestroyed()) { -// event.sender.send('error-start-public-streaming', err) -// } -// } -// ) -// } catch (err) { -// log.error(err) -// if (!event.sender.isDestroyed()) { -// event.sender.send('error-start-public-streaming', err) -// } -// } -// }) - -// ipcMain.on('stop-public-streaming', () => { -// if (publicStreaming !== null) { -// publicStreaming.stop() -// publicStreaming = null -// } -// }) - -// let listStreaming: ListStreaming | null = null - -// type ListStreamingOpts = { -// listID: string -// accountID: string -// } - -// ipcMain.on('start-list-streaming', async (event: IpcMainEvent, obj: ListStreamingOpts) => { -// const { listID, accountID } = obj -// try { -// const acct = await accountRepo.getAccount(accountID) - -// // Stop old list streaming -// if (listStreaming !== null) { -// listStreaming.stop() -// listStreaming = null -// } -// const proxy = await proxyConfiguration.forMastodon() -// const sns = await detector(acct.baseURL, proxy) -// const url = await StreamingURL(sns, acct, proxy) -// listStreaming = new ListStreaming(sns, acct, url, proxy) -// listStreaming.start( -// listID, -// (update: Entity.Status) => { -// if (!event.sender.isDestroyed()) { -// event.sender.send('update-start-list-streaming', update) -// } -// }, -// (id: string) => { -// if (!event.sender.isDestroyed()) { -// event.sender.send('delete-start-list-streaming', id) -// } -// }, -// (err: Error) => { -// log.error(err) -// if (!event.sender.isDestroyed()) { -// event.sender.send('error-start-list-streaming', err) -// } -// } -// ) -// } catch (err) { -// log.error(err) -// if (!event.sender.isDestroyed()) { -// event.sender.send('error-start-list-streaming', err) -// } -// } -// }) - -// ipcMain.on('stop-list-streaming', () => { -// if (listStreaming !== null) { -// listStreaming.stop() -// listStreaming = null -// } -// }) - -// let tagStreaming: TagStreaming | null = null - -// type TagStreamingOpts = { -// tag: string -// accountID: string -// } - -// ipcMain.on('start-tag-streaming', async (event: IpcMainEvent, obj: TagStreamingOpts) => { -// const { tag, accountID } = obj -// try { -// const acct = await accountRepo.getAccount(accountID) - -// // Stop old tag streaming -// if (tagStreaming !== null) { -// tagStreaming.stop() -// tagStreaming = null -// } -// const proxy = await proxyConfiguration.forMastodon() -// const sns = await detector(acct.baseURL, proxy) -// const url = await StreamingURL(sns, acct, proxy) -// tagStreaming = new TagStreaming(sns, acct, url, proxy) -// tagStreaming.start( -// tag, -// (update: Entity.Status) => { -// if (!event.sender.isDestroyed()) { -// event.sender.send('update-start-tag-streaming', update) -// } -// }, -// (id: string) => { -// if (!event.sender.isDestroyed()) { -// event.sender.send('delete-start-tag-streaming', id) -// } -// }, -// (err: Error) => { -// log.error(err) -// if (!event.sender.isDestroyed()) { -// event.sender.send('error-start-tag-streaming', err) -// } -// } -// ) -// } catch (err) { -// log.error(err) -// if (!event.sender.isDestroyed()) { -// event.sender.send('error-start-tag-streaming', err) -// } -// } -// }) - -// ipcMain.on('stop-tag-streaming', () => { -// if (tagStreaming !== null) { -// tagStreaming.stop() -// tagStreaming = null -// } -// }) - // sounds ipcMain.on('fav-rt-action-sound', () => { const preferences = new Preferences(preferencesDBPath) @@ -1049,7 +683,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) } }) @@ -1116,18 +750,17 @@ ipcMain.handle('list-fonts', async (_: IpcMainInvokeEvent) => { // Settings ipcMain.handle( 'get-account-setting', - async (_: IpcMainInvokeEvent, accountID: string): Promise => { - const settings = new Settings(settingsDBPath) - const setting = await settings.get(accountID) + async (_: IpcMainInvokeEvent, accountId: number): Promise => { + const setting = await getSetting(db, accountId) return setting } ) ipcMain.handle( 'update-account-setting', - async (_: IpcMainInvokeEvent, setting: Setting): Promise => { - const settings = new Settings(settingsDBPath) - const res = await settings.update(setting) + async (_: IpcMainInvokeEvent, setting: Setting): Promise => { + console.log(setting) + const res = await createOrUpdateSetting(db, setting) return res } ) @@ -1403,6 +1036,7 @@ const TrayMenu = (accountsChange: Array, i18n: I18n) { label: i18n.t('main_menu.application.quit'), click: () => { + stopAllStreamings() mainWindow!.destroy() } } @@ -1435,15 +1069,148 @@ async function reopenWindow() { } } -const publishNotification = async (notification: Entity.Notification, event: IpcMainEvent | IpcMainInvokeEvent, id: string) => { - const preferences = new Preferences(preferencesDBPath) +const decodeLanguage = (lang: string): LanguageType => { + const l = Object.keys(Language).find(k => Language[k].key === lang) + if (l === undefined) { + return Language.en + } else { + return Language[l] + } +} + +//---------------------------------------------- +// Streamings +//---------------------------------------------- +const userStreamings: { [key: number]: UserStreaming } = {} +const directStreamings: { [key: number]: DirectStreaming } = {} +const localStreamings: { [key: number]: DirectStreaming } = {} +const publicStreamings: { [key: number]: DirectStreaming } = {} + +const stopAllStreamings = () => { + Object.keys(userStreamings).forEach((key: string) => { + userStreamings[parseInt(key)].stop() + }) + Object.keys(directStreamings).forEach((key: string) => { + directStreamings[parseInt(key)].stop() + }) + Object.keys(localStreamings).forEach((key: string) => [localStreamings[parseInt(key)].stop()]) + Object.keys(publicStreamings).forEach((key: string) => { + publicStreamings[parseInt(key)].stop() + }) +} + +const startUserStreamings = async ( + accounts: Array<[LocalAccount, LocalServer]>, + proxyConfiguration: ProxyConfiguration, + preferences: Preferences +) => { + const proxy = await proxyConfiguration.forMastodon() + accounts.forEach(async ([account, server]) => { + const url = await StreamingURL(server.sns, account, server, proxy) + userStreamings[account.id] = new UserStreaming(server.sns, account, url, proxy) + userStreamings[account.id].start( + async (update: Entity.Status) => { + if (!mainWindow?.webContents.isDestroyed()) { + mainWindow?.webContents.send(`update-user-streamings-${account.id}`, update) + } + }, + async (notification: Entity.Notification) => { + await publishNotification(notification, account.id, preferences) + if (!mainWindow?.webContents.isDestroyed()) { + mainWindow?.webContents.send(`notification-user-streamings-${account.id}`, notification) + } + }, + (statusId: string) => { + if (!mainWindow?.webContents.isDestroyed()) { + mainWindow?.webContents.send(`delete-user-streamings-${account.id}`, statusId) + } + }, + (err: Error) => { + log.error(err) + } + ) + }) + + return userStreamings +} + +const startDirectStreamings = async (accounts: Array<[LocalAccount, LocalServer]>, proxyConfiguration: ProxyConfiguration) => { + const proxy = await proxyConfiguration.forMastodon() + accounts.forEach(async ([account, server]) => { + const url = await StreamingURL(server.sns, account, server, proxy) + directStreamings[account.id] = new DirectStreaming(server.sns, account, url, proxy) + directStreamings[account.id].start( + (update: Entity.Status) => { + if (!mainWindow?.webContents.isDestroyed()) { + mainWindow?.webContents.send(`update-direct-streamings-${account.id}`, update) + } + }, + (id: string) => { + if (!mainWindow?.webContents.isDestroyed()) { + mainWindow?.webContents.send(`delete-direct-streamings-${account.id}`, id) + } + }, + (err: Error) => { + log.error(err) + } + ) + }) +} + +const startLocalStreamings = async (accounts: Array<[LocalAccount, LocalServer]>, proxyConfiguration: ProxyConfiguration) => { + const proxy = await proxyConfiguration.forMastodon() + accounts.forEach(async ([account, server]) => { + const url = await StreamingURL(server.sns, account, server, proxy) + localStreamings[account.id] = new LocalStreaming(server.sns, account, url, proxy) + localStreamings[account.id].start( + (update: Entity.Status) => { + if (!mainWindow?.webContents.isDestroyed()) { + mainWindow?.webContents.send(`update-local-streamings-${account.id}`, update) + } + }, + (id: string) => { + if (!mainWindow?.webContents.isDestroyed()) { + mainWindow?.webContents.send(`delete-local-streamings-${account.id}`, id) + } + }, + (err: Error) => { + log.error(err) + } + ) + }) +} + +const startPublicStreamings = async (accounts: Array<[LocalAccount, LocalServer]>, proxyConfiguration: ProxyConfiguration) => { + const proxy = await proxyConfiguration.forMastodon() + accounts.forEach(async ([account, server]) => { + const url = await StreamingURL(server.sns, account, server, proxy) + publicStreamings[account.id] = new PublicStreaming(server.sns, account, url, proxy) + publicStreamings[account.id].start( + (update: Entity.Status) => { + if (!mainWindow?.webContents.isDestroyed()) { + mainWindow?.webContents.send(`update-public-streamings-${account.id}`, update) + } + }, + (id: string) => { + if (!mainWindow?.webContents.isDestroyed()) { + mainWindow?.webContents.send(`delete-public-streamings-${account.id}`, id) + } + }, + (err: Error) => { + log.error(err) + } + ) + }) +} + +const publishNotification = async (notification: Entity.Notification, accountId: number, preferences: Preferences) => { const conf = await preferences.load() const options = createNotification(notification, conf.notification.notify) if (options !== null) { const notify = new Notification(options) notify.on('click', _ => { - if (!event.sender.isDestroyed()) { - event.sender.send('open-notification-tab', id) + if (!mainWindow?.webContents.isDestroyed()) { + mainWindow?.webContents.send('open-notification-tab', accountId) } }) notify.show() @@ -1561,11 +1328,88 @@ const username = (account: Entity.Account): string => { } } -const decodeLanguage = (lang: string): LanguageType => { - const l = Object.keys(Language).find(k => Language[k].key === lang) - if (l === undefined) { - return Language.en - } else { - return Language[l] +//---------------------------------------- +// List streamings +//---------------------------------------- +const listStreamings: { [key: number]: ListStreaming } = {} + +type ListStreamingOpts = { + listId: string + accountId: number +} + +ipcMain.on('start-list-streaming', async (event: IpcMainEvent, obj: ListStreamingOpts) => { + const { listId, accountId } = obj + try { + const [account, server] = await getAccount(db, accountId) + + // Stop old list streaming + if (listStreamings[accountId] !== undefined) { + listStreamings[accountId].stop() + } + const proxy = await proxyConfiguration.forMastodon() + const url = await StreamingURL(server.sns, account, server, proxy) + listStreamings[accountId] = new ListStreaming(server.sns, account, url, proxy) + listStreamings[accountId].start( + listId, + (update: Entity.Status) => { + if (!event.sender.isDestroyed()) { + event.sender.send(`update-list-streamings-${accountId}`, update) + } + }, + (id: string) => { + if (!event.sender.isDestroyed()) { + event.sender.send(`delete-list-streamings-${accountId}`, id) + } + }, + (err: Error) => { + log.error(err) + } + ) + } catch (err) { + log.error(err) } +}) + +//---------------------------------------- +// Tag streamings +//---------------------------------------- +const tagStreamings: { [key: number]: TagStreaming } = {} + +type TagStreamingOpts = { + tag: string + accountId: number } + +ipcMain.on('start-tag-streaming', async (event: IpcMainEvent, obj: TagStreamingOpts) => { + const { tag, accountId } = obj + try { + const [account, server] = await getAccount(db, accountId) + + // Stop old tag streaming + if (tagStreamings[accountId] !== undefined) { + tagStreamings[accountId].stop() + } + const proxy = await proxyConfiguration.forMastodon() + const url = await StreamingURL(server.sns, account, server, proxy) + tagStreamings[accountId] = new TagStreaming(server.sns, account, url, proxy) + tagStreamings[accountId].start( + tag, + (update: Entity.Status) => { + if (!event.sender.isDestroyed()) { + event.sender.send(`update-tag-streamings-${accountId}`, update) + } + }, + (id: string) => { + if (!event.sender.isDestroyed()) { + event.sender.send(`delete-tag-streamings-${accountId}`, id) + } + }, + (err: Error) => { + log.error(err) + } + ) + } catch (err) { + log.error(err) + } +}) diff --git a/src/main/settings.ts b/src/main/settings.ts deleted file mode 100644 index c4c1025ecf..0000000000 --- a/src/main/settings.ts +++ /dev/null @@ -1,80 +0,0 @@ -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 { 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 { - try { - const settings = await this._get() - if (isEmpty(settings)) { - return [] - } - return settings - } catch (err) { - log.error(err) - return [] - } - } - - public async get(accountID: string): Promise { - const current = await this._load() - const find: Setting | undefined = current.find(d => { - return d.accountID === accountID - }) - if (find) { - return objectAssignDeep({}, DefaultSetting, find) - } - return objectAssignDeep({}, DefaultSetting, { - accountID: accountID - }) - } - - private _get(): Promise { - 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 { - 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 { - 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 - } - } -} diff --git a/src/renderer/components/Settings.vue b/src/renderer/components/Settings.vue index b5c411be1a..e7f6122e29 100644 --- a/src/renderer/components/Settings.vue +++ b/src/renderer/components/Settings.vue @@ -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`) }) diff --git a/src/renderer/components/Settings/Timeline.vue b/src/renderer/components/Settings/Timeline.vue index ba7f4c60cb..24a10fee0d 100644 --- a/src/renderer/components/Settings/Timeline.vue +++ b/src/renderer/components/Settings/Timeline.vue @@ -1,23 +1,6 @@