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 <noreply@anthropic.com>
This commit is contained in:
parent
3f1d043377
commit
8641b0ad90
4 changed files with 38 additions and 7 deletions
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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) {
|
|||
</section>
|
||||
|
||||
<section className="mt-8">
|
||||
<h2 className="text-lg font-semibold text-gray-900">
|
||||
{t("activities.title")} ({activities.length})
|
||||
</h2>
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<h2 className="text-lg font-semibold text-gray-900">
|
||||
{t("activities.title")} ({activities.length})
|
||||
</h2>
|
||||
{activities.length > 0 && (
|
||||
<div className="flex items-center gap-1 rounded-md border border-gray-200 p-0.5 text-sm">
|
||||
<a
|
||||
href={`?sort=startedAt`}
|
||||
className={`rounded px-2.5 py-1 ${activitySort === "startedAt" ? "bg-gray-100 font-medium text-gray-900" : "text-gray-500 hover:text-gray-700"}`}
|
||||
>
|
||||
{t("activities.sortByDate")}
|
||||
</a>
|
||||
<a
|
||||
href={`?sort=addedAt`}
|
||||
className={`rounded px-2.5 py-1 ${activitySort === "addedAt" ? "bg-gray-100 font-medium text-gray-900" : "text-gray-500 hover:text-gray-700"}`}
|
||||
>
|
||||
{t("activities.sortByAdded")}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{activities.length === 0 ? (
|
||||
<p className="mt-2 text-sm text-gray-500">{t("profile.noPublicActivities")}</p>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue