WIP Add lists

This commit is contained in:
2026-03-12 17:03:17 +04:00
parent e019cb01ba
commit 53b2c9b4f8

View File

@@ -31,6 +31,32 @@ const placeSchema = {
required: ['id', 'title', 'lat', 'lon', 'geohash', 'createdAt'], required: ['id', 'title', 'lat', 'lon', 'geohash', 'createdAt'],
} as const; } as const;
const listSchema = {
type: 'object',
properties: {
id: { type: 'string' },
title: { type: 'string' },
color: { type: 'string' },
placeRefs: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'string' },
geohash: { type: 'string' },
},
required: ['id', 'geohash'],
},
default: [],
},
createdAt: { type: 'string', format: 'date-time' },
updatedAt: { type: 'string', format: 'date-time' },
},
required: ['id', 'title', 'placeRefs', 'createdAt'],
} as const;
export type List = FromSchema<typeof listSchema> & { [key: string]: any };
/** /**
* Represents a Place object. * Represents a Place object.
* *
@@ -101,6 +127,46 @@ export interface PlacesClient {
* @returns An array of places. * @returns An array of places.
*/ */
getPlaces(prefixes?: string[]): Promise<Place[]>; getPlaces(prefixes?: string[]): Promise<Place[]>;
lists: {
/**
* Get all lists.
* @returns Array of List objects.
*/
getAll(): Promise<List[]>;
/**
* Get a single list by ID (slug).
* @param id - The slug ID of the list.
*/
get(id: string): Promise<List | null>;
/**
* Create or update a list.
* @param id - The slug ID (e.g., "to-go").
* @param title - Human readable title.
* @param color - Optional hex color code.
*/
create(id: string, title: string, color?: string): Promise<List>;
/**
* Delete a list.
* @param id - The slug ID of the list.
*/
delete(id: string): Promise<void>;
/**
* Add or remove a place from a list.
* @param listId - The slug ID of the list.
* @param placeId - The ID of the place.
* @param geohash - The geohash of the place (needed for reference).
*/
togglePlace(
listId: string,
placeId: string,
geohash: string
): Promise<List>;
};
} }
const Places = function ( const Places = function (
@@ -108,6 +174,7 @@ const Places = function (
): { exports: PlacesClient } { ): { exports: PlacesClient } {
// Define Schema // Define Schema
privateClient.declareType('place', placeSchema as any); privateClient.declareType('place', placeSchema as any);
privateClient.declareType('list', listSchema as any);
// Helper to normalize place object // Helper to normalize place object
function preparePlace(data: Partial<Place>): Place { function preparePlace(data: Partial<Place>): Place {
@@ -153,7 +220,93 @@ const Places = function (
return `${p1}/${p2}/${id}`; return `${p1}/${p2}/${id}`;
} }
const lists = {
async getAll(): Promise<List[]> {
const result = await privateClient.getAll('_lists/');
if (!result) return [];
// Normalize result: remoteStorage.getAll returns { 'slug': object }
return Object.values(result);
},
async get(id: string): Promise<List | null> {
const path = `_lists/${id}`;
return privateClient.getObject(path) as Promise<List | null>;
},
async create(id: string, title: string, color?: string): Promise<List> {
const path = `_lists/${id}`;
let list = (await privateClient.getObject(path)) as List;
const now = new Date().toISOString();
if (list) {
// Update existing
list.title = title;
if (color) list.color = color;
list.updatedAt = now;
} else {
// Create new
list = {
id,
title,
color,
placeRefs: [],
createdAt: now,
updatedAt: now,
} as List;
}
await privateClient.storeObject('list', path, list);
return list;
},
async delete(id: string): Promise<void> {
await privateClient.remove(`_lists/${id}`);
},
async togglePlace(
listId: string,
placeId: string,
geohash: string
): Promise<List> {
const path = `_lists/${listId}`;
const list = (await privateClient.getObject(path)) as List;
if (!list) {
throw new Error(`List not found: ${listId}`);
}
const index = list.placeRefs.findIndex((ref: any) => ref.id === placeId);
if (index !== -1) {
// Remove
list.placeRefs.splice(index, 1);
} else {
// Add
list.placeRefs.push({ id: placeId, geohash });
}
list.updatedAt = new Date().toISOString();
await privateClient.storeObject('list', path, list);
return list;
},
async initDefaults(): Promise<void> {
const defaults = [
{ id: 'to-go', title: 'Want to go', color: '#ff00ff' }, // Magenta
{ id: 'to-do', title: 'To do', color: '#008000' }, // Green
];
for (const def of defaults) {
const existing = await this.get(def.id);
if (!existing) {
await this.create(def.id, def.title, def.color);
}
}
},
};
const places = { const places = {
lists,
/** /**
* Store a place. * Store a place.
* Generates ID and Geohash if missing. * Generates ID and Geohash if missing.