mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-08-29 01:35:39 -04:00
Add @sofa/native Expo React Native app (#7)
This commit is contained in:
+15
-15
@@ -18,13 +18,13 @@
|
||||
"@fontsource/dm-serif-display": "5.2.8",
|
||||
"@orpc/client": "catalog:",
|
||||
"@orpc/contract": "catalog:",
|
||||
"@orpc/tanstack-query": "1.13.6",
|
||||
"@orpc/tanstack-query": "catalog:",
|
||||
"@player.style/sutro": "0.2.1",
|
||||
"@sofa/api": "workspace:*",
|
||||
"@tabler/icons-react": "3.40.0",
|
||||
"@tanstack/react-hotkeys": "0.4.1",
|
||||
"@tanstack/react-query": "5.90.21",
|
||||
"@tanstack/react-router": "1.166.6",
|
||||
"@tanstack/react-query": "catalog:",
|
||||
"@tanstack/react-router": "1.166.7",
|
||||
"better-auth": "catalog:",
|
||||
"class-variance-authority": "0.7.1",
|
||||
"clsx": "2.1.1",
|
||||
@@ -32,14 +32,14 @@
|
||||
"date-fns": "catalog:",
|
||||
"jotai": "2.18.1",
|
||||
"media-chrome": "4.18.0",
|
||||
"motion": "12.35.2",
|
||||
"react": "19.2.4",
|
||||
"motion": "12.36.0",
|
||||
"react": "catalog:",
|
||||
"react-day-picker": "9.14.0",
|
||||
"react-dom": "19.2.4",
|
||||
"react-dom": "catalog:",
|
||||
"recharts": "3.8.0",
|
||||
"shadcn": "4.0.3",
|
||||
"shadcn": "4.0.5",
|
||||
"sonner": "2.0.7",
|
||||
"tailwind-merge": "3.5.0",
|
||||
"tailwind-merge": "catalog:",
|
||||
"tw-animate-css": "1.4.0",
|
||||
"vaul": "1.1.2",
|
||||
"youtube-video-element": "1.9.0",
|
||||
@@ -47,18 +47,18 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "4.2.1",
|
||||
"@tanstack/devtools-vite": "0.5.3",
|
||||
"@tanstack/react-devtools": "0.9.10",
|
||||
"@tanstack/devtools-vite": "0.5.5",
|
||||
"@tanstack/react-devtools": "0.9.13",
|
||||
"@tanstack/react-query-devtools": "5.91.3",
|
||||
"@tanstack/react-router-devtools": "1.166.6",
|
||||
"@tanstack/router-plugin": "1.166.6",
|
||||
"@tanstack/react-router-devtools": "1.166.7",
|
||||
"@tanstack/router-plugin": "1.166.7",
|
||||
"@types/bun": "catalog:",
|
||||
"@types/node": "25.4.0",
|
||||
"@types/react": "19.2.14",
|
||||
"@types/node": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@vitejs/plugin-react": "5.1.4",
|
||||
"babel-plugin-react-compiler": "1.0.0",
|
||||
"tailwindcss": "4.2.1",
|
||||
"tailwindcss": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "7.3.1",
|
||||
"vite-tsconfig-paths": "6.1.1"
|
||||
|
||||
@@ -72,12 +72,12 @@ interface SearchResult {
|
||||
tmdbId: number;
|
||||
type: "movie" | "tv" | "person";
|
||||
title: string;
|
||||
posterPath?: string | null;
|
||||
profilePath?: string | null;
|
||||
releaseDate?: string | null;
|
||||
voteAverage?: number;
|
||||
knownFor?: string[];
|
||||
knownForDepartment?: string;
|
||||
posterPath: string | null;
|
||||
profilePath: string | null;
|
||||
releaseDate: string | null;
|
||||
voteAverage: number | null;
|
||||
knownFor: string[] | null;
|
||||
knownForDepartment: string | null;
|
||||
}
|
||||
|
||||
export function CommandPalette() {
|
||||
|
||||
@@ -148,11 +148,13 @@ export function StatsDisplay({ stats }: { stats: DashboardStats }) {
|
||||
const [episodePeriod, setEpisodePeriod] = useState<TimePeriod>("this_week");
|
||||
|
||||
const { data: movieStats } = useQuery(
|
||||
orpc.stats.queryOptions({ input: { type: "movies", period: moviePeriod } }),
|
||||
orpc.dashboard.watchHistory.queryOptions({
|
||||
input: { type: "movie", period: moviePeriod },
|
||||
}),
|
||||
);
|
||||
const { data: episodeStats } = useQuery(
|
||||
orpc.stats.queryOptions({
|
||||
input: { type: "episodes", period: episodePeriod },
|
||||
orpc.dashboard.watchHistory.queryOptions({
|
||||
input: { type: "episode", period: episodePeriod },
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ interface TitleGridItem {
|
||||
title: string;
|
||||
posterPath: string | null;
|
||||
releaseDate?: string | null;
|
||||
firstAirDate?: string | null;
|
||||
voteAverage?: number | null;
|
||||
userStatus?: "watchlist" | "in_progress" | "completed" | null;
|
||||
}
|
||||
@@ -45,7 +46,7 @@ export function TitleGrid({ items }: { items: TitleGridItem[] }) {
|
||||
type={t.type}
|
||||
title={t.title}
|
||||
posterPath={t.posterPath}
|
||||
releaseDate={t.releaseDate}
|
||||
releaseDate={t.releaseDate ?? t.firstAirDate}
|
||||
voteAverage={t.voteAverage}
|
||||
userStatus={t.userStatus}
|
||||
/>
|
||||
|
||||
@@ -16,7 +16,8 @@ interface TitleRowItem {
|
||||
title: string;
|
||||
posterPath: string | null;
|
||||
releaseDate: string | null;
|
||||
voteAverage: number;
|
||||
firstAirDate: string | null;
|
||||
voteAverage: number | null;
|
||||
}
|
||||
|
||||
type TitleStatus = "watchlist" | "in_progress" | "completed";
|
||||
@@ -45,7 +46,7 @@ export function FilterableTitleRow({
|
||||
orpc.discover.queryOptions({
|
||||
input:
|
||||
selectedGenre != null
|
||||
? { mediaType, genreId: selectedGenre }
|
||||
? { type: mediaType, genreId: selectedGenre }
|
||||
: skipToken,
|
||||
}),
|
||||
);
|
||||
@@ -140,7 +141,7 @@ export function FilterableTitleRow({
|
||||
type={item.type}
|
||||
title={item.title}
|
||||
posterPath={item.posterPath}
|
||||
releaseDate={item.releaseDate}
|
||||
releaseDate={item.releaseDate ?? item.firstAirDate}
|
||||
voteAverage={item.voteAverage}
|
||||
userStatus={userStatuses[`${item.tmdbId}-${item.type}`]}
|
||||
episodeProgress={
|
||||
|
||||
@@ -7,7 +7,8 @@ interface TitleRowItem {
|
||||
title: string;
|
||||
posterPath: string | null;
|
||||
releaseDate: string | null;
|
||||
voteAverage: number;
|
||||
firstAirDate: string | null;
|
||||
voteAverage: number | null;
|
||||
}
|
||||
|
||||
interface TitleRowProps {
|
||||
@@ -51,7 +52,7 @@ export function TitleRow({
|
||||
type={item.type}
|
||||
title={item.title}
|
||||
posterPath={item.posterPath}
|
||||
releaseDate={item.releaseDate}
|
||||
releaseDate={item.releaseDate ?? item.firstAirDate}
|
||||
voteAverage={item.voteAverage}
|
||||
userStatus={userStatuses?.[`${item.tmdbId}-${item.type}`]}
|
||||
episodeProgress={
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { SystemHealthData } from "@sofa/api/schemas";
|
||||
import type { CronJobName, SystemHealthData } from "@sofa/api/schemas";
|
||||
import {
|
||||
IconActivity,
|
||||
IconAlertTriangle,
|
||||
@@ -129,15 +129,12 @@ function LiveTimeAgo({
|
||||
/** Hydrates system health state and renders the 3 cards */
|
||||
export function SystemHealthCards() {
|
||||
const queryClient = useQueryClient();
|
||||
const {
|
||||
data: statusData,
|
||||
isPending,
|
||||
isFetching,
|
||||
} = useQuery(orpc.systemStatus.queryOptions());
|
||||
const data = statusData?.health ?? null;
|
||||
const { data, isPending, isFetching } = useQuery(
|
||||
orpc.system.health.queryOptions(),
|
||||
);
|
||||
const isRefreshing = isFetching;
|
||||
const refresh = () =>
|
||||
queryClient.invalidateQueries({ queryKey: orpc.systemStatus.key() });
|
||||
queryClient.invalidateQueries({ queryKey: orpc.system.health.key() });
|
||||
|
||||
if (isPending || !data) return <SkeletonCards />;
|
||||
|
||||
@@ -519,7 +516,9 @@ function BackgroundJobsCard({
|
||||
className="size-6"
|
||||
disabled={isRunning || job.disabled}
|
||||
onClick={() =>
|
||||
triggerJobMutation.mutate({ name: job.jobName })
|
||||
triggerJobMutation.mutate({
|
||||
name: job.jobName as CronJobName,
|
||||
})
|
||||
}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ function QuickAddButton({
|
||||
}, [userStatus]);
|
||||
|
||||
const quickAddMutation = useMutation(
|
||||
orpc.watchlist.quickAdd.mutationOptions({
|
||||
orpc.titles.quickAdd.mutationOptions({
|
||||
onSuccess: () => setAddedStatus("watchlist"),
|
||||
}),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user