Skip to content

Commit 4361458

Browse files
committed
Multi-selection of thumbnails using shift for range and Ctrl-A
1 parent 8bb796e commit 4361458

File tree

2 files changed

+68
-10
lines changed

2 files changed

+68
-10
lines changed

photonix/photos/management/commands/thumbnail_processor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class Command(BaseCommand):
3030
help = 'Processes full-sized photos into thumbnails of various sizes.'
3131

3232
def run_processors(self):
33-
num_workers = max(int(cpu_count() / 4), 1)
33+
num_workers = max(int(cpu_count() / 2), 1)
3434
threads = []
3535

3636
logger.info('Starting {} thumbnail processor workers'.format(num_workers))

ui/src/components/Thumbnails.js

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ const SectionHeading = styled('h2')`
7676

7777
const ESCAPE_KEY = 27
7878
const CTRL_KEY = 17
79+
const SHIFT_KEY = 16
80+
const A_KEY = 65
7981

8082
const Thumbnails = ({
8183
photoSections,
@@ -88,6 +90,8 @@ const Thumbnails = ({
8890
const history = useHistory()
8991
const [selected, setSelected] = useState([])
9092
const [ctrlKeyPressed, setCtrlKeyPressed] = useState(false)
93+
const [shiftKeyPressed, setShiftKeyPressed] = useState(false)
94+
const [lastSelectedId, setLastSelectedId] = useState(null)
9195

9296
const [removePhotosFromAlbum] = useMutation(REMOVE_PHOTOS_FROM_ALBUM)
9397
const [setPhotosDeleted] = useMutation(SET_PHOTOS_DELETED)
@@ -163,11 +167,47 @@ const Thumbnails = ({
163167
return null
164168
}
165169

166-
const addRemoveItem = (id) => {
170+
// Get flat array of all photo IDs in display order
171+
const getAllPhotoIds = () => {
172+
const ids = []
173+
if (!photoSections) return ids
174+
for (const section of photoSections) {
175+
for (const segment of section.segments) {
176+
for (const photo of segment.photos) {
177+
if (mode !== 'ALBUMS') {
178+
ids.push(photo.id)
179+
}
180+
}
181+
}
182+
}
183+
return ids
184+
}
185+
186+
const addRemoveItem = (id, isShiftClick = false) => {
187+
if (isShiftClick && lastSelectedId && lastSelectedId !== id) {
188+
// Range selection: select all items between lastSelectedId and id
189+
const allIds = getAllPhotoIds()
190+
const lastIndex = allIds.indexOf(lastSelectedId)
191+
const currentIndex = allIds.indexOf(id)
192+
193+
if (lastIndex !== -1 && currentIndex !== -1) {
194+
const startIndex = Math.min(lastIndex, currentIndex)
195+
const endIndex = Math.max(lastIndex, currentIndex)
196+
const rangeIds = allIds.slice(startIndex, endIndex + 1)
197+
198+
// Add range to existing selection (union)
199+
const newSelected = [...new Set([...selected, ...rangeIds])]
200+
setSelected(newSelected)
201+
return
202+
}
203+
}
204+
205+
// Normal toggle behavior for ctrl-click or single click
167206
let ids = [...selected]
168207
const index = ids.indexOf(id)
169208
index > -1 ? ids.splice(index, 1) : ids.push(id)
170209
setSelected(ids)
210+
setLastSelectedId(id)
171211
}
172212

173213
const bind = useLongPress(
@@ -196,10 +236,24 @@ const Thumbnails = ({
196236
switch (event.keyCode) {
197237
case ESCAPE_KEY:
198238
setSelected([])
239+
setLastSelectedId(null)
199240
break
200241
case CTRL_KEY:
201242
setCtrlKeyPressed(true)
202243
break
244+
case SHIFT_KEY:
245+
setShiftKeyPressed(true)
246+
break
247+
case A_KEY:
248+
if (event.ctrlKey || event.metaKey) {
249+
event.preventDefault()
250+
const allIds = getAllPhotoIds()
251+
setSelected(allIds)
252+
if (allIds.length > 0) {
253+
setLastSelectedId(allIds[allIds.length - 1])
254+
}
255+
}
256+
break
203257
default:
204258
break
205259
}
@@ -210,6 +264,9 @@ const Thumbnails = ({
210264
case CTRL_KEY:
211265
setCtrlKeyPressed(false)
212266
break
267+
case SHIFT_KEY:
268+
setShiftKeyPressed(false)
269+
break
213270
default:
214271
break
215272
}
@@ -222,14 +279,15 @@ const Thumbnails = ({
222279
document.removeEventListener('keydown', handleKeyDown)
223280
document.removeEventListener('keyup', handleKeyUp)
224281
}
225-
}, [])
282+
}, [photoSections, mode])
226283

227-
const onMouseDown = ctrlKeyPressed
228-
? (e) => {
229-
const id = getNode(e.target).getAttribute('data-id')
230-
addRemoveItem(id)
231-
}
232-
: bind.onMouseDown
284+
const onMouseDown =
285+
ctrlKeyPressed || shiftKeyPressed
286+
? (e) => {
287+
const id = getNode(e.target).getAttribute('data-id')
288+
addRemoveItem(id, shiftKeyPressed)
289+
}
290+
: bind.onMouseDown
233291

234292
return (
235293
<>
@@ -281,7 +339,7 @@ const Thumbnails = ({
281339
starRating={photo.starRating}
282340
rotation={photo.rotation}
283341
selected={selected.indexOf(photo.id) > -1}
284-
selectable={selected.length > 0 || ctrlKeyPressed}
342+
selectable={selected.length > 0 || ctrlKeyPressed || shiftKeyPressed}
285343
mode={mode}
286344
rateable={rateable}
287345
{...bind}

0 commit comments

Comments
 (0)