mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-28 15:37:44 +02:00
chore: archive inactive projects to apps-archived/
Move inactive projects out of active workspace: - bauntown (community website) - maerchenzauber (AI story generation) - memoro (voice memo app) - news (news aggregation) - nutriphi (nutrition tracking) - reader (reading app) - uload (URL shortener) - wisekeep (AI wisdom extraction) Update CLAUDE.md documentation: - Add presi to active projects - Document archived projects section - Update workspace configuration Archived apps can be re-activated by moving back to apps/ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b97149ac12
commit
61d181fbc2
3148 changed files with 437 additions and 46640 deletions
177
apps-archived/reader/apps/mobile/hooks/useTexts.ts
Normal file
177
apps-archived/reader/apps/mobile/hooks/useTexts.ts
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { supabase } from '~/utils/supabase';
|
||||
import { Text, TextData } from '~/types/database';
|
||||
|
||||
export const useTexts = () => {
|
||||
const [texts, setTexts] = useState<Text[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetchTexts();
|
||||
|
||||
// Realtime Subscription
|
||||
const subscription = supabase
|
||||
.channel('texts_changes')
|
||||
.on(
|
||||
'postgres_changes',
|
||||
{
|
||||
event: '*',
|
||||
schema: 'public',
|
||||
table: 'texts',
|
||||
},
|
||||
(payload) => {
|
||||
if (payload.eventType === 'INSERT') {
|
||||
// Check if text already exists to avoid duplicates
|
||||
setTexts((prev) => {
|
||||
const exists = prev.some((text) => text.id === payload.new.id);
|
||||
if (exists) return prev;
|
||||
return [payload.new as Text, ...prev];
|
||||
});
|
||||
} else if (payload.eventType === 'UPDATE') {
|
||||
setTexts((prev) =>
|
||||
prev.map((text) => (text.id === payload.new.id ? (payload.new as Text) : text))
|
||||
);
|
||||
} else if (payload.eventType === 'DELETE') {
|
||||
setTexts((prev) => prev.filter((text) => text.id !== payload.old.id));
|
||||
}
|
||||
}
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
return () => {
|
||||
subscription.unsubscribe();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const fetchTexts = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const { data, error } = await supabase
|
||||
.from('texts')
|
||||
.select('*')
|
||||
.order('updated_at', { ascending: false });
|
||||
|
||||
if (error) throw error;
|
||||
setTexts(data || []);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Ein Fehler ist aufgetreten');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const createText = async (title: string, content: string, initialData?: Partial<TextData>) => {
|
||||
try {
|
||||
// Get current user
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabase.auth.getUser();
|
||||
|
||||
if (!user) {
|
||||
throw new Error('Benutzer nicht eingeloggt');
|
||||
}
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('texts')
|
||||
.insert({
|
||||
title,
|
||||
content,
|
||||
user_id: user.id, // Explicitly set user_id
|
||||
data: {
|
||||
tts: { speed: 1.0, voice: 'de-DE-Neural2-A' },
|
||||
tags: [],
|
||||
stats: { playCount: 0, totalTime: 0, completed: false },
|
||||
...initialData,
|
||||
},
|
||||
})
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
// Refresh the texts list to ensure we have the latest data
|
||||
await fetchTexts();
|
||||
|
||||
return { data, error: null };
|
||||
} catch (err) {
|
||||
return {
|
||||
data: null,
|
||||
error: err instanceof Error ? err.message : 'Fehler beim Erstellen',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const updateText = async (textId: string, updates: Partial<Text>) => {
|
||||
try {
|
||||
const { data, error } = await supabase
|
||||
.from('texts')
|
||||
.update(updates)
|
||||
.eq('id', textId)
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
return { data, error: null };
|
||||
} catch (err) {
|
||||
return {
|
||||
data: null,
|
||||
error: err instanceof Error ? err.message : 'Fehler beim Aktualisieren',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const deleteText = async (textId: string) => {
|
||||
try {
|
||||
const { error } = await supabase.from('texts').delete().eq('id', textId);
|
||||
|
||||
if (error) throw error;
|
||||
return { error: null };
|
||||
} catch (err) {
|
||||
return {
|
||||
error: err instanceof Error ? err.message : 'Fehler beim Löschen',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const updatePosition = async (textId: string, position: number) => {
|
||||
const text = texts.find((t) => t.id === textId);
|
||||
if (!text) return { error: 'Text nicht gefunden' };
|
||||
|
||||
return updateText(textId, {
|
||||
data: {
|
||||
...text.data,
|
||||
tts: {
|
||||
...text.data.tts,
|
||||
lastPosition: position,
|
||||
lastPlayed: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const getTextsByTag = (tag: string) => {
|
||||
return texts.filter((text) => text.data.tags?.includes(tag));
|
||||
};
|
||||
|
||||
const getAllTags = () => {
|
||||
const tagSet = new Set<string>();
|
||||
texts.forEach((text) => {
|
||||
text.data.tags?.forEach((tag) => tagSet.add(tag));
|
||||
});
|
||||
return Array.from(tagSet).sort();
|
||||
};
|
||||
|
||||
return {
|
||||
texts,
|
||||
loading,
|
||||
error,
|
||||
createText,
|
||||
updateText,
|
||||
deleteText,
|
||||
updatePosition,
|
||||
getTextsByTag,
|
||||
getAllTags,
|
||||
refetch: fetchTexts,
|
||||
};
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue