Skip to content

Commit facaaca

Browse files
authored
Add contextmenu to notifications & Remove BottomBar (#24)
1 parent 6d20c22 commit facaaca

File tree

12 files changed

+459
-298
lines changed

12 files changed

+459
-298
lines changed

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"tauri-plugin-autostart-api": "github:tauri-apps/tauri-plugin-autostart",
2323
"tauri-plugin-store-api": "github:tauri-apps/tauri-plugin-store",
2424
"vue": "^3.3.4",
25-
"vue-selectable-items": "^1.0.0",
25+
"vue-selectable-items": "^1.0.1",
2626
"wowerlay": "1.0.0-beta.10"
2727
},
2828
"files": [
@@ -32,15 +32,16 @@
3232
"devDependencies": {
3333
"@actions/core": "^1.10.0",
3434
"@actions/github": "^5.1.1",
35-
"@antfu/eslint-config": "^0.34.1",
35+
"@antfu/eslint-config": "^0.39.5",
3636
"@iconify-json/octicon": "^1.1.30",
3737
"@iconify-json/ph": "^1.1.4",
3838
"@tauri-apps/cli": "^1.2.2",
3939
"@types/node": "^18.7.10",
4040
"@vitejs/plugin-vue": "^4.0.0",
4141
"@vue/devtools": "^6.5.0",
4242
"async-mutex": "^0.4.0",
43-
"eslint": "^8.32.0",
43+
"eslint": "^8.38.0",
44+
"eslint-plugin-unicorn": "^47.0.0",
4445
"execa": "^6.1.0",
4546
"focus-visible": "^5.2.0",
4647
"hotkeys-js": "^3.10.1",

pnpm-lock.yaml

Lines changed: 266 additions & 216 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/assets/main.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
--white-faded: rgb(190, 190, 190);
1111
--white: rgb(255, 255, 255);
12+
--gray-bright: rgb(150, 150, 150);
13+
--gray: rgb(95, 95, 95);
1214
--content-bg: rgba(30, 30, 30, .8);
1315
--page-header-bg: rgba(43, 43, 43, .6);
1416
--popover-bg: rgba(40, 40, 40, .6);

src/components/AppSidebar.vue

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
import { open } from '@tauri-apps/api/shell'
33
import { exit } from '@tauri-apps/api/process'
44
import { computed } from 'vue'
5-
import { type ItemRenderList } from 'vue-selectable-items'
65
import { Page, REPO_LINK, REPO_RELEASES_LINK } from '../constants'
76
import { useStore } from '../stores/store'
87
import { AppStorage } from '../storage'
8+
import { useKey } from '../composables/useKey'
99
import { Icons } from './Icons'
1010
import SidebarButton from './SidebarButton.vue'
1111
import Popover from './Popover.vue'
@@ -40,7 +40,11 @@ const moreItems = computed(() => [
4040
meta: { text: 'Exit app', icon: Icons.X },
4141
onSelect: () => exit(0),
4242
}),
43-
] as ItemRenderList)
43+
])
44+
45+
useKey('r', () => {
46+
store.fetchNotifications(true)
47+
}, { source: () => store.currentPage === Page.Home })
4448
</script>
4549

4650
<template>
@@ -103,7 +107,7 @@ const moreItems = computed(() => [
103107
<Tooltip
104108
:target="el"
105109
position="right"
106-
text="Reload notifications"
110+
text="Reload notifications (R)"
107111
/>
108112
</template>
109113
</SlotRef>

src/components/Icons.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ import SignOutIcon16 from 'virtual:icons/octicon/sign-out-16'
2828
import ChevronLeftIcon from 'virtual:icons/octicon/chevron-left'
2929
import DownloadIcon16 from 'virtual:icons/octicon/download-16'
3030
import BellSlashIcon from 'virtual:icons/octicon/bell-slash-24'
31+
import BellSlashIcon16 from 'virtual:icons/octicon/bell-slash-16'
32+
import MailIcon16 from 'virtual:icons/octicon/mail-16'
33+
import LinkExternalIcon16 from 'virtual:icons/octicon/link-external-16'
34+
import SquareIcon16 from 'virtual:icons/octicon/square-16'
35+
import CircleIcon16 from 'virtual:icons/octicon/Circle-16'
3136

3237
import CommandIcon from 'virtual:icons/ph/command'
3338

@@ -53,6 +58,7 @@ export const Icons = {
5358
Alert: markRaw(AlertIcon),
5459
Question: markRaw(QuestionIcon),
5560
Mail: markRaw(MailIcon),
61+
Mail16: markRaw(MailIcon16),
5662
SignOut16: markRaw(SignOutIcon16),
5763
Bell16: markRaw(BellIcon16),
5864
Gear16: markRaw(GearIcon16),
@@ -61,6 +67,10 @@ export const Icons = {
6167
Download16: markRaw(DownloadIcon16),
6268
Command: markRaw(CommandIcon),
6369
BellSlash: markRaw(BellSlashIcon),
70+
BellSlash16: markRaw(BellSlashIcon16),
6471
Info16: markRaw(InfoIcon16),
6572
Check16: markRaw(CheckIcon16),
73+
LinkExternal16: markRaw(LinkExternalIcon16),
74+
Square16: markRaw(SquareIcon16),
75+
Circle16: markRaw(CircleIcon16),
6676
}

src/components/MenuItems.vue

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<script lang="ts">
22
import {
33
type Context,
4-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
54
type Item,
65
type ItemRenderList, SelectableItems, createItemDefaults, item,
76
} from 'vue-selectable-items'
@@ -12,6 +11,7 @@ import { usePopoverContext } from './Popover.vue'
1211
export interface ItemMeta {
1312
text: string
1413
icon: typeof Icons[keyof typeof Icons]
14+
key?: string
1515
}
1616
1717
export const menuItem = item<ItemMeta>
@@ -35,7 +35,7 @@ defineOptions({
3535
inheritAttrs: false,
3636
})
3737
38-
const setupHandle = (ctx: Context) => {
38+
function setupHandle(ctx: Context) {
3939
useKey('up,shift+tab', () => ctx.focusPrevious(), { input: true, repeat: true, prevent: true })
4040
useKey('down,tab', () => ctx.focusNext(), { input: true, repeat: true, prevent: true })
4141
@@ -71,6 +71,12 @@ const setupHandle = (ctx: Context) => {
7171
<div class="item-text">
7272
{{ meta!.text }}
7373
</div>
74+
<span
75+
v-if="meta?.key"
76+
class="item-key"
77+
>
78+
{{ meta.key }}
79+
</span>
7480
</template>
7581
</SelectableItems>
7682
</template>
@@ -99,16 +105,33 @@ const setupHandle = (ctx: Context) => {
99105
color: var(--white);
100106
}
101107
108+
&-disabled {
109+
color: var(--gray);
110+
}
111+
102112
.item-text {
103113
font-size: 13px;
104114
vertical-align: middle;
105115
color: currentColor;
116+
width: 100%;
106117
}
107118
108119
.item-icon {
120+
flex-shrink: 0;
109121
margin-right: 10px;
110122
font-size: 14px;
111123
color: currentColor;
124+
125+
:deep(.icon) {
126+
max-width: 17px;
127+
max-height: 17px;
128+
}
129+
}
130+
131+
.item-key {
132+
color: var(--gray-bright);
133+
font-size: 10px;
134+
margin-left: 10px;
112135
}
113136
}
114137
}

src/components/NotificationItem.vue

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@ import type { NotificationList } from '../types'
55
import { formatReason, isRepository, isThread, notificationSubjectIcon } from '../utils/notification'
66
import Separator from './Separator.vue'
77
8-
interface Emits {
9-
(e: 'click:notification', notification: Thread): void
10-
(e: 'click:repo', repoFullName: string): void
11-
(e: 'update:checked', value: boolean): void
12-
}
13-
148
interface Props {
159
value: NotificationList[number]
1610
checked?: boolean
1711
checkable?: boolean
12+
checkboxVisible?: boolean
13+
}
14+
15+
interface Emits {
16+
(e: 'click:notification', notification: Thread): void
17+
(e: 'click:repo', repoFullName: string): void
18+
(e: 'update:checked', value: boolean): void
19+
(e: 'contextmenu', value: Thread, event: MouseEvent): void
1820
}
1921
2022
const props = withDefaults(defineProps<Props>(), {
@@ -62,6 +64,7 @@ function handleRepoClick(repo: MinimalRepository, event: MouseEvent | KeyboardEv
6264
@click="(event) => isRepository(value) && handleRepoClick(value, event)"
6365
>
6466
<img
67+
draggable="false"
6568
class="notification-title-icon"
6669
:src="value.owner.avatar_url"
6770
alt="repo logo"
@@ -75,7 +78,7 @@ function handleRepoClick(repo: MinimalRepository, event: MouseEvent | KeyboardEv
7578
class="notification-checkbox-wrapper"
7679
>
7780
<span
78-
:class="{ 'notification-checkbox-checked': checked }"
81+
:class="{ 'notification-checkbox-checked': checked, 'notification-checkbox-visible': checkboxVisible }"
7982
role="checkbox"
8083
tabindex="0"
8184
aria-checked="true"
@@ -94,6 +97,7 @@ function handleRepoClick(repo: MinimalRepository, event: MouseEvent | KeyboardEv
9497
v-else
9598
class="notification-item"
9699
:class="{ 'notification-item-read': !value.unread }"
100+
@contextmenu="(event) => emit('contextmenu', value as Thread, event)"
97101
@click="(event) => isThread(value) && handleThreadClick(value, event)"
98102
>
99103
<Component
@@ -118,7 +122,7 @@ function handleRepoClick(repo: MinimalRepository, event: MouseEvent | KeyboardEv
118122
class="notification-checkbox-wrapper"
119123
>
120124
<span
121-
:class="{ 'notification-checkbox-checked': checked }"
125+
:class="{ 'notification-checkbox-checked': checked, 'notification-checkbox-visible': checkboxVisible }"
122126
role="checkbox"
123127
tabindex="0"
124128
aria-checked="true"
@@ -150,6 +154,10 @@ function handleRepoClick(repo: MinimalRepository, event: MouseEvent | KeyboardEv
150154
border: 1px solid transparent;
151155
line-height: inherit;
152156
157+
&:hover .notification-checkbox {
158+
opacity: 1;
159+
}
160+
153161
&:hover,
154162
&:active {
155163
background-color: var(--item-hover-bg);
@@ -167,7 +175,7 @@ function handleRepoClick(repo: MinimalRepository, event: MouseEvent | KeyboardEv
167175
font-weight: bold;
168176
font-size: 14px;
169177
display: inline-block;
170-
color: white;
178+
color: var(--white);
171179
white-space: nowrap;
172180
overflow: hidden;
173181
text-overflow: ellipsis;
@@ -192,6 +200,10 @@ function handleRepoClick(repo: MinimalRepository, event: MouseEvent | KeyboardEv
192200
@include focus-visible;
193201
margin-top: 5px;
194202
203+
&:hover .notification-checkbox {
204+
opacity: 1;
205+
}
206+
195207
&-read {
196208
color: var(--white-faded) !important;
197209
}
@@ -235,24 +247,31 @@ function handleRepoClick(repo: MinimalRepository, event: MouseEvent | KeyboardEv
235247
@include focus-visible;
236248
width: 16px;
237249
height: 16px;
238-
border-radius: 50%;
239-
border: 1px solid rgb(101, 101, 101);
250+
border-radius: 6px;
251+
border: 1px solid var(--white-faded);
240252
flex-shrink: 0;
241253
padding: 3px;
242254
display: inline-flex;
255+
opacity: 0;
256+
transition-duration: .2s;
257+
transition-property: opacity;
258+
259+
&-visible {
260+
opacity: 1;
261+
}
243262
244263
&-checked {
245-
border-color: var(--white);
264+
border-color: var(--accent-color);
246265
247266
.notification-checkbox-dot {
248-
background-color: var(--white);
267+
background-color: var(--accent-color);
249268
}
250269
}
251270
252271
&-dot {
253272
width: 100%;
254273
height: 100%;
255-
border-radius: 50%;
274+
border-radius: 3px;
256275
}
257276
258277
&-wrapper {

src/components/Popover.vue

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,12 @@ const handleTransition: WowerlayTransitionFn = (type, element, done) => {
7777
7878
const popoverVisibleHooks = new Set<(el: ReferenceElement) => void>()
7979
80-
const runPopoverVisibleHooks = (el: ReferenceElement) => {
80+
function runPopoverVisibleHooks(el: ReferenceElement) {
8181
for (const cb of popoverVisibleHooks)
8282
cb(el)
8383
}
8484
85-
export const onPopoverVisible = (cb: (el: ReferenceElement) => void) => {
85+
export function onPopoverVisible(cb: (el: ReferenceElement) => void) {
8686
popoverVisibleHooks.add(cb)
8787
const cleanup = () => popoverVisibleHooks.delete(cb)
8888
onScopeDispose(cleanup)
@@ -92,6 +92,11 @@ export const onPopoverVisible = (cb: (el: ReferenceElement) => void) => {
9292
</script>
9393

9494
<script lang="ts" setup>
95+
interface Props {
96+
wowerlayOptions?: Partial<Omit<InstanceType<typeof Wowerlay>['$props'], 'visible' | 'target'>>
97+
target?: InstanceType<typeof Wowerlay>['$props']['target']
98+
}
99+
95100
const props = withDefaults(defineProps<Props>(), {
96101
wowerlayOptions: () => ({}),
97102
})
@@ -100,11 +105,6 @@ defineSlots<{
100105
default: (props: SlotProps) => any
101106
}>()
102107
103-
interface Props {
104-
wowerlayOptions?: Partial<Omit<InstanceType<typeof Wowerlay>['$props'], 'visible' | 'target'>>
105-
target?: InstanceType<typeof Wowerlay>['$props']['target']
106-
}
107-
108108
const visible = ref(false)
109109
110110
defineExpose({

src/composables/useElementNavigation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export enum Navigation {
1515
Previous,
1616
}
1717

18-
const getFocusedItemIndex = (elements: HTMLElement[], query: string) => {
18+
function getFocusedItemIndex(elements: HTMLElement[], query: string) {
1919
const activeElement = document.activeElement!.closest(query)
2020

2121
return elements

src/composables/useKey.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ export type UseKeyCallback = (event: KeyboardEvent, hotkeysEvent: HotkeysEvent)
1616

1717
const getLast = <T>(arr: T[]) => arr[arr.length - 1]
1818

19-
const isInputing = () =>
20-
document.activeElement instanceof HTMLTextAreaElement
19+
function isInputing() {
20+
return document.activeElement instanceof HTMLTextAreaElement
2121
|| document.activeElement?.hasAttribute('contenteditable')
2222
|| document.activeElement instanceof HTMLInputElement
2323
|| document.activeElement instanceof HTMLSelectElement
24+
}
2425

2526
hotkeys.filter = () => true
2627

0 commit comments

Comments
 (0)