From 8641b0ad902cb85611557941ddc62f74b7aa71ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Sat, 23 May 2026 18:06:06 +0200 Subject: [PATCH] Add activity sort toggle on user profile page Default sort is by activity date (startedAt); users can switch to "Date added" (createdAt) via a URL query param (?sort=addedAt). Activities without a startedAt fall to the bottom when sorted by date. Co-Authored-By: Claude Sonnet 4.6 --- apps/journal/app/lib/activities.server.ts | 8 +++-- apps/journal/app/routes/users.$username.tsx | 33 +++++++++++++++++---- packages/i18n/src/locales/de.ts | 2 ++ packages/i18n/src/locales/en.ts | 2 ++ 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/apps/journal/app/lib/activities.server.ts b/apps/journal/app/lib/activities.server.ts index d41d6f2..7f9f9dc 100644 --- a/apps/journal/app/lib/activities.server.ts +++ b/apps/journal/app/lib/activities.server.ts @@ -143,13 +143,17 @@ export async function listActivities(ownerId: string) { * listings (the public profile page); never includes `unlisted` or * `private` content. */ -export async function listPublicActivitiesForOwner(ownerId: string) { +export async function listPublicActivitiesForOwner( + ownerId: string, + sort: "startedAt" | "addedAt" = "startedAt", +) { const db = getDb(); + const order = sort === "addedAt" ? desc(activities.createdAt) : desc(activities.startedAt); const rows = await db .select() .from(activities) .where(and(eq(activities.ownerId, ownerId), eq(activities.visibility, "public"))) - .orderBy(desc(activities.createdAt)); + .orderBy(order); const ids = rows.map((r) => r.id); const geojsonMap = ids.length > 0 ? await getSimplifiedActivityGeojsonBatch(ids) : new Map(); diff --git a/apps/journal/app/routes/users.$username.tsx b/apps/journal/app/routes/users.$username.tsx index 83b566c..53bb470 100644 --- a/apps/journal/app/routes/users.$username.tsx +++ b/apps/journal/app/routes/users.$username.tsx @@ -43,10 +43,14 @@ export async function loader({ params, request }: Route.LoaderArgs) { countFollowers(user.id), countFollowing(user.id), ]); + const url = new URL(request.url); + const sortParam = url.searchParams.get("sort"); + const activitySort = sortParam === "addedAt" ? "addedAt" : "startedAt"; + const [publicRoutes, publicActivities] = canSeeContent ? await Promise.all([ listPublicRoutesForOwner(user.id), - listPublicActivitiesForOwner(user.id), + listPublicActivitiesForOwner(user.id, activitySort), ]) : [[], []]; @@ -80,6 +84,7 @@ export async function loader({ params, request }: Route.LoaderArgs) { startedAt: a.startedAt?.toISOString() ?? null, createdAt: a.createdAt.toISOString(), })), + activitySort, isOwn, isDemoUser, followers, @@ -113,7 +118,7 @@ export function meta({ data: loaderData }: Route.MetaArgs) { } export default function UserProfilePage({ loaderData }: Route.ComponentProps) { - const { user, routes, activities, isOwn, isDemoUser, followers, following, followState, isLoggedIn, canSeeContent } = loaderData; + const { user, routes, activities, activitySort, isOwn, isDemoUser, followers, following, followState, isLoggedIn, canSeeContent } = loaderData; const { t } = useTranslation("journal"); return ( @@ -259,9 +264,27 @@ export default function UserProfilePage({ loaderData }: Route.ComponentProps) {
-

- {t("activities.title")} ({activities.length}) -

+
+

+ {t("activities.title")} ({activities.length}) +

+ {activities.length > 0 && ( +
+ + {t("activities.sortByDate")} + + + {t("activities.sortByAdded")} + +
+ )} +
{activities.length === 0 ? (

{t("profile.noPublicActivities")}

) : ( diff --git a/packages/i18n/src/locales/de.ts b/packages/i18n/src/locales/de.ts index f54326d..ee60ca2 100644 --- a/packages/i18n/src/locales/de.ts +++ b/packages/i18n/src/locales/de.ts @@ -351,6 +351,8 @@ export default { delete: "Aktivität löschen", deleteConfirm: "Möchtest du diese Aktivität wirklich löschen?", importedFrom: "Importiert von {{provider}}", + sortByDate: "Aktivitätsdatum", + sortByAdded: "Hinzugefügt am", visibility: { label: "Sichtbarkeit", private: "Privat", diff --git a/packages/i18n/src/locales/en.ts b/packages/i18n/src/locales/en.ts index f380998..426e177 100644 --- a/packages/i18n/src/locales/en.ts +++ b/packages/i18n/src/locales/en.ts @@ -351,6 +351,8 @@ export default { delete: "Delete Activity", deleteConfirm: "Are you sure you want to delete this activity?", importedFrom: "Imported from {{provider}}", + sortByDate: "Activity date", + sortByAdded: "Date added", visibility: { label: "Visibility", private: "Private",