Skip to content

Commit

Permalink
Move fetch to hook for no reason
Browse files Browse the repository at this point in the history
  • Loading branch information
kadiryazici committed Mar 17, 2024
1 parent dadbd45 commit 516e807
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 97 deletions.
80 changes: 73 additions & 7 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<script setup lang="ts">
import { watchEffect } from 'vue'
import { onMounted, watchEffect } from 'vue'
import { open } from '@tauri-apps/api/shell'
import { invoke } from '@tauri-apps/api'
import { sendNotification } from '@tauri-apps/api/notification'
import AppScroller from './components/AppScroller.vue'
import AppSidebar from './components/AppSidebar.vue'
import HomePage from './pages/HomePage.vue'
import SettingsPage from './pages/SettingsPage.vue'
import { ColorPreference, FETCH_INTERVAL_DURATION } from './constants'
import { ColorPreference, FETCH_INTERVAL_DURATION, InvokeCommand } from './constants'
import { useStore } from './stores/store'
import LandingPage from './pages/LandingPage.vue'
import { useInterval } from './composables/useInterval'
Expand All @@ -15,18 +17,19 @@ import { useTheme } from './composables/useTheme'
import { Page, useRoute } from './composables/useRoute'
import { useContextMenu } from './composables/useContextMenu'
import { useAppHooks } from './composables/useAppHooks'
import { isRepository, isThread } from './utils/notification'
import { filterNewNotifications, isRepository, isThread, toNotificationList } from './utils/notification'
import { createGithubWebURL } from './utils/github'
import type { MinimalRepository, Thread } from './api/notifications'
import { markNotificationAsRead, unsubscribeNotification } from './api/notifications'
import { getNotifications, markNotificationAsRead, unsubscribeNotification } from './api/notifications'
const store = useStore()
const { currentPage } = useRoute()
const contextmenu = useContextMenu()
const { onOpen, onUnsubscribe, onMarkAsRead, emitMarkAsRead, onRefetch, emitRefetch } = useAppHooks()
useInterval(() => {
if (AppStorage.get('accessToken') && AppStorage.get('user')) {
store.fetchNotifications()
emitRefetch(false)
}
}, FETCH_INTERVAL_DURATION)
Expand Down Expand Up @@ -58,8 +61,6 @@ function getThreadsToProcess(target: Thread | MinimalRepository) {
return threads
}
const { onOpen, onUnsubscribe, onMarkAsRead, emitMarkAsRead } = useAppHooks()
onMarkAsRead((target) => {
let threads = [] as Thread[]
Expand Down Expand Up @@ -108,6 +109,71 @@ onUnsubscribe((target) => {
emitMarkAsRead(threads)
})
onRefetch(async (withSkeletons) => {
if (store.loadingNotifications) {
return
}
const accessToken = AppStorage.get('accessToken')
if (accessToken == null) {
return
}
const previousThreads = store.notifications.filter(isThread)
if (withSkeletons) {
store.skeletonVisible = true
store.notifications = []
}
store.loadingNotifications = true
store.failedLoadingNotifications = false
try {
const { data } = await getNotifications({
accessToken,
showOnlyParticipating: AppStorage.get('showOnlyParticipating'),
showReadNotifications: AppStorage.get('showReadNotifications'),
})
const threadSet = new Set(data.map(thread => thread.id))
store.checkedItems = store.checkedItems.filter(thread => threadSet.has(thread.id))
store.notifications = toNotificationList(data)
}
catch (error) {
store.notifications = []
store.failedLoadingNotifications = true
store.checkedItems = []
}
store.loadingNotifications = false
store.skeletonVisible = false
const newNotifications = filterNewNotifications(previousThreads, store.notifications.filter(isThread))
if (newNotifications.length > 0) {
if (AppStorage.get('soundsEnabled')) {
invoke(InvokeCommand.PlayNotificationSound)
}
if (AppStorage.get('showSystemNotifications')) {
sendNotification({
title: newNotifications[0].repository.full_name,
body: newNotifications[0].subject.title,
})
}
}
})
const token = AppStorage.get('accessToken')
const user = AppStorage.get('user')
if (token && user) {
emitRefetch(true)
}
</script>

<template>
Expand Down
7 changes: 5 additions & 2 deletions src/components/AppSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { AppStorage } from '../storage'
import { useKey } from '../composables/useKey'
import { useI18n } from '../composables/useI18n'
import { Page, useRoute } from '../composables/useRoute'
import { useAppHooks } from '../composables/useAppHooks'
import { Icons } from './Icons'
import SidebarButton from './SidebarButton.vue'
import Popover from './Popover.vue'
Expand Down Expand Up @@ -47,8 +48,10 @@ const moreItems = computed(() => [
}),
])
const { emitRefetch } = useAppHooks()
useKey('r', () => {
store.fetchNotifications(true)
emitRefetch(true)
}, { source: () => route.currentPage.value === Page.Home })
const morePopover = ref<InstanceType<typeof Popover> | null>(null)
Expand Down Expand Up @@ -116,7 +119,7 @@ useKey('.', () => {
<template #default>
<SidebarButton
:disabled="route.currentPage.value !== Page.Home"
@click="store.fetchNotifications(true)"
@click="emitRefetch(true)"
>
<Icons.Sync16 />
</SidebarButton>
Expand Down
2 changes: 1 addition & 1 deletion src/composables/useAppHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useCustomHook } from './useCustomHook'
export const useAppHooks = singleton(() => {
const [onMarkAsRead, emitMarkAsRead] = useCustomHook<[target: MinimalRepository | Thread | Thread[]]>()
const [onOpen, emitOpen] = useCustomHook<[target: MinimalRepository | Thread]>()
const [onRefetch, emitRefetch] = useCustomHook()
const [onRefetch, emitRefetch] = useCustomHook<[withSkeletons: boolean]>()
const [onUnsubscribe, emitUnsubscribe] = useCustomHook<[target: MinimalRepository | Thread]>()

return {
Expand Down
9 changes: 4 additions & 5 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ async function main() {
const token = AppStorage.get('accessToken')
const user = AppStorage.get('user')

if (token && user) {
route.go(Page.Home)
}

const [autoStartEnabled, notificationsGranted] = await Promise.all([isAutostartEnabled(), isPermissionGranted()])

AppStorage.set('openAtStartup', autoStartEnabled)
Expand All @@ -51,11 +55,6 @@ async function main() {
AppStorage.set('showSystemNotifications', false)
}

if (token && user) {
route.go(Page.Home)
store.fetchNotifications(true)
}

try {
const { shouldUpdate, manifest } = await checkUpdate()

Expand Down
11 changes: 3 additions & 8 deletions src/pages/HomePage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ import { useAppHooks } from '../composables/useAppHooks'
const store = useStore()
const route = useRoute()
const { t } = useI18n()
const { emitRefetch } = useAppHooks()
if (route.state.value.fetchOnEnter) {
store.fetchNotifications(true)
emitRefetch(true)
}
const { emitOpen, emitUnsubscribe, emitMarkAsRead } = useAppHooks()
Expand Down Expand Up @@ -166,12 +167,6 @@ function createContextmenuItems(item: Thread | MinimalRepository): ItemRenderLis
]
}
// Edge-Case
// If user reloaded in the middle of selecting notifications, clear the selection
whenever(() => store.skeletonVisible, () => {
store.checkedItems = []
})
function handleClickRepo(repo: MinimalRepository) {
open(`https://github.com/${repo.full_name}`)
}
Expand All @@ -191,7 +186,7 @@ function handleClickRepo(repo: MinimalRepository) {
:description="t.oopsieCouldntLoad"
>
<template #footer>
<AppButton @click="store.fetchNotifications(true)">
<AppButton @click="emitRefetch(true)">
{{ t.refresh }}
</AppButton>
</template>
Expand Down
5 changes: 3 additions & 2 deletions src/pages/LandingPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ import { useTimeoutPool } from '../composables/useTimeoutPool'
import { getServerPort } from '../api/app'
import { useI18n } from '../composables/useI18n'
import { Page, useRoute } from '../composables/useRoute'
import { useAppHooks } from '../composables/useAppHooks'
const store = useStore()
const route = useRoute()
const processing = ref(true)
const { t } = useI18n()
const { emitRefetch } = useAppHooks()
useTauriEvent<string>('code', async ({ payload }) => {
if (processing.value) {
Expand All @@ -41,7 +42,7 @@ useTauriEvent<string>('code', async ({ payload }) => {
AppStorage.set('user', user)
invoke(InvokeCommand.StopServer)
route.go(Page.Home)
store.fetchNotifications(true)
emitRefetch(true)
}
finally {
processing.value = false
Expand Down
67 changes: 1 addition & 66 deletions src/stores/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { type MinimalRepository, type Thread, getNotifications, markNotification
import { CheckedNotificationProcess, InvokeCommand, notificationApiMutex } from '../constants'
import { AppStorage } from '../storage'
import type { NotificationList, Option } from '../types'
import { filterNewThreads, isRepository, isThread, toNotificationList } from '../utils/notification'
import { isRepository, isThread, toNotificationList } from '../utils/notification'
import { everySome } from '../utils/array'
import { Page, useRoute } from '../composables/useRoute'

Expand All @@ -32,72 +32,8 @@ export const useStore = defineStore('store', () => {
}
}

let threadsRaw: Thread[] = []
let threadsPreviousRaw: Thread[] = []

const checkedItems = ref<Thread[]>([])

async function fetchNotifications(withSkeletons = false) {
if (loadingNotifications.value) {
return
}

const accessToken = AppStorage.get('accessToken')

if (accessToken == null) {
return
}

if (withSkeletons) {
skeletonVisible.value = true
notifications.value = []
}

loadingNotifications.value = true
failedLoadingNotifications.value = false

try {
const checkedThreads = checkedItems.value

const { data } = await getNotifications({
accessToken,
showOnlyParticipating: AppStorage.get('showOnlyParticipating'),
showReadNotifications: AppStorage.get('showReadNotifications'),
})

threadsPreviousRaw = threadsRaw
threadsRaw = data

notifications.value = toNotificationList(data)
checkedItems.value = checkedThreads.filter(checkedItem => (
threadsRaw.some(thread => thread.id === checkedItem.id)
))
}
catch (error) {
notifications.value = []
failedLoadingNotifications.value = true
checkedItems.value = []
}

loadingNotifications.value = false
skeletonVisible.value = false

const newNotifications = filterNewThreads(threadsRaw, threadsPreviousRaw)

if (newNotifications.length > 0) {
if (AppStorage.get('soundsEnabled')) {
invoke(InvokeCommand.PlayNotificationSound)
}

if (AppStorage.get('showSystemNotifications')) {
sendNotification({
title: newNotifications[0].repository.full_name,
body: newNotifications[0].subject.title,
})
}
}
}

const route = useRoute()

function logout() {
Expand Down Expand Up @@ -303,7 +239,6 @@ export const useStore = defineStore('store', () => {
runWithSnapshot,
updateAndRestart,
removeNotificationById,
fetchNotifications,
processCheckedNotifications,
logout,
}
Expand Down
15 changes: 9 additions & 6 deletions src/utils/notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@ export function isRepository(value: any): value is MinimalRepository {
return isObject<MinimalRepository>(value) && 'teams_url' in value
}

export function filterNewThreads(newThreads: Thread[], previousThreads: Thread[]) {
const newUnread = newThreads.filter(t => t.unread)
const previousUnread = previousThreads.filter(t => t.unread)

return newUnread.filter(t => t.unread)
.filter(thread => !previousUnread.some(pThread => pThread.id === thread.id))
export function filterNewNotifications(previousThreads: Thread[], newThreads: Thread[]) {
const newUnreadThreads = newThreads.filter(thread => thread.unread)
const previousUnreadThreadIds = new Set(
previousThreads
.filter(thread => thread.unread)
.map(thread => thread.id),
)

return newUnreadThreads.filter(thread => !previousUnreadThreadIds.has(thread.id))
}

0 comments on commit 516e807

Please sign in to comment.