Place lists #1
153
src/places.ts
153
src/places.ts
@@ -31,6 +31,32 @@ const placeSchema = {
|
||||
required: ['id', 'title', 'lat', 'lon', 'geohash', 'createdAt'],
|
||||
} 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.
|
||||
*
|
||||
@@ -101,6 +127,46 @@ export interface PlacesClient {
|
||||
* @returns An array of places.
|
||||
*/
|
||||
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 (
|
||||
@@ -108,6 +174,7 @@ const Places = function (
|
||||
): { exports: PlacesClient } {
|
||||
// Define Schema
|
||||
privateClient.declareType('place', placeSchema as any);
|
||||
privateClient.declareType('list', listSchema as any);
|
||||
|
||||
// Helper to normalize place object
|
||||
function preparePlace(data: Partial<Place>): Place {
|
||||
@@ -153,7 +220,93 @@ const Places = function (
|
||||
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 = {
|
||||
lists,
|
||||
/**
|
||||
* Store a place.
|
||||
* Generates ID and Geohash if missing.
|
||||
|
||||
Reference in New Issue
Block a user