import { data, redirect } from "react-router"; import { useTranslation } from "react-i18next"; import type { Route } from "./+types/sync.import.$provider"; import { getSessionUser } from "~/lib/auth.server"; import { getProvider } from "~/lib/sync/registry"; import { getConnection, updateTokens } from "~/lib/sync/connections.server"; import { getImportedIds, recordImport } from "~/lib/sync/imports.server"; import { createActivity } from "~/lib/activities.server"; import { ClientDate } from "~/components/ClientDate"; export async function loader({ params, request }: Route.LoaderArgs) { const user = await getSessionUser(request); if (!user) return redirect("/auth/login"); const provider = getProvider(params.provider); if (!provider) throw data({ error: "Unknown provider" }, { status: 404 }); const connection = await getConnection(user.id, provider.id); if (!connection) return redirect("/settings"); const url = new URL(request.url); const page = parseInt(url.searchParams.get("page") ?? "1"); // Refresh token if needed let tokens = { accessToken: connection.accessToken, refreshToken: connection.refreshToken, expiresAt: connection.expiresAt, }; if (new Date() >= tokens.expiresAt) { tokens = await provider.refreshToken(tokens.refreshToken); await updateTokens(connection.id, tokens); } const workoutList = await provider.listWorkouts(tokens, page); const importedIds = await getImportedIds( user.id, provider.id, workoutList.workouts.map((w) => w.id), ); return data({ provider: { id: provider.id, name: provider.name }, workouts: workoutList.workouts.map((w) => ({ ...w, imported: importedIds.has(w.id), })), page: workoutList.page, totalPages: Math.ceil(workoutList.total / workoutList.perPage), }); } export async function action({ params, request }: Route.ActionArgs) { const user = await getSessionUser(request); if (!user) return redirect("/auth/login"); const provider = getProvider(params.provider); if (!provider) throw data({ error: "Unknown provider" }, { status: 404 }); const connection = await getConnection(user.id, provider.id); if (!connection) return redirect("/settings"); const formData = await request.formData(); const workoutId = formData.get("workoutId") as string; const workoutName = formData.get("workoutName") as string; const fileUrl = formData.get("fileUrl") as string; // Refresh token if needed let tokens = { accessToken: connection.accessToken, refreshToken: connection.refreshToken, expiresAt: connection.expiresAt, }; if (new Date() >= tokens.expiresAt) { tokens = await provider.refreshToken(tokens.refreshToken); await updateTokens(connection.id, tokens); } const workout = { id: workoutId, name: workoutName, type: "", startedAt: "", duration: null, distance: null, fileUrl: fileUrl || undefined, }; const fileBuffer = await provider.downloadFile(tokens, workout); const gpx = await provider.convertToGpx(fileBuffer); const activityId = await createActivity(user.id, { name: workoutName || `${provider.name} workout`, gpx: gpx ?? undefined, }); await recordImport(user.id, provider.id, workoutId, activityId); return data({ imported: workoutId }); } export function meta({ data: loaderData }: Route.MetaArgs) { const name = (loaderData as { provider: { name: string } })?.provider?.name ?? "Import"; return [{ title: `Import from ${name} — trails.cool` }]; } export default function SyncImportPage({ loaderData, actionData }: Route.ComponentProps) { const { provider, workouts, page, totalPages } = loaderData; const { t } = useTranslation("journal"); const justImported = (actionData as { imported?: string })?.imported; return (
{t("sync.noWorkouts")}
) : ({w.name}