Merge branch 'main' into dependabot/npm_and_yarn/production-e9efbc881e
This commit is contained in:
commit
8eeb775f54
122 changed files with 4617 additions and 190 deletions
|
|
@ -144,9 +144,12 @@ export type Audience = "public" | "followers-only";
|
|||
|
||||
export const activities = journalSchema.table("activities", {
|
||||
id: text("id").primaryKey(),
|
||||
ownerId: text("owner_id")
|
||||
.notNull()
|
||||
.references(() => users.id),
|
||||
// Local author. NULL for activities ingested from a remote trails
|
||||
// actor's outbox, where `remoteActorIri` identifies the author —
|
||||
// exactly one of the two is set (check constraint below; same
|
||||
// pattern as follows.follower_id / follower_actor_iri). Resolves
|
||||
// the owner_id open question in social-federation design.md.
|
||||
ownerId: text("owner_id").references(() => users.id),
|
||||
routeId: text("route_id").references(() => routes.id),
|
||||
name: text("name").notNull(),
|
||||
description: text("description").default(""),
|
||||
|
|
@ -164,16 +167,25 @@ export const activities = journalSchema.table("activities", {
|
|||
// Federation provenance (spec: social-federation). NULL for local
|
||||
// activities. For rows ingested from a remote trails actor's outbox:
|
||||
// `remoteOriginIri` is the activity's IRI on the origin instance
|
||||
// (unique → replay-safe `ON CONFLICT DO NOTHING` ingestion), and
|
||||
// `remoteActorIri` keys into `remote_actors`.
|
||||
// (unique → replay-safe `ON CONFLICT DO NOTHING` ingestion),
|
||||
// `remoteActorIri` keys into `remote_actors`, and
|
||||
// `remotePublishedAt` carries the origin's publish time (feed sort
|
||||
// uses COALESCE(remote_published_at, created_at)).
|
||||
remoteOriginIri: text("remote_origin_iri").unique(),
|
||||
remoteActorIri: text("remote_actor_iri"),
|
||||
remotePublishedAt: timestamp("remote_published_at", { withTimezone: true }),
|
||||
audience: text("audience").$type<Audience>().notNull().default("public"),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
}, (t) => ({
|
||||
// Hot paths: listActivities (sort by started_at) and listPublicActivitiesForOwner.
|
||||
ownerStartedIdx: index("activities_owner_started_idx").on(t.ownerId, t.startedAt.desc()),
|
||||
ownerCreatedIdx: index("activities_owner_created_idx").on(t.ownerId, t.createdAt.desc()),
|
||||
// Feed join for remote rows (spec §8).
|
||||
remoteActorIdx: index("activities_remote_actor_idx").on(t.remoteActorIri),
|
||||
hasAuthorCheck: check(
|
||||
"activities_has_author_check",
|
||||
sql`(${t.ownerId} IS NOT NULL) <> (${t.remoteActorIri} IS NOT NULL)`,
|
||||
),
|
||||
}));
|
||||
|
||||
// --- OAuth2 PKCE (mobile app auth) ---
|
||||
|
|
@ -328,6 +340,11 @@ export const importBatches = journalSchema.table("import_batches", {
|
|||
importedCount: integer("imported_count").notNull().default(0),
|
||||
duplicateCount: integer("duplicate_count").notNull().default(0),
|
||||
errorMessage: text("error_message"),
|
||||
// Ranged backfill requests (Garmin): the activity time window this
|
||||
// batch asked the provider to re-deliver. NULL for pick-list style
|
||||
// imports (komoot bulk) that aren't range-shaped.
|
||||
rangeStart: timestamp("range_start", { withTimezone: true }),
|
||||
rangeEnd: timestamp("range_end", { withTimezone: true }),
|
||||
startedAt: timestamp("started_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
completedAt: timestamp("completed_at", { withTimezone: true }),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ export default {
|
|||
},
|
||||
federation: {
|
||||
title: "Föderiert by design",
|
||||
body: "Deine Journal-Instanz spricht via ActivityPub mit anderen trails.cool-Servern. Folge Freundinnen und Freunden überall.",
|
||||
body: "Teil des Fediverse via ActivityPub: Mastodon-Nutzer:innen können dir folgen und deine öffentlichen Aktivitäten sehen, und du folgst trails-Nutzer:innen auf jeder Instanz.",
|
||||
},
|
||||
ownership: {
|
||||
title: "Deine Daten, immer",
|
||||
|
|
@ -321,6 +321,19 @@ export default {
|
|||
count_other: "Folgt {{count}}",
|
||||
empty: "Folgt noch niemandem.",
|
||||
},
|
||||
outgoing: {
|
||||
heading: "Folgen über Instanzen hinweg",
|
||||
subtitle: "Folge Leuten auf anderen trails.cool-Instanzen. Anfragen bleiben ausstehend, bis ihre Instanz sie annimmt.",
|
||||
handleLabel: "Handle der Person",
|
||||
followButton: "Folgen",
|
||||
pendingBadge: "Ausstehend",
|
||||
acceptedBadge: "Folge ich",
|
||||
cancelRequest: "Anfrage zurückziehen",
|
||||
unfollow: "Entfolgen",
|
||||
empty: "Noch keine Follows auf anderen Instanzen. Gib oben ein Handle ein, um zu starten.",
|
||||
notTrails: "Ausgehende Föderation ist derzeit nur zwischen trails-Instanzen möglich — diese Instanz nutzt kein trails.cool.",
|
||||
disabled: "Föderation ist auf dieser Instanz nicht aktiviert.",
|
||||
},
|
||||
prevPage: "Zurück",
|
||||
nextPage: "Weiter",
|
||||
pageOfTotal: "Seite {{page}} von {{totalPages}}",
|
||||
|
|
@ -328,6 +341,7 @@ export default {
|
|||
title: "Feed",
|
||||
heading: "Folge ich",
|
||||
empty: "Du folgst noch niemandem. Stöbere Profile durch und klicke auf Folgen, um deinen Feed aufzubauen.",
|
||||
followRemote: "Folge jemandem auf einer anderen trails-Instanz →",
|
||||
seePublic: "Oder durchstöbere den öffentlichen Feed dieser Instanz →",
|
||||
toggle: {
|
||||
followed: "Folge ich",
|
||||
|
|
@ -464,6 +478,29 @@ export default {
|
|||
},
|
||||
},
|
||||
sync: {
|
||||
garmin: {
|
||||
title: "Von Garmin importieren",
|
||||
subtitle:
|
||||
"Garmin liefert Aktivitäten automatisch an trails.cool, sobald sie entstehen. Für ältere Aktivitäten fordere unten einen Zeitraum an — Garmin liefert sie asynchron nach.",
|
||||
notConnected: "Dein Garmin-Konto ist noch nicht verbunden.",
|
||||
goConnect: "Garmin in den Einstellungen verbinden",
|
||||
needsRelink: "Deine Garmin-Verbindung muss neu verknüpft werden, bevor du Importe anfordern kannst.",
|
||||
from: "Von",
|
||||
to: "Bis",
|
||||
request: "Import anfordern",
|
||||
requesting: "Wird angefordert…",
|
||||
requested: "Angefordert! Aktivitäten erscheinen, sobald Garmin sie liefert.",
|
||||
asyncNote:
|
||||
"Garmin verarbeitet Verlaufs-Anfragen auf ihrer Seite — große Zeiträume können dauern. Überlappende Zeiträume erneut anzufordern ist unproblematisch; nichts wird doppelt importiert.",
|
||||
importedSince_one: "{{count}} Aktivität seit dieser Anfrage importiert",
|
||||
importedSince_other: "{{count}} Aktivitäten seit dieser Anfrage importiert",
|
||||
errors: {
|
||||
not_connected: "Dein Garmin-Konto ist nicht verbunden.",
|
||||
needs_relink: "Deine Garmin-Verbindung muss zuerst neu verknüpft werden.",
|
||||
invalid_range: "Wähle einen gültigen Zeitraum in der Vergangenheit (Start vor Ende).",
|
||||
request_failed: "Garmin hat die Anfrage abgelehnt — versuche es in ein paar Minuten erneut.",
|
||||
},
|
||||
},
|
||||
import: "Importieren",
|
||||
importFrom: "Import von {{provider}}",
|
||||
imported: "Importiert",
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ export default {
|
|||
},
|
||||
federation: {
|
||||
title: "Federated by design",
|
||||
body: "Your Journal instance talks to other trails.cool servers via ActivityPub. Follow friends anywhere.",
|
||||
body: "Part of the fediverse via ActivityPub: Mastodon users can follow you and see your public activities, and you can follow trails users on any instance.",
|
||||
},
|
||||
ownership: {
|
||||
title: "Your data, always",
|
||||
|
|
@ -321,6 +321,19 @@ export default {
|
|||
count_other: "Following {{count}}",
|
||||
empty: "Not following anyone yet.",
|
||||
},
|
||||
outgoing: {
|
||||
heading: "Following across instances",
|
||||
subtitle: "Follow people on other trails.cool instances. Requests stay pending until their instance accepts.",
|
||||
handleLabel: "Their handle",
|
||||
followButton: "Follow",
|
||||
pendingBadge: "Pending",
|
||||
acceptedBadge: "Following",
|
||||
cancelRequest: "Cancel request",
|
||||
unfollow: "Unfollow",
|
||||
empty: "No follows on other instances yet. Enter a handle above to start.",
|
||||
notTrails: "Outbound federation is currently trails-to-trails only — that instance doesn't run trails.cool.",
|
||||
disabled: "Federation is not enabled on this instance.",
|
||||
},
|
||||
prevPage: "Previous",
|
||||
nextPage: "Next",
|
||||
pageOfTotal: "Page {{page}} of {{totalPages}}",
|
||||
|
|
@ -328,6 +341,7 @@ export default {
|
|||
title: "Feed",
|
||||
heading: "Following",
|
||||
empty: "You're not following anyone yet. Browse profiles and tap Follow to start building your feed.",
|
||||
followRemote: "Follow someone on another trails instance →",
|
||||
seePublic: "Or browse the instance public feed →",
|
||||
toggle: {
|
||||
followed: "Following",
|
||||
|
|
@ -464,6 +478,29 @@ export default {
|
|||
},
|
||||
},
|
||||
sync: {
|
||||
garmin: {
|
||||
title: "Import from Garmin",
|
||||
subtitle:
|
||||
"Garmin delivers activities to trails.cool as they happen. To pull in your history, request a date range below — Garmin sends those activities over asynchronously.",
|
||||
notConnected: "Your Garmin account isn't connected yet.",
|
||||
goConnect: "Connect Garmin in settings",
|
||||
needsRelink: "Your Garmin connection needs to be re-linked before requesting imports.",
|
||||
from: "From",
|
||||
to: "To",
|
||||
request: "Request import",
|
||||
requesting: "Requesting…",
|
||||
requested: "Requested! Activities will appear as Garmin delivers them.",
|
||||
asyncNote:
|
||||
"Garmin processes history requests on their side — large ranges can take a while to arrive. Re-requesting an overlapping range is safe; nothing gets imported twice.",
|
||||
importedSince_one: "{{count}} activity imported since this request",
|
||||
importedSince_other: "{{count}} activities imported since this request",
|
||||
errors: {
|
||||
not_connected: "Your Garmin account isn't connected.",
|
||||
needs_relink: "Your Garmin connection needs to be re-linked first.",
|
||||
invalid_range: "Pick a valid date range in the past (start before end).",
|
||||
request_failed: "Garmin rejected the request — try again in a few minutes.",
|
||||
},
|
||||
},
|
||||
import: "Import",
|
||||
importFrom: "Import from {{provider}}",
|
||||
imported: "Imported",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue