jvcgpt / src /contexts /topics.ts
Greums's picture
major improvements to the app
a417977
raw
history blame
9.91 kB
import {createContext} from "@/utils/context";
import {generateUUID} from "@/utils/uuids";
import {getCurrentTimeIso8601} from "@/utils/dates";
import {useContext} from "preact/hooks";
import {Settings} from "@/contexts/settings";
import {feedPosts, feedTopic} from "@/utils/model";
import {LogAction} from "@/contexts/log";
export type Post = {
user: string;
date: string; // date from AI if generated by AI tokens, ISO 8601, YYYY-MM-DDTHH:MM:SS
generationDate: string | null; //"null" means not generated by AI, ISO 8601, YYYY-MM-DDTHH:MM:SS
content: string;
}
export type Topic = {
id: string; // UUID
title: string;
posts: Post[];
}
export type TopicsContext = {
generation: "done" | "pending" | "error";
import: "done" | "error";
topics: Topic[];
}
const itemKey = "topics";
export const topicsCtx = createContext({
initialValue: () => {
const storedTopics = localStorage.getItem(itemKey);
return {
generation: "done",
import: "done",
topics: storedTopics ? JSON.parse(storedTopics) as Topic[] : []
} as TopicsContext;
},
controllers: (topicsContext: TopicsContext, setTopicsContext) => ({
effect: () => {
// console.log("**")
localStorage.setItem(itemKey, JSON.stringify(topicsContext.topics));
},
actions: {
reset: (): void => {
setTopicsContext({
generation: "done",
import: "done",
topics: []
});
},
addTopic: (user: string, title: string, text: string): string => {
const id = generateUUID();
setTopicsContext({
...topicsContext,
topics: [...topicsContext.topics, {
id: id,
title,
posts: [{
user: user,
date: getCurrentTimeIso8601(),
generationDate: null,
content: text,
}]
}],
});
return id;
},
deleteTopic: (topicId: string): void => {
setTopicsContext({
...topicsContext,
topics: topicsContext.topics.filter((topic) => topic.id !== topicId)
});
},
addPost: (topicId: string, user: string, text: string): void => {
const newPost: Post = {
user: user,
date: getCurrentTimeIso8601(),
generationDate: null,
content: text,
}
setTopicsContext({
...topicsContext,
topics: topicsContext.topics.map((topic) => topic.id === topicId ? {
...topic,
posts: [...topic.posts, newPost]
} : topic)
});
},
deletePost: (topicId: string, postIndex: number): void => {
setTopicsContext({
...topicsContext,
topics: topicsContext.topics.map((topic) => {
if (topic.id !== topicId) {
return topic;
}
// Delete all posts if the first is deleted
const posts: Post[] = postIndex === 0 ? [] : topic.posts.filter((_, index) => index !== postIndex);
return {
...topic,
posts
};
// Delete topic if it has not more posts
}).filter((t) => t.posts.length > 0)
});
},
generateTopic: async (settings: Settings, log: LogAction) => {
const id = generateUUID();
setTopicsContext({
...topicsContext,
generation: "pending",
});
log(`Topic: ${id} -> generation start.`)
feedTopic(settings, log, id, (topic: Topic) => {
if(topic.title.length < 1) return;
setTopicsContext((topicsContext: TopicsContext) => {
// console.log(topicsContext);
const topicIndex = topicsContext.topics.findIndex((topic) => topic.id === id);
// -1 if no topic found
if(topicIndex < 0) {
return {
...topicsContext,
generation: "pending",
topics: [...topicsContext.topics, topic]
} satisfies TopicsContext
}
return {
...topicsContext,
generation: "pending",
// Replace the old topic with the new one
topics: topicsContext.topics.map(oldTopic => oldTopic.id === id ? topic : oldTopic),
} satisfies TopicsContext
});
// console.log("feedTopic");
}).then(() => {
// console.log("then");
// TODO: check if the topic has been generated
setTopicsContext((topicsContext: TopicsContext) => ({
...topicsContext,
generation: "done",
}))
log(`Topic: ${id} -> generation done.`)
}).catch((e: Error) => {
setTopicsContext((topicsContext: TopicsContext) => ({
...topicsContext,
generation: "error",
}))
log(`Topic: ${id} -> generation error (${e.message}).`)
});
},
generatePosts: async (settings: Settings, log: LogAction, initialTopic: Topic) => {
setTopicsContext({
...topicsContext,
generation: "pending",
});
log(`Topic: ${initialTopic.id} -> generation start.`)
feedPosts(settings, log, initialTopic, (topic: Topic) => {
setTopicsContext((topicsContext: TopicsContext) => {
// console.log(topicsContext);
const topicIndex = topicsContext.topics.findIndex((topic) => topic.id === initialTopic.id);
// -1 if no topic found
if(topicIndex < 0) {
return {
...topicsContext,
generation: "pending",
topics: [...topicsContext.topics, topic]
} satisfies TopicsContext
}
return {
...topicsContext,
generation: "pending",
// Replace the old topic with the new one
topics: topicsContext.topics.map(oldTopic => oldTopic.id === initialTopic.id ? topic : oldTopic),
} satisfies TopicsContext
});
}).then(() => {
setTopicsContext((topicsContext: TopicsContext) => ({
...topicsContext,
generation: "done",
}))
log(`Topic: ${initialTopic.id} -> generation done.`)
}).catch((e: Error) => {
setTopicsContext((topicsContext: TopicsContext) => ({
...topicsContext,
generation: "error",
}))
log(`Topic: ${initialTopic.id} -> generation error (${e.message}).`)
});
},
importTopic: (json: string): void => {
const error = (message: string): void => {
throw new Error(message)
}
try {
const object = JSON.parse(json);
const id = generateUUID();
const topic: Topic = {
id: id,
title: typeof object.title == "string" ? object.title : error("title must be a string"),
posts: object.posts instanceof Array ? object.posts.map((object: unknown): Post => ({
user: typeof object.user == "string" ? object.user : error("posts.user must be a string"),
date: typeof object.date == "string" ? object.date : error("posts.date must be a string"),
generationDate: typeof object.generationDate == "string" || object.generationDate === null ? object.generationDate : error("posts.generationDate must be null or string"),
content: typeof object.content == "string" ? object.content : error("posts.content must be a string"),
})) : error("posts must be a string"),
}
setTopicsContext({
...topicsContext,
topics: [...topicsContext.topics, topic],
import: "done",
});
} catch (e) {
console.error(e);
setTopicsContext({
...topicsContext,
import: "error",
});
}
}
},
})
})