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 }