ADD - support home screen app folders

This commit is contained in:
smx.pusha
2026-08-13 13:15:25 +02:00
parent 90909e41f2
commit bc4b439eb6
15 changed files with 1976 additions and 194 deletions
+154 -14
View File
@@ -1,14 +1,21 @@
import { describe, expect, it } from 'vitest'
import {
addHomeAppToFolder,
addHomePage,
createHomeFolder,
createDefaultHomeLayout,
deleteHomePage,
extractHomeFolderApp,
getHomeFolder,
HOME_GRID_PAGE_SIZE,
homeKeyboardTarget,
isHomeFolder,
MAX_HOME_GRID_PAGES,
moveHomeFolderApp,
moveHomeApp,
parseHomeLayout,
renameHomeFolder,
removeHomeApp,
restoreHomeApp,
type HomeLayout,
@@ -35,7 +42,7 @@ describe('home layout', () => {
null,
])
expect(layout.dock).toEqual(['phone', 'messages', 'clock', null])
expect(layout.version).toBe(3)
expect(layout.version).toBe(5)
})
it('migrates compact persisted arrays and appends newly installed apps', () => {
@@ -52,12 +59,12 @@ describe('home layout', () => {
expect(layout.dock).toEqual(['messages', null, null, null])
expect(layout.grid.slice(0, 4)).toEqual(['mail', 'clock', 'notes', null])
expect(layout.hidden).toEqual(['phone'])
expect(layout.version).toBe(3)
expect(layout.version).toBe(5)
})
it('preserves explicit gaps in versioned layouts', () => {
const grid: HomeLayout['grid'] = Array.from(
{ length: HOME_GRID_PAGE_SIZE },
{ length: 20 },
() => null,
)
grid[0] = 'phone'
@@ -82,7 +89,7 @@ describe('home layout', () => {
it('keeps valid custom-app tombstones in version 3 layouts', () => {
const grid: HomeLayout['grid'] = Array.from(
{ length: HOME_GRID_PAGE_SIZE },
{ length: 20 },
() => null,
)
grid[6] = 'temporarily-missing' as HomeLayout['hidden'][number]
@@ -99,12 +106,35 @@ describe('home layout', () => {
)
expect(layout.grid[6]).toBe('temporarily-missing')
expect(layout.version).toBe(3)
expect(layout.version).toBe(5)
})
it('expands version 3 capacity without compacting persisted gaps', () => {
const grid: HomeLayout['grid'] = Array.from({ length: 40 }, () => null)
grid[19] = 'mail'
grid[20] = 'notes'
const layout = parseHomeLayout(
{
dock: ['phone', null, null, null],
grid,
hidden: [],
version: 3,
},
defaults,
[...installed],
)
expect(layout.grid).toHaveLength(HOME_GRID_PAGE_SIZE * 2)
expect(layout.grid[19]).toBe('mail')
expect(layout.grid[20]).toBe('notes')
expect(layout.grid.slice(21, 24)).toEqual([null, null, null])
expect(layout.version).toBe(5)
})
it('preserves independently positioned shortcuts for the same app', () => {
const grid: HomeLayout['grid'] = Array.from(
{ length: HOME_GRID_PAGE_SIZE },
{ length: 20 },
() => null,
)
grid[0] = 'phone'
@@ -144,33 +174,33 @@ describe('home layout', () => {
expect(homeKeyboardTarget(defaults, 'dock', 1, 'down')).toBeNull()
})
it('shifts occupied grid slots instead of replacing their apps', () => {
it('swaps occupied grid slots without moving unrelated apps', () => {
const reordered = moveHomeApp(defaults, 'grid', 2, 'grid', 0)
expect(reordered.grid.slice(0, 5)).toEqual([
'mail',
'phone',
'messages',
'phone',
'clock',
'notes',
])
})
it('shifts an occupied dock slot toward its gap', () => {
it('swaps an occupied dock slot with the exact grid source', () => {
const docked = moveHomeApp(defaults, 'grid', 2, 'dock', 2)
expect(docked.dock).toEqual(['phone', 'messages', 'mail', 'clock'])
expect(docked.grid[2]).toBeNull()
expect(docked.dock).toEqual(['phone', 'messages', 'mail', null])
expect(docked.grid[2]).toBe('clock')
})
it('moves an app displaced from a full dock into the source slot', () => {
it('swaps with an app in a full dock without shifting the dock', () => {
const layout: HomeLayout = {
...defaults,
dock: ['phone', 'messages', 'clock', 'notes'],
}
const docked = moveHomeApp(layout, 'grid', 2, 'dock', 1)
expect(docked.dock).toEqual(['phone', 'mail', 'messages', 'clock'])
expect(docked.grid[2]).toBe('notes')
expect(docked.dock).toEqual(['phone', 'mail', 'clock', 'notes'])
expect(docked.grid[2]).toBe('messages')
})
it('moves shortcuts between the dock and grid independently', () => {
@@ -218,4 +248,114 @@ describe('home layout', () => {
expect(deleted.grid).toContain('phone')
expect(deleteHomePage(deleted, 1)).toBe(deleted)
})
it('creates, renames, and persists a folder without moving other slots', () => {
const folderLayout = createHomeFolder(
defaults,
'grid',
2,
'grid',
4,
'folder-work-123456',
'Work',
)
const folder = folderLayout.grid[4]
expect(folderLayout.grid[2]).toBeNull()
expect(isHomeFolder(folder) && folder.apps).toEqual(['notes', 'mail'])
expect(folderLayout.grid[0]).toBe('phone')
const renamed = renameHomeFolder(
folderLayout,
'folder-work-123456',
' Dienstprogramme ',
)
expect(getHomeFolder(renamed, 'folder-work-123456')?.name).toBe(
'Dienstprogramme',
)
const parsed = parseHomeLayout(renamed, defaults, [...installed])
expect(parsed.version).toBe(5)
expect(getHomeFolder(parsed, 'folder-work-123456')).toEqual({
apps: ['notes', 'mail'],
id: 'folder-work-123456',
name: 'Dienstprogramme',
type: 'folder',
})
})
it('adds apps to a folder and swaps apps inside its 3x3 pages', () => {
const folderLayout = createHomeFolder(
defaults,
'grid',
2,
'grid',
4,
'folder-tools-123456',
'Tools',
)
const expanded = addHomeAppToFolder(
folderLayout,
'grid',
3,
'folder-tools-123456',
)
expect(expanded.grid[3]).toBeNull()
expect(getHomeFolder(expanded, 'folder-tools-123456')?.apps).toEqual([
'notes',
'mail',
'clock',
])
const reordered = moveHomeFolderApp(
expanded,
'folder-tools-123456',
2,
0,
)
expect(getHomeFolder(reordered, 'folder-tools-123456')?.apps).toEqual([
'clock',
'mail',
'notes',
])
})
it('extracts a folder app into an exact gap and dissolves one-app folders', () => {
const folderLayout = createHomeFolder(
defaults,
'grid',
2,
'grid',
4,
'folder-social-123456',
'Social',
)
const extracted = extractHomeFolderApp(
folderLayout,
'folder-social-123456',
1,
'grid',
12,
)
expect(extracted.grid[12]).toBe('mail')
expect(extracted.grid[4]).toBe('notes')
expect(getHomeFolder(extracted, 'folder-social-123456')).toBeNull()
})
it('removes hidden apps from folders and dissolves the folder when needed', () => {
const folderLayout = createHomeFolder(
defaults,
'grid',
2,
'grid',
4,
'folder-mixed-123456',
'Mixed',
)
const removed = removeHomeApp(folderLayout, 'mail')
expect(removed.grid[4]).toBe('notes')
expect(removed.hidden).toContain('mail')
})
})