-
Notifications
You must be signed in to change notification settings - Fork 585
/
Copy pathuse-widgets.ts
132 lines (113 loc) · 3.69 KB
/
use-widgets.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// TODO: move to widgets module
import {useState} from 'react'
import {MAX_WIDGETS} from '@/modules/widgets/shared/constants'
import {systemAppsKeyed, useApps} from '@/providers/apps'
import {AppState, trpcReact} from '@/trpc/trpc'
import {liveUsageWidgets} from './../modules/widgets/shared/constants'
export function useWidgets() {
// Consider having `selectedTooMany` outside this hook
const [selectedTooMany, setSelectedTooMany] = useState(false)
const apps = useApps()
const {selected, enable, disable, isLoading: isSelectedLoading} = useEnableWidgets()
const isLoading = apps.isLoading || isSelectedLoading
const availableUserAppWidgets = apps.userApps
? apps.userApps
// Don't want to allow users to select widgets while installing
// But after done installing, the app might not be reachable, but we still want to
// show its widgets.
.filter((app) => app.state !== 'installing')
.map((app) => ({
appId: app.id,
icon: app.icon,
name: app.name,
state: app.state,
widgets: app.widgets?.map((w) => ({...w, id: app.id + ':' + w.id})) ?? [],
}))
: []
// NOTE: the backend Umbrel system widgets always have an `umbrel:` prefix. For now this is good
// because it means we can associate them with any system app. It used to be that some system widgets
// were in the `settings` app. But they were moved to a new `live-usage` app.
const availableSystemWidgets = [
{
appId: 'live-usage',
icon: systemAppsKeyed['UMBREL_live-usage'].icon,
name: systemAppsKeyed['UMBREL_live-usage'].name,
state: 'ready' as const satisfies AppState,
widgets: liveUsageWidgets,
},
// Add others here
]
const availableWidgets = apps.userApps
? [...availableSystemWidgets, ...availableUserAppWidgets].filter(({widgets}) => widgets?.length)
: []
// No need to specify app id because widget endpoints are unique
// TODO: don't call it `toggle` because it's not a toggle
const toggleSelected = (widgetId: string, checked: boolean) => {
if (selected.length >= MAX_WIDGETS && checked) {
setSelectedTooMany(true)
setTimeout(() => setSelectedTooMany(false), 500)
return
}
setSelectedTooMany(false)
if (selected.includes(widgetId)) {
disable(widgetId)
} else {
enable(widgetId)
}
}
const appFromWidgetId = (id: string) => {
return availableWidgets.find((app) => app.widgets?.find((widget) => widget.id === id))
}
const selectedWithAppInfo = selected
.filter((id) => {
const app = appFromWidgetId(id)
return !!app
})
.map((id) => {
// Expect app to be found because we filtered out widgets without apps
const app = appFromWidgetId(id)!
// Assume we'll always find a widget
const widget = app.widgets.find((w) => w.id === id)!
return {
...widget,
app: {
id: app.appId,
icon: app.icon,
name: app.name,
state: app.state,
},
}
})
return {
availableWidgets,
selected: selectedWithAppInfo,
toggleSelected,
selectedTooMany,
isLoading,
}
}
function useEnableWidgets() {
const ctx = trpcReact.useContext()
const widgetQ = trpcReact.widget.enabled.useQuery()
const enableMut = trpcReact.widget.enable.useMutation({
onSuccess: () => {
ctx.user.invalidate()
ctx.widget.enabled.invalidate()
},
})
const disableMut = trpcReact.widget.disable.useMutation({
onSuccess: () => {
ctx.user.invalidate()
ctx.widget.enabled.invalidate()
},
})
const selected = widgetQ.data ?? []
// const setSelected = (widgets: WidgetT[]) => enableMut.mutate({widgets})
const isLoading = widgetQ.isLoading || enableMut.isLoading
return {
isLoading,
selected,
enable: (widgetId: string) => enableMut.mutate({widgetId}),
disable: (widgetId: string) => disableMut.mutate({widgetId}),
}
}