stop search button

Massimo Melina committed Dec 22, 2021 at 23:38 UTC 46cbf83cf0558f98ee5d710bc5fdab7575c978db
7 files changed +36 -12
frontend/src/Head.ts
+21 -8
@@ -2,7 +2,7 @@ import { createElement as h, Fragment, useContext, useMemo, useState } from 'rea
2 import { Link, useLocation } from 'react-router-dom'
3 import { ListContext } from './BrowseFiles'
4 import { login, logout } from './login'
5 -import { formatBytes, hIcon, prefix } from './misc'
5 +import { formatBytes, hIcon, prefix, wait } from './misc'
6 import { Spinner } from './components'
7 import { state, useSnapState } from './state'
8 import { useDebounce } from 'use-debounce'
@@ -22,7 +22,7 @@ function MenuPanel() {
22 ;[state.listFilter] = useDebounce(filter, 300)
23 if (!showFilter)
24 state.listFilter = ''
25 - const { remoteSearch } = useSnapState()
25 + const { remoteSearch, stopSearch } = useSnapState()
26 return h('div', { id:'menu-panel' },
27 h('div', { id:'menu-bar' },
28 h(LoginButton),
@@ -37,10 +37,23 @@ function MenuPanel() {
37 h(MenuButton, {
38 icon: 'search',
39 label: 'Search',
40 - onClick() {
40 + async onClick() {
41 const res = prompt('Search for...')
42 - if (res !== null)
43 - state.remoteSearch = res
42 + if (res === null) return
43 + if (state.stoppedSearch) {
44 + state.remoteSearch = ''
45 + await wait(500)
46 + }
47 + stopSearch?.()
48 + state.remoteSearch = res
49 + }
50 + }),
51 + stopSearch && h(MenuButton, {
52 + icon: 'stop',
53 + label: 'Stop list',
54 + onClick() {
55 + stopSearch()
56 + state.stoppedSearch = true
57 }
58 })
59 ),
@@ -98,14 +111,14 @@ function FolderStats() {
111 }
112 return { files, folders, size }
113 }, [list])
101 - const snap = useSnapState()
114 + const { filteredEntries, stoppedSearch } = useSnapState()
115 return h('div', { id:'folder-stats' },
103 - unfinished && h(Spinner),
116 + stoppedSearch ? hIcon('interrupted') : unfinished && h(Spinner),
117 [
118 prefix('', stats.files,' file(s)'),
119 prefix('', stats.folders, ' folder(s)'),
120 stats.size ? formatBytes(stats.size) : '',
108 - snap.filteredEntries >= 0 && snap.filteredEntries+' displayed',
121 + filteredEntries >= 0 && filteredEntries+' displayed',
122 ].filter(Boolean).join(', ')
123 )
124 }
frontend/src/components.ts
+1 -1
@@ -1,6 +1,6 @@
1 import { hIcon } from './misc'
2
3 export function Spinner() {
4 - return hIcon('spinner', { style: { animation:'1s spin infinite' } })
4 + return hIcon('spinner', { className:'spinner' })
5 }
6
frontend/src/icons.ts
+4 -3
@@ -8,6 +8,7 @@ const SYS_ICONS: Record<string,string> = {
8 file: 'description',
9 spinner: 'sports_baseball',
10 filter: 'filter_alt',
11 + interrupted: 'heart_broken',
12 }
13
14 document.fonts.ready.then(async ()=> {
@@ -17,12 +18,12 @@ document.fonts.ready.then(async ()=> {
18 state.iconsClass = name.replace(/ /g,'-').toLowerCase()
19 })
20
20 -export function Icon({ name, ...props }: { name:string, style?:any }) {
21 +export function Icon({ name, className, ...props }: { name:string, className?:string, style?:any }) {
22 name = SYS_ICONS[name] || name
23 const { iconsClass } = useSnapState()
24 return h('span',{
24 - className: iconsClass+' icon',
25 - ...props
25 + ...props,
26 + className: iconsClass+' icon '+className,
27 }, iconsClass ? name : '#')
28 }
29
frontend/src/index.scss
+7
@@ -53,6 +53,10 @@ header {
53 to { transform: rotate(360deg); }
54 }
55
56 +.spinner {
57 + animation: 1s spin infinite;
58 +}
59 +
60 .breadcrumb {
61 padding: 0.1em 0.6em 0.2em;
62 border-radius: 0.7em;
@@ -68,6 +72,9 @@ header {
72 padding: 0.1em 0.3em;
73 margin: 0.2em 0.5em;
74 float: right;
75 + & .icon {
76 + margin-right: .3em;
77 + }
78 }
79 header input {
80 width: 100%;
frontend/src/state.ts
+1
@@ -2,6 +2,7 @@ import { proxy, useSnapshot } from 'valtio'
2
3 export const state = proxy<{
4 stopSearch?: ()=>void,
5 + stoppedSearch?: boolean,
6 iconsClass: string,
7 username: string,
8 listFilter: string,
frontend/src/useFetchList.ts
+1
@@ -19,6 +19,7 @@ export default function useFetchList() {
19 }
20 const previous = lastPath.current
21 lastPath.current = desiredPath
22 + state.stoppedSearch = false
23 if (previous !== desiredPath && search) {
24 state.remoteSearch = ''
25 state.stopSearch?.()
src/index.ts
+1
@@ -54,6 +54,7 @@ srv.use(async (ctx, next) => {
54
55 srv.on('error', err => {
56 if (DEV && err.code === 'ENOENT' && err.path.endsWith('sockjs-node')) return // spam out
57 + if (err.code === 'ECONNRESET') return // someone interrupted, don't care
58 console.error('server error', err)
59 })
60 srv.listen(PORT, ()=> console.log('running on port', PORT, DEV, new Date().toLocaleString()))