mirror of
https://github.com/h3poteto/whalebird-desktop
synced 2025-01-30 09:04:56 +01:00
refs #921 Run all userstreaming and notify for all accounts
This commit is contained in:
parent
ae56ef451c
commit
80e9e6368c
@ -34,6 +34,7 @@ import Language from '../constants/language'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
import { LocalTag } from '~/src/types/localTag'
|
||||
import { UnreadNotification as UnreadNotificationConfig } from '~/src/types/unreadNotification'
|
||||
import { AccountNotification } from '~/src/types/accountNotification'
|
||||
|
||||
/**
|
||||
* Context menu
|
||||
@ -422,6 +423,65 @@ ipcMain.on('reset-badge', () => {
|
||||
}
|
||||
})
|
||||
|
||||
// user streaming
|
||||
let userStreamings: { [key: string]: StreamingManager | null } = {}
|
||||
|
||||
ipcMain.on('start-all-user-streamings', (event: Event, accounts: Array<LocalAccount>) => {
|
||||
accounts.map(account => {
|
||||
const id: string = account._id!
|
||||
accountManager
|
||||
.getAccount(id)
|
||||
.then(acct => {
|
||||
// Stop old user streaming
|
||||
if (userStreamings[id]) {
|
||||
userStreamings[id]!.stop()
|
||||
userStreamings[id] = null
|
||||
}
|
||||
userStreamings[id] = new StreamingManager(acct, true)
|
||||
userStreamings[id]!.startUser(
|
||||
(update: Status) => {
|
||||
event.sender.send(`update-start-all-user-streamings-${id}`, update)
|
||||
},
|
||||
(notification: Notification) => {
|
||||
const accountNotification: AccountNotification = {
|
||||
id: id,
|
||||
notification: notification
|
||||
}
|
||||
// To notiy badge
|
||||
event.sender.send('notification-start-all-user-streamings', accountNotification)
|
||||
// To update notification timeline
|
||||
event.sender.send(`notification-start-all-user-streamings-${id}`, notification)
|
||||
|
||||
// Does not exist a endpoint for only mention. And mention is a part of notification.
|
||||
// So we have to get mention from notification.
|
||||
if (notification.type === 'mention') {
|
||||
event.sender.send(`mention-start-all-user-streamings-${id}`, notification)
|
||||
}
|
||||
if (process.platform === 'darwin') {
|
||||
app.dock.setBadge('•')
|
||||
}
|
||||
},
|
||||
(id: string) => {
|
||||
event.sender.send(`delete-start-all-user-streamings-${id}`, id)
|
||||
},
|
||||
(err: Error) => {
|
||||
log.error(err)
|
||||
// In macOS, sometimes window is closed (not quit).
|
||||
// When window is closed, we can not send event to webContents; because it is destroyed.
|
||||
// So we have to guard it.
|
||||
if (!event.sender.isDestroyed()) {
|
||||
event.sender.send('error-start-all-user-streamings', err)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(err)
|
||||
event.sender.send('error-start-all-user-streamings', err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// streaming
|
||||
let userStreaming: StreamingManager | null = null
|
||||
|
||||
|
@ -3,21 +3,18 @@
|
||||
<el-menu
|
||||
v-if="!hide"
|
||||
:default-active="activeRoute()"
|
||||
class="el-menu-vertical account-menu"
|
||||
class="el-menu-vertical account-menu"
|
||||
:collapse="true"
|
||||
:router="true"
|
||||
:background-color="themeColor"
|
||||
text-color="#909399"
|
||||
active-text-color="#ffffff"
|
||||
role="menubar">
|
||||
role="menubar"
|
||||
>
|
||||
<el-menu-item :index="`/${account._id}/home`" v-for="(account, index) in accounts" v-bind:key="account._id" role="menuitem">
|
||||
<i v-if="account.avatar === undefined || account.avatar === null || account.avatar === ''" class="el-icon-menu"></i>
|
||||
<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"
|
||||
/>
|
||||
<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">
|
||||
@ -25,11 +22,10 @@
|
||||
<span slot="new">New</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<div :class="hide ? 'space no-global-header':'space with-global-header' ">
|
||||
<div :class="hide ? 'space no-global-header' : 'space with-global-header'">
|
||||
<router-view :key="$route.params.id"></router-view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -50,19 +46,16 @@ export default {
|
||||
themeColor: state => state.App.theme.global_header_color
|
||||
})
|
||||
},
|
||||
created () {
|
||||
created() {
|
||||
this.initialize()
|
||||
},
|
||||
methods: {
|
||||
activeRoute () {
|
||||
activeRoute() {
|
||||
return this.$route.path
|
||||
},
|
||||
async initialize () {
|
||||
await this.$store.dispatch('GlobalHeader/removeShortcutEvents')
|
||||
await this.$store.dispatch('GlobalHeader/loadHide')
|
||||
this.$store.dispatch('GlobalHeader/watchShortcutEvents')
|
||||
async initialize() {
|
||||
try {
|
||||
const accounts = await this.$store.dispatch('GlobalHeader/listAccounts')
|
||||
const accounts = await this.$store.dispatch('GlobalHeader/initLoad')
|
||||
if (this.$route.params.id === undefined) {
|
||||
return this.$router.push({ path: `/${accounts[0]._id}/home` })
|
||||
}
|
||||
|
@ -1,8 +1,14 @@
|
||||
import sanitizeHtml from 'sanitize-html'
|
||||
import { Account, Notification as NotificationType } from 'megalodon'
|
||||
import { ipcRenderer } from 'electron'
|
||||
import router from '@/router'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
import { Module, MutationTree, ActionTree } from 'vuex'
|
||||
import { RootState } from '@/store'
|
||||
import { Notify } from '~/src/types/notify'
|
||||
import { AccountNotification } from '~/src/types/accountNotification'
|
||||
|
||||
declare var Notification: any
|
||||
|
||||
export type GlobalHeaderState = {
|
||||
accounts: Array<LocalAccount>
|
||||
@ -35,6 +41,24 @@ const mutations: MutationTree<GlobalHeaderState> = {
|
||||
}
|
||||
|
||||
const actions: ActionTree<GlobalHeaderState, RootState> = {
|
||||
initLoad: async ({ dispatch }): Promise<Array<LocalAccount>> => {
|
||||
// Ignore error
|
||||
try {
|
||||
await dispatch('removeShortcutEvents')
|
||||
await dispatch('loadHide')
|
||||
dispatch('watchShortcutEvents')
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
const accounts = await dispatch('listAccounts')
|
||||
try {
|
||||
dispatch('bindUserStreamingsForNotify')
|
||||
dispatch('startUserStreamings')
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
return accounts
|
||||
},
|
||||
listAccounts: ({ dispatch, commit }): Promise<Array<LocalAccount>> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
ipcRenderer.send('list-accounts', 'list')
|
||||
@ -104,6 +128,30 @@ const actions: ActionTree<GlobalHeaderState, RootState> = {
|
||||
resolve(true)
|
||||
})
|
||||
})
|
||||
},
|
||||
startUserStreamings: ({ state }): Promise<{}> => {
|
||||
// @ts-ignore
|
||||
return new Promise((resolve, reject) => {
|
||||
ipcRenderer.once('error-start-all-user-streamings', (_, err: Error) => {
|
||||
reject(err)
|
||||
})
|
||||
ipcRenderer.send('start-all-user-streamings', state.accounts)
|
||||
})
|
||||
},
|
||||
bindUserStreamingsForNotify: ({ rootState }) => {
|
||||
ipcRenderer.on('notification-start-all-user-streamings', (_, accountNotification: AccountNotification) => {
|
||||
const { id, notification } = accountNotification
|
||||
let notify = createNotification(notification, rootState.App.notify as Notify)
|
||||
if (notify) {
|
||||
notify.onclick = () => {
|
||||
router.push(`/${id}/notifications`)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
unbindUserStreamings: () => {
|
||||
ipcRenderer.removeAllListeners('notification-start-all-user-streamings')
|
||||
ipcRenderer.removeAllListeners('error-start-all-user-streamings')
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,3 +163,48 @@ const GlobalHeader: Module<GlobalHeaderState, RootState> = {
|
||||
}
|
||||
|
||||
export default GlobalHeader
|
||||
|
||||
function createNotification(notification: NotificationType, notifyConfig: Notify) {
|
||||
switch (notification.type) {
|
||||
case 'favourite':
|
||||
if (notifyConfig.favourite) {
|
||||
return new Notification('Favourite', {
|
||||
body: `${username(notification.account)} favourited your status`
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'follow':
|
||||
if (notifyConfig.follow) {
|
||||
return new Notification('Follow', {
|
||||
body: `${username(notification.account)} is now following you`
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'mention':
|
||||
if (notifyConfig.reply) {
|
||||
// Clean html tags
|
||||
return new Notification(`${notification.status!.account.display_name}`, {
|
||||
body: sanitizeHtml(notification.status!.content, {
|
||||
allowedTags: [],
|
||||
allowedAttributes: []
|
||||
})
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'reblog':
|
||||
if (notifyConfig.reblog) {
|
||||
return new Notification('Reblog', {
|
||||
body: `${username(notification.account)} boosted your status`
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function username(account: Account) {
|
||||
if (account.display_name !== '') {
|
||||
return account.display_name
|
||||
} else {
|
||||
return account.username
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,17 @@
|
||||
import sanitizeHtml from 'sanitize-html'
|
||||
import { ipcRenderer } from 'electron'
|
||||
import Mastodon, { Account, Emoji, Instance, Status, Notification as NotificationType } from 'megalodon'
|
||||
import SideMenu, { SideMenuState } from './TimelineSpace/SideMenu'
|
||||
import HeaderMenu, { HeaderMenuState } from './TimelineSpace/HeaderMenu'
|
||||
import Modals, { ModalsModuleState } from './TimelineSpace/Modals'
|
||||
import Contents, { ContentsModuleState } from './TimelineSpace/Contents'
|
||||
import router from '@/router'
|
||||
import unreadSettings from '~/src/constants/unreadNotification'
|
||||
import { Module, MutationTree, ActionTree } from 'vuex'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
import { Notify } from '~/src/types/notify'
|
||||
import { RootState } from '@/store'
|
||||
import { UnreadNotification } from '~/src/types/unreadNotification'
|
||||
import { AccountLoadError } from '@/errors/load'
|
||||
import { TimelineFetchError } from '@/errors/fetch'
|
||||
|
||||
declare var Notification: any
|
||||
|
||||
type MyEmoji = {
|
||||
name: string
|
||||
image: string
|
||||
@ -121,8 +116,8 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
|
||||
throw new TimelineFetchError()
|
||||
})
|
||||
await dispatch('unbindStreamings')
|
||||
await dispatch('bindStreamings', account)
|
||||
dispatch('startStreamings', account)
|
||||
await dispatch('bindStreamings')
|
||||
dispatch('startStreamings')
|
||||
dispatch('fetchEmojis', account)
|
||||
dispatch('fetchInstance', account)
|
||||
return account
|
||||
@ -266,8 +261,8 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
|
||||
commit('TimelineSpace/Contents/Public/clearTimeline', {}, { root: true })
|
||||
commit('TimelineSpace/Contents/Mentions/clearMentions', {}, { root: true })
|
||||
},
|
||||
bindStreamings: ({ dispatch, state }, account: LocalAccount) => {
|
||||
dispatch('bindUserStreaming', account)
|
||||
bindStreamings: ({ dispatch, state }) => {
|
||||
dispatch('bindUserStreaming')
|
||||
if (state.unreadNotification.direct) {
|
||||
dispatch('bindDirectMessagesStreaming')
|
||||
}
|
||||
@ -279,7 +274,6 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
|
||||
}
|
||||
},
|
||||
startStreamings: ({ dispatch, state }) => {
|
||||
dispatch('startUserStreaming')
|
||||
if (state.unreadNotification.direct) {
|
||||
dispatch('startDirectMessagesStreaming')
|
||||
}
|
||||
@ -291,7 +285,6 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
|
||||
}
|
||||
},
|
||||
stopStreamings: ({ dispatch }) => {
|
||||
dispatch('stopUserStreaming')
|
||||
dispatch('stopDirectMessagesStreaming')
|
||||
dispatch('stopLocalStreaming')
|
||||
dispatch('stopPublicStreaming')
|
||||
@ -305,8 +298,8 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
|
||||
// ------------------------------------------------
|
||||
// Each streaming methods
|
||||
// ------------------------------------------------
|
||||
bindUserStreaming: ({ commit, rootState }, account: LocalAccount) => {
|
||||
ipcRenderer.on('update-start-user-streaming', (_, update: Status) => {
|
||||
bindUserStreaming: ({ commit, state, rootState }) => {
|
||||
ipcRenderer.on(`update-start-all-user-streamings-${state.account._id!}`, (_, update: Status) => {
|
||||
commit('TimelineSpace/Contents/Home/appendTimeline', update, { root: true })
|
||||
// Sometimes archive old statuses
|
||||
if (rootState.TimelineSpace.Contents.Home.heading && Math.random() > 0.8) {
|
||||
@ -314,45 +307,39 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
|
||||
}
|
||||
commit('TimelineSpace/SideMenu/changeUnreadHomeTimeline', true, { root: true })
|
||||
})
|
||||
ipcRenderer.on('notification-start-user-streaming', (_, notification: NotificationType) => {
|
||||
let notify = createNotification(notification, rootState.App.notify as Notify)
|
||||
if (notify) {
|
||||
notify.onclick = () => {
|
||||
router.push(`/${account._id}/notifications`)
|
||||
}
|
||||
}
|
||||
ipcRenderer.on(`notification-start-all-user-streamings-${state.account._id!}`, (_, notification: NotificationType) => {
|
||||
commit('TimelineSpace/Contents/Notifications/appendNotifications', notification, { root: true })
|
||||
if (rootState.TimelineSpace.Contents.Notifications.heading && Math.random() > 0.8) {
|
||||
commit('TimelineSpace/Contents/Notifications/archiveNotifications', null, { root: true })
|
||||
}
|
||||
commit('TimelineSpace/SideMenu/changeUnreadNotifications', true, { root: true })
|
||||
})
|
||||
ipcRenderer.on('mention-start-user-streaming', (_, mention: NotificationType) => {
|
||||
ipcRenderer.on(`mention-start-all-user-streamings-${state.account._id!}`, (_, mention: NotificationType) => {
|
||||
commit('TimelineSpace/Contents/Mentions/appendMentions', mention, { root: true })
|
||||
if (rootState.TimelineSpace.Contents.Mentions.heading && Math.random() > 0.8) {
|
||||
commit('TimelineSpace/Contents/Mentions/archiveMentions', null, { root: true })
|
||||
}
|
||||
commit('TimelineSpace/SideMenu/changeUnreadMentions', true, { root: true })
|
||||
})
|
||||
ipcRenderer.on('delete-start-user-streaming', (_, id: string) => {
|
||||
ipcRenderer.on(`delete-start-all-user-streamings-${state.account._id!}`, (_, id: string) => {
|
||||
commit('TimelineSpace/Contents/Home/deleteToot', id, { root: true })
|
||||
commit('TimelineSpace/Contents/Notifications/deleteToot', id, { root: true })
|
||||
commit('TimelineSpace/Contents/Mentions/deleteToot', id, { root: true })
|
||||
})
|
||||
},
|
||||
startUserStreaming: ({ state }): Promise<{}> => {
|
||||
// @ts-ignore
|
||||
return new Promise((resolve, reject) => {
|
||||
// eslint-disable-line no-unused-vars
|
||||
ipcRenderer.send('start-user-streaming', {
|
||||
account: state.account,
|
||||
useWebsocket: state.useWebsocket
|
||||
})
|
||||
ipcRenderer.once('error-start-user-streaming', (_, err: Error) => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
},
|
||||
// startUserStreaming: ({ state }): Promise<{}> => {
|
||||
// // @ts-ignore
|
||||
// return new Promise((resolve, reject) => {
|
||||
// // eslint-disable-line no-unused-vars
|
||||
// ipcRenderer.send('start-user-streaming', {
|
||||
// account: state.account,
|
||||
// useWebsocket: state.useWebsocket
|
||||
// })
|
||||
// ipcRenderer.once('error-start-user-streaming', (_, err: Error) => {
|
||||
// reject(err)
|
||||
// })
|
||||
// })
|
||||
// },
|
||||
bindLocalStreaming: ({ commit, rootState }) => {
|
||||
ipcRenderer.on('update-start-local-streaming', (_, update: Status) => {
|
||||
commit('TimelineSpace/Contents/Local/appendTimeline', update, { root: true })
|
||||
@ -428,16 +415,15 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
|
||||
})
|
||||
})
|
||||
},
|
||||
unbindUserStreaming: () => {
|
||||
ipcRenderer.removeAllListeners('update-start-user-streaming')
|
||||
ipcRenderer.removeAllListeners('mention-start-user-streaming')
|
||||
ipcRenderer.removeAllListeners('notification-start-user-streaming')
|
||||
ipcRenderer.removeAllListeners('delete-start-user-streaming')
|
||||
ipcRenderer.removeAllListeners('error-start-user-streaming')
|
||||
},
|
||||
stopUserStreaming: () => {
|
||||
ipcRenderer.send('stop-user-streaming')
|
||||
unbindUserStreaming: ({ state }) => {
|
||||
ipcRenderer.removeAllListeners(`update-start-all-user-streamings-${state.account._id!}`)
|
||||
ipcRenderer.removeAllListeners(`mention-start-all-user-streamings-${state.account._id!}`)
|
||||
ipcRenderer.removeAllListeners(`notification-start-all-user-streamings-${state.account._id!}`)
|
||||
ipcRenderer.removeAllListeners(`delete-start-all-user-streamings-${state.account._id!}`)
|
||||
},
|
||||
// stopUserStreaming: () => {
|
||||
// ipcRenderer.send('stop-user-streaming')
|
||||
// },
|
||||
unbindLocalStreaming: () => {
|
||||
ipcRenderer.removeAllListeners('error-start-local-streaming')
|
||||
ipcRenderer.removeAllListeners('update-start-local-streaming')
|
||||
@ -502,48 +488,3 @@ const TimelineSpace: Module<TimelineSpaceState, RootState> = {
|
||||
}
|
||||
|
||||
export default TimelineSpace
|
||||
|
||||
function createNotification(notification: NotificationType, notifyConfig: Notify) {
|
||||
switch (notification.type) {
|
||||
case 'favourite':
|
||||
if (notifyConfig.favourite) {
|
||||
return new Notification('Favourite', {
|
||||
body: `${username(notification.account)} favourited your status`
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'follow':
|
||||
if (notifyConfig.follow) {
|
||||
return new Notification('Follow', {
|
||||
body: `${username(notification.account)} is now following you`
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'mention':
|
||||
if (notifyConfig.reply) {
|
||||
// Clean html tags
|
||||
return new Notification(`${notification.status!.account.display_name}`, {
|
||||
body: sanitizeHtml(notification.status!.content, {
|
||||
allowedTags: [],
|
||||
allowedAttributes: []
|
||||
})
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'reblog':
|
||||
if (notifyConfig.reblog) {
|
||||
return new Notification('Reblog', {
|
||||
body: `${username(notification.account)} boosted your status`
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function username(account: Account) {
|
||||
if (account.display_name !== '') {
|
||||
return account.display_name
|
||||
} else {
|
||||
return account.username
|
||||
}
|
||||
}
|
||||
|
6
src/types/accountNotification.ts
Normal file
6
src/types/accountNotification.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { Notification } from 'megalodon'
|
||||
|
||||
export type AccountNotification = {
|
||||
id: string
|
||||
notification: Notification
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user