better code: split files
Massimo Melina committed
Jan 9, 2022 at 11:54 UTC
5842b5253eb1fe5e661169262391526bf3408669
4 files changed
+82
-68
frontend/src/UserPanel.ts
new
+45
@@ -0,0 +1,45 @@
1
+import { useSnapState } from './state'
2
+import { createElement as h } from 'react'
3
+import { alertDialog, closeDialog, newDialog, promptDialog } from './dialog'
4
+import { createVerifierAndSalt, SRPParameters, SRPRoutines } from 'tssrp6a'
5
+import { apiCall } from './api'
6
+import { logout } from './login'
7
+import { MenuButton } from './menu'
8
+
9
+export default function showUserPanel() {
10
+ newDialog({ Content })
11
+}
12
+
13
+function Content() {
14
+ const snap = useSnapState()
15
+ return h('div', { id: 'user-panel' },
16
+ h('div', {}, 'User: ' + snap.username),
17
+ h(MenuButton, {
18
+ icon: 'key',
19
+ label: 'Change password',
20
+ async onClick() {
21
+ const pwd = await promptDialog('Enter new password', { type: 'password' })
22
+ if (!pwd) return
23
+ const check = await promptDialog('RE-enter new password', { type: 'password' })
24
+ if (!check) return
25
+ if (check !== pwd)
26
+ return alertDialog('The second password you entered did not match the first. Procedure aborted.', 'warning')
27
+ const srp6aNimbusRoutines = new SRPRoutines(new SRPParameters())
28
+ const res = await createVerifierAndSalt(srp6aNimbusRoutines, snap.username, pwd)
29
+ await apiCall('change_srp', { salt: String(res.s), verifier: String(res.v) }).catch(e => {
30
+ if (e.code !== 406) // 406 = server was configured to support clear text authentication
31
+ throw e
32
+ return apiCall('change_password', { newPassword: pwd }) // unencrypted version
33
+ })
34
+ return alertDialog('Password changed')
35
+ }
36
+ }),
37
+ h(MenuButton, {
38
+ icon: 'logout',
39
+ label: 'Logout',
40
+ onClick() {
41
+ logout().then(closeDialog)
42
+ }
43
+ })
44
+ )
45
+}
frontend/src/menu.ts
+8
-66
@@ -1,12 +1,11 @@
1
import { state, useSnapState } from './state'
2
import { createElement as h, useEffect, useState } from 'react'
3
import { useDebounce } from 'use-debounce'
4
-import { alertDialog, closeDialog, newDialog, promptDialog } from './dialog'
4
+import { promptDialog } from './dialog'
5
import { hIcon, prefix } from './misc'
6
-import { login, logout } from './login'
7
-import { apiCall } from './api'
8
-import { Checkbox, FlexV } from './components'
9
-import { createVerifierAndSalt, SRPParameters, SRPRoutines } from 'tssrp6a'
6
+import { login } from './login'
7
+import { showOptions } from './options'
8
+import showUserPanel from './UserPanel'
9
10
export function MenuPanel() {
11
const { remoteSearch, stopSearch, stoppedSearch, listFilter } = useSnapState()
@@ -33,31 +32,9 @@ export function MenuPanel() {
32
}),
33
h(MenuButton, getSearchProps()),
34
h(MenuButton, {
36
- icon: 'options',
35
+ icon: 'settings',
36
label: 'Options',
38
- onClick(){
39
- const options = ['name','extension','size','time']
40
- const close = newDialog({ Content })
41
-
42
- function Content(){
43
- const snap = useSnapState()
44
- return h(FlexV, {},
45
- h('div', {}, 'Sort by'),
46
- options.map(x => h('button',{
47
- key: x,
48
- onClick(){
49
- close(state.sortBy = x)
50
- }
51
- }, x, ' ', snap.sortBy===x && hIcon('check'))),
52
- h(Checkbox, {
53
- value: snap.foldersFirst,
54
- onChange(v) {
55
- state.foldersFirst = v
56
- }
57
- }, 'Folders first')
58
- )
59
- }
60
- }
37
+ onClick: showOptions
38
}),
39
h(MenuButton, {
40
icon: 'archive',
@@ -113,7 +90,7 @@ interface MenuButtonProps {
90
onClick?: () => void
91
}
92
116
-function MenuButton({ icon, label, toggled, onClick, className = '' }: MenuButtonProps) {
93
+export function MenuButton({ icon, label, toggled, onClick, className = '' }: MenuButtonProps) {
94
return h('button', { title: label, onClick, className: className + ' ' + (toggled ? 'toggled' : '') },
95
hIcon(icon),
96
h('label', {}, label))
@@ -124,9 +101,7 @@ function LoginButton() {
101
return MenuButton(snap.username ? {
102
icon: 'user',
103
label: snap.username,
127
- onClick() {
128
- newDialog({ Content: UserPanel })
129
- },
104
+ onClick: showUserPanel
105
} : {
106
icon: 'login',
107
label: 'Login',
@@ -140,37 +115,4 @@ function LoginButton() {
115
})
116
}
117
143
-function UserPanel() {
144
- const snap = useSnapState()
145
- return h('div', { id: 'user-panel' },
146
- h('div', {}, 'User: ' + snap.username),
147
- h(MenuButton, {
148
- icon: 'key',
149
- label: 'Change password',
150
- async onClick() {
151
- const pwd = await promptDialog('Enter new password', { type: 'password' })
152
- if (!pwd) return
153
- const check = await promptDialog('RE-enter new password', { type: 'password' })
154
- if (!check) return
155
- if (check !== pwd)
156
- return alertDialog('The second password you entered did not match the first. Procedure aborted.', 'warning')
157
- const srp6aNimbusRoutines = new SRPRoutines(new SRPParameters())
158
- const res = await createVerifierAndSalt(srp6aNimbusRoutines, snap.username, pwd)
159
- await apiCall('change_srp', { salt: String(res.s), verifier: String(res.v) }).catch(e => {
160
- if (e.code !== 406) // 406 = server was configured to support clear text authentication
161
- throw e
162
- return apiCall('change_password', { newPassword: pwd }) // unencrypted version
163
- })
164
- return alertDialog('Password changed')
165
- }
166
- }),
167
- h(MenuButton, {
168
- icon: 'logout',
169
- label: 'Logout',
170
- onClick() {
171
- logout().then(closeDialog)
172
- }
173
- })
174
- )
175
-}
118
frontend/src/options.ts
new
+29
@@ -0,0 +1,29 @@
1
+import { newDialog } from './dialog'
2
+import { state, useSnapState } from './state'
3
+import { createElement as h } from 'react'
4
+import { Checkbox, FlexV } from './components'
5
+import { hIcon } from './misc'
6
+
7
+export function showOptions (){
8
+ const options = ['name','extension','size','time']
9
+ const close = newDialog({ Content })
10
+
11
+ function Content(){
12
+ const snap = useSnapState()
13
+ return h(FlexV, {},
14
+ h('div', {}, 'Sort by'),
15
+ options.map(x => h('button',{
16
+ key: x,
17
+ onClick(){
18
+ close(state.sortBy = x)
19
+ }
20
+ }, x, ' ', snap.sortBy===x && hIcon('check'))),
21
+ h(Checkbox, {
22
+ value: snap.foldersFirst,
23
+ onChange(v) {
24
+ state.foldersFirst = v
25
+ }
26
+ }, 'Folders first'),
27
+ )
28
+ }
29
+}
src/api.file_list.ts
-2
@@ -24,7 +24,6 @@ export const file_list:ApiHandler = async ({ path, offset, limit, search, omit,
24
25
async function produceEntries() {
26
const list = []
27
- const h = sseSrv && setInterval(()=> console.debug('walking'), 500)
27
for await (const sub of walker) {
28
if (sseSrv?.stopped || ctx.aborted) break
29
const filename = basename(sub.name||'')
@@ -51,7 +50,6 @@ export const file_list:ApiHandler = async ({ path, offset, limit, search, omit,
50
if (limit && !--limit)
51
break
52
}
54
- if (h) clearInterval(h)
53
sseSrv?.close()
54
return list
55
}