Merge pull request #3280 from h3poteto/feat/vue3

Upgrade vue3
This commit is contained in:
AkiraFukushima 2022-04-26 20:57:51 +09:00 committed by GitHub
commit c57b0ee273
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
141 changed files with 3357 additions and 3057 deletions

View File

@ -13,7 +13,7 @@ const { VueLoaderPlugin } = require('vue-loader')
let rendererConfig = {
entry: {
renderer: path.join(__dirname, '../src/renderer/main.ts')
renderer: path.join(__dirname, '../src/renderer/main.js')
},
module: {
rules: [
@ -205,8 +205,7 @@ let rendererConfig = {
alias: {
// Same as tsconfig.json
'@': path.join(__dirname, '../src/renderer'),
'~': path.join(__dirname, '../'),
vue$: 'vue/dist/vue.esm.js'
'~': path.join(__dirname, '../')
},
extensions: ['.ts', '.js', '.vue', '.json', '.css', '.node'],
fallback: {

1
.npmrc Normal file
View File

@ -0,0 +1 @@
@h3poteto:registry=https://npm.pkg.github.com

View File

@ -72,8 +72,8 @@
"@fortawesome/fontawesome-svg-core": "^6.1.0",
"@fortawesome/free-regular-svg-icons": "^6.1.0",
"@fortawesome/free-solid-svg-icons": "^6.1.0",
"@fortawesome/vue-fontawesome": "^2.0.6",
"@panter/vue-i18next": "^0.15.2",
"@fortawesome/vue-fontawesome": "^3.0.0-5",
"vue-virtual-scroller": "2.0.0-alpha.1",
"@trodi/electron-splashscreen": "^1.0.2",
"about-window": "^1.15.2",
"animate.css": "^4.1.0",
@ -85,13 +85,14 @@
"electron-json-storage": "^4.5.0",
"electron-log": "^4.4.6",
"electron-window-state": "^5.0.3",
"element-ui": "2.15.8",
"emoji-mart-vue": "^2.6.6",
"element-plus": "^2.1.9",
"emoji-mart-vue-fast": "^10.2.1",
"i18next": "^21.6.16",
"lodash": "^4.17.21",
"lokijs": "^1.5.12",
"megalodon": "4.0.1",
"minimist": "^1.2.6",
"mitt": "^3.0.0",
"moment": "^2.29.2",
"mousetrap": "^1.6.5",
"nedb": "^1.8.0",
@ -103,15 +104,13 @@
"system-font-families": "^0.6.0",
"tunnel-agent": "^0.6.0",
"unicode-emoji-json": "^0.3.1",
"vue": "^2.6.14",
"vue-click-outside": "^1.1.0",
"vue": "^3.2.31",
"vue-popperjs": "^2.3.0",
"vue-resize": "^1.0.1",
"vue-router": "^3.5.3",
"vue-shortkey": "^3.1.7",
"vue-virtual-scroller": "^1.0.10",
"vuex": "^3.6.2",
"vuex-router-sync": "^5.0.0"
"vue-resize": "^2.0.0-alpha.1",
"vue-router": "^4.0.14",
"vue3-i18next": "^0.1.0",
"vuex": "^4.0.2",
"vuex-router-sync": "^6.0.0-rc.1"
},
"devDependencies": {
"@babel/core": "^7.17.9",
@ -134,7 +133,7 @@
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"@typescript-eslint/typescript-estree": "^5.19.0",
"@vue/test-utils": "^1.3.0",
"@vue/compiler-sfc": "^3.2.31",
"ajv": "^8.11.0",
"all-object-keys": "^2.2.0",
"assert": "^2.0.0",
@ -211,9 +210,8 @@
"url-loader": "^4.1.1",
"utf-8-validate": "^5.0.9",
"vue-html-loader": "^1.2.4",
"vue-loader": "^15.9.8",
"vue-loader": "^17.0.0",
"vue-style-loader": "^4.1.3",
"vue-template-compiler": "^2.6.14",
"webpack": "^5.72.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.8.1",

View File

@ -1,5 +1,4 @@
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import { ipcMain, ipcRenderer } from '~/spec/mock/electron'
import App from '@/store/App'
import DisplayStyle from '~/src/constants/displayStyle'
@ -9,7 +8,8 @@ import TimeFormat from '~/src/constants/timeFormat'
import Language from '~/src/constants/language'
import DefaultFonts from '@/utils/fonts'
import { MyWindow } from '~/src/types/global'
;((window as any) as MyWindow).ipcRenderer = ipcRenderer
import { RootState } from '@/store'
;(window as any as MyWindow).ipcRenderer = ipcRenderer
const state = () => {
return {
@ -41,13 +41,10 @@ const initStore = () => {
}
describe('App', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
App: initStore()
}

View File

@ -1,9 +1,9 @@
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { RootState } from '@/store'
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 {
@ -32,13 +32,10 @@ const routerState = {
}
describe('GlobalHeader', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
GlobalHeader: initStore(),
route: routerState

View File

@ -1,9 +1,9 @@
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import { ipcMain, ipcRenderer } from '~/spec/mock/electron'
import Login, { LoginState } from '@/store/Login'
import { MyWindow } from '~/src/types/global'
;((window as any) as MyWindow).ipcRenderer = ipcRenderer
import { RootState } from '@/store'
;(window as any as MyWindow).ipcRenderer = ipcRenderer
jest.mock('megalodon', () => ({
...jest.requireActual<object>('megalodon'),
@ -36,13 +36,10 @@ const appState = {
}
describe('Login', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Login: initStore(),
App: appState

View File

@ -1,10 +1,10 @@
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
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'
;((window as any) as MyWindow).ipcRenderer = ipcRenderer
import { RootState } from '@/store'
;(window as any as MyWindow).ipcRenderer = ipcRenderer
const account: LocalAccount = {
_id: 'sample',
@ -36,16 +36,20 @@ const initStore = () => {
}
}
const preferencesStore = () => ({
namespaced: true,
modules: {
Account: initStore()
}
})
describe('Account', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Account: initStore()
Preferences: preferencesStore()
}
})
})
@ -56,7 +60,7 @@ describe('Account', () => {
throw new Error()
})
await store.dispatch('Account/loadAccounts').catch((err: Error) => {
await store.dispatch('Preferences/Account/loadAccounts').catch((err: Error) => {
expect(err instanceof Error).toEqual(true)
})
ipcMain.removeHandler('list-accounts')
@ -65,8 +69,8 @@ describe('Account', () => {
ipcMain.handle('list-accounts', () => {
return [account]
})
await store.dispatch('Account/loadAccounts')
expect(store.state.Account.accounts).toEqual([account])
await store.dispatch('Preferences/Account/loadAccounts')
expect(store.state.Preferences.Account.accounts).toEqual([account])
ipcMain.removeHandler('list-accounts')
})
})
@ -76,7 +80,7 @@ describe('Account', () => {
ipcMain.handle('remove-account', async () => {
throw new Error()
})
await store.dispatch('Account/removeAccount', account).catch((err: Error) => {
await store.dispatch('Preferences/Account/removeAccount', account).catch((err: Error) => {
expect(err instanceof Error).toEqual(true)
})
ipcMain.removeHandler('remove-account')
@ -85,7 +89,7 @@ describe('Account', () => {
ipcMain.handle('remove-account', () => {
return true
})
const res = await store.dispatch('Account/removeAccount', account)
const res = await store.dispatch('Preferences/Account/removeAccount', account)
expect(res).toEqual(undefined)
ipcMain.removeHandler('remove-account')
})
@ -96,7 +100,7 @@ describe('Account', () => {
ipcMain.handle('forward-account', async () => {
throw new Error()
})
await store.dispatch('Account/forwardAccount', account).catch((err: Error) => {
await store.dispatch('Preferences/Account/forwardAccount', account).catch((err: Error) => {
expect(err instanceof Error).toEqual(true)
})
ipcMain.removeHandler('forward-account')
@ -105,7 +109,7 @@ describe('Account', () => {
ipcMain.handle('forward-account', () => {
return {}
})
const res = await store.dispatch('Account/forwardAccount', account)
const res = await store.dispatch('Preferences/Account/forwardAccount', account)
expect(res).toEqual(undefined)
ipcMain.removeHandler('forward-account')
})
@ -116,7 +120,7 @@ describe('Account', () => {
ipcMain.handle('backward-account', () => {
throw new Error()
})
await store.dispatch('Account/backwardAccount', account).catch((err: Error) => {
await store.dispatch('Preferences/Account/backwardAccount', account).catch((err: Error) => {
expect(err instanceof Error).toEqual(true)
})
ipcMain.removeHandler('backward-account')
@ -125,7 +129,7 @@ describe('Account', () => {
ipcMain.handle('backward-account', () => {
return {}
})
const res = await store.dispatch('Account/backwardAccount', account)
const res = await store.dispatch('Preferences/Account/backwardAccount', account)
expect(res).toEqual(undefined)
ipcMain.removeHandler('backward-account')
})
@ -136,7 +140,7 @@ describe('Account', () => {
ipcMain.handle('remove-all-accounts', () => {
throw new Error()
})
await store.dispatch('Account/removeAllAccounts', account).catch((err: Error) => {
await store.dispatch('Preferences/Account/removeAllAccounts', account).catch((err: Error) => {
expect(err instanceof Error).toEqual(true)
})
ipcMain.removeHandler('remove-all-accounts')
@ -145,7 +149,7 @@ describe('Account', () => {
ipcMain.handle('remove-all-accounts', () => {
return {}
})
const res = await store.dispatch('Account/removeAllAccounts', account)
const res = await store.dispatch('Preferences/Account/removeAllAccounts', account)
expect(res).toEqual(undefined)
ipcMain.removeHandler('remove-all-accounts')
})

View File

@ -1,6 +1,5 @@
import { IpcMainInvokeEvent } from 'electron'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import Theme from '~/src/constants/theme'
import DisplayStyle from '~/src/constants/displayStyle'
import TimeFormat from '~/src/constants/timeFormat'
@ -9,7 +8,8 @@ import DefaultFonts from '@/utils/fonts'
import Appearance, { AppearanceState } from '@/store/Preferences/Appearance'
import { ipcMain, ipcRenderer } from '~/spec/mock/electron'
import { MyWindow } from '~/src/types/global'
;((window as any) as MyWindow).ipcRenderer = ipcRenderer
import { RootState } from '@/store'
;(window as any as MyWindow).ipcRenderer = ipcRenderer
const state = (): AppearanceState => {
return {
@ -35,6 +35,13 @@ const initStore = () => {
}
}
const preferencesStore = () => ({
namespaced: true,
modules: {
Appearance: initStore()
}
})
const App = {
namespaced: true,
actions: {
@ -43,15 +50,12 @@ const App = {
}
describe('Preferences/Appearance', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Preferences: initStore(),
Preferences: preferencesStore(),
App: App
}
})
@ -73,9 +77,9 @@ describe('Preferences/Appearance', () => {
ipcMain.removeHandler('get-preferences')
})
it('should be loaded', async () => {
await store.dispatch('Preferences/loadAppearance')
expect(store.state.Preferences.appearance.theme).toEqual(Theme.Dark.key)
expect(store.state.Preferences.appearance.fontSize).toEqual(15)
await store.dispatch('Preferences/Appearance/loadAppearance')
expect(store.state.Preferences.Appearance.appearance.theme).toEqual(Theme.Dark.key)
expect(store.state.Preferences.Appearance.appearance.fontSize).toEqual(15)
})
})
describe('loadFonts', () => {
@ -88,8 +92,8 @@ describe('Preferences/Appearance', () => {
ipcMain.removeHandler('list-fonts')
})
it('should be loaded', async () => {
await store.dispatch('Preferences/loadFonts')
expect(store.state.Preferences.fonts).toEqual([DefaultFonts[0], 'my-font'])
await store.dispatch('Preferences/Appearance/loadFonts')
expect(store.state.Preferences.Appearance.fonts).toEqual([DefaultFonts[0], 'my-font'])
})
})
})
@ -104,38 +108,38 @@ describe('Preferences/Appearance', () => {
ipcMain.removeHandler('update-preferences')
})
it('updateTheme', async () => {
await store.dispatch('Preferences/updateTheme', Theme.Dark.key)
expect(store.state.Preferences.appearance.theme).toEqual(Theme.Dark.key)
await store.dispatch('Preferences/Appearance/updateTheme', Theme.Dark.key)
expect(store.state.Preferences.Appearance.appearance.theme).toEqual(Theme.Dark.key)
expect(App.actions.loadPreferences).toBeCalled()
})
it('updateFontSize', async () => {
await store.dispatch('Preferences/updateFontSize', 15)
expect(store.state.Preferences.appearance.fontSize).toEqual(15)
await store.dispatch('Preferences/Appearance/updateFontSize', 15)
expect(store.state.Preferences.Appearance.appearance.fontSize).toEqual(15)
expect(App.actions.loadPreferences).toBeCalled()
})
it('updateDisplayNameStyle', async () => {
await store.dispatch('Preferences/updateDisplayNameStyle', DisplayStyle.DisplayName.value)
expect(store.state.Preferences.appearance.displayNameStyle).toEqual(DisplayStyle.DisplayName.value)
await store.dispatch('Preferences/Appearance/updateDisplayNameStyle', DisplayStyle.DisplayName.value)
expect(store.state.Preferences.Appearance.appearance.displayNameStyle).toEqual(DisplayStyle.DisplayName.value)
expect(App.actions.loadPreferences).toBeCalled()
})
it('updateTimeFormat', async () => {
await store.dispatch('Preferences/updateTimeFormat', TimeFormat.Relative.value)
expect(store.state.Preferences.appearance.timeFormat).toEqual(TimeFormat.Relative.value)
await store.dispatch('Preferences/Appearance/updateTimeFormat', TimeFormat.Relative.value)
expect(store.state.Preferences.Appearance.appearance.timeFormat).toEqual(TimeFormat.Relative.value)
expect(App.actions.loadPreferences).toBeCalled()
})
it('updateCustomThemeColor', async () => {
await store.dispatch('Preferences/updateCustomThemeColor', DarkTheme)
expect(store.state.Preferences.appearance.customThemeColor).toEqual(DarkTheme)
await store.dispatch('Preferences/Appearance/updateCustomThemeColor', DarkTheme)
expect(store.state.Preferences.Appearance.appearance.customThemeColor).toEqual(DarkTheme)
expect(App.actions.loadPreferences).toBeCalled()
})
it('updateFont', async () => {
await store.dispatch('Preferences/updateFont', DefaultFonts[1])
expect(store.state.Preferences.appearance.font).toEqual(DefaultFonts[1])
await store.dispatch('Preferences/Appearance/updateFont', DefaultFonts[1])
expect(store.state.Preferences.Appearance.appearance.font).toEqual(DefaultFonts[1])
expect(App.actions.loadPreferences).toBeCalled()
})
})

View File

@ -1,10 +1,10 @@
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import { ipcMain, ipcRenderer } from '~/spec/mock/electron'
import General, { GeneralState } from '@/store/Preferences/General'
import { MyWindow } from '~/src/types/global'
import { IpcMainInvokeEvent } from 'electron'
;((window as any) as MyWindow).ipcRenderer = ipcRenderer
import { RootState } from '@/store'
;(window as any as MyWindow).ipcRenderer = ipcRenderer
const state = (): GeneralState => {
return {
@ -34,6 +34,13 @@ const initStore = () => {
}
}
const preferencesStore = () => ({
namespaced: true,
modules: {
General: initStore()
}
})
const app = {
namespaced: true,
actions: {
@ -44,15 +51,12 @@ const app = {
}
describe('Preferences/General', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Preferences: initStore(),
Preferences: preferencesStore(),
App: app
}
})
@ -75,10 +79,10 @@ describe('Preferences/General', () => {
ipcMain.removeHandler('get-preferences')
})
it('should be updated', async () => {
await store.dispatch('Preferences/loadGeneral')
expect(store.state.Preferences.general.sound.fav_rb).toEqual(false)
expect(store.state.Preferences.general.sound.toot).toEqual(false)
expect(store.state.Preferences.loading).toEqual(false)
await store.dispatch('Preferences/General/loadGeneral')
expect(store.state.Preferences.General.general.sound.fav_rb).toEqual(false)
expect(store.state.Preferences.General.general.sound.toot).toEqual(false)
expect(store.state.Preferences.General.loading).toEqual(false)
})
})
@ -92,13 +96,13 @@ describe('Preferences/General', () => {
ipcMain.removeHandler('update-preferences')
})
it('should be updated', async () => {
await store.dispatch('Preferences/updateSound', {
await store.dispatch('Preferences/General/updateSound', {
fav_rb: false,
toot: false
})
expect(store.state.Preferences.general.sound.fav_rb).toEqual(false)
expect(store.state.Preferences.general.sound.toot).toEqual(false)
expect(store.state.Preferences.loading).toEqual(false)
expect(store.state.Preferences.General.general.sound.fav_rb).toEqual(false)
expect(store.state.Preferences.General.general.sound.toot).toEqual(false)
expect(store.state.Preferences.General.loading).toEqual(false)
})
})
@ -112,15 +116,15 @@ describe('Preferences/General', () => {
ipcMain.removeHandler('update-preferences')
})
it('should be updated', async () => {
await store.dispatch('Preferences/updateTimeline', {
await store.dispatch('Preferences/General/updateTimeline', {
cw: true,
nsfw: true,
hideAllAttachments: true
})
expect(store.state.Preferences.general.timeline.cw).toEqual(true)
expect(store.state.Preferences.general.timeline.nsfw).toEqual(true)
expect(store.state.Preferences.general.timeline.hideAllAttachments).toEqual(true)
expect(store.state.Preferences.loading).toEqual(false)
expect(store.state.Preferences.General.general.timeline.cw).toEqual(true)
expect(store.state.Preferences.General.general.timeline.nsfw).toEqual(true)
expect(store.state.Preferences.General.general.timeline.hideAllAttachments).toEqual(true)
expect(store.state.Preferences.General.loading).toEqual(false)
})
})
})

View File

@ -1,10 +1,10 @@
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import { ipcMain, ipcRenderer } from '~/spec/mock/electron'
import Language, { LanguageState } from '@/store/Preferences/Language'
import DefaultLanguage from '~/src/constants/language'
import { MyWindow } from '~/src/types/global'
;((window as any) as MyWindow).ipcRenderer = ipcRenderer
import { RootState } from '@/store'
;(window as any as MyWindow).ipcRenderer = ipcRenderer
const state = (): LanguageState => {
return {
@ -27,16 +27,20 @@ const initStore = () => {
}
}
const preferencesStore = () => ({
namespaced: true,
modules: {
Language: initStore()
}
})
describe('Preferences/Language', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Language: initStore()
Preferences: preferencesStore()
}
})
})
@ -59,8 +63,8 @@ describe('Preferences/Language', () => {
ipcMain.removeHandler('get-preferences')
})
it('should be updated', async () => {
await store.dispatch('Language/loadLanguage')
expect(store.state.Language.language.language).toEqual(DefaultLanguage.ja.key)
await store.dispatch('Preferences/Language/loadLanguage')
expect(store.state.Preferences.Language.language.language).toEqual(DefaultLanguage.ja.key)
})
})
@ -74,8 +78,8 @@ describe('Preferences/Language', () => {
ipcMain.removeHandler('change-language')
})
it('should be changed', async () => {
await store.dispatch('Language/changeLanguage', DefaultLanguage.ja.key)
expect(store.state.Language.language.language).toEqual(DefaultLanguage.ja.key)
await store.dispatch('Preferences/Language/changeLanguage', DefaultLanguage.ja.key)
expect(store.state.Preferences.Language.language.language).toEqual(DefaultLanguage.ja.key)
})
})
})

View File

@ -1,9 +1,9 @@
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import { ipcMain, ipcRenderer } from '~/spec/mock/electron'
import Notification, { NotificationState } from '@/store/Preferences/Notification'
import { MyWindow } from '~/src/types/global'
;((window as any) as MyWindow).ipcRenderer = ipcRenderer
import { RootState } from '@/store'
;(window as any as MyWindow).ipcRenderer = ipcRenderer
const state = (): NotificationState => {
return {
@ -32,6 +32,13 @@ const initStore = () => {
}
}
const preferencesStore = () => ({
namespaced: true,
modules: {
Notification: initStore()
}
})
const App = {
namespaced: true,
actions: {
@ -40,15 +47,12 @@ const App = {
}
describe('Preferences/Notification', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Notification: initStore(),
Preferences: preferencesStore(),
App: App
}
})
@ -77,8 +81,8 @@ describe('Preferences/Notification', () => {
ipcMain.removeHandler('get-preferences')
})
it('should be updated', async () => {
await store.dispatch('Notification/loadNotification')
expect(store.state.Notification.notification).toEqual({
await store.dispatch('Preferences/Notification/loadNotification')
expect(store.state.Preferences.Notification.notification).toEqual({
notify: {
reply: false,
reblog: false,
@ -105,11 +109,11 @@ describe('Preferences/Notification', () => {
ipcMain.removeHandler('update-preferences')
})
it('should be updated', async () => {
await store.dispatch('Notification/updateNotify', {
await store.dispatch('Preferences/Notification/updateNotify', {
reply: false,
reblog: false
})
expect(store.state.Notification.notification).toEqual({
expect(store.state.Preferences.Notification.notification).toEqual({
notify: {
reply: false,
reblog: false,

View File

@ -1,10 +1,10 @@
import { RootState } from '@/store'
import { Entity, Response } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
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
;(window as any as MyWindow).ipcRenderer = ipcRenderer
const emacsEmoji: Entity.Emoji = {
shortcode: 'emacs',
@ -172,13 +172,10 @@ const appState = {
}
describe('TimelineSpace', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
TimelineSpace: initStore(),
App: appState

View File

@ -1,7 +1,7 @@
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import DirectMessages, { DirectMessagesState } from '@/store/TimelineSpace/Contents/DirectMessages'
import { RootState } from '@/store'
const mockClient = {
getConversationTimeline: () => {
@ -149,7 +149,14 @@ const initStore = () => {
}
}
const timelineState = {
const contentsStore = () => ({
namespaced: true,
modules: {
DirectMessages: initStore()
}
})
const timelineStore = () => ({
namespaced: true,
state: {
account: {
@ -157,8 +164,11 @@ const timelineState = {
baseURL: 'http://localhost'
},
sns: 'mastodon'
},
modules: {
Contents: contentsStore()
}
}
})
const appState = {
namespaced: true,
@ -168,16 +178,12 @@ const appState = {
}
describe('Home', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
DirectMessages: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -185,9 +191,9 @@ describe('Home', () => {
describe('fetchTimeline', () => {
it('should be updated', async () => {
const statuses = await store.dispatch('DirectMessages/fetchTimeline')
const statuses = await store.dispatch('TimelineSpace/Contents/DirectMessages/fetchTimeline')
expect(statuses).toEqual([status1])
expect(store.state.DirectMessages.timeline).toEqual([status1])
expect(store.state.TimelineSpace.Contents.DirectMessages.timeline).toEqual([status1])
})
})
@ -215,9 +221,9 @@ describe('Home', () => {
resolve(res)
})
}
await store.dispatch('DirectMessages/lazyFetchTimeline', status1)
expect(store.state.DirectMessages.lazyLoading).toEqual(false)
expect(store.state.DirectMessages.timeline).toEqual([status1, status2])
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])
})
})
})

View File

@ -1,8 +1,8 @@
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
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: () => {
@ -147,7 +147,14 @@ const initStore = () => {
}
}
const timelineState = {
const contentsStore = () => ({
namespaced: true,
modules: {
Favourites: initStore()
}
})
const timelineStore = () => ({
namespaced: true,
state: {
account: {
@ -155,8 +162,11 @@ const timelineState = {
baseURL: 'http://localhost'
},
sns: 'mastodon'
},
modules: {
Contents: contentsStore()
}
}
})
const appState = {
namespaced: true,
@ -166,16 +176,12 @@ const appState = {
}
describe('Favourites', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Favourites: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -183,9 +189,9 @@ describe('Favourites', () => {
describe('fetchFavourites', () => {
it('does not exist header', async () => {
await store.dispatch('Favourites/fetchFavourites', localAccount)
expect(store.state.Favourites.favourites).toEqual([status1])
expect(store.state.Favourites.maxId).toEqual(null)
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 () => {
@ -202,9 +208,9 @@ describe('Favourites', () => {
resolve(res)
})
}
await store.dispatch('Favourites/fetchFavourites', localAccount)
expect(store.state.Favourites.favourites).toEqual([status1])
expect(store.state.Favourites.maxId).toEqual(null)
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 () => {
@ -222,9 +228,9 @@ describe('Favourites', () => {
})
}
await store.dispatch('Favourites/fetchFavourites', localAccount)
expect(store.state.Favourites.favourites).toEqual([status1])
expect(store.state.Favourites.maxId).toEqual('2')
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')
})
})
@ -240,7 +246,7 @@ describe('Favourites', () => {
}
})
it('should not be updated', async () => {
const res = await store.dispatch('Favourites/lazyFetchFavourites')
const res = await store.dispatch('TimelineSpace/Contents/Favourites/lazyFetchFavourites')
expect(res).toEqual(null)
})
})
@ -256,7 +262,7 @@ describe('Favourites', () => {
}
})
it('should not be updated', async () => {
const res = await store.dispatch('Favourites/lazyFetchFavourites')
const res = await store.dispatch('TimelineSpace/Contents/Favourites/lazyFetchFavourites')
expect(res).toEqual(null)
})
})
@ -286,9 +292,9 @@ describe('Favourites', () => {
})
}
await store.dispatch('Favourites/lazyFetchFavourites')
expect(store.state.Favourites.favourites).toEqual([status1, status2])
expect(store.state.Favourites.maxId).toEqual(null)
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 () => {
@ -306,9 +312,9 @@ describe('Favourites', () => {
})
}
await store.dispatch('Favourites/lazyFetchFavourites')
expect(store.state.Favourites.favourites).toEqual([status1, status2])
expect(store.state.Favourites.maxId).toEqual('3')
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')
})
})
})

View File

@ -1,8 +1,8 @@
import { Entity, Response } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
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: () => {
@ -110,19 +110,27 @@ const sideMenuState = (): SideMenuState => {
}
}
const sideMenuStore = {
const sideMenuStore = () => ({
namespaced: true,
state: sideMenuState(),
actions: {
fetchFollowRequests: jest.fn()
},
mutations: {}
}
})
const timelineState = {
const contentsStore = () => ({
namespaced: true,
modules: {
SideMenu: sideMenuStore
FollowRequests: initStore()
}
})
const timelineStore = () => ({
namespaced: true,
modules: {
SideMenu: sideMenuStore(),
Contents: contentsStore()
},
state: {
account: {
@ -131,7 +139,7 @@ const timelineState = {
},
sns: 'mastodon'
}
}
})
const appState = {
namespaced: true,
@ -141,16 +149,12 @@ const appState = {
}
describe('Home', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
FollowRequests: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -158,8 +162,8 @@ describe('Home', () => {
describe('fetchRequests', () => {
it('should be updated', async () => {
await store.dispatch('FollowRequests/fetchRequests')
expect(store.state.FollowRequests.requests).toEqual([account])
await store.dispatch('TimelineSpace/Contents/FollowRequests/fetchRequests')
expect(store.state.TimelineSpace.Contents.FollowRequests.requests).toEqual([account])
})
})
@ -184,8 +188,8 @@ describe('Home', () => {
})
}
await store.dispatch('FollowRequests/acceptRequest', account)
expect(store.state.FollowRequests.requests).toEqual([])
await store.dispatch('TimelineSpace/Contents/FollowRequests/acceptRequest', account)
expect(store.state.TimelineSpace.Contents.FollowRequests.requests).toEqual([])
})
})
@ -210,8 +214,8 @@ describe('Home', () => {
})
}
await store.dispatch('FollowRequests/rejectRequest', account)
expect(store.state.FollowRequests.requests).toEqual([])
await store.dispatch('TimelineSpace/Contents/FollowRequests/rejectRequest', account)
expect(store.state.TimelineSpace.Contents.FollowRequests.requests).toEqual([])
})
})
})

View File

@ -1,11 +1,11 @@
import { IpcMainInvokeEvent } from 'electron'
import { createLocalVue } from '@vue/test-utils'
import { createStore, Store } from 'vuex'
import { ipcMain, ipcRenderer } from '~/spec/mock/electron'
import Vuex from 'vuex'
import { LocalTag } from '~/src/types/localTag'
import List, { ListState } from '@/store/TimelineSpace/Contents/Hashtag/List'
import { MyWindow } from '~/src/types/global'
;((window as any) as MyWindow).ipcRenderer = ipcRenderer
import { RootState } from '@/store'
;(window as any as MyWindow).ipcRenderer = ipcRenderer
const tag1: LocalTag = {
tagName: 'tag1',
@ -32,6 +32,20 @@ const initStore = () => {
}
}
const hashtagStore = () => ({
namespaced: true,
modules: {
List: initStore()
}
})
const contentsStore = () => ({
namespaced: true,
modules: {
Hashtag: hashtagStore()
}
})
const sideMenuStore = {
namespaced: true,
actions: {
@ -39,24 +53,21 @@ const sideMenuStore = {
}
}
const timelineState = {
const timelineStore = () => ({
namespaced: true,
modules: {
SideMenu: sideMenuStore
SideMenu: sideMenuStore,
Contents: contentsStore()
}
}
})
describe('Hashtag/List', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
List: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineStore()
}
})
})
@ -69,8 +80,8 @@ describe('Hashtag/List', () => {
afterEach(() => {
ipcMain.removeHandler('list-hashtags')
})
await store.dispatch('List/listTags')
expect(store.state.List.tags).toEqual([tag1, tag2])
await store.dispatch('TimelineSpace/Contents/Hashtag/List/listTags')
expect(store.state.TimelineSpace.Contents.Hashtag.List.tags).toEqual([tag1, tag2])
})
})
@ -79,7 +90,7 @@ describe('Hashtag/List', () => {
ipcMain.handle('remove-hashtag', (_: IpcMainInvokeEvent, tag: LocalTag) => {
expect(tag).toEqual(tag1)
})
await store.dispatch('List/removeTag', tag1)
await store.dispatch('TimelineSpace/Contents/Hashtag/List/removeTag', tag1)
})
})
})

View File

@ -1,8 +1,8 @@
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
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: () => {
@ -134,15 +134,32 @@ const initStore = () => {
}
}
const timelineState = {
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,
@ -152,16 +169,12 @@ const appState = {
}
describe('Home', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Tag: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -169,9 +182,9 @@ describe('Home', () => {
describe('fetch', () => {
it('should be updated', async () => {
const statuses = await store.dispatch('Tag/fetch', 'tag')
const statuses = await store.dispatch('TimelineSpace/Contents/Hashtag/Tag/fetch', 'tag')
expect(statuses).toEqual([status1])
expect(store.state.Tag.timeline).toEqual([status1])
expect(store.state.TimelineSpace.Contents.Hashtag.Tag.timeline).toEqual([status1])
})
})
@ -203,9 +216,9 @@ describe('Home', () => {
status: status1,
tag: 'tag'
}
await store.dispatch('Tag/lazyFetchTimeline', loadPositionWithTag)
expect(store.state.Tag.lazyLoading).toEqual(false)
expect(store.state.Tag.timeline).toEqual([status1, status2])
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])
})
})
})

View File

@ -1,7 +1,7 @@
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import Home, { HomeState } from '@/store/TimelineSpace/Contents/Home'
import { RootState } from '@/store'
const mockClient = {
getHomeTimeline: () => {
@ -126,7 +126,7 @@ let state = (): HomeState => {
}
}
const initStore = () => {
const homeStore = () => {
return {
namespaced: true,
state: state(),
@ -135,7 +135,14 @@ const initStore = () => {
}
}
const timelineState = {
const contentsStore = () => ({
namespaced: true,
modules: {
Home: homeStore()
}
})
const timelineStore = () => ({
namespaced: true,
state: {
account: {
@ -148,8 +155,11 @@ const timelineState = {
notifications: false
}
}
},
modules: {
Contents: contentsStore()
}
}
})
const appState = {
namespaced: true,
@ -159,16 +169,12 @@ const appState = {
}
describe('Home', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Home: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -176,9 +182,9 @@ describe('Home', () => {
describe('fetchTimeline', () => {
it('should be updated', async () => {
const statuses = await store.dispatch('Home/fetchTimeline')
const statuses = await store.dispatch('TimelineSpace/Contents/Home/fetchTimeline')
expect(statuses).toEqual([status1])
expect(store.state.Home.timeline).toEqual([status1])
expect(store.state.TimelineSpace.Contents.Home.timeline).toEqual([status1])
})
})
@ -210,9 +216,9 @@ describe('Home', () => {
})
}
await store.dispatch('Home/lazyFetchTimeline', status1)
expect(store.state.Home.lazyLoading).toEqual(false)
expect(store.state.Home.timeline).toEqual([status1, status2])
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])
})
})
})

View File

@ -1,8 +1,8 @@
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
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: () => {
@ -72,7 +72,21 @@ const initStore = () => {
}
}
const timelineState = {
const listsStore = () => ({
namespaced: true,
modules: {
Edit: initStore()
}
})
const contentsStore = () => ({
namespaced: true,
modules: {
Lists: listsStore()
}
})
const timelineStore = () => ({
namespaced: true,
state: {
account: {
@ -80,8 +94,11 @@ const timelineState = {
baseURL: 'http://localhost'
},
sns: 'mastodon'
},
modules: {
Contents: contentsStore()
}
}
})
const appState = {
namespaced: true,
@ -91,16 +108,12 @@ const appState = {
}
describe('Lists/Edit', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Edit: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -108,8 +121,8 @@ describe('Lists/Edit', () => {
describe('fetchMembers', () => {
it('should get', async () => {
await store.dispatch('Edit/fetchMembers', 'id')
expect(store.state.Edit.members).toEqual([account])
await store.dispatch('TimelineSpace/Contents/Lists/Edit/fetchMembers', 'id')
expect(store.state.TimelineSpace.Contents.Lists.Edit.members).toEqual([account])
})
})
@ -119,7 +132,7 @@ describe('Lists/Edit', () => {
account: account,
listId: 'id'
}
const res = await store.dispatch('Edit/removeAccount', removeFromList)
const res = await store.dispatch('TimelineSpace/Contents/Lists/Edit/removeAccount', removeFromList)
expect(res.data).toEqual({})
})
})

View File

@ -1,7 +1,7 @@
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import Index, { IndexState } from '@/store/TimelineSpace/Contents/Lists/Index'
import { RootState } from '@/store'
const mockClient = {
getLists: () => {
@ -54,7 +54,21 @@ const initStore = () => {
}
}
const timelineState = {
const listsStore = () => ({
namespaced: true,
modules: {
Index: initStore()
}
})
const contentsStore = () => ({
namespaced: true,
modules: {
Lists: listsStore()
}
})
const timelineStore = () => ({
namespaced: true,
state: {
account: {
@ -62,8 +76,11 @@ const timelineState = {
baseURL: 'http://localhost'
},
sns: 'mastodon'
},
modules: {
Contents: contentsStore()
}
}
})
const appState = {
namespaced: true,
@ -73,16 +90,12 @@ const appState = {
}
describe('Lists/Index', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Index: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -90,14 +103,14 @@ describe('Lists/Index', () => {
describe('fetchLists', () => {
it('should be updated', async () => {
await store.dispatch('Index/fetchLists')
expect(store.state.Index.lists).toEqual([list])
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('Index/createList', 'list1')
const res: Entity.List = await store.dispatch('TimelineSpace/Contents/Lists/Index/createList', 'list1')
expect(res.title).toEqual('list1')
})
})

View File

@ -1,8 +1,8 @@
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
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: () => {
@ -136,15 +136,32 @@ const initStore = () => {
}
}
const timelineState = {
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,
@ -154,16 +171,12 @@ const appState = {
}
describe('Lists/Show', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Show: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -171,8 +184,8 @@ describe('Lists/Show', () => {
describe('fetchTimeline', () => {
it('should be updated', async () => {
await store.dispatch('Show/fetchTimeline', '1')
expect(store.state.Show.timeline).toEqual([status1])
await store.dispatch('TimelineSpace/Contents/Lists/Show/fetchTimeline', '1')
expect(store.state.TimelineSpace.Contents.Lists.Show.timeline).toEqual([status1])
})
})
@ -204,9 +217,9 @@ describe('Lists/Show', () => {
status: status1,
list_id: '1'
}
await store.dispatch('Show/lazyFetchTimeline', loadPosition)
expect(store.state.Show.timeline).toEqual([status1, status2])
expect(store.state.Show.lazyLoading).toEqual(false)
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)
})
})
})

View File

@ -1,7 +1,7 @@
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import Local, { LocalState } from '@/store/TimelineSpace/Contents/Local'
import { RootState } from '@/store'
const mockClient = {
getLocalTimeline: () => {
@ -133,15 +133,25 @@ const initStore = () => {
}
}
const timelineState = {
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,
@ -151,16 +161,12 @@ const appState = {
}
describe('Home', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Local: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -168,9 +174,9 @@ describe('Home', () => {
describe('fetchLocalTimeline', () => {
it('should be updated', async () => {
const statuses = await store.dispatch('Local/fetchLocalTimeline')
const statuses = await store.dispatch('TimelineSpace/Contents/Local/fetchLocalTimeline')
expect(statuses).toEqual([status1])
expect(store.state.Local.timeline).toEqual([status1])
expect(store.state.TimelineSpace.Contents.Local.timeline).toEqual([status1])
})
})
@ -199,9 +205,9 @@ describe('Home', () => {
})
}
await store.dispatch('Local/lazyFetchTimeline', status1)
expect(store.state.Local.lazyLoading).toEqual(false)
expect(store.state.Local.timeline).toEqual([status1, status2])
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])
})
})
})

View File

@ -1,6 +1,6 @@
import { RootState } from '@/store'
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import Mentions from '~/src/renderer/store/TimelineSpace/Contents/Mentions'
const mockClient = {
@ -129,7 +129,15 @@ const initStore = () => {
getters: Mentions.getters
}
}
const timelineState = {
const contentsStore = () => ({
namespaced: true,
modules: {
Mentions: initStore()
}
})
const timelineStore = () => ({
namespaced: true,
state: {
account: {
@ -143,8 +151,11 @@ const timelineState = {
mentions: false
}
}
},
modules: {
Contents: contentsStore()
}
}
})
const appState = {
namespaced: true,
@ -154,16 +165,12 @@ const appState = {
}
describe('Mentions', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Mentions: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -171,8 +178,8 @@ describe('Mentions', () => {
describe('fetchMentions', () => {
it('should be updated', async () => {
await store.dispatch('Mentions/fetchMentions')
expect(store.state.Mentions.mentions).toEqual([mention, reblog, favourite, follow])
await store.dispatch('TimelineSpace/Contents/Mentions/fetchMentions')
expect(store.state.TimelineSpace.Contents.Mentions.mentions).toEqual([mention, reblog, favourite, follow])
})
})
@ -189,7 +196,7 @@ describe('Mentions', () => {
}
})
it('should not be updated', async () => {
const result = await store.dispatch('Mentions/lazyFetchMentions', {})
const result = await store.dispatch('TimelineSpace/Contents/Mentions/lazyFetchMentions', {})
expect(result).toEqual(null)
})
})
@ -218,9 +225,9 @@ describe('Mentions', () => {
})
}
await store.dispatch('Mentions/lazyFetchMentions', { id: 1 })
expect(store.state.Mentions.mentions).toEqual([mention, reblog, favourite, follow])
expect(store.state.Mentions.lazyLoading).toEqual(false)
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)
})
})
})
@ -237,7 +244,7 @@ describe('Mentions', () => {
}
})
it('should return only mentions', () => {
const mentions = store.getters['Mentions/mentions']
const mentions = store.getters['TimelineSpace/Contents/Mentions/mentions']
expect(mentions).toEqual([mention])
})
})

View File

@ -1,7 +1,7 @@
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import Notifications, { NotificationsState } from '@/store/TimelineSpace/Contents/Notifications'
import { RootState } from '@/store'
const mockClient = {
getNotifications: () => {
@ -209,7 +209,14 @@ const initStore = () => {
}
}
const timelineState = {
const contentsStore = () => ({
namespaced: true,
modules: {
Notifications: initStore()
}
})
const timelineStore = () => ({
namespaced: true,
state: {
account: {
@ -223,8 +230,11 @@ const timelineState = {
mentions: false
}
}
},
modules: {
Contents: contentsStore()
}
}
})
const appState = {
namespaced: true,
@ -235,16 +245,12 @@ const appState = {
}
describe('Notifications', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Notifications: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -252,9 +258,9 @@ describe('Notifications', () => {
describe('fetchNotifications', () => {
it('should be updated', async () => {
const response = await store.dispatch('Notifications/fetchNotifications')
const response = await store.dispatch('TimelineSpace/Contents/Notifications/fetchNotifications')
expect(response).toEqual([notification1])
expect(store.state.Notifications.notifications).toEqual([notification1])
expect(store.state.TimelineSpace.Contents.Notifications.notifications).toEqual([notification1])
})
})
@ -281,9 +287,9 @@ describe('Notifications', () => {
resolve(res)
})
}
await store.dispatch('Notifications/lazyFetchNotifications', notification1)
expect(store.state.Notifications.lazyLoading).toEqual(false)
expect(store.state.Notifications.notifications).toEqual([notification1, notification2])
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])
})
})
})

View File

@ -1,7 +1,7 @@
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import Public, { PublicState } from '@/store/TimelineSpace/Contents/Public'
import { RootState } from '@/store'
const mockClient = {
getPublicTimeline: () => {
@ -133,15 +133,25 @@ const initStore = () => {
}
}
const timelineState = {
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,
@ -151,16 +161,12 @@ const appState = {
}
describe('Home', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Public: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -168,9 +174,9 @@ describe('Home', () => {
describe('fetchPublicTimeline', () => {
it('should be updated', async () => {
const statuses = await store.dispatch('Public/fetchPublicTimeline')
const statuses = await store.dispatch('TimelineSpace/Contents/Public/fetchPublicTimeline')
expect(statuses).toEqual([status1])
expect(store.state.Public.timeline).toEqual([status1])
expect(store.state.TimelineSpace.Contents.Public.timeline).toEqual([status1])
})
})
@ -198,9 +204,9 @@ describe('Home', () => {
resolve(res)
})
}
await store.dispatch('Public/lazyFetchTimeline', status1)
expect(store.state.Public.lazyLoading).toEqual(false)
expect(store.state.Public.timeline).toEqual([status1, status2])
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])
})
})
})

View File

@ -1,7 +1,7 @@
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import AccountStore, { AccountState } from '@/store/TimelineSpace/Contents/Search/Account'
import { RootState } from '@/store'
const account: Entity.Account = {
id: '1',
@ -60,19 +60,29 @@ const initStore = () => {
}
}
const contentsStore = {
const searchStore = () => ({
namespaced: true,
modules: {
Account: initStore()
}
})
const contentsStore = () => ({
namespaced: true,
state: {},
mutations: {
changeLoading: jest.fn()
},
actions: {}
}
actions: {},
modules: {
Search: searchStore()
}
})
const timelineState = {
const timelineStore = () => ({
namespaced: true,
modules: {
Contents: contentsStore
Contents: contentsStore()
},
state: {
account: {
@ -81,7 +91,7 @@ const timelineState = {
},
sns: 'mastodon'
}
}
})
const appState = {
namespaced: true,
@ -91,16 +101,12 @@ const appState = {
}
describe('Search/Account', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Account: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -108,8 +114,8 @@ describe('Search/Account', () => {
describe('search', () => {
it('should be updated', async () => {
await store.dispatch('Account/search', 'query')
expect(store.state.Account.results).toEqual([account])
await store.dispatch('TimelineSpace/Contents/Search/Account/search', 'query')
expect(store.state.TimelineSpace.Contents.Search.Account.results).toEqual([account])
})
})
})

View File

@ -1,7 +1,7 @@
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import TagStore, { TagState } from '@/store/TimelineSpace/Contents/Search/Tag'
import { RootState } from '@/store'
const tag1: Entity.Tag = {
name: 'tag1',
@ -48,19 +48,29 @@ const initStore = () => {
}
}
const contentsStore = {
const searchStore = () => ({
namespaced: true,
modules: {
Tag: initStore()
}
})
const contentsStore = () => ({
namespaced: true,
state: {},
mutations: {
changeLoading: jest.fn()
},
actions: {}
}
actions: {},
modules: {
Search: searchStore()
}
})
const timelineState = {
const timelineStore = () => ({
namespaced: true,
modules: {
Contents: contentsStore
Contents: contentsStore()
},
state: {
account: {
@ -69,7 +79,8 @@ const timelineState = {
},
sns: 'mastodon'
}
}
})
const appState = {
namespaced: true,
state: {
@ -78,16 +89,12 @@ const appState = {
}
describe('Search/Tag', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Tag: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -95,8 +102,8 @@ describe('Search/Tag', () => {
describe('search', () => {
it('should be updated', async () => {
await store.dispatch('Tag/search', 'query')
expect(store.state.Tag.results).toEqual([tag1])
await store.dispatch('TimelineSpace/Contents/Search/Tag/search', 'query')
expect(store.state.TimelineSpace.Contents.Search.Tag.results).toEqual([tag1])
})
})
})

View File

@ -1,7 +1,7 @@
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import Toots, { TootsState } from '@/store/TimelineSpace/Contents/Search/Toots'
import { RootState } from '@/store'
const mockClient = {
search: () => {
@ -100,19 +100,29 @@ const initStore = () => {
}
}
const contentsStore = {
const searchStore = () => ({
namespaced: true,
modules: {
Toots: initStore()
}
})
const contentsStore = () => ({
namespaced: true,
state: {},
mutations: {
changeLoading: jest.fn()
},
actions: {}
}
actions: {},
modules: {
Search: searchStore()
}
})
const timelineState = {
const timelineStore = () => ({
namespaced: true,
modules: {
Contents: contentsStore
Contents: contentsStore()
},
state: {
account: {
@ -121,7 +131,7 @@ const timelineState = {
},
sns: 'mastodon'
}
}
})
const appState = {
namespaced: true,
@ -131,16 +141,12 @@ const appState = {
}
describe('Search/Account', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Toots: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -148,8 +154,8 @@ describe('Search/Account', () => {
describe('search', () => {
it('should be updated', async () => {
await store.dispatch('Toots/search', 'query')
expect(store.state.Toots.results).toEqual([status])
await store.dispatch('TimelineSpace/Contents/Search/Toots/search', 'query')
expect(store.state.TimelineSpace.Contents.Search.Toots.results).toEqual([status])
})
})
})

View File

@ -1,6 +1,6 @@
import { RootState } from '@/store'
import { Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import AccountProfile, { AccountProfileState } from '~/src/renderer/store/TimelineSpace/Contents/SideBar/AccountProfile'
const state = (account: Entity.Account | null): AccountProfileState => {
@ -12,7 +12,31 @@ const state = (account: Entity.Account | null): AccountProfileState => {
}
}
const timelineSpace = {
const initStore = (account: Entity.Account | null) => {
return {
namespaced: true,
state: state(account),
actions: AccountProfile.actions,
mutations: AccountProfile.mutations,
getters: AccountProfile.getters
}
}
const sidebarStore = (account: Entity.Account | null) => ({
namespaced: true,
modules: {
AccountProfile: initStore(account)
}
})
const contentsStore = (account: Entity.Account | null) => ({
namespaced: true,
modules: {
SideBar: sidebarStore(account)
}
})
const timelineStore = (account: Entity.Account | null) => ({
namespaced: true,
state: {
account: {
@ -27,30 +51,19 @@ const timelineSpace = {
avatar: null,
order: 1
}
},
modules: {
Contents: contentsStore(account)
}
}
const initStore = (account: Entity.Account | null) => {
return {
namespaced: true,
state: state(account),
actions: AccountProfile.actions,
mutations: AccountProfile.mutations,
getters: AccountProfile.getters
}
}
})
describe('AccountProfile', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
AccountProfile: initStore(null),
TimelineSpace: timelineSpace
TimelineSpace: timelineStore(null)
}
})
})
@ -79,15 +92,14 @@ describe('AccountProfile', () => {
bot: false
}
beforeEach(() => {
store = new Vuex.Store({
store = createStore({
modules: {
AccountProfile: initStore(account),
TimelineSpace: timelineSpace
TimelineSpace: timelineStore(account)
}
})
})
it('should be matched', () => {
expect(store.getters['AccountProfile/isOwnProfile']).toBeTruthy()
expect(store.getters['TimelineSpace/Contents/SideBar/AccountProfile/isOwnProfile']).toBeTruthy()
})
})
@ -114,15 +126,14 @@ describe('AccountProfile', () => {
bot: false
}
beforeEach(() => {
store = new Vuex.Store({
store = createStore({
modules: {
AccountProfile: initStore(account),
TimelineSpace: timelineSpace
TimelineSpace: timelineStore(account)
}
})
})
it('should be matched', () => {
expect(store.getters['AccountProfile/isOwnProfile']).toBeFalsy()
expect(store.getters['TimelineSpace/Contents/SideBar/AccountProfile/isOwnProfile']).toBeFalsy()
})
})
@ -149,15 +160,14 @@ describe('AccountProfile', () => {
bot: false
}
beforeEach(() => {
store = new Vuex.Store({
store = createStore({
modules: {
AccountProfile: initStore(account),
TimelineSpace: timelineSpace
TimelineSpace: timelineStore(account)
}
})
})
it('should be matched', () => {
expect(store.getters['AccountProfile/isOwnProfile']).toBeTruthy()
expect(store.getters['TimelineSpace/Contents/SideBar/AccountProfile/isOwnProfile']).toBeTruthy()
})
})
@ -184,15 +194,14 @@ describe('AccountProfile', () => {
bot: false
}
beforeEach(() => {
store = new Vuex.Store({
store = createStore({
modules: {
AccountProfile: initStore(account),
TimelineSpace: timelineSpace
TimelineSpace: timelineStore(account)
}
})
})
it('should be matched', () => {
expect(store.getters['AccountProfile/isOwnProfile']).toBeFalsy()
expect(store.getters['TimelineSpace/Contents/SideBar/AccountProfile/isOwnProfile']).toBeFalsy()
})
})
})

View File

@ -1,6 +1,6 @@
import { RootState } from '@/store'
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import HeaderMenu, { HeaderMenuState } from '~/src/renderer/store/TimelineSpace/HeaderMenu'
const list: Entity.List = {
@ -45,7 +45,7 @@ const initStore = () => {
}
}
const timelineState = {
const timelineStore = () => ({
namespaced: true,
state: {
account: {
@ -53,8 +53,11 @@ const timelineState = {
baseURL: 'http://localhost'
},
sns: 'mastodon'
},
modules: {
HeaderMenu: initStore()
}
}
})
const appState = {
namespaced: true,
@ -64,16 +67,12 @@ const appState = {
}
describe('HeaderMenu', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
HeaderMenu: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -81,9 +80,9 @@ describe('HeaderMenu', () => {
describe('fetchLists', () => {
it('should be updated', async () => {
const l = await store.dispatch('HeaderMenu/fetchList', list.id)
const l = await store.dispatch('TimelineSpace/HeaderMenu/fetchList', list.id)
expect(l).toEqual(list)
expect(store.state.HeaderMenu.title).toEqual(`#${list.title}`)
expect(store.state.TimelineSpace.HeaderMenu.title).toEqual(`#${list.title}`)
})
})
})

View File

@ -1,7 +1,7 @@
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import AddListMember, { AddListMemberState } from '@/store/TimelineSpace/Modals/AddListMember'
import { RootState } from '@/store'
const mockClient = {
searchAccount: () => {
@ -73,15 +73,25 @@ const initStore = () => {
}
}
const timelineState = {
const modalsStore = () => ({
namespaced: true,
modules: {
AddListMember: initStore()
}
})
const timelineStore = () => ({
namespaced: true,
state: {
account: {
_id: '0'
},
sns: 'mastodon'
},
modules: {
Modals: modalsStore()
}
}
})
const appState = {
namespaced: true,
@ -91,16 +101,13 @@ const appState = {
}
describe('AddListMember', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
AddListMember: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -108,21 +115,21 @@ describe('AddListMember', () => {
describe('changeModal', () => {
it('should change modal', () => {
store.dispatch('AddListMember/changeModal', true)
expect(store.state.AddListMember.modalOpen).toEqual(true)
store.dispatch('TimelineSpace/Modals/AddListMember/changeModal', true)
expect(store.state.TimelineSpace.Modals.AddListMember.modalOpen).toEqual(true)
})
})
describe('search', () => {
it('should be searched', async () => {
await store.dispatch('AddListMember/search', 'akira')
expect(store.state.AddListMember.accounts).toEqual([account])
await store.dispatch('TimelineSpace/Modals/AddListMember/search', 'akira')
expect(store.state.TimelineSpace.Modals.AddListMember.accounts).toEqual([account])
})
})
describe('add', () => {
it('should be added a member to the list', async () => {
const result = await store.dispatch('AddListMember/add', 'akira')
const result = await store.dispatch('TimelineSpace/Modals/AddListMember/add', 'akira')
expect(result).toEqual({})
})
})

View File

@ -1,5 +1,5 @@
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { RootState } from '@/store'
import { createStore, Store } from 'vuex'
import ImageViewer, { ImageViewerState } from '~/src/renderer/store/TimelineSpace/Modals/ImageViewer'
const state = (): ImageViewerState => {
@ -21,16 +21,27 @@ const initStore = () => {
}
}
const modalsStore = () => ({
namespaced: true,
modules: {
ImageViewer: initStore()
}
})
const timelineStore = () => ({
namespaced: true,
modules: {
Modals: modalsStore()
}
})
describe('ImageViewer', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
ImageViewer: initStore()
TimelineSpace: timelineStore()
}
})
})
@ -38,46 +49,46 @@ describe('ImageViewer', () => {
// Actions
describe('openModal', () => {
it('should be changed', () => {
store.dispatch('ImageViewer/openModal', {
store.dispatch('TimelineSpace/Modals/ImageViewer/openModal', {
currentIndex: 1,
mediaList: ['media1', 'media2']
})
expect(store.state.ImageViewer.modalOpen).toEqual(true)
expect(store.state.ImageViewer.currentIndex).toEqual(1)
expect(store.state.ImageViewer.mediaList).toEqual(['media1', 'media2'])
expect(store.state.ImageViewer.loading).toEqual(true)
expect(store.state.TimelineSpace.Modals.ImageViewer.modalOpen).toEqual(true)
expect(store.state.TimelineSpace.Modals.ImageViewer.currentIndex).toEqual(1)
expect(store.state.TimelineSpace.Modals.ImageViewer.mediaList).toEqual(['media1', 'media2'])
expect(store.state.TimelineSpace.Modals.ImageViewer.loading).toEqual(true)
})
})
describe('closeModal', () => {
beforeEach(() => {
store.dispatch('ImageViewer/openModal', {
store.dispatch('TimelineSpace/Modals/ImageViewer/openModal', {
currentIndex: 1,
mediaList: ['media1', 'media2']
})
})
it('should be changed', () => {
store.dispatch('ImageViewer/closeModal')
expect(store.state.ImageViewer.modalOpen).toEqual(false)
expect(store.state.ImageViewer.currentIndex).toEqual(-1)
expect(store.state.ImageViewer.mediaList).toEqual([])
expect(store.state.ImageViewer.loading).toEqual(false)
store.dispatch('TimelineSpace/Modals/ImageViewer/closeModal')
expect(store.state.TimelineSpace.Modals.ImageViewer.modalOpen).toEqual(false)
expect(store.state.TimelineSpace.Modals.ImageViewer.currentIndex).toEqual(-1)
expect(store.state.TimelineSpace.Modals.ImageViewer.mediaList).toEqual([])
expect(store.state.TimelineSpace.Modals.ImageViewer.loading).toEqual(false)
})
})
describe('incrementIndex', () => {
it('should be changed', () => {
store.dispatch('ImageViewer/incrementIndex')
expect(store.state.ImageViewer.currentIndex).toEqual(0)
expect(store.state.ImageViewer.loading).toEqual(true)
store.dispatch('TimelineSpace/Modals/ImageViewer/incrementIndex')
expect(store.state.TimelineSpace.Modals.ImageViewer.currentIndex).toEqual(0)
expect(store.state.TimelineSpace.Modals.ImageViewer.loading).toEqual(true)
})
})
describe('decrementIndex', () => {
it('should be changed', () => {
store.dispatch('ImageViewer/decrementIndex')
expect(store.state.ImageViewer.currentIndex).toEqual(-2)
expect(store.state.ImageViewer.loading).toEqual(true)
store.dispatch('TimelineSpace/Modals/ImageViewer/decrementIndex')
expect(store.state.TimelineSpace.Modals.ImageViewer.currentIndex).toEqual(-2)
expect(store.state.TimelineSpace.Modals.ImageViewer.loading).toEqual(true)
})
})
@ -85,7 +96,7 @@ describe('ImageViewer', () => {
describe('imageURL', () => {
describe('currentIndex exists', () => {
beforeEach(() => {
store.dispatch('ImageViewer/openModal', {
store.dispatch('TimelineSpace/Modals/ImageViewer/openModal', {
currentIndex: 0,
mediaList: [
{
@ -98,7 +109,7 @@ describe('ImageViewer', () => {
})
})
it('should return url', () => {
const url = store.getters['ImageViewer/imageURL']
const url = store.getters['TimelineSpace/Modals/ImageViewer/imageURL']
expect(url).toEqual('http://joinmastodon.org')
})
})
@ -107,7 +118,7 @@ describe('ImageViewer', () => {
describe('imageType', () => {
describe('currentIndex exists', () => {
beforeEach(() => {
store.dispatch('ImageViewer/openModal', {
store.dispatch('TimelineSpace/Modals/ImageViewer/openModal', {
currentIndex: 0,
mediaList: [
{
@ -120,7 +131,7 @@ describe('ImageViewer', () => {
})
})
it('should return type', () => {
const type = store.getters['ImageViewer/imageType']
const type = store.getters['TimelineSpace/Modals/ImageViewer/imageType']
expect(type).toEqual('image/png')
})
})
@ -130,7 +141,7 @@ describe('ImageViewer', () => {
describe('currentIndex > 0', () => {
describe('mediaList > 1', () => {
beforeEach(() => {
store.dispatch('ImageViewer/openModal', {
store.dispatch('TimelineSpace/Modals/ImageViewer/openModal', {
currentIndex: 1,
mediaList: [
{
@ -143,13 +154,13 @@ describe('ImageViewer', () => {
})
})
it('should return true', () => {
const left = store.getters['ImageViewer/showLeft']
const left = store.getters['TimelineSpace/Modals/ImageViewer/showLeft']
expect(left).toEqual(true)
})
})
describe('mediaList < 1', () => {
beforeEach(() => {
store.dispatch('ImageViewer/openModal', {
store.dispatch('TimelineSpace/Modals/ImageViewer/openModal', {
currentIndex: 0,
mediaList: [
{
@ -159,7 +170,7 @@ describe('ImageViewer', () => {
})
})
it('should not return true', () => {
const left = store.getters['ImageViewer/showLeft']
const left = store.getters['TimelineSpace/Modals/ImageViewer/showLeft']
expect(left).toEqual(false)
})
})
@ -170,7 +181,7 @@ describe('ImageViewer', () => {
describe('current index is lower than media list length', () => {
describe('media list length > 1', () => {
beforeEach(() => {
store.dispatch('ImageViewer/openModal', {
store.dispatch('TimelineSpace/Modals/ImageViewer/openModal', {
currentIndex: 0,
mediaList: [
{
@ -183,13 +194,13 @@ describe('ImageViewer', () => {
})
})
it('should return true', () => {
const right = store.getters['ImageViewer/showRight']
const right = store.getters['TimelineSpace/Modals/ImageViewer/showRight']
expect(right).toEqual(true)
})
})
describe('media list length <= 1', () => {
beforeEach(() => {
store.dispatch('ImageViewer/openModal', {
store.dispatch('TimelineSpace/Modals/ImageViewer/openModal', {
currentIndex: 0,
mediaList: [
{
@ -199,7 +210,7 @@ describe('ImageViewer', () => {
})
})
it('should not return true', () => {
const right = store.getters['ImageViewer/showRight']
const right = store.getters['TimelineSpace/Modals/ImageViewer/showRight']
expect(right).toEqual(false)
})
})

View File

@ -1,8 +1,8 @@
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import i18n from '~/src/config/i18n'
import router from '@/router'
import Jump, { JumpState, Channel } from '~/src/renderer/store/TimelineSpace/Modals/Jump'
import { RootState } from '@/store'
const state = (): JumpState => {
return {
@ -59,34 +59,40 @@ const initStore = () => {
}
}
const timelineState = {
const modalsStore = () => ({
namespaced: true,
modules: {
Jump: initStore()
}
})
const timelineStore = () => ({
namespaced: true,
state: {
account: {
_id: '0'
}
},
modules: {
Modals: modalsStore()
}
}
})
describe('Jump', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
Jump: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineStore()
}
})
})
describe('jumpCurrentSelected', () => {
it('should be changed', () => {
store.dispatch('Jump/jumpCurrentSelected')
expect(store.state.Jump.modalOpen).toEqual(false)
store.dispatch('TimelineSpace/Modals/Jump/jumpCurrentSelected')
expect(store.state.TimelineSpace.Modals.Jump.modalOpen).toEqual(false)
expect(router.push).toHaveBeenCalledWith({ path: '/0/home' })
})
})
@ -97,8 +103,8 @@ describe('Jump', () => {
name: 'public',
path: 'public'
}
store.dispatch('Jump/jump', channel)
expect(store.state.Jump.modalOpen).toEqual(false)
store.dispatch('TimelineSpace/Modals/Jump/jump', channel)
expect(store.state.TimelineSpace.Modals.Jump.modalOpen).toEqual(false)
expect(router.push).toHaveBeenCalledWith({ path: '/0/public' })
})
})

View File

@ -1,7 +1,7 @@
import { Response, Entity } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { createStore, Store } from 'vuex'
import ListMembership, { ListMembershipState } from '@/store/TimelineSpace/Modals/ListMembership'
import { RootState } from '@/store'
const mockClient = {
getAccountLists: () => {
@ -119,14 +119,24 @@ const initStore = () => {
}
}
const timelineState = {
const modalsStore = () => ({
namespaced: true,
modules: {
ListMembership: initStore()
}
})
const timelineStore = () => ({
namespaced: true,
state: {
account: {
_id: '0'
}
},
modules: {
Modals: modalsStore()
}
}
})
const appState = {
namespaced: true,
@ -136,16 +146,12 @@ const appState = {
}
describe('ListMembership', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
ListMembership: initStore(),
TimelineSpace: timelineState,
TimelineSpace: timelineStore(),
App: appState
}
})
@ -153,17 +159,17 @@ describe('ListMembership', () => {
describe('fetchListMembership', () => {
it('should get', async () => {
await store.dispatch('ListMembership/fetchListMembership', {
await store.dispatch('TimelineSpace/Modals/ListMembership/fetchListMembership', {
id: '5'
})
expect(store.state.ListMembership.belongToLists).toEqual([list1, list2])
expect(store.state.TimelineSpace.Modals.ListMembership.belongToLists).toEqual([list1, list2])
})
})
describe('fetchLists', () => {
it('should be changed', async () => {
await store.dispatch('ListMembership/fetchLists')
expect(store.state.ListMembership.lists).toEqual([list1, list2])
await store.dispatch('TimelineSpace/Modals/ListMembership/fetchLists')
expect(store.state.TimelineSpace.Modals.ListMembership.lists).toEqual([list1, list2])
})
})
@ -179,8 +185,8 @@ describe('ListMembership', () => {
}
})
it('should be changed', async () => {
await store.dispatch('ListMembership/changeBelongToLists', [list1.id, list2.id])
expect(store.state.ListMembership.belongToLists).toEqual([list1, list2])
await store.dispatch('TimelineSpace/Modals/ListMembership/changeBelongToLists', [list1.id, list2.id])
expect(store.state.TimelineSpace.Modals.ListMembership.belongToLists).toEqual([list1, list2])
})
})
})

View File

@ -1,11 +1,11 @@
import { Entity, Response } from 'megalodon'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
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'
;((window as any) as MyWindow).ipcRenderer = ipcRenderer
import { RootState } from '@/store'
;(window as any as MyWindow).ipcRenderer = ipcRenderer
const mockClient = {
getLists: () => {
@ -82,25 +82,24 @@ const appStore = {
}
}
const timelineStore = {
const timelineStore = () => ({
namespaced: true,
state: {
sns: 'mastodon'
},
modules: {
SideMenu: initStore()
}
}
})
describe('SideMenu', () => {
let store
let localVue
let store: Store<RootState>
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
store = createStore({
modules: {
SideMenu: initStore(),
App: appStore,
TimelineSpace: timelineStore
TimelineSpace: timelineStore()
}
})
// mockedMegalodon.mockClear()
@ -113,27 +112,27 @@ describe('SideMenu', () => {
accessToken: 'token',
baseURL: 'http://localhost'
}
const lists = await store.dispatch('SideMenu/fetchLists', account)
expect(store.state.SideMenu.lists).toEqual([list1, list2])
const lists = await store.dispatch('TimelineSpace/SideMenu/fetchLists', account)
expect(store.state.TimelineSpace.SideMenu.lists).toEqual([list1, list2])
expect(lists).toEqual([list1, list2])
})
})
describe('clearUnread', () => {
it('should be resetted', () => {
store.dispatch('SideMenu/clearUnread')
expect(store.state.SideMenu.unreadHomeTimeline).toEqual(false)
expect(store.state.SideMenu.unreadNotifications).toEqual(false)
expect(store.state.SideMenu.unreadLocalTimeline).toEqual(false)
expect(store.state.SideMenu.unreadDirectMessagesTimeline).toEqual(false)
expect(store.state.SideMenu.unreadPublicTimeline).toEqual(false)
store.dispatch('TimelineSpace/SideMenu/clearUnread')
expect(store.state.TimelineSpace.SideMenu.unreadHomeTimeline).toEqual(false)
expect(store.state.TimelineSpace.SideMenu.unreadNotifications).toEqual(false)
expect(store.state.TimelineSpace.SideMenu.unreadLocalTimeline).toEqual(false)
expect(store.state.TimelineSpace.SideMenu.unreadDirectMessagesTimeline).toEqual(false)
expect(store.state.TimelineSpace.SideMenu.unreadPublicTimeline).toEqual(false)
})
})
describe('changeCollapse', () => {
it('should be changed', () => {
store.dispatch('SideMenu/changeCollapse', true)
expect(store.state.SideMenu.collapse).toEqual(true)
store.dispatch('TimelineSpace/SideMenu/changeCollapse', true)
expect(store.state.TimelineSpace.SideMenu.collapse).toEqual(true)
})
})
@ -142,8 +141,8 @@ describe('SideMenu', () => {
ipcMain.handle('get-collapse', () => {
return true
})
await store.dispatch('SideMenu/readCollapse')
expect(store.state.SideMenu.collapse).toEqual(true)
await store.dispatch('TimelineSpace/SideMenu/readCollapse')
expect(store.state.TimelineSpace.SideMenu.collapse).toEqual(true)
})
})
@ -158,8 +157,8 @@ describe('SideMenu', () => {
ipcMain.handle('list-hashtags', () => {
return [tag1, tag2]
})
await store.dispatch('SideMenu/listTags')
expect(store.state.SideMenu.tags).toEqual([tag1, tag2])
await store.dispatch('TimelineSpace/SideMenu/listTags')
expect(store.state.TimelineSpace.SideMenu.tags).toEqual([tag1, tag2])
})
})
})

View File

@ -608,11 +608,10 @@ ipcMain.handle('confirm-timelines', async (_event: IpcMainInvokeEvent, account:
// user streaming
const userStreamings: { [key: string]: UserStreaming | null } = {}
ipcMain.on('start-all-user-streamings', (event: IpcMainEvent, accounts: Array<LocalAccount>) => {
accounts.map(async account => {
const id: string = account._id!
ipcMain.on('start-all-user-streamings', (event: IpcMainEvent, accounts: Array<string>) => {
accounts.map(async id => {
const acct = await accountRepo.getAccount(id)
try {
const acct = await accountRepo.getAccount(id)
// Stop old user streaming
if (userStreamings[id]) {
userStreamings[id]!.stop()
@ -678,7 +677,7 @@ ipcMain.on('start-all-user-streamings', (event: IpcMainEvent, accounts: Array<Lo
}
} catch (err: any) {
log.error(err)
const streamingError = new StreamingError(err.message, account.domain)
const streamingError = new StreamingError(err.message, acct.domain)
if (!event.sender.isDestroyed()) {
event.sender.send('error-start-all-user-streamings', streamingError)
}
@ -708,16 +707,11 @@ const stopUserStreaming = (id: string) => {
})
}
type StreamingSetting = {
account: LocalAccount
}
let directMessagesStreaming: DirectStreaming | null = null
ipcMain.on('start-directmessages-streaming', async (event: IpcMainEvent, obj: StreamingSetting) => {
const { account } = obj
ipcMain.on('start-directmessages-streaming', async (event: IpcMainEvent, id: string) => {
try {
const acct = await accountRepo.getAccount(account._id!)
const acct = await accountRepo.getAccount(id)
// Stop old directmessages streaming
if (directMessagesStreaming !== null) {
@ -763,10 +757,9 @@ ipcMain.on('stop-directmessages-streaming', () => {
let localStreaming: LocalStreaming | null = null
ipcMain.on('start-local-streaming', async (event: IpcMainEvent, obj: StreamingSetting) => {
const { account } = obj
ipcMain.on('start-local-streaming', async (event: IpcMainEvent, id: string) => {
try {
const acct = await accountRepo.getAccount(account._id!)
const acct = await accountRepo.getAccount(id)
// Stop old local streaming
if (localStreaming !== null) {
@ -812,10 +805,9 @@ ipcMain.on('stop-local-streaming', () => {
let publicStreaming: PublicStreaming | null = null
ipcMain.on('start-public-streaming', async (event: IpcMainEvent, obj: StreamingSetting) => {
const { account } = obj
ipcMain.on('start-public-streaming', async (event: IpcMainEvent, id: string) => {
try {
const acct = await accountRepo.getAccount(account._id!)
const acct = await accountRepo.getAccount(id)
// Stop old public streaming
if (publicStreaming !== null) {
@ -861,14 +853,15 @@ ipcMain.on('stop-public-streaming', () => {
let listStreaming: ListStreaming | null = null
type ListID = {
type ListStreamingOpts = {
listID: string
accountID: string
}
ipcMain.on('start-list-streaming', async (event: IpcMainEvent, obj: ListID & StreamingSetting) => {
const { listID, account } = obj
ipcMain.on('start-list-streaming', async (event: IpcMainEvent, obj: ListStreamingOpts) => {
const { listID, accountID } = obj
try {
const acct = await accountRepo.getAccount(account._id!)
const acct = await accountRepo.getAccount(accountID)
// Stop old list streaming
if (listStreaming !== null) {
@ -915,14 +908,15 @@ ipcMain.on('stop-list-streaming', () => {
let tagStreaming: TagStreaming | null = null
type Tag = {
type TagStreamingOpts = {
tag: string
accountID: string
}
ipcMain.on('start-tag-streaming', async (event: IpcMainEvent, obj: Tag & StreamingSetting) => {
const { tag, account } = obj
ipcMain.on('start-tag-streaming', async (event: IpcMainEvent, obj: TagStreamingOpts) => {
const { tag, accountID } = obj
try {
const acct = await accountRepo.getAccount(account._id!)
const acct = await accountRepo.getAccount(accountID)
// Stop old tag streaming
if (tagStreaming !== null) {

View File

@ -34,10 +34,10 @@ export default {
created() {
this.$store.dispatch('App/watchShortcutsEvents')
this.$store.dispatch('App/loadPreferences').then(conf => {
this.$i18n.i18next.changeLanguage(conf.language.language)
this.$i18n.locale = conf.language.language
})
},
destroyed() {
unmounted() {
this.$store.dispatch('App/removeShortcutsEvents')
}
}

View File

@ -4,7 +4,9 @@
<el-header>
<el-row>
<el-col :span="24" class="close">
<el-button type="text" icon="el-icon-close" @click="close" class="close-button"> </el-button>
<el-button type="text" @click="close" class="close-button">
<font-awesome-icon icon="x-mark"></font-awesome-icon>
</el-button>
</el-col>
</el-row>
</el-header>
@ -43,6 +45,14 @@
<script>
export default {
data() {
return {
authorizeForm: {
code: null
},
submitting: false
}
},
name: 'authorize',
props: {
url: {
@ -54,14 +64,6 @@ export default {
default: 'mastodon'
}
},
data() {
return {
authorizeForm: {
code: null
},
submitting: false
}
},
mounted() {
console.log(this.url)
},
@ -101,7 +103,7 @@ export default {
</script>
<style lang="scss" scoped>
#authorize /deep/ {
#authorize {
background-color: #292f3f;
color: #ffffff;
text-align: center;
@ -130,14 +132,16 @@ export default {
margin: 0 auto;
}
.el-form-item__label {
color: #f0f3f9;
}
.authorize-form :deep() {
.el-form-item__label {
color: #f0f3f9;
}
.el-input__inner {
background-color: #373d48;
color: #ffffff;
border: 0;
.el-input__inner {
background-color: #373d48;
color: #ffffff;
border: 0;
}
}
.hidden {

View File

@ -18,13 +18,13 @@
v-bind:key="account._id"
role="menuitem"
>
<i v-if="account.avatar === undefined || account.avatar === null || account.avatar === ''" class="el-icon-menu"></i>
<font-awesome-icon icon="circle-user" v-if="account.avatar === undefined || account.avatar === null || account.avatar === ''" />
<FailoverImg v-else :src="account.avatar" class="avatar" :title="account.username + '@' + account.domain" />
<FailoverImg :src="`${account.baseURL}/favicon.ico`" :failoverSrc="`${account.baseURL}/favicon.png`" class="instance-icon" />
<span slot="title">{{ account.domain }}</span>
</el-menu-item>
<el-menu-item index="/login" :title="$t('global_header.add_new_account')" role="menuitem">
<i class="el-icon-plus"></i>
<el-menu-item index="/login" :title="$t('global_header.add_new_account')" role="menuitem" class="add-new-account">
<font-awesome-icon icon="plus" />
<span slot="new">New</span>
</el-menu-item>
</el-menu>
@ -40,10 +40,10 @@ import FailoverImg from '~/src/renderer/components/atoms/FailoverImg'
import { StreamingError } from '~/src/errors/streamingError'
export default {
name: 'global-header',
components: {
FailoverImg
},
name: 'global-header',
computed: {
...mapState('GlobalHeader', {
accounts: state => state.accounts,
@ -67,7 +67,9 @@ export default {
this.$store.dispatch('GlobalHeader/startStreamings').catch(err => {
if (err instanceof StreamingError) {
this.$message({
message: this.$t('message.start_all_streamings_error', { domain: err.domain }),
message: this.$t('message.start_all_streamings_error', {
domain: err.domain
}),
type: 'error'
})
}
@ -85,7 +87,14 @@ export default {
</script>
<style lang="scss" scoped>
#global_header /deep/ {
.account-menu :deep(.el-menu-item) {
display: flex;
justify-content: center;
align-items: flex-end;
padding: 0 !important;
}
#global_header {
.account-menu {
height: 100%;
position: fixed;
@ -95,12 +104,6 @@ export default {
padding-top: 24px;
border: 0;
.el-tooltip {
outline: 0;
text-align: center;
padding: 0 !important;
}
.avatar {
width: 42px;
height: 42px;
@ -138,5 +141,9 @@ export default {
.with-global-header {
margin-left: 65px;
}
.add-new-account {
align-items: center;
}
}
</style>

View File

@ -3,12 +3,14 @@
<el-header>
<el-row>
<el-col :span="24" class="close">
<el-button type="text" icon="el-icon-close" @click="close" class="close-button"> </el-button>
<el-button type="text" @click="close" class="close-button">
<font-awesome-icon icon="xmark" />
</el-button>
</el-col>
</el-row>
</el-header>
<el-container>
<div v-shortkey="['esc']" @shortkey="close"></div>
<div></div>
<login-form></login-form>
</el-container>
</el-container>

View File

@ -9,7 +9,7 @@
:model="form"
>
<el-form-item :label="$t('login.domain_name_label')" prop="domainName">
<el-input v-model="form.domainName" placeholder="mastodon.social" v-shortkey="['enter']" @shortkey.native="handleKey"></el-input>
<el-input v-model="form.domainName" placeholder="mastodon.social"></el-input>
</el-form-item>
<p class="proxy-info">
{{ $t('login.proxy_info') }}<router-link to="/preferences/network">{{ $t('login.proxy_here') }}</router-link>
@ -22,7 +22,14 @@
<el-button type="primary" class="login" @click="login" v-if="allowLogin">
{{ $t('login.login') }}
</el-button>
<el-button type="primary" v-else @click="confirm('loginForm')" v-loading="searching" element-loading-background="rgba(0, 0, 0, 0.8)">
<el-button
type="primary"
class="search"
v-else
@click="confirm('loginForm')"
v-loading="searching"
element-loading-background="rgba(0, 0, 0, 0.8)"
>
{{ $t('login.search') }}
</el-button>
</el-form-item>
@ -48,7 +55,7 @@ export default {
searching: state => state.Login.searching,
sns: state => state.Login.sns
}),
allowLogin: function() {
allowLogin: function () {
return this.selectedInstance && this.form.domainName === this.selectedInstance
},
rules: {
@ -83,7 +90,10 @@ export default {
.then(url => {
loading.close()
this.$store.dispatch('Login/pageBack')
this.$router.push({ path: '/authorize', query: { url: url, sns: this.sns } })
this.$router.push({
path: '/authorize',
query: { url: url, sns: this.sns }
})
})
.catch(() => {
loading.close()
@ -100,13 +110,17 @@ export default {
.dispatch('Login/confirmInstance', this.form.domainName)
.then(() => {
this.$message({
message: this.$t('message.domain_confirmed', { domain: this.form.domainName }),
message: this.$t('message.domain_confirmed', {
domain: this.form.domainName
}),
type: 'success'
})
})
.catch(() => {
this.$message({
message: this.$t('message.domain_doesnt_exist', { domain: this.form.domainName }),
message: this.$t('message.domain_doesnt_exist', {
domain: this.form.domainName
}),
type: 'error'
})
})
@ -131,20 +145,10 @@ export default {
</script>
<style lang="scss" scoped>
.login-form /deep/ {
.login-form {
margin: 0 auto;
width: 300px;
.el-form-item__label {
color: #f0f3f9;
}
.el-input__inner {
background-color: #373d48;
color: #fff;
border: 0;
}
.instance-group {
text-align: left;
margin: 0 auto;
@ -158,12 +162,12 @@ export default {
margin-bottom: 10px;
}
.submit {
margin: 0;
.search {
margin: 0 auto;
}
.back {
margin-right: 20px;
.login {
margin: 0 auto;
}
.hidden {
@ -175,4 +179,16 @@ export default {
margin-bottom: 24px;
}
}
.login-form :deep() {
.el-form-item__label {
color: #f0f3f9;
}
.el-input__inner {
background-color: #373d48;
color: #fff;
box-shadow: none;
}
}
</style>

View File

@ -5,13 +5,15 @@
<el-col :span="23">
<h1>{{ $t('preferences.title') }}</h1>
</el-col>
<el-col :span="1">
<el-button type="text" icon="el-icon-close" @click="close" class="close-button" role="button"></el-button>
<el-col :span="1" class="close-area">
<el-button type="text" @click="close" class="close-button" role="button">
<font-awesome-icon icon="xmark" />
</el-button>
</el-col>
</el-row>
</el-header>
<el-container>
<div v-shortkey="['esc']" @shortkey="close"></div>
<div></div>
<el-aside width="240px" class="menu">
<el-menu
:default-active="activeRoute()"
@ -85,8 +87,13 @@ export default {
border-bottom: 1px solid var(--theme-border-color);
user-select: none;
.close-button {
font-size: 28px;
.close-area {
display: flex;
align-items: center;
.close-button {
font-size: 28px;
}
}
}
@ -98,13 +105,13 @@ export default {
border-right: solid 1px var(--theme-border-color);
}
.setting-menu /deep/ {
.setting-menu {
height: 100%;
user-select: none;
}
.icon {
margin-right: 9px;
}
.setting-menu :deep(.icon) {
margin-right: 9px;
}
}
}

View File

@ -1,7 +1,7 @@
<template>
<div id="account">
<h2>{{ $t('preferences.account.title') }}</h2>
<el-form class="connected-account section" size="small">
<el-form class="connected-account section">
<h3>{{ $t('preferences.account.connected') }}</h3>
<el-form-item>
<el-table
@ -15,29 +15,24 @@
<el-table-column prop="username" :label="$t('preferences.account.username')"> </el-table-column>
<el-table-column prop="domain" :label="$t('preferences.account.domain')"> </el-table-column>
<el-table-column :label="$t('preferences.account.association')">
<template slot-scope="scope">
<el-button @click.native.prevent="removeAccount(scope.$index, accounts)" type="text" class="action">
<i class="el-icon-close"></i> {{ $t('preferences.account.remove_association') }}
<template #default="scope">
<el-button @click.prevent="removeAccount(scope.$index, accounts)" type="text" class="action">
<font-awesome-icon icon="xmark" />
{{ $t('preferences.account.remove_association') }}
</el-button>
</template>
</el-table-column>
<el-table-column :label="$t('preferences.account.order')" width="60">
<template slot-scope="scope">
<template #default="scope">
<div class="allow-up">
<el-button
class="arrow-up action"
type="text"
icon="el-icon-arrow-up"
@click.native.prevent="forward(scope.$index, accounts)"
></el-button>
<el-button class="arrow-up action" type="text" @click.prevent="forward(scope.$index, accounts)">
<font-awesome-icon icon="arrow-up" />
</el-button>
</div>
<div class="allow-down">
<el-button
class="arrow-down action"
type="text"
icon="el-icon-arrow-down"
@click.native.prevent="backward(scope.$index, accounts)"
></el-button>
<el-button class="arrow-down action" type="text" @click.prevent="backward(scope.$index, accounts)">
<font-awesome-icon icon="arrow-down" />
</el-button>
</div>
</template>
</el-table-column>
@ -47,10 +42,12 @@
<el-popover placement="top" width="160" v-model="deletePopoverVisible">
<p>{{ $t('preferences.account.confirm_message') }}</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="deletePopoverVisible = false">{{ $t('preferences.account.cancel') }}</el-button>
<el-button type="danger" size="mini" @click="removeAllAssociations">{{ $t('preferences.account.confirm') }}</el-button>
<el-button size="small" type="text" @click="deletePopoverVisible = false">{{ $t('preferences.account.cancel') }}</el-button>
<el-button type="danger" size="small" @click="removeAllAssociations">{{ $t('preferences.account.confirm') }}</el-button>
</div>
<el-button slot="reference" type="danger">{{ $t('preferences.account.remove_all_associations') }}</el-button>
<template #reference>
<el-button type="danger">{{ $t('preferences.account.remove_all_associations') }}</el-button>
</template>
</el-popover>
</el-form-item>
</el-form>
@ -61,13 +58,13 @@
import { mapState } from 'vuex'
export default {
name: 'account',
data() {
return {
openRemoveDialog: false,
deletePopoverVisible: false
}
},
name: 'account',
computed: {
...mapState({
accounts: state => state.Preferences.Account.accounts,
@ -129,16 +126,16 @@ export default {
<style lang="scss" scoped>
#account {
.section /deep/ {
.section {
margin-bottom: 40px;
}
.el-form-item__label {
color: var(--theme-primary-color);
}
.section :deep(.el-form-item__label) {
color: var(--theme-primary-color);
}
.connected-account {
.el-table /deep/ {
.el-table :deep() {
tr,
th,
td {
@ -161,5 +158,9 @@ export default {
font-size: var(--base-font-size);
}
}
.action :deep(.svg-inline--fa) {
padding-right: 4px;
}
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div id="appearance">
<h2>{{ $t('preferences.appearance.title') }}</h2>
<el-form class="theme section" size="small" label-position="top">
<el-form class="theme section" label-position="top">
<div class="left">
<el-form-item for="theme" :label="$t('preferences.appearance.theme_color')">
<el-select id="theme" v-model="theme" placeholder="theme">
@ -16,29 +16,29 @@
<div class="color-pallet section" v-if="customizeThemeColor">
<color-pallet></color-pallet>
</div>
<el-form class="font section" size="small" label-position="top">
<el-form class="font section" label-position="top">
<el-form-item for="font-family" :label="$t('preferences.appearance.font_family')">
<el-select id="font-family" v-model="font" placeholder="fonts">
<el-option v-for="f in fontList" :key="f" :label="f" :value="f" />
</el-select>
</el-form-item>
<el-form-item for="font-size" :label="$t('preferences.appearance.font_size')">
<el-input-number id="font-size" :value="fontSize" :min="9" :max="72" @change="updateFontSize"></el-input-number>
<el-input-number id="font-size" :model-value="fontSize" :min="9" :max="72" @change="updateFontSize"></el-input-number>
</el-form-item>
</el-form>
<el-form class="toot-padding section" size="small" label-position="top">
<el-form class="toot-padding section" label-position="top">
<el-form-item for="toot-padding" :label="$t('preferences.appearance.toot_padding')">
<el-input-number id="toot-padding" :value="tootPadding" :min="0" :max="24" @change="updateTootPadding"></el-input-number>
<el-input-number id="toot-padding" :model-value="tootPadding" :min="0" :max="24" @change="updateTootPadding"></el-input-number>
</el-form-item>
</el-form>
<el-form class="display-style section" size="small" label-position="top">
<el-form class="display-style section" label-position="top">
<el-form-item for="display-style" :label="$t('preferences.appearance.display_style.title')">
<el-select id="display-style" v-model="displayNameStyle" placeholder="style">
<el-option v-for="style in nameStyles" :key="style.value" :label="$t(style.name)" :value="style.value"> </el-option>
</el-select>
</el-form-item>
</el-form>
<el-form class="time-format section" size="small" label-position="top">
<el-form class="time-format section" label-position="top">
<el-form-item for="time-format" :label="$t('preferences.appearance.time_format.title')">
<el-select id="time-format" v-model="timeFormat" placeholder="format">
<el-option v-for="format in timeFormats" :key="format.value" :label="$t(format.name)" :value="format.value"> </el-option>
@ -140,12 +140,12 @@ export default {
}
}
.section /deep/ {
.section {
margin-bottom: 40px;
}
.el-form-item__label {
color: var(--theme-primary-color);
}
.section :deep(.el-form-item__label) {
color: var(--theme-primary-color);
}
}
</style>

View File

@ -1,46 +1,71 @@
<template>
<el-form class="pallet" label-position="top" size="small">
<div class="item">
<el-form-item :label="$t('preferences.appearance.custom_theme.background_color') ">
<el-color-picker v-model="background"></el-color-picker>
</el-form-item>
<el-form-item :label="$t('preferences.appearance.custom_theme.selected_background_color')">
<el-color-picker v-model="selectedBackground"></el-color-picker>
</el-form-item>
</div>
<div class="item">
<el-form-item :label="$t('preferences.appearance.custom_theme.global_header_color')">
<el-color-picker v-model="globalHeader"></el-color-picker>
</el-form-item>
<el-form-item :label="$t('preferences.appearance.custom_theme.side_menu_color')">
<el-color-picker v-model="sideMenu"></el-color-picker>
</el-form-item>
</div>
<div class="item">
<el-form-item :label="$t('preferences.appearance.custom_theme.primary_color')">
<el-color-picker v-model="primary"></el-color-picker>
</el-form-item>
<el-form-item :label="$t('preferences.appearance.custom_theme.regular_color')">
<el-color-picker v-model="regular"></el-color-picker>
</el-form-item>
</div>
<div class="item">
<el-form-item :label="$t('preferences.appearance.custom_theme.secondary_color')">
<el-color-picker v-model="secondary"></el-color-picker>
</el-form-item>
<el-form-item :label="$t('preferences.appearance.custom_theme.border_color')">
<el-color-picker v-model="border"></el-color-picker>
</el-form-item>
</div>
<div class="item">
<el-form-item :label="$t('preferences.appearance.custom_theme.header_menu_color')">
<el-color-picker v-model="headerMenu"></el-color-picker>
</el-form-item>
<el-form-item :label="$t('preferences.appearance.custom_theme.wrapper_mask_color')">
<el-color-picker v-model="wrapperMask" :show-alpha="true"></el-color-picker>
</el-form-item>
</div>
</el-form>
<el-form class="pallet" label-position="top" size="small">
<div class="item">
<el-form-item
:label="$t('preferences.appearance.custom_theme.background_color')"
>
<el-color-picker v-model="background"></el-color-picker>
</el-form-item>
<el-form-item
:label="
$t('preferences.appearance.custom_theme.selected_background_color')
"
>
<el-color-picker v-model="selectedBackground"></el-color-picker>
</el-form-item>
</div>
<div class="item">
<el-form-item
:label="$t('preferences.appearance.custom_theme.global_header_color')"
>
<el-color-picker v-model="globalHeader"></el-color-picker>
</el-form-item>
<el-form-item
:label="$t('preferences.appearance.custom_theme.side_menu_color')"
>
<el-color-picker v-model="sideMenu"></el-color-picker>
</el-form-item>
</div>
<div class="item">
<el-form-item
:label="$t('preferences.appearance.custom_theme.primary_color')"
>
<el-color-picker v-model="primary"></el-color-picker>
</el-form-item>
<el-form-item
:label="$t('preferences.appearance.custom_theme.regular_color')"
>
<el-color-picker v-model="regular"></el-color-picker>
</el-form-item>
</div>
<div class="item">
<el-form-item
:label="$t('preferences.appearance.custom_theme.secondary_color')"
>
<el-color-picker v-model="secondary"></el-color-picker>
</el-form-item>
<el-form-item
:label="$t('preferences.appearance.custom_theme.border_color')"
>
<el-color-picker v-model="border"></el-color-picker>
</el-form-item>
</div>
<div class="item">
<el-form-item
:label="$t('preferences.appearance.custom_theme.header_menu_color')"
>
<el-color-picker v-model="headerMenu"></el-color-picker>
</el-form-item>
<el-form-item
:label="$t('preferences.appearance.custom_theme.wrapper_mask_color')"
>
<el-color-picker
v-model="wrapperMask"
:show-alpha="true"
></el-color-picker>
</el-form-item>
</div>
</el-form>
</template>
<script>
@ -48,106 +73,116 @@ export default {
name: 'color-pallet',
computed: {
background: {
get () {
return this.$store.state.Preferences.Appearance.appearance.customThemeColor.background_color
get() {
return this.$store.state.Preferences.Appearance.appearance
.customThemeColor.background_color
},
set (value) {
set(value) {
this.$store.dispatch('Preferences/Appearance/updateCustomThemeColor', {
background_color: value
background_color: value,
})
}
},
},
selectedBackground: {
get () {
return this.$store.state.Preferences.Appearance.appearance.customThemeColor.selected_background_color
get() {
return this.$store.state.Preferences.Appearance.appearance
.customThemeColor.selected_background_color
},
set (value) {
set(value) {
this.$store.dispatch('Preferences/Appearance/updateCustomThemeColor', {
selected_background_color: value
selected_background_color: value,
})
}
},
},
globalHeader: {
get () {
return this.$store.state.Preferences.Appearance.appearance.customThemeColor.global_header_color
get() {
return this.$store.state.Preferences.Appearance.appearance
.customThemeColor.global_header_color
},
set (value) {
set(value) {
this.$store.dispatch('Preferences/Appearance/updateCustomThemeColor', {
global_header_color: value
global_header_color: value,
})
}
},
},
sideMenu: {
get () {
return this.$store.state.Preferences.Appearance.appearance.customThemeColor.side_menu_color
get() {
return this.$store.state.Preferences.Appearance.appearance
.customThemeColor.side_menu_color
},
set (value) {
set(value) {
this.$store.dispatch('Preferences/Appearance/updateCustomThemeColor', {
side_menu_color: value
side_menu_color: value,
})
}
},
},
primary: {
get () {
return this.$store.state.Preferences.Appearance.appearance.customThemeColor.primary_color
get() {
return this.$store.state.Preferences.Appearance.appearance
.customThemeColor.primary_color
},
set (value) {
set(value) {
this.$store.dispatch('Preferences/Appearance/updateCustomThemeColor', {
primary_color: value
primary_color: value,
})
}
},
},
regular: {
get () {
return this.$store.state.Preferences.Appearance.appearance.customThemeColor.regular_color
get() {
return this.$store.state.Preferences.Appearance.appearance
.customThemeColor.regular_color
},
set (value) {
set(value) {
this.$store.dispatch('Preferences/Appearance/updateCustomThemeColor', {
regular_color: value
regular_color: value,
})
}
},
},
secondary: {
get () {
return this.$store.state.Preferences.Appearance.appearance.customThemeColor.secondary_color
get() {
return this.$store.state.Preferences.Appearance.appearance
.customThemeColor.secondary_color
},
set (value) {
set(value) {
this.$store.dispatch('Preferences/Appearance/updateCustomThemeColor', {
secondary_color: value
secondary_color: value,
})
}
},
},
border: {
get () {
return this.$store.state.Preferences.Appearance.appearance.customThemeColor.border_color
get() {
return this.$store.state.Preferences.Appearance.appearance
.customThemeColor.border_color
},
set (value) {
set(value) {
this.$store.dispatch('Preferences/Appearance/updateCustomThemeColor', {
border_color: value
border_color: value,
})
}
},
},
headerMenu: {
get () {
return this.$store.state.Preferences.Appearance.appearance.customThemeColor.header_menu_color
get() {
return this.$store.state.Preferences.Appearance.appearance
.customThemeColor.header_menu_color
},
set (value) {
set(value) {
this.$store.dispatch('Preferences/Appearance/updateCustomThemeColor', {
header_menu_color: value
header_menu_color: value,
})
}
},
},
wrapperMask: {
get () {
return this.$store.state.Preferences.Appearance.appearance.customThemeColor.wrapper_mask_color
get() {
return this.$store.state.Preferences.Appearance.appearance
.customThemeColor.wrapper_mask_color
},
set (value) {
set(value) {
this.$store.dispatch('Preferences/Appearance/updateCustomThemeColor', {
wrapper_mask_color: value
wrapper_mask_color: value,
})
}
}
}
},
},
},
}
</script>

View File

@ -34,27 +34,6 @@
<span class="count">
{{ favouritesCount }}
</span>
<popper trigger="click" :options="{ placement: 'bottom' }" ref="popper">
<div class="popper toot-menu">
<ul class="menu-list">
<li role="button">
{{ $t('cards.toot.view_toot_detail') }}
</li>
<li role="button">
{{ $t('cards.toot.open_in_browser') }}
</li>
<li role="button">
{{ $t('cards.toot.copy_link_to_toot') }}
</li>
<li role="button" class="separate">
{{ $t('cards.toot.delete') }}
</li>
</ul>
</div>
<el-button slot="reference" type="text" :title="$t('cards.toot.detail')">
<font-awesome-icon icon="ellipsis" size="sm" />
</el-button>
</popper>
</div>
<div class="application">
{{ $t('cards.toot.via', { application: 'Whalebird' }) }}
@ -174,7 +153,7 @@ export default {
}
}
.content-wrapper /deep/ {
.content-wrapper {
font-size: var(--base-font-size);
color: var(--theme-primary-color);
@ -182,11 +161,6 @@ export default {
margin: var(--toot-padding) 0;
word-wrap: break-word;
}
.emojione {
width: 20px;
height: 20px;
}
}
.tool-box {

View File

@ -1,9 +1,11 @@
<template>
<div id="general" v-loading="loading" :element-loading-background="backgroundColor">
<h2>{{ $t('preferences.general.title') }}</h2>
<el-form class="sounds section" label-position="right" label-width="250px" size="small">
<el-form class="sounds section" label-position="right" label-width="250px">
<h3>{{ $t('preferences.general.sounds.title') }}</h3>
<p class="description">{{ $t('preferences.general.sounds.description') }}</p>
<p class="description">
{{ $t('preferences.general.sounds.description') }}
</p>
<el-form-item for="fav_rb" :label="$t('preferences.general.sounds.fav_rb')">
<el-switch id="fav_rb" v-model="sound_fav_rb" active-color="#13ce66"> </el-switch>
</el-form-item>
@ -11,9 +13,11 @@
<el-switch id="sound_toot" v-model="sound_toot" active-color="#13ce66"> </el-switch>
</el-form-item>
</el-form>
<el-form class="timeline section" label-potision="right" label-width="302px" size="samll">
<el-form class="timeline section" label-potision="right" label-width="302px">
<h3>{{ $t('preferences.general.timeline.title') }}</h3>
<p class="description">{{ $t('preferences.general.timeline.description') }}</p>
<p class="description">
{{ $t('preferences.general.timeline.description') }}
</p>
<el-form-item for="cw" :label="$t('preferences.general.timeline.cw')">
<el-switch id="cw" v-model="timeline_cw" active-color="#13ce66"> </el-switch>
</el-form-item>
@ -24,7 +28,7 @@
<el-switch id="hideAllAttachments" v-model="timeline_hide_attachments" active-color="#13ce66"> </el-switch>
</el-form-item>
</el-form>
<el-form class="other section" label-position="right" label-width="250px" size="small" v-if="notDarwin">
<el-form class="other section" label-position="right" label-width="250px" v-if="notDarwin">
<h3>{{ $t('preferences.general.other.title') }}</h3>
<el-form-item for="launch" :label="$t('preferences.general.other.launch')">
<el-switch id="launch" v-model="other_launch" active-color="#13ce66"> </el-switch>
@ -123,7 +127,7 @@ export default {
this.$store
.dispatch('Preferences/General/reset')
.then(language => {
this.$i18n.i18next.changeLanguage(language)
this.$i18n.locale = language
})
.catch(() => {
this.$message({
@ -142,12 +146,12 @@ export default {
margin: 24px 0 20px;
}
.section /deep/ {
.section {
margin-bottom: 40px;
}
.el-form-item__label {
color: var(--theme-primary-color);
}
.section :deep(.el-form-item__label) {
color: var(--theme-primary-color);
}
.selection {

View File

@ -1,7 +1,7 @@
<template>
<div id="language">
<h2>{{ $t('preferences.language.title') }}</h2>
<el-form class="display-language section" label-position="top" size="small">
<el-form class="display-language section" label-position="top">
<h3>{{ $t('preferences.language.language.title') }}</h3>
<el-form-item for="language" :label="$t('preferences.language.language.description')">
<el-select id="language" v-model="displayLanguage" placeholder="style">
@ -9,7 +9,7 @@
</el-select>
</el-form-item>
</el-form>
<el-form class="spellchecker section" label-position="top" size="small">
<el-form class="spellchecker section" label-position="top">
<h3>{{ $t('preferences.language.spellchecker.title') }}</h3>
<el-form-item for="spellcheck" :label="$t('preferences.language.spellchecker.enabled')">
<el-switch id="spellcheck" v-model="spellcheck" active-color="#13ce66"> </el-switch>
@ -67,7 +67,7 @@ export default {
},
set(value) {
this.$store.dispatch('Preferences/Language/changeLanguage', value).then(key => {
this.$i18n.i18next.changeLanguage(key)
this.$i18n.locale = key
})
}
},
@ -105,12 +105,12 @@ export default {
margin: 24px 0 20px;
}
.section /deep/ {
.section {
margin-bottom: 40px;
}
.el-form-item__label {
color: var(--theme-primary-color);
}
.section :deep(.el-form-item__label) {
color: var(--theme-primary-color);
}
.selection {

View File

@ -1,7 +1,7 @@
<template>
<div id="network">
<h2>{{ $t('preferences.network.proxy.title') }}</h2>
<el-form class="network section" size="small" label-width="120px">
<el-form class="network section" label-width="120px">
<div class="proxy-source">
<el-radio v-model="source" label="no">{{ $t('preferences.network.proxy.no') }}</el-radio>
</div>
@ -113,21 +113,21 @@ export default {
</script>
<style lang="scss" scoped>
.network /deep/ {
.network {
max-width: 720px;
.proxy-source {
line-height: 32px;
margin: 12px 8px;
font-size: 14px;
.el-radio {
color: var(--theme-primary-color);
}
}
.el-form-item__label {
.proxy-source :deep(.el-radio) {
color: var(--theme-primary-color);
}
}
.network :deep(.el-form-item__label) {
color: var(--theme-primary-color);
}
</style>

View File

@ -1,8 +1,10 @@
<template>
<div id="notification">
<h2>{{ $t('preferences.notification.title') }}</h2>
<el-form class="section" label-position="right" label-width="360px" size="small">
<p class="description">{{ $t('preferences.notification.enable.description') }}</p>
<el-form class="section" label-position="right" label-width="360px">
<p class="description">
{{ $t('preferences.notification.enable.description') }}
</p>
<el-form-item for="notifyReply" :label="$t('preferences.notification.enable.reply')">
<el-switch id="notifyReply" v-model="notifyReply" active-color="#13ce66"> </el-switch>
</el-form-item>
@ -146,12 +148,12 @@ export default {
margin: 24px 0 20px;
}
.section /deep/ {
.section {
margin-bottom: 40px;
}
.el-form-item__label {
color: var(--theme-primary-color);
}
.section :deep(.el-form-item__label) {
color: var(--theme-primary-color);
}
}
</style>

View File

@ -5,13 +5,15 @@
<el-col :span="23">
<h1>{{ $t('settings.title') }}</h1>
</el-col>
<el-col :span="1">
<el-button type="text" icon="el-icon-close" @click="close" class="close-button" role="button"></el-button>
<el-col :span="1" class="close-area">
<el-button type="text" @click="close" class="close-button" role="button">
<font-awesome-icon icon="xmark" />
</el-button>
</el-col>
</el-row>
</el-header>
<el-container>
<div v-shortkey="['esc']" @shortkey="close"></div>
<div></div>
<el-aside width="240px" class="menu">
<el-menu
:default-active="activeRoute()"
@ -81,8 +83,13 @@ export default {
user-select: none;
}
.close-button {
font-size: 28px;
.close-area {
display: flex;
align-items: center;
.close-button {
font-size: 28px;
}
}
.menu {
@ -93,13 +100,12 @@ export default {
border-right: solid 1px var(--theme-border-color);
}
.setting-menu /deep/ {
.setting-menu {
height: 100%;
user-select: none;
.icon {
margin-right: 9px;
}
}
.setting-menu :deep(.icon) {
margin-right: 9px;
}
}
}

View File

@ -3,45 +3,43 @@
<h2>{{ $t('settings.filters.title') }}</h2>
<div class="new-filter">
<el-button type="primary" :disabled="sns === 'misskey'">
<router-link tag="span" :to="`/${id()}/settings/filters/new`">
<router-link :to="`/${id()}/settings/filters/new`">
{{ $t('settings.filters.new.title') }}
</router-link>
</el-button>
</div>
<template>
<el-table
:data="filters"
tooltip-effect="dark"
empty-text="No filters"
style="width: 100%"
v-loading="filtersLoading"
:element-loading-background="backgroundColor"
>
<el-table-column prop="phrase" label="Keyword" width="180"> </el-table-column>
<el-table-column label="Context">
<template slot-scope="scope">
<span>{{ filters[scope.$index].context.join(',') }}</span>
</template>
</el-table-column>
<el-table-column prop="expires_at" label="Expires" width="180"> </el-table-column>
<el-table-column width="80">
<template slot-scope="scope">
<el-button type="text">
<router-link tag="span" :to="`/${id()}/settings/filters/${filters[scope.$index].id}/edit`">
{{ $t('settings.filters.edit.title') }}
</router-link>
</el-button>
</template>
</el-table-column>
<el-table-column width="80">
<template slot-scope="scope">
<el-button type="text" @click="deleteFilter(filters[scope.$index].id)">
{{ $t('settings.filters.delete.title') }}
</el-button>
</template>
</el-table-column>
</el-table>
</template>
<el-table
:data="filters"
tooltip-effect="dark"
empty-text="No filters"
style="width: 100%"
v-loading="filtersLoading"
:element-loading-background="backgroundColor"
>
<el-table-column prop="phrase" label="Keyword" width="180"> </el-table-column>
<el-table-column label="Context">
<template #default="scope">
<span>{{ filters[scope.$index].context.join(',') }}</span>
</template>
</el-table-column>
<el-table-column prop="expires_at" label="Expires" width="180"> </el-table-column>
<el-table-column width="80">
<template #default="scope">
<el-button type="text">
<router-link tag="span" :to="`/${id()}/settings/filters/${filters[scope.$index].id}/edit`">
{{ $t('settings.filters.edit.title') }}
</router-link>
</el-button>
</template>
</el-table-column>
<el-table-column width="80">
<template #default="scope">
<el-button type="text" @click="deleteFilter(filters[scope.$index].id)">
{{ $t('settings.filters.delete.title') }}
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
@ -87,9 +85,14 @@ export default {
.new-filter {
display: flex;
justify-content: flex-end;
a {
color: var(--theme-primary-color);
text-decoration: none;
}
}
.el-table /deep/ {
.el-table :deep() {
tr,
th,
td,

View File

@ -1,7 +1,14 @@
<template>
<div id="edit_filter">
<h2>{{ $t('settings.filters.edit.title') }}</h2>
<FilterForm v-model="filter" @cancel="cancel" @onSubmit="onSubmit" :loading="loading" :sns="sns"> </FilterForm>
<FilterForm
v-model="filter"
@cancel="cancel"
@onSubmit="onSubmit"
:loading="loading"
:sns="sns"
>
</FilterForm>
</div>
</template>
@ -15,10 +22,10 @@ export default {
components: { FilterForm },
computed: {
...mapState('Settings/Filters/Edit', {
loading: state => state.loading
loading: (state) => state.loading,
}),
...mapState('TimelineSpace', {
sns: state => state.sns
sns: (state) => state.sns,
}),
filter: {
get() {
@ -26,11 +33,14 @@ export default {
},
set(value) {
this.$store.dispatch('Settings/Filters/Edit/editFilter', value)
}
}
},
},
},
async created() {
await this.$store.dispatch('Settings/Filters/Edit/fetchFilter', this.filter_id)
await this.$store.dispatch(
'Settings/Filters/Edit/fetchFilter',
this.filter_id
)
},
methods: {
cancel() {
@ -42,14 +52,14 @@ export default {
.then(() => {
this.$router.go(-1)
})
.catch(err => {
.catch((err) => {
console.error(err)
this.$message({
message: this.$t('message.update_filter_error'),
type: 'error'
type: 'error',
})
})
}
}
},
},
}
</script>

View File

@ -1,7 +1,14 @@
<template>
<div id="new_filter">
<h2>{{ $t('settings.filters.new.title') }}</h2>
<FilterForm v-model="filter" @cancel="cancel" @onSubmit="onSubmit" :loading="loading" :sns="sns"> </FilterForm>
<FilterForm
v-model="filter"
@cancel="cancel"
@onSubmit="onSubmit"
:loading="loading"
:sns="sns"
>
</FilterForm>
</div>
</template>
@ -14,10 +21,10 @@ export default {
components: { FilterForm },
computed: {
...mapState('Settings/Filters/New', {
loading: state => state.loading
loading: (state) => state.loading,
}),
...mapState('TimelineSpace', {
sns: state => state.sns
sns: (state) => state.sns,
}),
filter: {
get() {
@ -25,8 +32,8 @@ export default {
},
set(value) {
this.$store.dispatch('Settings/Filters/New/editFilter', value)
}
}
},
},
},
methods: {
cancel() {
@ -38,14 +45,14 @@ export default {
.then(() => {
this.$router.go(-1)
})
.catch(err => {
.catch((err) => {
console.error(err)
this.$message({
message: this.$t('message.create_filter_error'),
type: 'error'
type: 'error',
})
})
}
}
},
},
}
</script>

View File

@ -1,5 +1,5 @@
<template>
<el-form ref="form" class="section" label-width="200px" label-position="right" size="medium">
<el-form ref="form" class="section" label-width="200px" label-position="right" size="default">
<el-form-item :label="$t('settings.filters.form.phrase')">
<el-input v-model="filterPhrase"></el-input>
</el-form-item>
@ -9,15 +9,13 @@
</el-select>
</el-form-item>
<el-form-item :label="$t('settings.filters.form.context')">
<template>
<el-checkbox-group v-model="filterContext">
<el-checkbox label="home"></el-checkbox>
<el-checkbox label="notifications"></el-checkbox>
<el-checkbox label="public"></el-checkbox>
<el-checkbox label="thread"></el-checkbox>
<el-checkbox label="account" :disabled="accountDisabled()"></el-checkbox>
</el-checkbox-group>
</template>
<el-checkbox-group v-model="filterContext">
<el-checkbox label="home"></el-checkbox>
<el-checkbox label="notifications"></el-checkbox>
<el-checkbox label="public"></el-checkbox>
<el-checkbox label="thread"></el-checkbox>
<el-checkbox label="account" :disabled="accountDisabled()"></el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item>
<el-checkbox v-model="filterIrreversible">{{ $t('settings.filters.form.irreversible') }}</el-checkbox>
@ -157,9 +155,11 @@ export default {
</script>
<style lang="scss" scoped>
.section /deep/ {
.section {
margin-bottom: 40px;
}
.section :deep() {
.el-form-item__label {
color: var(--theme-primary-color);
}

View File

@ -1,16 +1,18 @@
<template>
<div id="general">
<h2>{{ $t('settings.general.title') }}</h2>
<el-form class="toot section" label-width="250px" label-position="right" size="medium">
<el-form class="toot section" label-width="250px" label-position="right" size="default">
<h3>{{ $t('settings.general.toot.title') }}</h3>
<el-form-item for="visibility" :label="$t('settings.general.toot.visibility.description')">
<el-select id="visibility" v-model="tootVisibility" placeholder="visibility">
<el-select id="visibility" :model-value="tootVisibility" placeholder="visibility" @change="changeVisibility">
<el-option v-for="v in visibilities" :key="v.value" :label="$t(v.name)" :value="v.value"> </el-option>
</el-select>
<p class="notice">{{ $t('settings.general.toot.visibility.notice') }}</p>
<p class="notice">
{{ $t('settings.general.toot.visibility.notice') }}
</p>
</el-form-item>
<el-form-item for="sensitive" :label="$t('settings.general.toot.sensitive.description')">
<el-switch id="sensitive" v-model="tootSensitive"></el-switch>
<el-switch id="sensitive" :model-value="tootSensitive" @change="changeSensitive"></el-switch>
</el-form-item>
</el-form>
</div>
@ -46,18 +48,26 @@ export default {
},
created() {
this.$store.dispatch('Settings/General/fetchSettings')
},
methods: {
changeVisibility(value) {
this.tootVisibility = value
},
changeSensitive(value) {
this.tootSensitive = value
}
}
}
</script>
<style lang="scss" scoped>
#general {
.section /deep/ {
.section {
margin-bottom: 40px;
}
.el-form-item__label {
color: var(--theme-primary-color);
}
.section :deep(.el-form-item__label) {
color: var(--theme-primary-color);
}
.notice {

View File

@ -1,9 +1,11 @@
<template>
<div id="timeline">
<h2>{{ $t('settings.timeline.title') }}</h2>
<el-form class="unread-notification section" size="medium" label-position="right" label-width="250px">
<el-form class="unread-notification section" size="default" label-position="right" label-width="250px">
<h3>{{ $t('settings.timeline.unread_notification.title') }}</h3>
<p class="description">{{ $t('settings.timeline.unread_notification.description') }}</p>
<p class="description">
{{ $t('settings.timeline.unread_notification.description') }}
</p>
<el-form-item for="direct" :label="$t('settings.timeline.unread_notification.direct')">
<el-switch v-model="direct" id="direct" />
@ -16,7 +18,7 @@
</el-form-item>
</el-form>
<el-form class="use-marker section" size="medium" label-position="right" label-width="250px">
<el-form class="use-marker section" size="default" label-position="right" label-width="250px">
<h3>{{ $t('settings.timeline.use_marker.title') }}</h3>
<el-form-item for="marker_home" :label="$t('settings.timeline.use_marker.home')">
<el-switch v-model="marker_home" id="marker_home" />
@ -108,12 +110,12 @@ export default {
margin: 32px 0 20px;
}
.section /deep/ {
.section {
margin-bottom: 40px;
}
.el-form-item__label {
color: var(--theme-primary-color);
}
.section :deep(.el-form-item__label) {
color: var(--theme-primary-color);
}
}
</style>

View File

@ -5,8 +5,6 @@
:element-loading-text="$t('message.loading')"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)"
v-shortkey="shortcutEnabled ? { help: ['shift', '?'] } : {}"
@shortkey="handleKey"
>
<side-menu></side-menu>
<div :class="collapse ? 'page-narrow' : 'page'">
@ -31,7 +29,7 @@ import ReceiveDrop from './TimelineSpace/ReceiveDrop'
import { AccountLoadError } from '@/errors/load'
import { TimelineFetchError } from '@/errors/fetch'
import { NewTootAttachLength } from '@/errors/validations'
import { Event } from '~/src/renderer/components/event'
import { EventEmitter } from '~/src/renderer/components/event'
export default {
name: 'timeline-space',
@ -67,7 +65,7 @@ export default {
this.$store.commit('TimelineSpace/Modals/Jump/changeModal', true)
})
},
beforeDestroy() {
beforeUnmount() {
window.removeEventListener('dragenter', this.onDragEnter)
window.removeEventListener('dragleave', this.onDragLeave)
window.removeEventListener('dragover', this.onDragOver)
@ -125,7 +123,7 @@ export default {
this.$store
.dispatch('TimelineSpace/Modals/NewToot/uploadImage', file)
.then(() => {
Event.$emit('image-uploaded')
EventEmitter.emit('image-uploaded')
})
.catch(err => {
if (err instanceof NewTootAttachLength) {
@ -180,7 +178,6 @@ export default {
width: calc(100% - 180px);
position: fixed;
top: 0;
height: 48px;
border-bottom: solid 1px var(--theme-border-color);
}
}

View File

@ -76,7 +76,7 @@ export default {
#contents {
--current-sidebar-width: 360px;
padding-top: 49px;
padding-top: 53px;
height: 100%;
box-sizing: border-box;
user-select: text;
@ -97,7 +97,7 @@ export default {
background-color: var(--theme-border-color);
height: calc(100% - 48px);
position: fixed;
top: 48px;
top: 53px;
right: var(--current-sidebar-width);
}
@ -124,7 +124,7 @@ export default {
#side_bar {
position: fixed;
top: 48px;
top: 52px;
right: 0;
width: var(--current-sidebar-width);
height: calc(100% - 48px);

View File

@ -1,6 +1,6 @@
<template>
<div id="bookmarks" v-shortkey="shortcutEnabled ? { next: ['j'] } : {}" @shortkey="handleKey">
<div v-shortkey="{ linux: ['ctrl', 'r'], mac: ['meta', 'r'] }" @shortkey="reload()"></div>
<div id="bookmarks">
<div></div>
<DynamicScroller :items="bookmarks" :min-item-size="60" id="scroller" class="scroller" ref="scroller">
<template v-slot="{ item, index, active }">
<DynamicScrollerItem :item="item" :active="active" :size-dependencies="[item.uri]" :data-index="index" :watchData="true">
@ -20,7 +20,9 @@
</template>
</DynamicScroller>
<div :class="openSideBar ? 'upper-with-side-bar' : 'upper'" v-show="!heading">
<el-button type="primary" icon="el-icon-arrow-up" @click="upper" circle> </el-button>
<el-button type="primary" @click="upper" circle>
<font-awesome-icon icon="angle-up" class="upper-icon" />
</el-button>
</div>
</div>
</template>
@ -29,18 +31,18 @@
import { mapState, mapGetters } from 'vuex'
import Toot from '@/components/organisms/Toot'
import reloadable from '~/src/renderer/components/mixins/reloadable'
import { Event } from '~/src/renderer/components/event'
import { EventEmitter } from '~/src/renderer/components/event'
export default {
name: 'bookmarks',
components: { Toot },
mixins: [reloadable],
data() {
return {
heading: true,
focusedId: null
}
},
name: 'bookmarks',
components: { Toot },
mixins: [reloadable],
computed: {
...mapState('TimelineSpace', {
account: state => state.account
@ -79,7 +81,7 @@ export default {
},
mounted() {
document.getElementById('scroller').addEventListener('scroll', this.onScroll)
Event.$on('focus-timeline', () => {
EventEmitter.on('focus-timeline', () => {
// If focusedId does not change, we have to refresh focusedId because Toot component watch change events.
const previousFocusedId = this.focusedId
this.focusedId = 0
@ -88,10 +90,10 @@ export default {
})
})
},
beforeDestroy() {
Event.$off('focus-timeline')
beforeUnmount() {
EventEmitter.off('focus-timeline')
},
destroyed() {
unmounted() {
this.$store.commit('TimelineSpace/Contents/Bookmarks/updateBookmarks', [])
if (document.getElementById('scroller') !== undefined && document.getElementById('scroller') !== null) {
document.getElementById('scroller').removeEventListener('scroll', this.onScroll)
@ -178,7 +180,7 @@ export default {
this.focusedId = message.id
},
focusSidebar() {
Event.$emit('focus-sidebar')
EventEmitter.emit('focus-sidebar')
},
handleKey(event) {
switch (event.srcKey) {
@ -213,5 +215,9 @@ export default {
right: calc(20px + var(--current-sidebar-width));
transition: all 0.5s;
}
.upper-icon {
padding: 3px;
}
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<div id="directmessages" v-shortkey="shortcutEnabled ? { next: ['j'] } : {}" @shortkey="handleKey">
<div v-shortkey="{ linux: ['ctrl', 'r'], mac: ['meta', 'r'] }" @shortkey="reload()"></div>
<div id="directmessages">
<div></div>
<DynamicScroller :items="timeline" :min-item-size="86" id="scroller" class="scroller" ref="scroller">
<template v-slot="{ item, index, active }">
<DynamicScrollerItem :item="item" :active="active" :size-dependencies="[item.uri]" :data-index="index" :watchData="true">
@ -22,7 +22,9 @@
</template>
</DynamicScroller>
<div :class="openSideBar ? 'upper-with-side-bar' : 'upper'" v-show="!heading">
<el-button type="primary" icon="el-icon-arrow-up" @click="upper" circle> </el-button>
<el-button type="primary" @click="upper" circle>
<font-awesome-icon icon="angle-up" class="upper-icon" />
</el-button>
</div>
</div>
</template>
@ -32,13 +34,10 @@ import { mapState, mapGetters } from 'vuex'
import moment from 'moment'
import Toot from '~/src/renderer/components/organisms/Toot'
import reloadable from '~/src/renderer/components/mixins/reloadable'
import { Event } from '~/src/renderer/components/event'
import { EventEmitter } from '~/src/renderer/components/event'
import { ScrollPosition } from '~/src/renderer/components/utils/scroll'
export default {
name: 'directmessages',
components: { Toot },
mixins: [reloadable],
data() {
return {
focusedId: null,
@ -48,6 +47,9 @@ export default {
resizeTime: null
}
},
name: 'directmessages',
components: { Toot },
mixins: [reloadable],
computed: {
...mapState('TimelineSpace/Contents/DirectMessages', {
timeline: state => state.timeline,
@ -84,7 +86,7 @@ export default {
})
}
Event.$on('focus-timeline', () => {
EventEmitter.on('focus-timeline', () => {
// If focusedId does not change, we have to refresh focusedId because Toot component watch change events.
const previousFocusedId = this.focusedId
this.focusedId = 0
@ -114,15 +116,15 @@ export default {
this.scrollPosition.prepare()
}
},
beforeDestroy() {
beforeUnmount() {
if (!this.unreadNotification.direct) {
this.$store.dispatch('TimelineSpace/stopDirectMessagesStreaming')
this.$store.dispatch('TimelineSpace/unbindDirectMessagesStreaming')
}
Event.$off('focus-timeline')
EventEmitter.off('focus-timeline')
this.observer.disconnect()
},
destroyed() {
unmounted() {
this.$store.commit('TimelineSpace/Contents/DirectMessages/changeHeading', true)
this.$store.commit('TimelineSpace/Contents/DirectMessages/archiveTimeline')
if (!this.unreadNotification.direct) {
@ -247,7 +249,7 @@ export default {
this.focusedId = message.uri + message.id
},
focusSidebar() {
Event.$emit('focus-sidebar')
EventEmitter.emit('focus-sidebar')
},
handleKey(event) {
switch (event.srcKey) {
@ -297,6 +299,11 @@ export default {
right: calc(20px + var(--current-sidebar-width));
transition: all 0.5s;
}
.upper-icon {
padding: 3px;
}
}
</style>
<style lang="scss" src="@/assets/timeline-transition.scss"></style>

View File

@ -1,6 +1,6 @@
<template>
<div id="favourites" v-shortkey="shortcutEnabled ? { next: ['j'] } : {}" @shortkey="handleKey">
<div v-shortkey="{ linux: ['ctrl', 'r'], mac: ['meta', 'r'] }" @shortkey="reload()"></div>
<div id="favourites">
<div></div>
<DynamicScroller :items="favourites" :min-item-size="60" id="scroller" class="scroller" ref="scroller">
<template v-slot="{ item, index, active }">
<DynamicScrollerItem :item="item" :active="active" :size-dependencies="[item.uri]" :data-index="index" :watchData="true">
@ -21,7 +21,9 @@
</template>
</DynamicScroller>
<div :class="openSideBar ? 'upper-with-side-bar' : 'upper'" v-show="!heading">
<el-button type="primary" icon="el-icon-arrow-up" @click="upper" circle> </el-button>
<el-button type="primary" @click="upper" circle>
<font-awesome-icon icon="angle-up" class="upper-icon" />
</el-button>
</div>
</div>
</template>
@ -30,18 +32,18 @@
import { mapState, mapGetters } from 'vuex'
import Toot from '~/src/renderer/components/organisms/Toot'
import reloadable from '~/src/renderer/components/mixins/reloadable'
import { Event } from '~/src/renderer/components/event'
import { EventEmitter } from '~/src/renderer/components/event'
export default {
name: 'favourites',
components: { Toot },
mixins: [reloadable],
data() {
return {
heading: true,
focusedId: null
}
},
name: 'favourites',
components: { Toot },
mixins: [reloadable],
computed: {
...mapState({
openSideBar: state => state.TimelineSpace.Contents.SideBar.openSideBar,
@ -72,7 +74,7 @@ export default {
},
mounted() {
document.getElementById('scroller').addEventListener('scroll', this.onScroll)
Event.$on('focus-timeline', () => {
EventEmitter.on('focus-timeline', () => {
// If focusedId does not change, we have to refresh focusedId because Toot component watch change events.
const previousFocusedId = this.focusedId
this.focusedId = 0
@ -81,10 +83,10 @@ export default {
})
})
},
beforeDestroy() {
Event.$off('focus-timeline')
beforeUnmount() {
EventEmitter.off('focus-timeline')
},
destroyed() {
unmounted() {
this.$store.commit('TimelineSpace/Contents/Favourites/updateFavourites', [])
if (document.getElementById('scroller') !== undefined && document.getElementById('scroller') !== null) {
document.getElementById('scroller').removeEventListener('scroll', this.onScroll)
@ -173,7 +175,7 @@ export default {
this.focusedId = message.id
},
focusSidebar() {
Event.$emit('focus-sidebar')
EventEmitter.emit('focus-sidebar')
},
handleKey(event) {
switch (event.srcKey) {
@ -208,5 +210,9 @@ export default {
right: calc(20px + var(--current-sidebar-width));
transition: all 0.5s;
}
.upper-icon {
padding: 3px;
}
}
</style>

View File

@ -1,7 +1,12 @@
<template>
<div id="follow-requests">
<template v-for="account in requests">
<user :user="account" :request="true" @acceptRequest="accept" @rejectRequest="reject"></user>
<user
:user="account"
:request="true"
@acceptRequest="accept"
@rejectRequest="reject"
></user>
</template>
</div>
</template>
@ -15,39 +20,49 @@ export default {
components: { User },
computed: {
...mapState('TimelineSpace/Contents/FollowRequests', {
requests: state => state.requests
})
requests: (state) => state.requests,
}),
},
async mounted() {
await this.initialize()
},
methods: {
async initialize() {
await this.$store.dispatch('TimelineSpace/Contents/FollowRequests/fetchRequests').catch(_ => {
this.$message({
message: this.$t('message.timeline_fetch_error'),
type: 'error'
await this.$store
.dispatch('TimelineSpace/Contents/FollowRequests/fetchRequests')
.catch((_) => {
this.$message({
message: this.$t('message.timeline_fetch_error'),
type: 'error',
})
})
})
},
accept(account) {
this.$store.dispatch('TimelineSpace/Contents/FollowRequests/acceptRequest', account).catch(_ => {
this.$message({
message: this.$t('message.follow_request_accept_error'),
type: 'error'
this.$store
.dispatch(
'TimelineSpace/Contents/FollowRequests/acceptRequest',
account
)
.catch((_) => {
this.$message({
message: this.$t('message.follow_request_accept_error'),
type: 'error',
})
})
})
},
reject(account) {
this.$store.dispatch('TimelineSpace/Contents/FollowRequests/rejectRequest', account).catch(_ => {
this.$message({
message: this.$t('message.follow_request_reject_error'),
type: 'error'
this.$store
.dispatch(
'TimelineSpace/Contents/FollowRequests/rejectRequest',
account
)
.catch((_) => {
this.$message({
message: this.$t('message.follow_request_reject_error'),
type: 'error',
})
})
})
}
}
},
},
}
</script>
<style lang="scss" scorped></style>

View File

@ -9,14 +9,7 @@
</el-button>
</div>
<div class="form-item input">
<input
v-model="tag"
:placeholder="$t('hashtag.tag_name')"
class="search-keyword"
v-shortkey.avoid
v-on:keyup.enter="search"
autofocus
/>
<input v-model="tag" :placeholder="$t('hashtag.tag_name')" class="search-keyword" v-on:keyup.enter="search" autofocus />
</div>
<div class="form-item" v-show="tagPage()">
<el-button type="text" @click="save" :title="$t('hashtag.save_tag')">
@ -72,7 +65,6 @@ export default {
<style lang="scss" scoped>
#hashtag {
border-top: 1px solid var(--theme-border-color);
height: calc(100% - 1px);
overflow: hidden;

View File

@ -32,7 +32,9 @@ export default {
},
methods: {
openTimeline(tagName) {
this.$router.push({ path: `/${this.$route.params.id}/hashtag/${tagName}` })
this.$router.push({
path: `/${this.$route.params.id}/hashtag/${tagName}`
})
},
deleteTag(tag) {
this.$store.dispatch('TimelineSpace/Contents/Hashtag/List/removeTag', tag)
@ -63,7 +65,7 @@ export default {
.action {
width: 20px;
.el-button /deep/ {
.el-button {
padding: 0;
color: var(--theme-secondary-color);
}

View File

@ -1,6 +1,6 @@
<template>
<div name="tag" class="tag-timeline" v-shortkey="shortcutEnabled ? { next: ['j'] } : {}" @shortkey="handleKey">
<div v-shortkey="{ linux: ['ctrl', 'r'], mac: ['meta', 'r'] }" @shortkey="reload()"></div>
<div name="tag" class="tag-timeline">
<div></div>
<DynamicScroller :items="timeline" :min-item-size="86" id="scroller" class="scroller" ref="scroller">
<template v-slot="{ item, index, active }">
<DynamicScrollerItem :item="item" :active="active" :size-dependencies="[item.uri]" :data-index="index" :watchData="true">
@ -22,7 +22,9 @@
</template>
</DynamicScroller>
<div :class="openSideBar ? 'upper-with-side-bar' : 'upper'" v-show="!heading">
<el-button type="primary" icon="el-icon-arrow-up" @click="upper" circle> </el-button>
<el-button type="primary" @click="upper" circle>
<font-awesome-icon icon="angle-up" class="upper-icon" />
</el-button>
</div>
</div>
</template>
@ -32,14 +34,10 @@ import { mapState, mapGetters } from 'vuex'
import moment from 'moment'
import Toot from '~/src/renderer/components/organisms/Toot'
import reloadable from '~/src/renderer/components/mixins/reloadable'
import { Event } from '~/src/renderer/components/event'
import { EventEmitter } from '~/src/renderer/components/event'
import { ScrollPosition } from '~/src/renderer/components/utils/scroll'
export default {
name: 'tag',
components: { Toot },
mixins: [reloadable],
props: ['tag'],
data() {
return {
focusedId: null,
@ -49,6 +47,10 @@ export default {
resizeTime: null
}
},
name: 'tag',
components: { Toot },
mixins: [reloadable],
props: ['tag'],
computed: {
...mapState({
openSideBar: state => state.TimelineSpace.Contents.SideBar.openSideBar,
@ -79,7 +81,7 @@ export default {
})
document.getElementById('scroller').addEventListener('scroll', this.onScroll)
Event.$on('focus-timeline', () => {
EventEmitter.on('focus-timeline', () => {
// If focusedId does not change, we have to refresh focusedId because Toot component watch change events.
const previousFocusedId = this.focusedId
this.focusedId = 0
@ -129,10 +131,10 @@ export default {
}
}
},
beforeDestroy() {
beforeUnmount() {
this.$store.dispatch('TimelineSpace/Contents/Hashtag/Tag/stopStreaming')
this.reset()
Event.$off('focus-timeline')
EventEmitter.off('focus-timeline')
this.observer.disconnect()
},
methods: {
@ -263,7 +265,7 @@ export default {
this.focusedId = message.uri + message.id
},
focusSidebar() {
Event.$emit('focus-sidebar')
EventEmitter.emit('focus-sidebar')
},
handleKey(event) {
switch (event.srcKey) {
@ -304,6 +306,11 @@ export default {
right: calc(20px + var(--current-sidebar-width));
transition: all 0.5s;
}
.upper-icon {
padding: 3px;
}
}
</style>
<style lang="scss" src="@/assets/timeline-transition.scss"></style>

View File

@ -1,6 +1,6 @@
<template>
<div id="home" v-shortkey="shortcutEnabled ? { next: ['j'] } : {}" @shortkey="handleKey">
<div v-shortkey="{ linux: ['ctrl', 'r'], mac: ['meta', 'r'] }" @shortkey="reload()"></div>
<div id="home">
<div></div>
<DynamicScroller :items="filteredTimeline" :min-item-size="86" id="scroller" class="scroller" ref="scroller">
<template v-slot="{ item, index, active }">
<template v-if="item.id === 'loading-card'">
@ -30,7 +30,9 @@
</DynamicScroller>
<div :class="openSideBar ? 'upper-with-side-bar' : 'upper'" v-show="!heading">
<el-button type="primary" icon="el-icon-arrow-up" @click="upper" circle> </el-button>
<el-button type="primary" @click="upper" circle>
<font-awesome-icon icon="angle-up" class="upper-icon" />
</el-button>
</div>
</div>
</template>
@ -41,13 +43,10 @@ import moment from 'moment'
import Toot from '~/src/renderer/components/organisms/Toot'
import StatusLoading from '~/src/renderer/components/organisms/StatusLoading'
import reloadable from '~/src/renderer/components/mixins/reloadable'
import { Event } from '~/src/renderer/components/event'
import { EventEmitter } from '~/src/renderer/components/event'
import { ScrollPosition } from '~/src/renderer/components/utils/scroll'
export default {
name: 'home',
components: { Toot, StatusLoading },
mixins: [reloadable],
data() {
return {
focusedId: null,
@ -58,6 +57,9 @@ export default {
loadingMore: false
}
},
name: 'home',
components: { Toot, StatusLoading },
mixins: [reloadable],
computed: {
...mapState('TimelineSpace/Contents/Home', {
timeline: state => state.timeline,
@ -100,7 +102,7 @@ export default {
mounted() {
this.$store.commit('TimelineSpace/SideMenu/changeUnreadHomeTimeline', false)
document.getElementById('scroller').addEventListener('scroll', this.onScroll)
Event.$on('focus-timeline', () => {
EventEmitter.on('focus-timeline', () => {
// If focusedId does not change, we have to refresh focusedId because Toot component watch change events.
const previousFocusedId = this.focusedId
this.focusedId = 0
@ -134,11 +136,11 @@ export default {
this.scrollPosition.prepare()
}
},
beforeDestroy() {
Event.$off('focus-timeline')
beforeUnmount() {
EventEmitter.off('focus-timeline')
this.observer.disconnect()
},
destroyed() {
unmounted() {
this.$store.commit('TimelineSpace/Contents/Home/changeHeading', true)
this.$store.commit('TimelineSpace/Contents/Home/archiveTimeline')
if (document.getElementById('scroller') !== undefined && document.getElementById('scroller') !== null) {
@ -161,10 +163,13 @@ export default {
this.$store.commit('TimelineSpace/Contents/Home/changeHeading', true)
}
},
timeline: function (newState, _oldState) {
if (this.heading && newState.length > 0) {
this.$store.dispatch('TimelineSpace/Contents/Home/saveMarker')
}
timeline: {
handler(newState, _oldState) {
if (this.heading && newState.length > 0) {
this.$store.dispatch('TimelineSpace/Contents/Home/saveMarker')
}
},
deep: true
}
},
methods: {
@ -263,7 +268,7 @@ export default {
this.focusedId = message.uri + message.id
},
focusSidebar() {
Event.$emit('focus-sidebar')
EventEmitter.emit('focus-sidebar')
},
handleKey(event) {
switch (event.srcKey) {
@ -313,6 +318,11 @@ export default {
right: calc(20px + var(--current-sidebar-width));
transition: all 0.5s;
}
.upper-icon {
padding: 3px;
}
}
</style>
<style lang="scss" src="@/assets/timeline-transition.scss"></style>

View File

@ -6,7 +6,11 @@
</el-button>
</div>
<template v-for="account in members">
<user :user="account" :remove="true" @removeAccount="removeAccount"></user>
<user
:user="account"
:remove="true"
@removeAccount="removeAccount"
></user>
</template>
</div>
</template>
@ -21,8 +25,8 @@ export default {
components: { User },
computed: {
...mapState({
members: state => state.TimelineSpace.Contents.Lists.Edit.members
})
members: (state) => state.TimelineSpace.Contents.Lists.Edit.members,
}),
},
created() {
this.init()
@ -31,11 +35,14 @@ export default {
async init() {
this.$store.commit('TimelineSpace/Contents/changeLoading', true)
try {
await this.$store.dispatch('TimelineSpace/Contents/Lists/Edit/fetchMembers', this.list_id)
await this.$store.dispatch(
'TimelineSpace/Contents/Lists/Edit/fetchMembers',
this.list_id
)
} catch (err) {
this.$message({
message: this.$t('message.members_fetch_error'),
type: 'error'
type: 'error',
})
} finally {
this.$store.commit('TimelineSpace/Contents/changeLoading', false)
@ -44,25 +51,37 @@ export default {
async removeAccount(account) {
this.$store.commit('TimelineSpace/Contents/changeLoading', true)
try {
await this.$store.dispatch('TimelineSpace/Contents/Lists/Edit/removeAccount', {
account: account,
listId: this.list_id
})
await this.$store.dispatch('TimelineSpace/Contents/Lists/Edit/fetchMembers', this.list_id)
await this.$store.dispatch(
'TimelineSpace/Contents/Lists/Edit/removeAccount',
{
account: account,
listId: this.list_id,
}
)
await this.$store.dispatch(
'TimelineSpace/Contents/Lists/Edit/fetchMembers',
this.list_id
)
} catch (err) {
this.$message({
message: this.$t('message.remove_user_error'),
type: 'error'
type: 'error',
})
} finally {
this.$store.commit('TimelineSpace/Contents/changeLoading', false)
}
},
addAccount() {
this.$store.commit('TimelineSpace/Modals/AddListMember/setListId', this.list_id)
this.$store.dispatch('TimelineSpace/Modals/AddListMember/changeModal', true)
}
}
this.$store.commit(
'TimelineSpace/Modals/AddListMember/setListId',
this.list_id
)
this.$store.dispatch(
'TimelineSpace/Modals/AddListMember/changeModal',
true
)
},
},
}
</script>

View File

@ -123,7 +123,7 @@ export default {
.list {
padding: 4px 24px;
display: flex;
flex-dirrection: row;
flex-direction: row;
align-items: baseline;
justify-content: space-between;
border-bottom: 1px solid var(--theme-border-color);

View File

@ -1,6 +1,6 @@
<template>
<div name="list" class="list-timeline" v-shortkey="shortcutEnabled ? { next: ['j'] } : {}" @shortkey="handleKey">
<div v-shortkey="{ linux: ['ctrl', 'r'], mac: ['meta', 'r'] }" @shortkey="reload()"></div>
<div name="list" class="list-timeline">
<div></div>
<DynamicScroller :items="timeline" :min-item-size="86" id="scroller" class="scroller" ref="scroller">
<template v-slot="{ item, index, active }">
<DynamicScrollerItem :item="item" :active="active" :size-dependencies="[item.uri]" :data-index="index" :watchData="true">
@ -22,7 +22,9 @@
</template>
</DynamicScroller>
<div :class="openSideBar ? 'upper-with-side-bar' : 'upper'" v-show="!heading">
<el-button type="primary" icon="el-icon-arrow-up" @click="upper" circle> </el-button>
<el-button type="primary" @click="upper" circle>
<font-awesome-icon icon="angle-up" class="upper-icon" />
</el-button>
</div>
</div>
</template>
@ -32,14 +34,10 @@ import { mapState, mapGetters } from 'vuex'
import moment from 'moment'
import Toot from '~/src/renderer/components/organisms/Toot'
import reloadable from '~/src/renderer/components/mixins/reloadable'
import { Event } from '~/src/renderer/components/event'
import { EventEmitter } from '~/src/renderer/components/event'
import { ScrollPosition } from '~/src/renderer/components/utils/scroll'
export default {
name: 'list',
props: ['list_id'],
components: { Toot },
mixins: [reloadable],
data() {
return {
focusedId: null,
@ -49,6 +47,10 @@ export default {
resizeTime: null
}
},
name: 'list',
props: ['list_id'],
components: { Toot },
mixins: [reloadable],
computed: {
...mapState({
openSideBar: state => state.TimelineSpace.Contents.SideBar.openSideBar,
@ -80,7 +82,7 @@ export default {
},
mounted() {
document.getElementById('scroller').addEventListener('scroll', this.onScroll)
Event.$on('focus-timeline', () => {
EventEmitter.on('focus-timeline', () => {
// If focusedId does not change, we have to refresh focusedId because Toot component watch change events.
const previousFocusedId = this.focusedId
this.focusedId = 0
@ -129,11 +131,11 @@ export default {
}
}
},
beforeDestroy() {
beforeUnmount() {
this.$store.dispatch('TimelineSpace/Contents/Lists/Show/stopStreaming')
this.observer.disconnect()
},
destroyed() {
unmounted() {
this.$store.commit('TimelineSpace/Contents/Lists/Show/changeHeading', true)
this.$store.commit('TimelineSpace/Contents/Lists/Show/archiveTimeline')
this.$store.commit('TimelineSpace/Contents/Lists/Show/clearTimeline')
@ -263,7 +265,7 @@ export default {
this.focusedId = message.uri + message.id
},
focusSidebar() {
Event.$emit('focus-sidebar')
EventEmitter.emit('focus-sidebar')
},
handleKey(event) {
switch (event.srcKey) {
@ -304,6 +306,11 @@ export default {
right: calc(20px + var(--current-sidebar-width));
transition: all 0.5s;
}
.upper-icon {
padding: 3px;
}
}
</style>
<style lang="scss" src="@/assets/timeline-transition.scss"></style>

View File

@ -1,6 +1,6 @@
<template>
<div id="local" v-shortkey="shortcutEnabled ? { next: ['j'] } : {}" @shortkey="handleKey">
<div v-shortkey="{ linux: ['ctrl', 'r'], mac: ['meta', 'r'] }" @shortkey="reload()"></div>
<div id="local">
<div></div>
<DynamicScroller :items="timeline" :min-item-size="86" id="scroller" class="scroller" ref="scroller">
<template v-slot="{ item, index, active }">
<DynamicScrollerItem :item="item" :active="active" :size-dependencies="[item.uri]" :data-index="index" :watchData="true">
@ -22,7 +22,9 @@
</template>
</DynamicScroller>
<div :class="openSideBar ? 'upper-with-side-bar' : 'upper'" v-show="!heading">
<el-button type="primary" icon="el-icon-arrow-up" @click="upper" circle> </el-button>
<el-button type="primary" @click="upper" circle>
<font-awesome-icon icon="angle-up" class="upper-icon" />
</el-button>
</div>
</div>
</template>
@ -32,13 +34,10 @@ import { mapState, mapGetters } from 'vuex'
import moment from 'moment'
import Toot from '~/src/renderer/components/organisms/Toot'
import reloadable from '~/src/renderer/components/mixins/reloadable'
import { Event } from '~/src/renderer/components/event'
import { EventEmitter } from '~/src/renderer/components/event'
import { ScrollPosition } from '~/src/renderer/components/utils/scroll'
export default {
name: 'local',
components: { Toot },
mixins: [reloadable],
data() {
return {
focusedId: null,
@ -48,6 +47,9 @@ export default {
resizeTime: null
}
},
name: 'local',
components: { Toot },
mixins: [reloadable],
computed: {
...mapState('TimelineSpace/Contents/Local', {
timeline: state => state.timeline,
@ -59,7 +61,7 @@ export default {
openSideBar: state => state.TimelineSpace.Contents.SideBar.openSideBar,
backgroundColor: state => state.App.theme.background_color,
startReload: state => state.TimelineSpace.HeaderMenu.reload,
unreadNotification: state => state.TimelineSpace.unreadNotification
unreadNotification: state => state.TimelineSpace.timelineSetting.unreadNotification
}),
...mapGetters('TimelineSpace/Modals', ['modalOpened']),
shortcutEnabled: function () {
@ -75,6 +77,7 @@ export default {
}
},
async mounted() {
console.log(this.unreadNotification)
this.$store.commit('TimelineSpace/SideMenu/changeUnreadLocalTimeline', false)
document.getElementById('scroller').addEventListener('scroll', this.onScroll)
if (!this.unreadNotification.local) {
@ -84,7 +87,7 @@ export default {
})
}
Event.$on('focus-timeline', () => {
EventEmitter.on('focus-timeline', () => {
// If focusedId does not change, we have to refresh focusedId because Toot component watch change events.
const previousFocusedId = this.focusedId
this.focusedId = 0
@ -114,15 +117,15 @@ export default {
this.scrollPosition.prepare()
}
},
beforeDestroy() {
beforeUnmount() {
if (!this.unreadNotification.local) {
this.$store.dispatch('TimelineSpace/stopLocalStreaming')
this.$store.dispatch('TimelineSpace/unbindLocalStreaming')
}
Event.$off('focus-timeline')
EventEmitter.off('focus-timeline')
this.observer.disconnect()
},
destroyed() {
unmounted() {
this.$store.commit('TimelineSpace/Contents/Local/changeHeading', true)
this.$store.commit('TimelineSpace/Contents/Local/archiveTimeline')
if (!this.unreadNotification.local) {
@ -245,7 +248,7 @@ export default {
this.focusedId = message.uri + message.id
},
focusSidebar() {
Event.$emit('focus-sidebar')
EventEmitter.emit('focus-sidebar')
},
handleKey(event) {
switch (event.srcKey) {
@ -294,6 +297,11 @@ export default {
right: calc(20px + var(--current-sidebar-width));
transition: all 0.5s;
}
.upper-icon {
padding: 3px;
}
}
</style>
<style lang="scss" src="@/assets/timeline-transition.scss"></style>

View File

@ -1,6 +1,6 @@
<template>
<div id="mentions" v-shortkey="shortcutEnabled ? { next: ['j'] } : {}" @shortkey="handleKey">
<div v-shortkey="{ linux: ['ctrl', 'r'], mac: ['meta', 'r'] }" @shortkey="reload()"></div>
<div id="mentions">
<div></div>
<DynamicScroller :items="mentions" :min-item-size="86" id="scroller" class="scroller" ref="scroller">
<template v-slot="{ item, index, active }">
<template v-if="item.id === 'loading-card'">
@ -28,7 +28,9 @@
</template>
</DynamicScroller>
<div :class="openSideBar ? 'upper-with-side-bar' : 'upper'" v-show="!heading">
<el-button type="primary" icon="el-icon-arrow-up" @click="upper" circle> </el-button>
<el-button type="primary" @click="upper" circle>
<font-awesome-icon icon="angle-up" class="upper-icon" />
</el-button>
</div>
</div>
</template>
@ -39,13 +41,10 @@ import moment from 'moment'
import Notification from '~/src/renderer/components/organisms/Notification'
import StatusLoading from '~/src/renderer/components/organisms/StatusLoading'
import reloadable from '~/src/renderer/components/mixins/reloadable'
import { Event } from '~/src/renderer/components/event'
import { EventEmitter } from '~/src/renderer/components/event'
import { ScrollPosition } from '~/src/renderer/components/utils/scroll'
export default {
name: 'mentions',
components: { Notification, StatusLoading },
mixins: [reloadable],
data() {
return {
focusedId: null,
@ -56,6 +55,9 @@ export default {
loadingMore: false
}
},
name: 'mentions',
components: { Notification, StatusLoading },
mixins: [reloadable],
computed: {
...mapState('App', {
backgroundColor: state => state.theme.background_color
@ -88,7 +90,7 @@ export default {
mounted() {
this.$store.commit('TimelineSpace/SideMenu/changeUnreadMentions', false)
document.getElementById('scroller').addEventListener('scroll', this.onScroll)
Event.$on('focus-timeline', () => {
EventEmitter.on('focus-timeline', () => {
// If focusedId does not change, we have to refresh focusedId because Toot component watch change events.
const previousFocusedId = this.focusedId
this.focusedId = 0
@ -122,11 +124,11 @@ export default {
this.scrollPosition.prepare()
}
},
beforeDestroy() {
Event.$off('focus-timeline')
beforeUnmount() {
EventEmitter.off('focus-timeline')
this.observer.disconnect()
},
destroyed() {
unounted() {
this.$store.commit('TimelineSpace/Contents/Mentions/changeHeading', true)
this.$store.commit('TimelineSpace/Contents/Mentions/archiveMentions')
if (document.getElementById('scroller') !== undefined && document.getElementById('scroller') !== null) {
@ -149,10 +151,13 @@ export default {
this.$store.commit('TimelineSpace/Contents/Mentions/changeHeading', true)
}
},
mentions: function (newState, _oldState) {
if (this.heading && newState.length > 0) {
this.$store.dispatch('TimelineSpace/Contents/Mentions/saveMarker')
}
mentions: {
handler(newState, _oldState) {
if (this.heading && newState.length > 0) {
this.$store.dispatch('TimelineSpace/Contents/Mentions/saveMarker')
}
},
deep: true
}
},
methods: {
@ -249,7 +254,7 @@ export default {
this.focusedId = message.id
},
focusSidebar() {
Event.$emit('focus-sidebar')
EventEmitter.emit('focus-sidebar')
},
handleKey(event) {
switch (event.srcKey) {
@ -298,6 +303,11 @@ export default {
right: calc(20px + var(--current-sidebar-width));
transition: all 0.5s;
}
.upper-icon {
padding: 3px;
}
}
</style>
<style lang="scss" src="@/assets/timeline-transition.scss"></style>

View File

@ -1,6 +1,6 @@
<template>
<div id="notifications" v-shortkey="shortcutEnabled ? { next: ['j'] } : {}" @shortkey="handleKey">
<div v-shortkey="{ linux: ['ctrl', 'r'], mac: ['meta', 'r'] }" @shortkey="reload()"></div>
<div id="notifications">
<div></div>
<DynamicScroller :items="handledNotifications" :min-item-size="20" id="scroller" class="scroller" ref="scroller">
<template v-slot="{ item, index, active }">
<template v-if="item.id === 'loading-card'">
@ -27,7 +27,9 @@
</template>
</DynamicScroller>
<div :class="openSideBar ? 'upper-with-side-bar' : 'upper'" v-show="!heading">
<el-button type="primary" icon="el-icon-arrow-up" @click="upper" circle> </el-button>
<el-button type="primary" @click="upper" circle>
<font-awesome-icon icon="angle-up" class="upper-icon" />
</el-button>
</div>
</div>
</template>
@ -38,13 +40,10 @@ import moment from 'moment'
import Notification from '~/src/renderer/components/organisms/Notification'
import StatusLoading from '~/src/renderer/components/organisms/StatusLoading'
import reloadable from '~/src/renderer/components/mixins/reloadable'
import { Event } from '~/src/renderer/components/event'
import { EventEmitter } from '~/src/renderer/components/event'
import { ScrollPosition } from '~/src/renderer/components/utils/scroll'
export default {
name: 'notifications',
components: { Notification, StatusLoading },
mixins: [reloadable],
data() {
return {
focusedId: null,
@ -55,6 +54,9 @@ export default {
loadingMore: false
}
},
name: 'notifications',
components: { Notification, StatusLoading },
mixins: [reloadable],
computed: {
...mapState({
openSideBar: state => state.TimelineSpace.Contents.SideBar.openSideBar,
@ -86,7 +88,7 @@ export default {
this.$store.dispatch('TimelineSpace/Contents/Notifications/resetBadge')
document.getElementById('scroller').addEventListener('scroll', this.onScroll)
Event.$on('focus-timeline', () => {
EventEmitter.on('focus-timeline', () => {
// If focusedId does not change, we have to refresh focusedId because Toot component watch change events.
const previousFocusedId = this.focusedId
this.focusedId = 0
@ -120,11 +122,11 @@ export default {
this.scrollPosition.prepare()
}
},
beforeDestroy() {
Event.$off('focus-timeline')
beforeUnmount() {
EventEmitter.off('focus-timeline')
this.observer.disconnect()
},
destroyed() {
unmounted() {
this.$store.commit('TimelineSpace/Contents/Notifications/changeHeading', true)
this.$store.commit('TimelineSpace/Contents/Notifications/archiveNotifications')
if (document.getElementById('scroller') !== undefined && document.getElementById('scroller') !== null) {
@ -148,10 +150,13 @@ export default {
this.$store.dispatch('TimelineSpace/Contents/Notifications/resetBadge')
}
},
notifications: function (newState, _oldState) {
if (this.heading && newState.length > 0) {
this.$store.dispatch('TimelineSpace/Contents/Notifications/saveMarker')
}
notifications: {
handler(newState, _oldState) {
if (this.heading && newState.length > 0) {
this.$store.dispatch('TimelineSpace/Contents/Notifications/saveMarker')
}
},
deep: true
}
},
methods: {
@ -252,7 +257,7 @@ export default {
this.focusedId = notification.id
},
focusSidebar() {
Event.$emit('focus-sidebar')
EventEmitter.emit('focus-sidebar')
},
handleKey(event) {
switch (event.srcKey) {
@ -295,6 +300,11 @@ export default {
right: calc(20px + var(--current-sidebar-width));
transition: all 0.5s;
}
.upper-icon {
padding: 3px;
}
}
</style>
<style lang="scss" src="@/assets/timeline-transition.scss"></style>

View File

@ -1,6 +1,6 @@
<template>
<div id="public" v-shortkey="shortcutEnabled ? { next: ['j'] } : {}" @shortkey="handleKey">
<div v-shortkey="{ linux: ['ctrl', 'r'], mac: ['meta', 'r'] }" @shortkey="reload()"></div>
<div id="public">
<div></div>
<DynamicScroller :items="timeline" :min-item-size="86" id="scroller" class="scroller" ref="scroller">
<template v-slot="{ item, index, active }">
<DynamicScrollerItem :item="item" :active="active" :size-dependencies="[item.uri]" :data-index="index" :watchData="true">
@ -22,7 +22,9 @@
</template>
</DynamicScroller>
<div :class="openSideBar ? 'upper-with-side-bar' : 'upper'" v-show="!heading">
<el-button type="primary" icon="el-icon-arrow-up" @click="upper" circle> </el-button>
<el-button type="primary" @click="upper" circle>
<font-awesome-icon icon="angle-up" class="upper-icon" />
</el-button>
</div>
</div>
</template>
@ -32,13 +34,10 @@ import { mapState, mapGetters } from 'vuex'
import moment from 'moment'
import Toot from '~/src/renderer/components/organisms/Toot'
import reloadable from '~/src/renderer/components/mixins/reloadable'
import { Event } from '~/src/renderer/components/event'
import { EventEmitter } from '~/src/renderer/components/event'
import { ScrollPosition } from '~/src/renderer/components/utils/scroll'
export default {
name: 'public',
components: { Toot },
mixins: [reloadable],
data() {
return {
focusedId: null,
@ -48,6 +47,9 @@ export default {
resizeTime: null
}
},
name: 'public',
components: { Toot },
mixins: [reloadable],
computed: {
...mapState('TimelineSpace/Contents/Public', {
timeline: state => state.timeline,
@ -85,7 +87,7 @@ export default {
})
}
Event.$on('focus-timeline', () => {
EventEmitter.on('focus-timeline', () => {
// If focusedId does not change, we have to refresh focusedId because Toot component watch change events.
const previousFocusedId = this.focusedId
this.focusedId = 0
@ -115,15 +117,15 @@ export default {
this.scrollPosition.prepare()
}
},
beforeDestroy() {
beforeUnmount() {
if (!this.unreadNotification.public) {
this.$store.dispatch('TimelineSpace/stopPublicStreaming')
this.$store.dispatch('TimelineSpace/unbindPublicStreaming')
}
Event.$off('focus-timeline')
EventEmitter.off('focus-timeline')
this.observer.disconnect()
},
destroyed() {
unmounted() {
this.$store.commit('TimelineSpace/Contents/Public/changeHeading', true)
this.$store.commit('TimelineSpace/Contents/Public/archiveTimeline')
if (!this.unreadNotification.public) {
@ -248,7 +250,7 @@ export default {
this.focusedId = message.uri + message.id
},
focusSidebar() {
Event.$emit('focus-sidebar')
EventEmitter.emit('focus-sidebar')
},
handleKey(event) {
switch (event.srcKey) {
@ -297,6 +299,11 @@ export default {
right: calc(20px + var(--current-sidebar-width));
transition: all 0.5s;
}
.upper-icon {
padding: 3px;
}
}
</style>
<style lang="scss" src="@/assets/timeline-transition.scss"></style>

View File

@ -2,17 +2,10 @@
<div id="search">
<div class="search-header">
<el-form :inline="true">
<el-select v-model="target" :placeholder="$t('search.search')" class="search-target">
<el-select v-model="target" :placeholder="$t('search.search')" class="search-target" size="large">
<el-option v-for="item in searchTargets" :key="item.target" :label="item.label" :value="item.target"> </el-option>
</el-select>
<input
v-model="query"
:placeholder="$t('search.keyword')"
class="search-keyword"
v-shortkey.avoid
v-on:keyup.enter="search"
autofocus
/>
<input v-model="query" :placeholder="$t('search.keyword')" class="search-keyword" v-on:keyup.enter="search" autofocus />
<div class="clearfix"></div>
</el-form>
</div>
@ -25,6 +18,7 @@
</template>
<script>
import { ref } from 'vue'
import SearchAccount from './Search/Account'
import SearchTag from './Search/Tag'
import SearchToots from './Search/Toots'
@ -34,28 +28,22 @@ export default {
components: { SearchAccount, SearchTag, SearchToots },
data() {
return {
target: 'account',
query: ''
}
},
computed: {
searchTargets: {
get() {
return [
{
target: 'account',
label: this.$t('search.account')
},
{
target: 'tag',
label: this.$t('search.tag')
},
{
target: 'toot',
label: this.$t('search.toot')
}
]
}
target: ref('account'),
query: '',
searchTargets: [
{
target: 'account',
label: this.$t('search.account')
},
{
target: 'tag',
label: this.$t('search.tag')
},
{
target: 'toot',
label: this.$t('search.toot')
}
]
}
},
methods: {
@ -95,22 +83,21 @@ export default {
<style lang="scss" scoped>
#search {
border-top: 1px solid var(--theme-border-color);
.search-header {
background-color: var(--theme-selected-background-color);
padding: 8px 12px;
.search-target /deep/ {
.search-target {
float: left;
width: 20%;
}
.el-input__inner {
background-color: var(--theme-background-color);
border: none;
border-radius: 4px 0 0 4px;
color: var(--theme-primary-color);
}
.search-target :deep(.el-input__inner) {
background-color: var(--theme-background-color);
border: none;
border-radius: 4px 0 0 4px;
color: var(--theme-primary-color);
box-shadow: none;
}
.search-keyword {

View File

@ -22,10 +22,8 @@ export default {
results: state => state.TimelineSpace.Contents.Search.Account.results
})
},
destroyed() {
unmounted() {
this.$store.commit('TimelineSpace/Contents/Search/Account/updateResults', [])
}
}
</script>
<style lang="scss" scoped></style>

View File

@ -22,7 +22,7 @@ export default {
results: state => state.results
})
},
destroyed() {
unmounted() {
this.$store.commit('TimelineSpace/Contents/Search/Tag/updateResults', [])
}
}

View File

@ -22,10 +22,8 @@ export default {
results: state => state.TimelineSpace.Contents.Search.Toots.results
})
},
destroyed() {
unmounted() {
this.$store.commit('TimelineSpace/Contents/Search/Toots/updateResults', [])
}
}
</script>
<style lang="scss" scoped></style>

View File

@ -1,9 +1,12 @@
<template>
<div class="side-bar" v-if="openSideBar" v-shortkey="shortcutEnabled ? { close: ['esc'] } : {}" @shortkey="handleKey">
<div class="side-bar" v-if="openSideBar">
<div class="header">
<i class="el-icon-loading" v-show="loading"></i>
<i class="el-icon-refresh" @click="reload"></i>
<i class="el-icon-close" @click="close"></i>
<el-button type="text" @click="reload" class="action">
<font-awesome-icon icon="rotate" />
</el-button>
<el-button type="text" @click="close" class="action">
<font-awesome-icon icon="xmark" />
</el-button>
</div>
<div id="sidebar_scrollable">
<account-profile v-if="component === 1" v-on:change-loading="changeLoading"></account-profile>
@ -26,11 +29,11 @@ import TootDetail from './SideBar/TootDetail'
import AccountProfile from './SideBar/AccountProfile'
export default {
name: 'side-bar',
components: {
TootDetail,
AccountProfile
},
name: 'side-bar',
props: {
overlaid: {
type: Boolean,
@ -48,11 +51,11 @@ export default {
component: state => state.TimelineSpace.Contents.SideBar.component,
backgroundColor: state => state.App.theme.background_color
}),
shortcutEnabled: function() {
shortcutEnabled: function () {
return !this.overlaid
}
},
beforeDestroy() {
beforeUnmount() {
this.close()
},
methods: {
@ -88,12 +91,14 @@ export default {
box-sizing: border-box;
font-size: 18px;
.el-icon-close {
cursor: pointer;
}
.action {
color: var(--theme-secondary-color);
padding: 0;
margin-left: 8px;
.el-icon-refresh {
cursor: pointer;
&:hover {
color: #409eff;
}
}
}

View File

@ -13,7 +13,7 @@
<div class="relationship" v-if="relationship !== null && relationship !== '' && !isOwnProfile">
<div class="follower-status">
<el-tag class="status" size="small" v-if="relationship.followed_by">{{ $t('side_bar.account_profile.follows_you') }}</el-tag>
<el-tag class="status" size="medium" v-else>{{ $t('side_bar.account_profile.doesnt_follow_you') }}</el-tag>
<el-tag class="status" size="default" v-else>{{ $t('side_bar.account_profile.doesnt_follow_you') }}</el-tag>
</div>
<div class="notify" v-if="relationship !== null && relationship !== '' && !isOwnProfile">
<div
@ -22,44 +22,44 @@
@click="unsubscribe(account)"
:title="$t('side_bar.account_profile.unsubscribe')"
>
<font-awesome-icon icon="bell" size="xl" />
<font-awesome-icon icon="bell" size="lg" />
</div>
<div v-else class="subscribe" @click="subscribe(account)" :title="$t('side_bar.account_profile.subscribe')">
<font-awesome-icon :icon="['far', 'bell']" size="xl" />
<font-awesome-icon :icon="['far', 'bell']" size="lg" />
</div>
</div>
</div>
<div class="user-info">
<div class="more" v-if="relationship !== null && relationship !== '' && !isOwnProfile">
<popper trigger="click" :options="{ placement: 'bottom' }" ref="popper">
<div class="popper">
<ul class="menu-list">
<li role="button" @click="openBrowser(account)">
{{ $t('side_bar.account_profile.open_in_browser') }}
</li>
<li role="button" @click="addToList(account)">
{{ $t('side_bar.account_profile.manage_list_memberships') }}
</li>
<li role="button" @click="unmute(account)" v-if="muting">
{{ $t('side_bar.account_profile.unmute') }}
</li>
<li role="button" @click="confirmMute(account)" v-else>
{{ $t('side_bar.account_profile.mute') }}
</li>
<li role="button" @click="unblock(account)" v-if="blocking">
{{ $t('side_bar.account_profile.unblock') }}
</li>
<li role="button" @click="block(account)" v-else>
{{ $t('side_bar.account_profile.block') }}
</li>
</ul>
</div>
<el-popover placement="bottom" width="200" trigger="click" popper-class="account-menu-popper">
<ul class="menu-list">
<li role="button" @click="openBrowser(account)">
{{ $t('side_bar.account_profile.open_in_browser') }}
</li>
<li role="button" @click="addToList(account)">
{{ $t('side_bar.account_profile.manage_list_memberships') }}
</li>
<li role="button" @click="unmute(account)" v-if="muting">
{{ $t('side_bar.account_profile.unmute') }}
</li>
<li role="button" @click="confirmMute(account)" v-else>
{{ $t('side_bar.account_profile.mute') }}
</li>
<li role="button" @click="unblock(account)" v-if="blocking">
{{ $t('side_bar.account_profile.unblock') }}
</li>
<li role="button" @click="block(account)" v-else>
{{ $t('side_bar.account_profile.block') }}
</li>
</ul>
<el-button slot="reference" type="text" :title="$t('side_bar.account_profile.detail')">
<font-awesome-icon icon="gear" size="xl" />
</el-button>
</popper>
<template #reference>
<el-button type="text" :title="$t('side_bar.account_profile.detail')">
<font-awesome-icon icon="gear" size="xl" />
</el-button>
</template>
</el-popover>
</div>
<div class="icon" role="presentation">
<FailoverImg :src="account.avatar" :alt="`Avatar of ${account.username}`" />
@ -88,7 +88,9 @@
<dt>
{{ identity.provider }}
</dt>
<dd @click.capture.prevent="identityOpen(identity.profile_url)">{{ identity.provider_username }}</dd>
<dd @click.capture.prevent="identityOpen(identity.profile_url)">
{{ identity.provider_username }}
</dd>
</dl>
</div>
<div class="metadata">
@ -114,7 +116,9 @@
</el-col>
<el-col :span="8" :class="activeTab === 3 ? 'info info-active' : 'info'">
<el-button type="text" class="tab" @click="changeTab(3)">
<div class="title">{{ $t('side_bar.account_profile.followers') }}</div>
<div class="title">
{{ $t('side_bar.account_profile.followers') }}
</div>
<div class="count">{{ account.followers_count }}</div>
</el-button>
</el-col>
@ -225,29 +229,23 @@ export default {
},
openBrowser(account) {
window.shell.openExternal(account.url)
this.$refs.popper.doClose()
},
addToList(account) {
this.$store.dispatch('TimelineSpace/Modals/ListMembership/setAccount', account)
this.$store.dispatch('TimelineSpace/Modals/ListMembership/changeModal', true)
this.$refs.popper.doClose()
},
confirmMute(account) {
this.$store.dispatch('TimelineSpace/Modals/MuteConfirm/changeAccount', account)
this.$store.dispatch('TimelineSpace/Modals/MuteConfirm/changeModal', true)
this.$refs.popper.doClose()
},
unmute(account) {
this.$store.dispatch('TimelineSpace/Contents/SideBar/AccountProfile/unmute', account)
this.$refs.popper.doClose()
},
block(account) {
this.$store.dispatch('TimelineSpace/Contents/SideBar/AccountProfile/block', account)
this.$refs.popper.doClose()
},
unblock(account) {
this.$store.dispatch('TimelineSpace/Contents/SideBar/AccountProfile/unblock', account)
this.$refs.popper.doClose()
},
metadataClick(e) {
const link = findLink(e.target, 'metadata')
@ -340,34 +338,6 @@ export default {
}
}
.more {
.popper {
padding: 2px 0;
border-color: #909399;
}
.menu-list {
padding: 0;
font-size: calc(var(--base-font-size) * 0.9);
list-style-type: none;
line-height: 32px;
text-align: left;
color: #303133;
margin: 4px 0;
li {
box-sizing: border-box;
padding: 0 32px 0 16px;
&:hover {
background-color: #409eff;
color: #fff;
cursor: pointer;
}
}
}
}
.icon {
padding: 12px;
@ -378,27 +348,25 @@ export default {
}
}
.username /deep/ {
.username {
overflow: hidden;
text-overflow: ellipsis;
font-size: calc(var(--base-font-size) * 1.71);
margin: 0 auto 12px auto;
}
.emojione {
max-width: 1em;
max-height: 1em;
}
.username :deep(.emojione) {
max-width: 1em;
max-height: 1em;
}
.account {
color: #409eff;
}
.note {
& /deep/ .emojione {
max-width: 1.2em;
height: 1.2em;
}
.note :deep(.emojione) {
max-width: 1.2em;
height: 1.2em;
}
}
@ -477,6 +445,12 @@ export default {
width: 100%;
text-align: left;
line-height: 20px;
height: auto;
display: block;
}
.tab :deep(span) {
display: block;
}
.title {
@ -509,3 +483,31 @@ export default {
}
}
</style>
<style lang="scss">
.account-menu-popper {
padding: 2px 0 !important;
border-color: #909399;
.menu-list {
padding: 0;
font-size: calc(var(--base-font-size) * 0.9);
list-style-type: none;
line-height: 32px;
text-align: left;
color: #303133;
margin: 4px 0;
li {
box-sizing: border-box;
padding: 0 32px 0 16px;
&:hover {
background-color: #409eff;
color: #fff;
cursor: pointer;
}
}
}
}
</style>

View File

@ -41,7 +41,7 @@ export default {
mounted() {
document.getElementById('sidebar_scrollable').addEventListener('scroll', this.onScroll)
},
destroyed() {
unmounted() {
if (document.getElementById('sidebar_scrollable') !== undefined && document.getElementById('sidebar_scrollable') !== null) {
document.getElementById('sidebar_scrollable').removeEventListener('scroll', this.onScroll)
}

View File

@ -42,7 +42,7 @@ export default {
mounted() {
document.getElementById('sidebar_scrollable').addEventListener('scroll', this.onScroll)
},
destroyed() {
unmounted() {
if (document.getElementById('sidebar_scrollable') !== undefined && document.getElementById('sidebar_scrollable') !== null) {
document.getElementById('sidebar_scrollable').removeEventListener('scroll', this.onScroll)
}

View File

@ -1,6 +1,6 @@
<template>
<div class="tabs" id="sidebar_tabs">
<el-tabs v-model="activeName" @tab-click="handleClick" stretch>
<div id="sidebar_tabs">
<el-tabs :model-value="activeName" @tab-click="handleClick" class="tabs">
<el-tab-pane label="Posts" name="posts"><Posts :account="account" :buffer="buffer" :filters="filters" /></el-tab-pane>
<el-tab-pane label="Posts and replies" name="posts_and_replies"
><PostsAndReplies :account="account" :buffer="buffer" :filters="filters"
@ -49,7 +49,7 @@ export default {
</script>
<style lang="scss" scoped>
.tabs /deep/ {
.tabs :deep() {
.el-tabs__header {
background-color: var(--theme-selected-background-color);
}

View File

@ -27,7 +27,7 @@
<script>
import { mapState, mapGetters } from 'vuex'
import Toot from '~/src/renderer/components/organisms/Toot'
import { Event } from '~/src/renderer/components/event'
import { EventEmitter } from '~/src/renderer/components/event'
export default {
name: 'media',
@ -54,18 +54,18 @@ export default {
mounted() {
this.$store.dispatch('TimelineSpace/Contents/SideBar/AccountProfile/Timeline/Media/clearTimeline')
document.getElementById('sidebar_scrollable').addEventListener('scroll', this.onScroll)
Event.$on('focus-sidebar', () => {
EventEmitter.on('focus-sidebar', () => {
this.focusedId = 0
this.$nextTick(function () {
this.focusedId = this.timeline[0].uri + this.timeline[0].id
})
})
},
beforeDestroy() {
Event.$emit('focus-timeline')
Event.$off('focus-sidebar')
beforeUnmount() {
EventEmitter.emit('focus-timeline')
EventEmitter.off('focus-sidebar')
},
destroyed() {
unmounted() {
if (document.getElementById('sidebar_scrollable') !== undefined && document.getElementById('sidebar_scrollable') !== null) {
document.getElementById('sidebar_scrollable').removeEventListener('scroll', this.onScroll)
}
@ -130,7 +130,7 @@ export default {
},
focusTimeline() {
this.focusedId = 0
Event.$emit('focus-timeline')
EventEmitter.emit('focus-timeline')
}
}
}

View File

@ -1,9 +1,8 @@
<template>
<div id="timeline">
<template v-for="message in pinnedToots">
<template v-for="message in pinnedToots" :key="message.id">
<toot
:message="message"
:key="message.id"
:focused="message.uri + message.id === focusedId"
:pinned="true"
:overlaid="modalOpened"
@ -44,7 +43,7 @@
<script>
import { mapState, mapGetters } from 'vuex'
import Toot from '~/src/renderer/components/organisms/Toot'
import { Event } from '~/src/renderer/components/event'
import { EventEmitter } from '~/src/renderer/components/event'
export default {
name: 'posts',
@ -72,18 +71,18 @@ export default {
mounted() {
this.$store.dispatch('TimelineSpace/Contents/SideBar/AccountProfile/Timeline/Posts/clearTimeline')
document.getElementById('sidebar_scrollable').addEventListener('scroll', this.onScroll)
Event.$on('focus-sidebar', () => {
EventEmitter.on('focus-sidebar', () => {
this.focusedId = 0
this.$nextTick(function () {
this.focusedId = this.timeline[0].uri + this.timeline[0].id
})
})
},
beforeDestroy() {
Event.$emit('focus-timeline')
Event.$off('focus-sidebar')
beforeUnmount() {
EventEmitter.emit('focus-timeline')
EventEmitter.off('focus-sidebar')
},
destroyed() {
unmounted() {
if (document.getElementById('sidebar_scrollable') !== undefined && document.getElementById('sidebar_scrollable') !== null) {
document.getElementById('sidebar_scrollable').removeEventListener('scroll', this.onScroll)
}
@ -151,7 +150,7 @@ export default {
},
focusTimeline() {
this.focusedId = 0
Event.$emit('focus-timeline')
EventEmitter.emit('focus-timeline')
}
}
}

View File

@ -27,7 +27,7 @@
<script>
import { mapState, mapGetters } from 'vuex'
import Toot from '~/src/renderer/components/organisms/Toot'
import { Event } from '~/src/renderer/components/event'
import { EventEmitter } from '~/src/renderer/components/event'
export default {
name: 'posts-and-replies',
@ -54,18 +54,18 @@ export default {
mounted() {
this.$store.dispatch('TimelineSpace/Contents/SideBar/AccountProfile/Timeline/PostsAndReplies/clearTimeline')
document.getElementById('sidebar_scrollable').addEventListener('scroll', this.onScroll)
Event.$on('focus-sidebar', () => {
EventEmitter.on('focus-sidebar', () => {
this.focusedId = 0
this.$nextTick(function () {
this.focusedId = this.timeline[0].uri + this.timeline[0].id
})
})
},
beforeDestroy() {
Event.$emit('focus-timeline')
Event.$off('focus-sidebar')
beforeUnmount() {
EventEmitter.emit('focus-timeline')
EventEmitter.off('focus-sidebar')
},
destroyed() {
unmounted() {
if (document.getElementById('sidebar_scrollable') !== undefined && document.getElementById('sidebar_scrollable') !== null) {
document.getElementById('sidebar_scrollable').removeEventListener('scroll', this.onScroll)
}
@ -132,7 +132,7 @@ export default {
},
focusTimeline() {
this.focusedId = 0
Event.$emit('focus-timeline')
EventEmitter.emit('focus-timeline')
}
}
}

View File

@ -52,7 +52,7 @@
<script>
import { mapState, mapGetters } from 'vuex'
import Toot from '~/src/renderer/components/organisms/Toot'
import { Event } from '~/src/renderer/components/event'
import { EventEmitter } from '~/src/renderer/components/event'
export default {
name: 'toot-detail',
@ -76,7 +76,7 @@ export default {
this.load()
},
mounted() {
Event.$on('focus-sidebar', () => {
EventEmitter.on('focus-sidebar', () => {
this.focusedId = 0
this.$nextTick(function () {
this.focusedId = this.timeline[0].uri + this.timeline[0].id
@ -88,9 +88,9 @@ export default {
this.load()
}
},
beforeDestroy() {
Event.$emit('focus-timeline')
Event.$off('focus-sidebar')
beforeUnmount() {
EventEmitter.emit('focus-timeline')
EventEmitter.off('focus-sidebar')
},
methods: {
originalMessage(message) {
@ -151,7 +151,7 @@ export default {
},
focusTimeline() {
this.focusedId = 0
Event.$emit('focus-timeline')
EventEmitter.emit('focus-timeline')
}
}
}

View File

@ -11,21 +11,23 @@
<el-button v-show="reloadable()" type="text" class="action" @click="reload" :title="$t('header_menu.reload')">
<font-awesome-icon icon="rotate" />
</el-button>
<el-popover placement="left-start" width="180" popper-class="theme-popover" trigger="click" v-model="TLOptionVisible">
<el-popover v-if="TLOption()" placement="left-start" width="180" popper-class="theme-popover" trigger="click">
<div>
<el-form role="form" label-position="left" label-width="125px" size="medium">
<el-form role="form" label-position="left" label-width="125px" size="default">
<el-form-item for="show-reblogs" :label="$t('header_menu.option.show_reblogs')">
<el-checkbox id="show-reblogs" v-model="showReblogs"></el-checkbox>
<el-checkbox id="show-reblogs" :model-value="showReblogs"></el-checkbox>
</el-form-item>
<el-form-item for="show-replies" :label="$t('header_menu.option.show_replies')">
<el-checkbox id="show-replies" v-model="showReplies"></el-checkbox>
<el-checkbox id="show-replies" :model-value="showReplies"></el-checkbox>
</el-form-item>
<el-button type="primary" @click="applyTLOption">{{ $t('header_menu.option.apply') }}</el-button>
</el-form>
</div>
<el-button v-show="TLOption()" slot="reference" type="text" class="action" :title="$t('header_menu.option.title')">
<font-awesome-icon icon="sliders" />
</el-button>
<template #reference>
<el-button type="text" class="action" :title="$t('header_menu.option.title')">
<font-awesome-icon icon="sliders" />
</el-button>
</template>
</el-popover>
<el-button type="text" class="action" @click="settings" :title="$t('header_menu.settings')">
<font-awesome-icon icon="gear" />
@ -41,7 +43,6 @@ export default {
name: 'header-menu',
data() {
return {
TLOptionVisible: false,
showReblogs: true,
showReplies: true
}
@ -177,7 +178,6 @@ export default {
default:
console.log('Not implemented')
}
this.TLOptionVisible = false
},
TLOption() {
switch (this.$route.name) {
@ -211,6 +211,7 @@ export default {
h1 {
margin: 0;
line-height: 32px;
}
}

View File

@ -31,7 +31,7 @@ export default {
AddListMember,
MuteConfirm,
Shortcut,
Report
}
Report,
},
}
</script>

View File

@ -1,34 +1,36 @@
<template>
<el-dialog :title="$t('modals.add_list_member.title')" :visible.sync="addListMemberModal" width="400px" class="add-member">
<div class="search-account" :element-loading-background="loadingBackground">
<el-form :inline="true">
<input v-model="name" placeholder="Account name" class="account-name" v-shortkey="['enter']" @shortkey="search" autofocus />
<el-button type="text" class="search" @click="search">
<font-awesome-icon icon="magnifying-glass" />
</el-button>
</el-form>
<div class="search-results">
<template v-for="user in accounts">
<div class="user">
<div class="icon">
<img :src="user.avatar" />
</div>
<div class="name">
<div class="username">
{{ username(user) }}
<div class="add-member">
<el-dialog :title="$t('modals.add_list_member.title')" :model-value="addListMemberModal" width="400px" custom-class="add-member-modal">
<div class="search-account" :element-loading-background="loadingBackground">
<el-form :inline="true">
<input v-model="name" placeholder="Account name" class="account-name" autofocus />
<el-button type="text" class="search" @click="search">
<font-awesome-icon icon="magnifying-glass" />
</el-button>
</el-form>
<div class="search-results">
<template v-for="user in accounts">
<div class="user">
<div class="icon">
<img :src="user.avatar" />
</div>
<div class="name">
<div class="username">
{{ username(user) }}
</div>
<div class="acct">@{{ user.acct }}</div>
</div>
<div class="add">
<el-button type="text" @click="add(user)">
<font-awesome-icon icon="plus" />
</el-button>
</div>
<div class="acct">@{{ user.acct }}</div>
</div>
<div class="add">
<el-button type="text" @click="add(user)">
<font-awesome-icon icon="plus" />
</el-button>
</div>
</div>
</template>
</template>
</div>
</div>
</div>
</el-dialog>
</el-dialog>
</div>
</template>
<script>
@ -86,9 +88,10 @@ export default {
</script>
<style lang="scss" scoped>
.add-member /deep/ {
.add-member :deep() {
.el-dialog__header {
background-color: var(--theme-header-menu-color);
margin-right: 0;
.el-dialog__title {
color: var(--theme-secondary-color);
@ -100,7 +103,9 @@ export default {
color: var(--theme-secondary-color);
padding: 8px 12px 30px;
}
}
.add-member-modal {
.search-account {
background-color: var(--theme-selected-background-color);

View File

@ -1,18 +1,24 @@
<template>
<transition name="image-viewer">
<div id="image" v-show="modalOpen" @click="close" :aria-hidden="!modalOpen" aria-modal="true" role="dialog">
<div class="image-wrapper" v-shortkey="modalOpen ? {close: ['esc']} : {}" @shortkey="closeHandle">
<div class="image-header">
<el-button type="text" icon="el-icon-close" @click="close" class="close-button"></el-button>
</div>
<div class="image-content" role="presentation">
<span class="button-area"><el-button type="text" v-show="showLeft" v-shortkey="['arrowleft']" @shortkey.native="decrementIndex()"><i class="el-icon-arrow-left" @click.stop="decrementIndex"></i></el-button></span>
<Media :src="imageURL" :type="imageType"></Media>
<span class="button-area"><el-button type="text" v-show="showRight" v-shortkey="['arrowright']" @shortkey.native="incrementIndex()"><i class="el-icon-arrow-right" @click.stop="incrementIndex"></i></el-button></span>
<transition name="image-viewer">
<div id="image" v-show="modalOpen" :aria-hidden="!modalOpen" aria-modal="true" role="dialog">
<div class="image-wrapper">
<div class="image-header">
<el-button type="text" @click="close" class="close-button">
<font-awesome-icon icon="xmark" />
</el-button>
</div>
<div class="image-content" role="presentation">
<span class="button-area"
><el-button type="text" v-show="showLeft" @click="decrementIndex()"> <font-awesome-icon icon="angle-left" /> </el-button
></span>
<Media :src="imageURL" :type="imageType"></Media>
<span class="button-area"
><el-button type="text" v-show="showRight" @click="incrementIndex()"> <font-awesome-icon icon="angle-right" /> </el-button
></span>
</div>
</div>
</div>
</div>
</transition>
</transition>
</template>
<script>
@ -20,38 +26,38 @@ import Media from './Media'
import { mapState } from 'vuex'
export default {
name: 'image-viewer',
components: {
Media
},
name: 'image-viewer',
computed: {
...mapState({
modalOpen: state => state.TimelineSpace.Modals.ImageViewer.modalOpen
}),
imageURL () {
imageURL() {
return this.$store.getters['TimelineSpace/Modals/ImageViewer/imageURL']
},
imageType () {
imageType() {
return this.$store.getters['TimelineSpace/Modals/ImageViewer/imageType']
},
showLeft () {
showLeft() {
return this.$store.getters['TimelineSpace/Modals/ImageViewer/showLeft']
},
showRight () {
showRight() {
return this.$store.getters['TimelineSpace/Modals/ImageViewer/showRight']
}
},
methods: {
close () {
close() {
this.$store.dispatch('TimelineSpace/Modals/ImageViewer/closeModal')
},
decrementIndex () {
decrementIndex() {
if (this.showLeft) this.$store.dispatch('TimelineSpace/Modals/ImageViewer/decrementIndex')
},
incrementIndex () {
incrementIndex() {
if (this.showRight) this.$store.dispatch('TimelineSpace/Modals/ImageViewer/incrementIndex')
},
closeHandle (event) {
closeHandle(event) {
switch (event.srcKey) {
case 'close':
this.close()
@ -75,7 +81,7 @@ export default {
.image-header {
width: 100%;
text-align: right;
padding: 8px 8px 0 0 ;
padding: 8px 8px 0 0;
color: #409eff;
box-sizing: border-box;
position: fixed;
@ -93,17 +99,21 @@ export default {
}
}
.image-viewer-enter-active, .image-viewer-leave-active {
.image-viewer-enter-active,
.image-viewer-leave-active {
transition: opacity 0.5s;
}
.image-viewer-enter, .image-viewer-leave-to {
.image-viewer-enter,
.image-viewer-leave-to {
opacity: 0;
}
.button-area {
display: inline-block;
width: 52px;
height: 77px;
i {
width: 27px;
margin: 0 12px;
svg {
font-size: 50px;
}
}

View File

@ -1,20 +1,17 @@
<template>
<el-dialog
:visible.sync="jumpModal"
width="440px"
class="jump-modal">
<el-dialog :model-value="jumpModal" width="440px" class="jump-modal">
<el-form class="jump-form" v-on:submit.prevent="jump">
<div class="channel">
<input
type="text"
v-model="channel"
:placeholder="$t('modals.jump.jump_to')"
ref="channel"
v-shortkey="shortcutEnabled ? {next: ['arrowdown'], prev: ['arrowup'], select: ['enter']} : {}"
@shortkey="handleKey"
/>
<input type="text" v-model="channel" :placeholder="$t('modals.jump.jump_to')" ref="channel" />
<ul class="channel-list">
<li v-for="c in filterdChannel" :class="c.name === selectedChannel.name ? 'channel-list-item selected' : 'channel-list-item'" @click="jump(c)" @mouseover="changeSelected(c)">{{ c.name }}</li>
<li
v-for="c in filterdChannel"
:class="c.name === selectedChannel.name ? 'channel-list-item selected' : 'channel-list-item'"
@click="jump(c)"
@mouseover="changeSelected(c)"
>
{{ c.name }}
</li>
</ul>
</div>
<!-- Dummy form to guard submitting with enter -->
@ -36,21 +33,21 @@ export default {
selectedChannel: state => state.selectedChannel
}),
channel: {
get () {
get() {
return this.$store.state.TimelineSpace.Modals.Jump.channel
},
set (value) {
set(value) {
this.$store.commit('TimelineSpace/Modals/Jump/updateChannel', value)
}
},
filterdChannel () {
filterdChannel() {
return this.filterChannelForm()
},
jumpModal: {
get () {
get() {
return this.$store.state.TimelineSpace.Modals.Jump.modalOpen
},
set (value) {
set(value) {
this.$store.commit('TimelineSpace/Modals/Jump/changeModal', value)
}
},
@ -75,41 +72,41 @@ export default {
}
},
methods: {
filterChannelForm () {
return this.channelList.filter((c) => {
filterChannelForm() {
return this.channelList.filter(c => {
return c.name.toLowerCase().indexOf(this.channel.toLowerCase()) !== -1
})
},
nextChannel () {
nextChannel() {
const filterd = this.filterChannelForm()
const i = filterd.findIndex((e) => {
const i = filterd.findIndex(e => {
return e.name === this.selectedChannel.name
})
if (i !== undefined && i < (filterd.length - 1)) {
if (i !== undefined && i < filterd.length - 1) {
this.$store.commit('TimelineSpace/Modals/Jump/changeSelected', filterd[i + 1])
}
},
prevChannel () {
prevChannel() {
const filterd = this.filterChannelForm()
const i = filterd.findIndex((e) => {
const i = filterd.findIndex(e => {
return e.name === this.selectedChannel.name
})
if (i !== undefined && i > 0) {
this.$store.commit('TimelineSpace/Modals/Jump/changeSelected', filterd[i - 1])
}
},
changeSelected (channel) {
changeSelected(channel) {
this.$store.commit('TimelineSpace/Modals/Jump/changeSelected', channel)
},
jumpCurrentSelected () {
jumpCurrentSelected() {
if (this.jumpModal) {
this.$store.dispatch('TimelineSpace/Modals/Jump/jumpCurrentSelected')
}
},
jump (channel) {
jump(channel) {
this.$store.dispatch('TimelineSpace/Modals/Jump/jump', channel)
},
handleKey (event) {
handleKey(event) {
switch (event.srcKey) {
case 'next':
this.nextChannel()

View File

@ -1,5 +1,5 @@
<template>
<el-dialog :title="$t('modals.list_membership.title')" :visible.sync="listMembershipModal" width="400px" class="list-membership-modal">
<el-dialog :title="$t('modals.list_membership.title')" :model-value="listMembershipModal" width="400px" class="list-membership-modal">
<el-checkbox-group v-model="belongToLists" v-loading="loading">
<table class="lists">
<tbody>

View File

@ -1,8 +1,32 @@
<template>
<div id="current-media" v-loading="loading" element-loading-background="rgba(0, 0, 0, 0.8)">
<video :src="src" v-if="isMovieFile()" autoplay loop controls v-on:loadstart="loaded()"></video>
<video :src="src" v-else-if="isGIF()" autoplay loop v-on:loadstart="loaded()"></video>
<video :src="src" v-else-if="isAudio()" autoplay loop controls v-on:loadstart="loaded()"></video>
<div
id="current-media"
v-loading="loading"
element-loading-background="rgba(0, 0, 0, 0.8)"
>
<video
:src="src"
v-if="isMovieFile()"
autoplay
loop
controls
v-on:loadstart="loaded()"
></video>
<video
:src="src"
v-else-if="isGIF()"
autoplay
loop
v-on:loadstart="loaded()"
></video>
<video
:src="src"
v-else-if="isAudio()"
autoplay
loop
controls
v-on:loadstart="loaded()"
></video>
<img :src="imageSrc" v-else v-on:load="loaded()" />
</div>
</template>
@ -15,16 +39,16 @@ export default {
props: {
src: {
type: String,
default: ''
default: '',
},
type: {
type: String,
default: ''
}
default: '',
},
},
data() {
return {
imageSrc: this.src
imageSrc: this.src,
}
},
watch: {
@ -38,12 +62,12 @@ export default {
console.error(err)
}
}
}
},
},
computed: {
...mapState({
loading: state => state.TimelineSpace.Modals.ImageViewer.loading
})
loading: (state) => state.TimelineSpace.Modals.ImageViewer.loading,
}),
},
methods: {
isMovieFile() {
@ -57,8 +81,8 @@ export default {
},
async loaded() {
this.$store.dispatch('TimelineSpace/Modals/ImageViewer/loaded')
}
}
},
},
}
</script>

View File

@ -1,20 +1,15 @@
<template>
<el-dialog
:title="$t('modals.mute_confirm.title')"
:visible.sync="muteConfirmModal"
width="400px"
custom-class="mute-confirm"
>
<el-form class="description">
<el-form-item for="notify" :label="$t('modals.mute_confirm.body')">
<el-switch id="notify" v-model="notify"></el-switch>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="closeModal">{{ $t('modals.mute_confirm.cancel') }}</el-button>
<el-button type="primary" @click="submit">{{ $t('modals.mute_confirm.ok') }}</el-button>
</span>
</el-dialog>
<el-dialog :title="$t('modals.mute_confirm.title')" :model-value="muteConfirmModal" width="400px" custom-class="mute-confirm">
<el-form class="description">
<el-form-item for="notify" :label="$t('modals.mute_confirm.body')">
<el-switch id="notify" v-model="notify"></el-switch>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="closeModal">{{ $t('modals.mute_confirm.cancel') }}</el-button>
<el-button type="primary" @click="submit">{{ $t('modals.mute_confirm.ok') }}</el-button>
</span>
</el-dialog>
</template>
<script>
@ -22,7 +17,7 @@ import { mapState } from 'vuex'
export default {
name: 'MuteConfirm',
data () {
data() {
return {
notify: true
}
@ -32,19 +27,19 @@ export default {
account: state => state.account
}),
muteConfirmModal: {
get () {
get() {
return this.$store.state.TimelineSpace.Modals.MuteConfirm.modalOpen
},
set (value) {
set(value) {
this.$store.dispatch('TimelineSpace/Modals/MuteConfirm/changeModal', value)
}
}
},
methods: {
closeModal () {
closeModal() {
this.muteConfirmModal = false
},
async submit () {
async submit() {
this.closeModal()
await this.$store.dispatch('TimelineSpace/Modals/MuteConfirm/submit', this.notify)
}

View File

@ -1,144 +1,147 @@
<template>
<el-dialog
:title="$t('modals.new_toot.title')"
:visible.sync="newTootModal"
v-if="newTootModal"
:before-close="closeConfirm"
width="600px"
class="new-toot-modal"
ref="dialog"
>
<el-form v-on:submit.prevent="toot" role="form">
<Quote :message="quoteToMessage" :displayNameStyle="displayNameStyle" v-if="quoteToMessage !== null" ref="quote"></Quote>
<div class="spoiler" v-if="showContentWarning" ref="spoiler">
<div class="el-input">
<input type="text" class="el-input__inner" :placeholder="$t('modals.new_toot.cw')" v-model="spoiler" v-shortkey.avoid />
<div class="new-toot">
<el-dialog
:title="$t('modals.new_toot.title')"
:model-value="newTootModal"
@update:model-value="newTootModal = $event"
:before-close="closeConfirm"
width="600px"
custom-class="new-toot-modal"
ref="dialog"
>
<el-form v-on:submit.prevent="toot" role="form">
<Quote :message="quoteToMessage" :displayNameStyle="displayNameStyle" v-if="quoteToMessage !== null" ref="quote"></Quote>
<div class="spoiler" v-if="showContentWarning" ref="spoiler">
<div class="el-input">
<input type="text" class="el-input__inner" :placeholder="$t('modals.new_toot.cw')" v-model="spoiler" />
</div>
</div>
<Status
v-model="status"
:opened="newTootModal"
:fixCursorPos="hashtagInserting"
:height="statusHeight"
@paste="onPaste"
@toot="toot"
@pickerOpened="innerElementOpened"
@suggestOpened="innerElementOpened"
/>
</el-form>
<Poll
v-if="openPoll"
v-model="polls"
@addPoll="addPoll"
@removePoll="removePoll"
:defaultExpire="pollExpire"
@changeExpire="changeExpire"
ref="poll"
></Poll>
<div class="preview" ref="preview">
<div class="image-wrapper" v-for="media in attachedMedias" v-bind:key="media.id">
<img :src="media.preview_url" class="preview-image" />
<el-button type="text" @click="removeAttachment(media)" class="remove-image"><font-awesome-icon icon="circle-xmark" /></el-button>
<textarea
maxlength="420"
class="image-description"
:placeholder="$t('modals.new_toot.description')"
:value="mediaDescriptions[media.id]"
@input="updateDescription(media.id, $event.target.value)"
role="textbox"
contenteditable="true"
aria-multiline="true"
>
</textarea>
</div>
</div>
<Status
v-model="status"
:opened="newTootModal"
:fixCursorPos="hashtagInserting"
:height="statusHeight"
@paste="onPaste"
@toot="toot"
@pickerOpened="innerElementOpened"
@suggestOpened="innerElementOpened"
/>
</el-form>
<Poll
v-if="openPoll"
v-model="polls"
@addPoll="addPoll"
@removePoll="removePoll"
:defaultExpire="pollExpire"
@changeExpire="changeExpire"
ref="poll"
></Poll>
<div class="preview" ref="preview">
<div class="image-wrapper" v-for="media in attachedMedias" v-bind:key="media.id">
<img :src="media.preview_url" class="preview-image" />
<el-button type="text" @click="removeAttachment(media)" class="remove-image"><font-awesome-icon icon="circle-xmark" /></el-button>
<textarea
maxlength="420"
class="image-description"
:placeholder="$t('modals.new_toot.description')"
:value="mediaDescriptions[media.id]"
@input="updateDescription(media.id, $event.target.value)"
v-shortkey.avoid
role="textbox"
contenteditable="true"
aria-multiline="true"
>
</textarea>
</div>
</div>
<div slot="footer" class="dialog-footer">
<div class="upload-image">
<el-button size="default" type="text" @click="selectImage" :title="$t('modals.new_toot.footer.add_image')">
<font-awesome-icon icon="camera" />
</el-button>
<input name="image" type="file" class="image-input" ref="image" @change="onChangeImage" :key="attachedMediaId" />
</div>
<div class="poll">
<el-button size="default" type="text" @click="togglePollForm" :title="$t('modals.new_toot.footer.poll')">
<font-awesome-icon icon="square-poll-horizontal" />
</el-button>
</div>
<div class="privacy">
<el-dropdown trigger="click" @command="changeVisibility">
<el-button size="default" type="text" :title="$t('modals.new_toot.footer.change_visibility')">
<font-awesome-icon :icon="visibilityIcon" />
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="visibilityList.Public.value">
<font-awesome-icon icon="globe" class="privacy-icon" />
{{ $t(visibilityList.Public.name) }}
</el-dropdown-item>
<el-dropdown-item :command="visibilityList.Unlisted.value">
<font-awesome-icon icon="unlock" class="privacy-icon" />
{{ $t(visibilityList.Unlisted.name) }}
</el-dropdown-item>
<el-dropdown-item :command="visibilityList.Private.value">
<font-awesome-icon icon="lock" class="privacy-icon" />
{{ $t(visibilityList.Private.name) }}
</el-dropdown-item>
<el-dropdown-item :command="visibilityList.Direct.value">
<font-awesome-icon icon="envelope" class="privacy-icon" size="sm" />
{{ $t(visibilityList.Direct.name) }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<div class="sensitive" v-show="attachedMedias.length > 0">
<el-button
size="default"
type="text"
@click="changeSensitive"
:title="$t('modals.new_toot.footer.change_sensitive')"
:aria-pressed="sensitive"
>
<font-awesome-icon icon="eye-slash" v-show="!sensitive" />
<font-awesome-icon icon="eye" v-show="sensitive" />
</el-button>
</div>
<div class="content-warning">
<el-button
size="default"
type="text"
@click="toggleContentWarning()"
:title="$t('modals.new_toot.footer.add_cw')"
:class="showContentWarning ? '' : 'clickable'"
:aria-pressed="showContentWarning"
>
<font-awesome-icon icon="eye-slash" />
</el-button>
</div>
<div class="pined-hashtag">
<el-button
size="default"
type="text"
@click="pinedHashtag = !pinedHashtag"
:title="$t('modals.new_toot.footer.pined_hashtag')"
:class="pinedHashtag ? '' : 'clickable'"
:aria-pressed="pinedHashtag"
>
<font-awesome-icon icon="hashtag" />
</el-button>
</div>
<div class="info">
<img src="../../../assets/images/loading-spinner-wide.svg" v-show="loading" class="loading" />
<span class="text-count">{{ tootMax - status.length }}</span>
<template #footer>
<div class="dialog-footer">
<div class="upload-image">
<el-button size="default" type="text" @click="selectImage" :title="$t('modals.new_toot.footer.add_image')">
<font-awesome-icon icon="camera" />
</el-button>
<input name="image" type="file" class="image-input" ref="image" @change="onChangeImage" :key="attachedMediaId" />
</div>
<div class="poll">
<el-button size="default" type="text" @click="togglePollForm" :title="$t('modals.new_toot.footer.poll')">
<font-awesome-icon icon="square-poll-horizontal" />
</el-button>
</div>
<div class="privacy">
<el-dropdown trigger="click" @command="changeVisibility">
<el-button size="default" type="text" :title="$t('modals.new_toot.footer.change_visibility')">
<font-awesome-icon :icon="visibilityIcon" />
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="visibilityList.Public.value">
<font-awesome-icon icon="globe" class="privacy-icon" />
{{ $t(visibilityList.Public.name) }}
</el-dropdown-item>
<el-dropdown-item :command="visibilityList.Unlisted.value">
<font-awesome-icon icon="unlock" class="privacy-icon" />
{{ $t(visibilityList.Unlisted.name) }}
</el-dropdown-item>
<el-dropdown-item :command="visibilityList.Private.value">
<font-awesome-icon icon="lock" class="privacy-icon" />
{{ $t(visibilityList.Private.name) }}
</el-dropdown-item>
<el-dropdown-item :command="visibilityList.Direct.value">
<font-awesome-icon icon="envelope" class="privacy-icon" size="sm" />
{{ $t(visibilityList.Direct.name) }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div class="sensitive" v-show="attachedMedias.length > 0">
<el-button
size="default"
type="text"
@click="changeSensitive"
:title="$t('modals.new_toot.footer.change_sensitive')"
:aria-pressed="sensitive"
>
<font-awesome-icon icon="eye-slash" v-show="!sensitive" />
<font-awesome-icon icon="eye" v-show="sensitive" />
</el-button>
</div>
<div class="content-warning">
<el-button
size="default"
type="text"
@click="toggleContentWarning()"
:title="$t('modals.new_toot.footer.add_cw')"
:class="showContentWarning ? '' : 'clickable'"
:aria-pressed="showContentWarning"
>
<font-awesome-icon icon="eye-slash" />
</el-button>
</div>
<div class="pined-hashtag">
<el-button
size="default"
type="text"
@click="pinedHashtag = !pinedHashtag"
:title="$t('modals.new_toot.footer.pined_hashtag')"
:class="pinedHashtag ? '' : 'clickable'"
:aria-pressed="pinedHashtag"
>
<font-awesome-icon icon="hashtag" />
</el-button>
</div>
<div class="info">
<img src="../../../assets/images/loading-spinner-wide.svg" v-show="loading" class="loading" />
<span class="text-count">{{ tootMax - status.length }}</span>
<el-button class="toot-action" size="small" @click="closeConfirm(close)">{{ $t('modals.new_toot.cancel') }}</el-button>
<el-button class="toot-action" size="small" type="primary" @click="toot" :loading="blockSubmit">{{
$t('modals.new_toot.toot')
}}</el-button>
</div>
<div class="clearfix"></div>
</div>
<resize-observer @notify="handleResize" />
</el-dialog>
<el-button class="toot-action" @click="closeConfirm(close)">{{ $t('modals.new_toot.cancel') }}</el-button>
<el-button class="toot-action" type="primary" @click="toot" :loading="blockSubmit">{{ $t('modals.new_toot.toot') }}</el-button>
</div>
<div class="clearfix"></div>
</div>
</template>
<resize-observer @notify="handleResize" />
</el-dialog>
</div>
</template>
<script>
@ -148,7 +151,7 @@ import Status from './NewToot/Status'
import Poll from './NewToot/Poll'
import Quote from './NewToot/Quote'
import { NewTootTootLength, NewTootAttachLength, NewTootModalOpen, NewTootBlockSubmit, NewTootPollInvalid } from '@/errors/validations'
import { Event } from '~/src/renderer/components/event'
import { EventEmitter } from '~/src/renderer/components/event'
export default {
name: 'new-toot',
@ -238,7 +241,7 @@ export default {
this.$store.dispatch('TimelineSpace/Modals/NewToot/setupLoading')
},
mounted() {
Event.$on('image-uploaded', () => {
EventEmitter.on('image-uploaded', () => {
if (this.$refs.preview) {
this.statusHeight = this.statusHeight - this.$refs.preview.offsetHeight
}
@ -291,7 +294,10 @@ export default {
console.error(err)
if (err instanceof NewTootTootLength) {
this.$message({
message: this.$t('validation.new_toot.toot_length', { min: 1, max: this.tootMax }),
message: this.$t('validation.new_toot.toot_length', {
min: 1,
max: this.tootMax
}),
type: 'error'
})
} else if (err instanceof NewTootAttachLength) {
@ -458,189 +464,190 @@ export default {
event.height - footerHeight - headerHeight - this.$refs.preview.offsetHeight - pollHeight - spoilerHeight - quoteHeight
}
},
innerElementOpened(open) {
if (open) {
this.$refs.dialog.$el.firstChild.style.overflow = 'visible'
} else {
this.$refs.dialog.$el.firstChild.style.overflow = 'hidden'
}
innerElementOpened() {
// if (open) {
// this.$refs.dialog.$el.firstChild.style.overflow = 'visible'
// } else {
// this.$refs.dialog.$el.firstChild.style.overflow = 'hidden'
// }
}
}
}
</script>
<style lang="scss" scoped>
.new-toot-modal /deep/ {
.el-dialog {
.new-toot :deep() {
.new-toot-modal {
background-color: var(--theme-selected-background-color);
overflow: hidden;
resize: both;
padding-bottom: 20px;
max-height: calc(100% - 15vh - 80px);
max-width: 95%;
}
.el-dialog__header {
background-color: #4a5664;
.el-dialog__title {
color: #ebeef5;
}
}
.el-dialog__body {
padding: 0;
.el-input__inner {
background-color: var(--theme-background-color);
color: var(--theme-primary-color);
border: 1px solid var(--theme-border-color);
}
.spoiler {
box-sizing: border-box;
padding: 4px 0;
.el-dialog__header {
background-color: #4a5664;
margin-right: 0;
input {
border-radius: 0;
&::placeholder {
color: #c0c4cc;
}
.el-dialog__title {
color: #ebeef5;
}
}
.preview {
box-sizing: border-box;
display: flex;
flex-direction: row;
flex-wrap: wrap;
.el-dialog__body {
padding: 0;
.image-wrapper {
position: relative;
flex: 1 1 0;
min-width: 10%;
height: 150px;
margin: 4px;
.el-input__inner {
background-color: var(--theme-background-color);
color: var(--theme-primary-color);
border: 1px solid var(--theme-border-color);
}
.preview-image {
width: 100%;
height: 100%;
object-fit: cover;
border: 0;
border-radius: 8px;
}
.spoiler {
box-sizing: border-box;
padding: 4px 0;
background-color: #4a5664;
.image-description {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
box-sizing: border-box;
border: 0;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
background: linear-gradient(0deg, rgba(0, 0, 0, 0.8) 0, rgba(0, 0, 0, 0.35) 80%, transparent);
font-size: var(--font-base-size);
color: #fff;
opacity: 1;
resize: none;
overflow: scroll;
input {
border-radius: 0;
&::placeholder {
color: #c0c4cc;
}
}
}
.remove-image {
position: absolute;
top: 2px;
left: 2px;
padding: 0;
cursor: pointer;
font-size: 1.5rem;
.preview {
box-sizing: border-box;
display: flex;
flex-direction: row;
flex-wrap: wrap;
.fa-icon {
font-size: 0.9rem;
width: auto;
height: 1em;
max-width: 100%;
max-height: 100%;
.image-wrapper {
position: relative;
flex: 1 1 0;
min-width: 10%;
height: 150px;
margin: 4px;
.preview-image {
width: 100%;
height: 100%;
object-fit: cover;
border: 0;
border-radius: 8px;
}
.image-description {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
box-sizing: border-box;
border: 0;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
background: linear-gradient(0deg, rgba(0, 0, 0, 0.8) 0, rgba(0, 0, 0, 0.35) 80%, transparent);
font-size: var(--font-base-size);
color: #fff;
opacity: 1;
resize: none;
overflow: scroll;
&::placeholder {
color: #c0c4cc;
}
}
.remove-image {
position: absolute;
top: 2px;
left: 2px;
padding: 0;
cursor: pointer;
font-size: 1.5rem;
.fa-icon {
font-size: 0.9rem;
width: auto;
height: 1em;
max-width: 100%;
max-height: 100%;
}
}
}
}
}
}
.el-dialog__footer {
background-color: var(--theme-selected-background-color);
font-size: var(--base-font-size);
padding-bottom: 0;
.el-dialog__footer {
background-color: var(--theme-selected-background-color);
font-size: var(--base-font-size);
padding-bottom: 0;
.upload-image {
text-align: left;
float: left;
.upload-image {
text-align: left;
float: left;
.image-input {
display: none;
}
}
.poll {
float: left;
margin-left: 8px;
}
.privacy {
float: left;
margin-left: 8px;
}
.sensitive {
float: left;
margin-left: 8px;
}
.content-warning {
float: left;
margin-left: 8px;
.cw-text {
font-weight: 800;
line-height: 18px;
}
}
.pined-hashtag {
float: left;
margin-left: 8px;
}
.clickable {
color: #909399;
}
.info {
display: flex;
justify-content: flex-end;
align-items: center;
.loading {
width: 18px;
margin-right: 4px;
.image-input {
display: none;
}
}
.text-count {
padding-right: 10px;
.poll {
float: left;
margin-left: 8px;
}
.privacy {
float: left;
margin-left: 8px;
}
.sensitive {
float: left;
margin-left: 8px;
}
.content-warning {
float: left;
margin-left: 8px;
.cw-text {
font-weight: 800;
line-height: 18px;
}
}
.pined-hashtag {
float: left;
margin-left: 8px;
}
.clickable {
color: #909399;
}
}
.toot-action {
font-size: var(--base-font-size);
margin-top: 2px;
margin-bottom: 2px;
.info {
display: flex;
justify-content: flex-end;
align-items: center;
.loading {
width: 18px;
margin-right: 4px;
}
.text-count {
padding-right: 10px;
color: #909399;
}
}
.toot-action {
font-size: var(--base-font-size);
margin-top: 2px;
margin-bottom: 2px;
}
}
}
}

View File

@ -3,14 +3,43 @@
<ul class="poll-list">
<li class="poll-option" v-for="(option, id) in value" v-bind:key="id">
<el-radio :disabled="true" :label="id">
<el-input :placeholder="`choice ${id}`" :value="value[id]" @input="value => updateOption(value, id)" size="small"></el-input>
<el-button class="remove-poll" type="text" @click="removePoll(id)" size="small"><font-awesome-icon icon="xmark" /></el-button>
<el-input
:placeholder="`choice ${id}`"
:model-value="value[id]"
@input="(value) => updateOption(value, id)"
size="small"
></el-input>
<el-button
class="remove-poll"
type="text"
@click="removePoll(id)"
size="small"
><font-awesome-icon icon="xmark"
/></el-button>
</el-radio>
</li>
</ul>
<el-button class="add-poll" type="info" size="small" @click="addPoll" plain>{{ $t('modals.new_toot.poll.add_choice') }}</el-button>
<el-select v-model="expiresIn" size="small" value-key="value" @change="changeExpire">
<el-option v-for="exp in expires" :key="exp.value" :label="exp.label" :value="exp"> </el-option>
<el-button
class="add-poll"
type="info"
size="small"
@click="addPoll"
plain
>{{ $t('modals.new_toot.poll.add_choice') }}</el-button
>
<el-select
v-model="expiresIn"
size="small"
value-key="value"
@change="changeExpire"
>
<el-option
v-for="exp in expires"
:key="exp.value"
:label="exp.label"
:value="exp"
>
</el-option>
</el-select>
</div>
</template>
@ -24,34 +53,34 @@ export default {
expires: [
{
label: this.$t('modals.new_toot.poll.expires.5_minutes'),
value: 60 * 5
value: 60 * 5,
},
{
label: this.$t('modals.new_toot.poll.expires.30_minutes'),
value: 60 * 30
value: 60 * 30,
},
{
label: this.$t('modals.new_toot.poll.expires.1_hour'),
value: 3600
value: 3600,
},
{
label: this.$t('modals.new_toot.poll.expires.6_hours'),
value: 3600 * 6
value: 3600 * 6,
},
{
label: this.$t('modals.new_toot.poll.expires.1_day'),
value: 3600 * 24
value: 3600 * 24,
},
{
label: this.$t('modals.new_toot.poll.expires.3_days'),
value: 3600 * 24 * 3
value: 3600 * 24 * 3,
},
{
label: this.$t('modals.new_toot.poll.expires.7_days'),
value: 3600 * 24 * 7
}
value: 3600 * 24 * 7,
},
],
expiresIn: null
expiresIn: null,
}
},
created() {
@ -65,13 +94,17 @@ export default {
this.$emit('removePoll', id)
},
updateOption(item, index) {
const newValue = [...this.value.slice(0, index), item, ...this.value.slice(index + 1)]
const newValue = [
...this.value.slice(0, index),
item,
...this.value.slice(index + 1),
]
this.$emit('input', newValue)
},
changeExpire(exp) {
this.$emit('changeExpire', exp)
}
}
},
},
}
</script>

View File

@ -96,7 +96,7 @@ export default {
float: left;
width: calc(100% - 52px);
.content-wrapper /deep/ {
.content-wrapper {
font-size: var(--base-font-size);
color: var(--theme-primary-color);
@ -119,11 +119,11 @@ export default {
.content p {
unicode-bidi: plaintext;
}
}
.emojione {
width: 20px;
height: 20px;
}
.content-wrapper :deep(.emojione) {
width: 20px;
height: 20px;
}
.toot-header {
@ -135,14 +135,14 @@ export default {
overflow: hidden;
text-overflow: ellipsis;
.display-name /deep/ {
.display-name {
font-weight: 800;
color: var(--theme-primary-color);
}
.emojione {
max-width: 14px;
max-height: 14px;
}
.display-name :deep(.emojione) {
max-width: 14px;
max-height: 14px;
}
.acct {

View File

@ -3,12 +3,6 @@
<textarea
v-model="status"
ref="status"
v-shortkey="
openSuggest
? { up: ['arrowup'], down: ['arrowdown'], enter: ['enter'], esc: ['esc'] }
: { linux: ['ctrl', 'enter'], mac: ['meta', 'enter'], left: ['arrowleft'], right: ['arrowright'] }
"
@shortkey="handleKey"
@paste="onPaste"
v-on:input="startSuggest"
:placeholder="$t('modals.new_toot.status')"
@ -19,13 +13,12 @@
autofocus
>
</textarea>
<el-popover placement="bottom-start" width="300" trigger="manual" :value="openSuggest" popper-class="suggest-popper">
<el-popover placement="bottom-start" width="300" trigger="manual" v-model:visible="openSuggest" popper-class="suggest-popper">
<ul class="suggest-list">
<li
v-for="(item, index) in filteredSuggestion"
:key="index"
@click="insertItem(item)"
@shortkey="insertItem(item)"
@mouseover="highlightedIndex = index"
:class="{ highlighted: highlightedIndex === index }"
>
@ -38,29 +31,45 @@
{{ item.name }}
</li>
</ul>
<!-- dummy object to open suggest popper -->
<template #reference>
<span></span>
</template>
</el-popover>
<div v-click-outside="hideEmojiPicker">
<el-button type="text" class="emoji-selector" @click="toggleEmojiPicker">
<font-awesome-icon :icon="['far', 'face-smile']" size="lg" />
</el-button>
<div v-if="openEmojiPicker" class="emoji-picker">
<picker set="emojione" :autoFocus="true" :custom="pickerEmojis" @select="selectEmoji" />
</div>
<div>
<el-popover placement="bottom" width="281" trigger="click" popper-class="new-toot-emoji-picker" ref="new_toot_emoji_picker">
<picker
:data="emojiIndex"
set="twitter"
:autoFocus="true"
@select="selectEmoji"
:custom="pickerEmojis"
:perLine="7"
:emojiSize="24"
:showPreview="false"
:emojiTooltip="true"
/>
<template #reference>
<el-button class="emoji-selector" type="text">
<font-awesome-icon :icon="['far', 'face-smile']" size="lg" />
</el-button>
</template>
</el-popover>
</div>
</div>
</template>
<script>
import 'emoji-mart-vue-fast/css/emoji-mart.css'
import data from 'emoji-mart-vue-fast/data/all.json'
import { mapState, mapGetters } from 'vuex'
import { Picker } from 'emoji-mart-vue'
import ClickOutside from 'vue-click-outside'
import { Picker, EmojiIndex } from 'emoji-mart-vue-fast/src'
import suggestText from '@/utils/suggestText'
const emojiIndex = new EmojiIndex(data)
export default {
name: 'status',
directives: {
ClickOutside
},
components: {
Picker
},
@ -84,7 +93,8 @@ export default {
data() {
return {
highlightedIndex: 0,
openEmojiPicker: false
openEmojiPicker: false,
emojiIndex: emojiIndex
}
},
computed: {
@ -125,7 +135,6 @@ export default {
})
} else if (oldState && !newState) {
this.closeSuggest()
this.hideEmojiPicker()
}
}
},
@ -257,12 +266,6 @@ export default {
this.openEmojiPicker = !this.openEmojiPicker
this.$emit('pickerOpened', this.openEmojiPicker)
},
hideEmojiPicker() {
if (this.openEmojiPicker) {
this.$emit('pickerOpened', false)
}
this.openEmojiPicker = false
},
selectEmoji(emoji) {
const current = this.$refs.status.selectionStart
if (emoji.native) {
@ -281,6 +284,37 @@ export default {
.suggest-popper {
background-color: var(--theme-background-color);
border: 1px solid var(--theme-header-menu-color);
.suggest-list {
list-style: none;
padding: 6px 0;
margin: 0;
box-sizing: border-box;
li {
font-size: var(--base-font-size);
padding: 0 20px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
height: 34px;
line-height: 34px;
box-sizing: border-box;
cursor: pointer;
color: var(--theme-regular-color);
.icon {
display: inline-block;
vertical-align: middle;
width: 20px;
height: 20px;
}
}
.highlighted {
background-color: var(--theme-selected-background-color);
}
}
}
</style>
@ -318,37 +352,6 @@ export default {
}
}
.suggest-list {
list-style: none;
padding: 6px 0;
margin: 0;
box-sizing: border-box;
li {
font-size: var(--base-font-size);
padding: 0 20px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
height: 34px;
line-height: 34px;
box-sizing: border-box;
cursor: pointer;
color: var(--theme-regular-color);
.icon {
display: inline-block;
vertical-align: middle;
width: 20px;
height: 20px;
}
}
.highlighted {
background-color: var(--theme-selected-background-color);
}
}
.emoji-selector {
position: absolute;
top: 4px;

View File

@ -1,16 +1,13 @@
<template>
<el-dialog
:title="$t('modals.report.title')"
:visible.sync="reportModal"
width="400px"
custom-class="report"
>
<el-input type="textarea" v-model="comment" :placeholder="$t('modals.report.comment')"></el-input>
<span slot="footer" class="dialog-footer">
<el-button @click="closeModal">{{ $t('modals.report.cancel') }}</el-button>
<el-button type="primary" @click="submit">{{ $t('modals.report.ok') }}</el-button>
</span>
</el-dialog>
<el-dialog :title="$t('modals.report.title')" :model-value="reportModal" width="400px" custom-class="report">
<el-input type="textarea" v-model="comment" :placeholder="$t('modals.report.comment')"></el-input>
<template #footer>
<span class="dialog-footer">
<el-button @click="closeModal">{{ $t('modals.report.cancel') }}</el-button>
<el-button type="primary" @click="submit">{{ $t('modals.report.ok') }}</el-button>
</span>
</template>
</el-dialog>
</template>
<script>
@ -18,7 +15,7 @@ import { mapState } from 'vuex'
export default {
name: 'Report',
data () {
data() {
return {
comment: ''
}
@ -28,19 +25,19 @@ export default {
toot: state => state.message
}),
reportModal: {
get () {
get() {
return this.$store.state.TimelineSpace.Modals.Report.modalOpen
},
set (value) {
set(value) {
this.$store.commit('TimelineSpace/Modals/Report/changeModalOpen', value)
}
}
},
methods: {
closeModal () {
closeModal() {
this.reportModal = false
},
async submit () {
async submit() {
this.closeModal()
await this.$store.dispatch('TimelineSpace/Modals/Report/submit', {
account_id: this.toot.account.id,
@ -52,5 +49,4 @@ export default {
}
</script>
<style lang="scss" scoped>
</style>
<style lang="scss" scoped></style>

Some files were not shown because too many files have changed in this diff Show More