adds profile timeline and follows
This commit is contained in:
80
db/migrations/1734196620_created_follows.go
Normal file
80
db/migrations/1734196620_created_follows.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
"github.com/pocketbase/pocketbase/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(db dbx.Builder) error {
|
||||||
|
jsonData := `{
|
||||||
|
"id": "8obn1ukumze565i",
|
||||||
|
"created": "2024-12-14 17:17:00.381Z",
|
||||||
|
"updated": "2024-12-14 17:17:00.381Z",
|
||||||
|
"name": "follows",
|
||||||
|
"type": "base",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "in1traur",
|
||||||
|
"name": "follower",
|
||||||
|
"type": "relation",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "_pb_users_auth_",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "wxwomfd5",
|
||||||
|
"name": "followee",
|
||||||
|
"type": "relation",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "_pb_users_auth_",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": "@request.auth.id = follower.id || @request.auth.id = followee.id",
|
||||||
|
"viewRule": "@request.auth.id = follower.id || @request.auth.id = followee.id",
|
||||||
|
"createRule": "@request.auth.id = follower.id",
|
||||||
|
"updateRule": "@request.auth.id = follower.id",
|
||||||
|
"deleteRule": "@request.auth.id = follower.id",
|
||||||
|
"options": {}
|
||||||
|
}`
|
||||||
|
|
||||||
|
collection := &models.Collection{}
|
||||||
|
if err := json.Unmarshal([]byte(jsonData), &collection); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return daos.New(db).SaveCollection(collection)
|
||||||
|
}, func(db dbx.Builder) error {
|
||||||
|
dao := daos.New(db);
|
||||||
|
|
||||||
|
collection, err := dao.FindCollectionByNameOrId("8obn1ukumze565i")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dao.DeleteCollection(collection)
|
||||||
|
})
|
||||||
|
}
|
||||||
74
db/migrations/1734200418_created_follow_counts.go
Normal file
74
db/migrations/1734200418_created_follow_counts.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
"github.com/pocketbase/pocketbase/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(db dbx.Builder) error {
|
||||||
|
jsonData := `{
|
||||||
|
"id": "j6w72f0kb5ivd7x",
|
||||||
|
"created": "2024-12-14 18:20:18.920Z",
|
||||||
|
"updated": "2024-12-14 18:20:18.920Z",
|
||||||
|
"name": "follow_counts",
|
||||||
|
"type": "view",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "w81n64il",
|
||||||
|
"name": "followers",
|
||||||
|
"type": "json",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSize": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "rfnejpto",
|
||||||
|
"name": "following",
|
||||||
|
"type": "json",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSize": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": "",
|
||||||
|
"viewRule": "",
|
||||||
|
"createRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {
|
||||||
|
"query": "SELECT \n users.id,\n COALESCE(followers.count, 0) AS followers,\n COALESCE(following.count, 0) AS following\nFROM users\nLEFT JOIN (\n SELECT followee AS user_id, COUNT(*) AS count\n FROM follows\n GROUP BY followee\n) AS followers ON users.id = followers.user_id\nLEFT JOIN (\n SELECT follower AS user_id, COUNT(*) AS count\n FROM follows\n GROUP BY follower\n) AS following ON users.id = following.user_id;"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
collection := &models.Collection{}
|
||||||
|
if err := json.Unmarshal([]byte(jsonData), &collection); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return daos.New(db).SaveCollection(collection)
|
||||||
|
}, func(db dbx.Builder) error {
|
||||||
|
dao := daos.New(db);
|
||||||
|
|
||||||
|
collection, err := dao.FindCollectionByNameOrId("j6w72f0kb5ivd7x")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dao.DeleteCollection(collection)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -85,8 +85,8 @@
|
|||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 6px solid #fff;
|
border: 6px solid rgba(var(--content));
|
||||||
border-color: #fff transparent #fff transparent;
|
border-color: rgba(var(--content)) transparent rgba(var(--content)) transparent;
|
||||||
animation: spinner 1.2s linear infinite;
|
animation: spinner 1.2s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,12 +23,13 @@
|
|||||||
on:click
|
on:click
|
||||||
{type}
|
{type}
|
||||||
>
|
>
|
||||||
{#if !loading}
|
<div class:invisible={loading}>
|
||||||
{#if icon}
|
{#if icon}
|
||||||
<i class="fa fa-{icon} mr-2"></i>
|
<i class="fa fa-{icon} mr-2"></i>
|
||||||
{/if}
|
{/if}
|
||||||
<slot />
|
<slot />
|
||||||
{:else}
|
</div>
|
||||||
<div class="spinner"></div>
|
{#if loading}
|
||||||
|
<div class="absolute aspect-square spinner"></div>
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -24,8 +24,7 @@
|
|||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<a class="underline" href="/profile/{user.id}"> {user.username} </a>
|
<a class="underline" href="/profile/{user.id}"> {user.username} </a>
|
||||||
{activity.type === "trail" ? "planned" : "completed"}
|
{activity.type === "trail" ? $_("planned-a-trail") : $_("completed-a-trail")}
|
||||||
a trail
|
|
||||||
<p class="text-xs text-gray-500 mb-3">
|
<p class="text-xs text-gray-500 mb-3">
|
||||||
{new Date(activity.date).toLocaleDateString(undefined, {
|
{new Date(activity.date).toLocaleDateString(undefined, {
|
||||||
month: "long",
|
month: "long",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
import { pb } from "$lib/pocketbase";
|
import { pb } from "$lib/pocketbase";
|
||||||
|
|
||||||
export let trail: Trail;
|
export let trail: Trail;
|
||||||
|
export let fullWidth: boolean = false;
|
||||||
|
|
||||||
$: thumbnail = trail.photos.length
|
$: thumbnail = trail.photos.length
|
||||||
? getFileURL(trail, trail.photos[trail.thumbnail])
|
? getFileURL(trail, trail.photos[trail.thumbnail])
|
||||||
@@ -20,7 +21,9 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="trail-card relative rounded-2xl border border-input-border min-w-72 cursor-pointer"
|
class="trail-card relative rounded-2xl border border-input-border {fullWidth
|
||||||
|
? 'min-w-72'
|
||||||
|
: 'w-72'} cursor-pointer"
|
||||||
on:mouseenter
|
on:mouseenter
|
||||||
on:mouseleave
|
on:mouseleave
|
||||||
role="listitem"
|
role="listitem"
|
||||||
@@ -65,7 +68,8 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#if trail.expand.author}
|
{#if trail.expand.author}
|
||||||
<p class="text-xs text-gray-500 mb-3">
|
<p class="text-xs text-gray-500 mb-3">
|
||||||
{$_("by")} <img
|
{$_("by")}
|
||||||
|
<img
|
||||||
class="rounded-full w-5 aspect-square mx-1 inline"
|
class="rounded-full w-5 aspect-square mx-1 inline"
|
||||||
src={getFileURL(
|
src={getFileURL(
|
||||||
trail.expand.author,
|
trail.expand.author,
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
page: 1,
|
page: 1,
|
||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
export let loading: boolean = false;
|
export let loading: boolean = false;
|
||||||
|
export let fullWidthCards: boolean = false;
|
||||||
|
|
||||||
const displayOptions: SelectItem[] = [
|
const displayOptions: SelectItem[] = [
|
||||||
{ text: $_("card", { values: { n: 2 } }), value: "cards" },
|
{ text: $_("card", { values: { n: 2 } }), value: "cards" },
|
||||||
@@ -180,7 +180,7 @@
|
|||||||
data-sveltekit-preload-data="off"
|
data-sveltekit-preload-data="off"
|
||||||
>
|
>
|
||||||
{#if selectedDisplayOption === "cards"}
|
{#if selectedDisplayOption === "cards"}
|
||||||
<TrailCard {trail}></TrailCard>
|
<TrailCard fullWidth={fullWidthCards} {trail}></TrailCard>
|
||||||
{:else}
|
{:else}
|
||||||
<TrailListItem {trail}></TrailListItem>
|
<TrailListItem {trail}></TrailListItem>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"about": "Über",
|
"about": "Über",
|
||||||
"account-delete-confirm": "Du bist dabei, dein Konto zu löschen. Alle deine Routen werden ebenfalls gelöscht. Möchtest du fortfahren?",
|
"account-delete-confirm": "Du bist dabei, dein Konto zu löschen. Alle deine Routen werden ebenfalls gelöscht. Möchtest du fortfahren?",
|
||||||
"activity": "{n, plural, =1 {Aktivität} other {Aktivitäten}}",
|
"activity": "{n, plural, =1 {Aktivität} other {Aktivitäten}}",
|
||||||
|
"add-bio": "Bio hinzufügen",
|
||||||
"add-entry": "Eintrag hinzufügen",
|
"add-entry": "Eintrag hinzufügen",
|
||||||
"add-to-list": "Zu Liste hinzufügen",
|
"add-to-list": "Zu Liste hinzufügen",
|
||||||
"add-waypoint": "Wegpunkt hinzufügen",
|
"add-waypoint": "Wegpunkt hinzufügen",
|
||||||
@@ -39,10 +40,11 @@
|
|||||||
"close": "Schließen",
|
"close": "Schließen",
|
||||||
"comment": "{n, plural, =1 {Kommentar} other {Kommentare}}",
|
"comment": "{n, plural, =1 {Kommentar} other {Kommentare}}",
|
||||||
"completed": "Abgeschlossen",
|
"completed": "Abgeschlossen",
|
||||||
|
"completed-a-trail": "hat eine Route abgeschlossen",
|
||||||
"completion-status": "Abschlussstatus",
|
"completion-status": "Abschlussstatus",
|
||||||
"confirm": "Bestätigen",
|
"confirm": "Bestätigen",
|
||||||
"confirm-deletion": "Löschen bestätigen",
|
"confirm-deletion": "Löschen bestätigen",
|
||||||
"confirm-publish": "",
|
"confirm-publish": "Veröffentlichen bestätigen",
|
||||||
"confirm-share": "Teilen bestätigen",
|
"confirm-share": "Teilen bestätigen",
|
||||||
"contribute": "Mitwirken",
|
"contribute": "Mitwirken",
|
||||||
"copy-link": "Link kopieren",
|
"copy-link": "Link kopieren",
|
||||||
@@ -80,6 +82,9 @@
|
|||||||
"elevation-gain": "Höhenunterschied (aufw.)",
|
"elevation-gain": "Höhenunterschied (aufw.)",
|
||||||
"elevation-loss": "Höhenunterschied (abw.)",
|
"elevation-loss": "Höhenunterschied (abw.)",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
"empty-activities": "{username} hat noch keine Aktivitäten",
|
||||||
|
"empty-bio": "{username} hat noch keine Biographie hinzugefügt",
|
||||||
|
"empty-lists": "{username} hat keine öffentlichen Listen",
|
||||||
"english": "Englisch",
|
"english": "Englisch",
|
||||||
"entry": "Eintrag",
|
"entry": "Eintrag",
|
||||||
"error-creating-user": "Fehler beim Erstellen des Nutzers",
|
"error-creating-user": "Fehler beim Erstellen des Nutzers",
|
||||||
@@ -96,13 +101,16 @@
|
|||||||
"explore-some-trails": "Erkunden Sie einige Routen",
|
"explore-some-trails": "Erkunden Sie einige Routen",
|
||||||
"export": "Exportieren",
|
"export": "Exportieren",
|
||||||
"export-all-trails": "Alle Routen exportieren",
|
"export-all-trails": "Alle Routen exportieren",
|
||||||
"favourite-sport": "",
|
"favourite-sport": "Lieblingssportart",
|
||||||
"features": "Features",
|
"features": "Features",
|
||||||
"file-format": "Dateiformat",
|
"file-format": "Dateiformat",
|
||||||
"file-too-big": "Datei {file} ist zu groß (max. {size})",
|
"file-too-big": "Datei {file} ist zu groß (max. {size})",
|
||||||
"filter-categories": "Kategorien filtern",
|
"filter-categories": "Kategorien filtern",
|
||||||
"filter-difficulty": "Schwierigkeit filtern",
|
"filter-difficulty": "Schwierigkeit filtern",
|
||||||
"focus-map-on": "Karte fokussieren auf",
|
"focus-map-on": "Karte fokussieren auf",
|
||||||
|
"follow": "Folgen",
|
||||||
|
"followers": "Folgen dir",
|
||||||
|
"following": "Du folgst",
|
||||||
"forgot-your-password": "Passwort vergessen?",
|
"forgot-your-password": "Passwort vergessen?",
|
||||||
"french": "Französisch",
|
"french": "Französisch",
|
||||||
"german": "Deutsch",
|
"german": "Deutsch",
|
||||||
@@ -124,13 +132,14 @@
|
|||||||
"invalid-date": "Ungültiges Datum",
|
"invalid-date": "Ungültiges Datum",
|
||||||
"invalid-username": "Ungültiger Nutzername",
|
"invalid-username": "Ungültiger Nutzername",
|
||||||
"italian": "Italienisch",
|
"italian": "Italienisch",
|
||||||
|
"joined": "Beigetreten",
|
||||||
"language": "Sprache",
|
"language": "Sprache",
|
||||||
"latitude": "Breitengrad",
|
"latitude": "Breitengrad",
|
||||||
"license": "Lizenz",
|
"license": "Lizenz",
|
||||||
"link-copied": "Link kopiert",
|
"link-copied": "Link kopiert",
|
||||||
"list": "{n, plural, =1 {Liste} other {Listen}}",
|
"list": "{n, plural, =1 {Liste} other {Listen}}",
|
||||||
"list-not-shared": "Mit niemandem geteilt",
|
"list-not-shared": "Mit niemandem geteilt",
|
||||||
"list-public-warning": "",
|
"list-public-warning": "Alle routen in dieser liste werden veröffentlicht.",
|
||||||
"list-saved-successfully": "Liste gespeichert",
|
"list-saved-successfully": "Liste gespeichert",
|
||||||
"list-share-warning": "Durch das Teilen einer Liste werden automatisch alle darin enthaltenen Routen freigegeben.",
|
"list-share-warning": "Durch das Teilen einer Liste werden automatisch alle darin enthaltenen Routen freigegeben.",
|
||||||
"list-share-warning-update": "Hinzugefügte Routen werden mit allen geteilt, die Zugriff auf diese Liste haben.",
|
"list-share-warning-update": "Hinzugefügte Routen werden mit allen geteilt, die Zugriff auf diese Liste haben.",
|
||||||
@@ -179,6 +188,7 @@
|
|||||||
"passwords-must-match": "Passwörter müssen übereinstimmen",
|
"passwords-must-match": "Passwörter müssen übereinstimmen",
|
||||||
"photos": "Fotos",
|
"photos": "Fotos",
|
||||||
"pick-a-trail": "Route auswählen",
|
"pick-a-trail": "Route auswählen",
|
||||||
|
"planned-a-trail": "hat eine Route geplant",
|
||||||
"polish": "Polnisch",
|
"polish": "Polnisch",
|
||||||
"portuguese": "Portugiesisch",
|
"portuguese": "Portugiesisch",
|
||||||
"print": "Drucken",
|
"print": "Drucken",
|
||||||
@@ -211,6 +221,7 @@
|
|||||||
"slope": "Steigung",
|
"slope": "Steigung",
|
||||||
"sort": "Sortieren",
|
"sort": "Sortieren",
|
||||||
"speed": "Geschwindigkeit",
|
"speed": "Geschwindigkeit",
|
||||||
|
"statistics": "Statistiken",
|
||||||
"stop-drawing": "Zeichnen beenden",
|
"stop-drawing": "Zeichnen beenden",
|
||||||
"summit-book": "Gipfelbuch",
|
"summit-book": "Gipfelbuch",
|
||||||
"table": "Tabelle",
|
"table": "Tabelle",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"about": "About",
|
"about": "About",
|
||||||
"account-delete-confirm": "You are about to delete your account. All your trails will also be deleted. Do you want to proceed?",
|
"account-delete-confirm": "You are about to delete your account. All your trails will also be deleted. Do you want to proceed?",
|
||||||
"activity": "{n, plural, =1 {Activity} other {Activities}}",
|
"activity": "{n, plural, =1 {Activity} other {Activities}}",
|
||||||
|
"add-bio": "Add Bio",
|
||||||
"add-entry": "Add Entry",
|
"add-entry": "Add Entry",
|
||||||
"add-to-list": "Add to list",
|
"add-to-list": "Add to list",
|
||||||
"add-waypoint": "Add Waypoint",
|
"add-waypoint": "Add Waypoint",
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
"close": "Close",
|
"close": "Close",
|
||||||
"comment": "{n, plural, =1 {Comment} other {Comments}}",
|
"comment": "{n, plural, =1 {Comment} other {Comments}}",
|
||||||
"completed": "Completed",
|
"completed": "Completed",
|
||||||
|
"completed-a-trail": "completed a trail",
|
||||||
"completion-status": "Completion Status",
|
"completion-status": "Completion Status",
|
||||||
"confirm": "Confirm",
|
"confirm": "Confirm",
|
||||||
"confirm-deletion": "Confirm Deletion",
|
"confirm-deletion": "Confirm Deletion",
|
||||||
@@ -80,6 +82,9 @@
|
|||||||
"elevation-gain": "Elevation Gain",
|
"elevation-gain": "Elevation Gain",
|
||||||
"elevation-loss": "Elevation Loss",
|
"elevation-loss": "Elevation Loss",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
"empty-activities": "{username} has no activity yet",
|
||||||
|
"empty-bio": "{username} has not added a bio yet",
|
||||||
|
"empty-lists": "{username} has no public lists",
|
||||||
"english": "English",
|
"english": "English",
|
||||||
"entry": "Entry",
|
"entry": "Entry",
|
||||||
"error-creating-user": "Error creating user",
|
"error-creating-user": "Error creating user",
|
||||||
@@ -103,6 +108,9 @@
|
|||||||
"filter-categories": "Filter categories",
|
"filter-categories": "Filter categories",
|
||||||
"filter-difficulty": "Filter difficulty",
|
"filter-difficulty": "Filter difficulty",
|
||||||
"focus-map-on": "Focus map on",
|
"focus-map-on": "Focus map on",
|
||||||
|
"follow": "Follow",
|
||||||
|
"followers": "Followers",
|
||||||
|
"following": "Following",
|
||||||
"forgot-your-password": "Forgot your password?",
|
"forgot-your-password": "Forgot your password?",
|
||||||
"french": "French",
|
"french": "French",
|
||||||
"german": "German",
|
"german": "German",
|
||||||
@@ -124,6 +132,7 @@
|
|||||||
"invalid-date": "Invalid Date",
|
"invalid-date": "Invalid Date",
|
||||||
"invalid-username": "Invalid username",
|
"invalid-username": "Invalid username",
|
||||||
"italian": "Italian",
|
"italian": "Italian",
|
||||||
|
"joined": "Joined",
|
||||||
"language": "Language",
|
"language": "Language",
|
||||||
"latitude": "Latitude",
|
"latitude": "Latitude",
|
||||||
"license": "License",
|
"license": "License",
|
||||||
@@ -179,6 +188,7 @@
|
|||||||
"passwords-must-match": "Passwords must match",
|
"passwords-must-match": "Passwords must match",
|
||||||
"photos": "Photos",
|
"photos": "Photos",
|
||||||
"pick-a-trail": "Pick a trail",
|
"pick-a-trail": "Pick a trail",
|
||||||
|
"planned-a-trail": "planned a trail",
|
||||||
"polish": "Polish",
|
"polish": "Polish",
|
||||||
"portuguese": "Portuguese",
|
"portuguese": "Portuguese",
|
||||||
"print": "Print",
|
"print": "Print",
|
||||||
@@ -211,6 +221,7 @@
|
|||||||
"slope": "Slope",
|
"slope": "Slope",
|
||||||
"sort": "Sort",
|
"sort": "Sort",
|
||||||
"speed": "Speed",
|
"speed": "Speed",
|
||||||
|
"statistics": "Statistics",
|
||||||
"stop-drawing": "Stop drawing",
|
"stop-drawing": "Stop drawing",
|
||||||
"summit-book": "Summit Book",
|
"summit-book": "Summit Book",
|
||||||
"table": "Table",
|
"table": "Table",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"about": "Informations",
|
"about": "Informations",
|
||||||
"account-delete-confirm": "Vous êtes sur le point de supprimer votre compte. Toutes vos trails seront également supprimées. Voulez-vous continuer ?",
|
"account-delete-confirm": "Vous êtes sur le point de supprimer votre compte. Toutes vos trails seront également supprimées. Voulez-vous continuer ?",
|
||||||
"activity": "",
|
"activity": "",
|
||||||
|
"add-bio": "",
|
||||||
"add-entry": "Ajouter une entrée",
|
"add-entry": "Ajouter une entrée",
|
||||||
"add-to-list": "Ajouter à une liste",
|
"add-to-list": "Ajouter à une liste",
|
||||||
"add-waypoint": "Ajouter un point de repère",
|
"add-waypoint": "Ajouter un point de repère",
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
"close": "",
|
"close": "",
|
||||||
"comment": "{n, plural, =1 {Commentaire} other {Commentaires}}",
|
"comment": "{n, plural, =1 {Commentaire} other {Commentaires}}",
|
||||||
"completed": "Compléter",
|
"completed": "Compléter",
|
||||||
|
"completed-a-trail": "",
|
||||||
"completion-status": "L'état d'achèvement",
|
"completion-status": "L'état d'achèvement",
|
||||||
"confirm": "",
|
"confirm": "",
|
||||||
"confirm-deletion": "",
|
"confirm-deletion": "",
|
||||||
@@ -80,6 +82,9 @@
|
|||||||
"elevation-gain": "Gain d'altitude",
|
"elevation-gain": "Gain d'altitude",
|
||||||
"elevation-loss": "",
|
"elevation-loss": "",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
"empty-activities": "",
|
||||||
|
"empty-bio": "",
|
||||||
|
"empty-lists": "",
|
||||||
"english": "Anglais",
|
"english": "Anglais",
|
||||||
"entry": "Entrée",
|
"entry": "Entrée",
|
||||||
"error-creating-user": "Erreur durant la création de l'utilisateur",
|
"error-creating-user": "Erreur durant la création de l'utilisateur",
|
||||||
@@ -103,6 +108,9 @@
|
|||||||
"filter-categories": "",
|
"filter-categories": "",
|
||||||
"filter-difficulty": "",
|
"filter-difficulty": "",
|
||||||
"focus-map-on": "",
|
"focus-map-on": "",
|
||||||
|
"follow": "",
|
||||||
|
"followers": "",
|
||||||
|
"following": "",
|
||||||
"forgot-your-password": "",
|
"forgot-your-password": "",
|
||||||
"french": "Français",
|
"french": "Français",
|
||||||
"german": "Allemand",
|
"german": "Allemand",
|
||||||
@@ -124,6 +132,7 @@
|
|||||||
"invalid-date": "Date invalide",
|
"invalid-date": "Date invalide",
|
||||||
"invalid-username": "Nom d'utilisateur invalide",
|
"invalid-username": "Nom d'utilisateur invalide",
|
||||||
"italian": "Italien",
|
"italian": "Italien",
|
||||||
|
"joined": "",
|
||||||
"language": "Langue",
|
"language": "Langue",
|
||||||
"latitude": "Latitude",
|
"latitude": "Latitude",
|
||||||
"license": "Licence",
|
"license": "Licence",
|
||||||
@@ -179,6 +188,7 @@
|
|||||||
"passwords-must-match": "",
|
"passwords-must-match": "",
|
||||||
"photos": "Photos",
|
"photos": "Photos",
|
||||||
"pick-a-trail": "Choisir un itinéraire",
|
"pick-a-trail": "Choisir un itinéraire",
|
||||||
|
"planned-a-trail": "",
|
||||||
"polish": "polonais",
|
"polish": "polonais",
|
||||||
"portuguese": "Portugais",
|
"portuguese": "Portugais",
|
||||||
"print": "Imprimer",
|
"print": "Imprimer",
|
||||||
@@ -211,6 +221,7 @@
|
|||||||
"slope": "",
|
"slope": "",
|
||||||
"sort": "Trier",
|
"sort": "Trier",
|
||||||
"speed": "",
|
"speed": "",
|
||||||
|
"statistics": "",
|
||||||
"stop-drawing": "",
|
"stop-drawing": "",
|
||||||
"summit-book": "Livre du sommet",
|
"summit-book": "Livre du sommet",
|
||||||
"table": "Tableau",
|
"table": "Tableau",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"about": "A programról",
|
"about": "A programról",
|
||||||
"account-delete-confirm": "Ön most a profilját készül törölni. Minden nyomvonala törlődik. Szeretné folytatni?",
|
"account-delete-confirm": "Ön most a profilját készül törölni. Minden nyomvonala törlődik. Szeretné folytatni?",
|
||||||
"activity": "",
|
"activity": "",
|
||||||
|
"add-bio": "",
|
||||||
"add-entry": "Bejegyzés hozzáadása",
|
"add-entry": "Bejegyzés hozzáadása",
|
||||||
"add-to-list": "Hozzáadás a listához",
|
"add-to-list": "Hozzáadás a listához",
|
||||||
"add-waypoint": "Útvonalpont hozzáadása",
|
"add-waypoint": "Útvonalpont hozzáadása",
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
"close": "",
|
"close": "",
|
||||||
"comment": "{n, plural, =1 {Megjegyzés} other {Megjegyzés}}",
|
"comment": "{n, plural, =1 {Megjegyzés} other {Megjegyzés}}",
|
||||||
"completed": "Teljesítve",
|
"completed": "Teljesítve",
|
||||||
|
"completed-a-trail": "",
|
||||||
"completion-status": "Befejezés állapota",
|
"completion-status": "Befejezés állapota",
|
||||||
"confirm": "",
|
"confirm": "",
|
||||||
"confirm-deletion": "",
|
"confirm-deletion": "",
|
||||||
@@ -80,6 +82,9 @@
|
|||||||
"elevation-gain": "Magasságnövekedés",
|
"elevation-gain": "Magasságnövekedés",
|
||||||
"elevation-loss": "",
|
"elevation-loss": "",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
"empty-activities": "",
|
||||||
|
"empty-bio": "",
|
||||||
|
"empty-lists": "",
|
||||||
"english": "Angol",
|
"english": "Angol",
|
||||||
"entry": "Bejegyzés",
|
"entry": "Bejegyzés",
|
||||||
"error-creating-user": "Hiba felhasználó hozzáadása közben",
|
"error-creating-user": "Hiba felhasználó hozzáadása közben",
|
||||||
@@ -103,6 +108,9 @@
|
|||||||
"filter-categories": "",
|
"filter-categories": "",
|
||||||
"filter-difficulty": "",
|
"filter-difficulty": "",
|
||||||
"focus-map-on": "",
|
"focus-map-on": "",
|
||||||
|
"follow": "",
|
||||||
|
"followers": "",
|
||||||
|
"following": "",
|
||||||
"forgot-your-password": "",
|
"forgot-your-password": "",
|
||||||
"french": "Francia",
|
"french": "Francia",
|
||||||
"german": "Német",
|
"german": "Német",
|
||||||
@@ -124,6 +132,7 @@
|
|||||||
"invalid-date": "Érvénytelen dátum",
|
"invalid-date": "Érvénytelen dátum",
|
||||||
"invalid-username": "Érvénytelen felhasználó",
|
"invalid-username": "Érvénytelen felhasználó",
|
||||||
"italian": "Olasz",
|
"italian": "Olasz",
|
||||||
|
"joined": "",
|
||||||
"language": "Nyelf",
|
"language": "Nyelf",
|
||||||
"latitude": "Szélesség",
|
"latitude": "Szélesség",
|
||||||
"license": "License",
|
"license": "License",
|
||||||
@@ -179,6 +188,7 @@
|
|||||||
"passwords-must-match": "",
|
"passwords-must-match": "",
|
||||||
"photos": "Képek",
|
"photos": "Képek",
|
||||||
"pick-a-trail": "Válasszon útvonalat",
|
"pick-a-trail": "Válasszon útvonalat",
|
||||||
|
"planned-a-trail": "",
|
||||||
"polish": "Lengyel",
|
"polish": "Lengyel",
|
||||||
"portuguese": "Portugál",
|
"portuguese": "Portugál",
|
||||||
"print": "",
|
"print": "",
|
||||||
@@ -211,6 +221,7 @@
|
|||||||
"slope": "",
|
"slope": "",
|
||||||
"sort": "Rendezés",
|
"sort": "Rendezés",
|
||||||
"speed": "",
|
"speed": "",
|
||||||
|
"statistics": "",
|
||||||
"stop-drawing": "",
|
"stop-drawing": "",
|
||||||
"summit-book": "Csúcspont könyv",
|
"summit-book": "Csúcspont könyv",
|
||||||
"table": "Táblázat",
|
"table": "Táblázat",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"about": "Su di noi",
|
"about": "Su di noi",
|
||||||
"account-delete-confirm": "Stai per eliminare il tuo account. Tutti i tuoi percorsi saranno cancellati. Vuoi procedere?",
|
"account-delete-confirm": "Stai per eliminare il tuo account. Tutti i tuoi percorsi saranno cancellati. Vuoi procedere?",
|
||||||
"activity": "",
|
"activity": "",
|
||||||
|
"add-bio": "",
|
||||||
"add-entry": "Aggiungi voce",
|
"add-entry": "Aggiungi voce",
|
||||||
"add-to-list": "Aggiungi alla lista",
|
"add-to-list": "Aggiungi alla lista",
|
||||||
"add-waypoint": "Aggiungere un waypoint",
|
"add-waypoint": "Aggiungere un waypoint",
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
"close": "Chiudi",
|
"close": "Chiudi",
|
||||||
"comment": "{n, plural, =1 {Commento} other {Commenti}}",
|
"comment": "{n, plural, =1 {Commento} other {Commenti}}",
|
||||||
"completed": "Completato",
|
"completed": "Completato",
|
||||||
|
"completed-a-trail": "",
|
||||||
"completion-status": "Stato di completamento",
|
"completion-status": "Stato di completamento",
|
||||||
"confirm": "",
|
"confirm": "",
|
||||||
"confirm-deletion": "",
|
"confirm-deletion": "",
|
||||||
@@ -80,6 +82,9 @@
|
|||||||
"elevation-gain": "Guadagno di quota",
|
"elevation-gain": "Guadagno di quota",
|
||||||
"elevation-loss": "",
|
"elevation-loss": "",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
"empty-activities": "",
|
||||||
|
"empty-bio": "",
|
||||||
|
"empty-lists": "",
|
||||||
"english": "Inglese",
|
"english": "Inglese",
|
||||||
"entry": "Voce",
|
"entry": "Voce",
|
||||||
"error-creating-user": "Errore durante la creazione dell'utente",
|
"error-creating-user": "Errore durante la creazione dell'utente",
|
||||||
@@ -103,6 +108,9 @@
|
|||||||
"filter-categories": "",
|
"filter-categories": "",
|
||||||
"filter-difficulty": "",
|
"filter-difficulty": "",
|
||||||
"focus-map-on": "",
|
"focus-map-on": "",
|
||||||
|
"follow": "",
|
||||||
|
"followers": "",
|
||||||
|
"following": "",
|
||||||
"forgot-your-password": "",
|
"forgot-your-password": "",
|
||||||
"french": "Francese",
|
"french": "Francese",
|
||||||
"german": "Tedesco",
|
"german": "Tedesco",
|
||||||
@@ -124,6 +132,7 @@
|
|||||||
"invalid-date": "Data non valida",
|
"invalid-date": "Data non valida",
|
||||||
"invalid-username": "Nome utente non valido",
|
"invalid-username": "Nome utente non valido",
|
||||||
"italian": "",
|
"italian": "",
|
||||||
|
"joined": "",
|
||||||
"language": "Lingua",
|
"language": "Lingua",
|
||||||
"latitude": "Latitudine",
|
"latitude": "Latitudine",
|
||||||
"license": "Licenza",
|
"license": "Licenza",
|
||||||
@@ -179,6 +188,7 @@
|
|||||||
"passwords-must-match": "",
|
"passwords-must-match": "",
|
||||||
"photos": "Foto",
|
"photos": "Foto",
|
||||||
"pick-a-trail": "Scegli un percorso",
|
"pick-a-trail": "Scegli un percorso",
|
||||||
|
"planned-a-trail": "",
|
||||||
"polish": "Polacco",
|
"polish": "Polacco",
|
||||||
"portuguese": "Portoghese",
|
"portuguese": "Portoghese",
|
||||||
"print": "Stampa",
|
"print": "Stampa",
|
||||||
@@ -211,6 +221,7 @@
|
|||||||
"slope": "Pendenza",
|
"slope": "Pendenza",
|
||||||
"sort": "Ordina",
|
"sort": "Ordina",
|
||||||
"speed": "Velocità",
|
"speed": "Velocità",
|
||||||
|
"statistics": "",
|
||||||
"stop-drawing": "Smettere di disegnare",
|
"stop-drawing": "Smettere di disegnare",
|
||||||
"summit-book": "Libro di vetta",
|
"summit-book": "Libro di vetta",
|
||||||
"table": "Tavolo",
|
"table": "Tavolo",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"about": "Over",
|
"about": "Over",
|
||||||
"account-delete-confirm": "Je staat op het punt je account te verwijderen. Al je wandelroutes worden hierdoor eveneens verwijderd. Wil je doorgaan?",
|
"account-delete-confirm": "Je staat op het punt je account te verwijderen. Al je wandelroutes worden hierdoor eveneens verwijderd. Wil je doorgaan?",
|
||||||
"activity": "",
|
"activity": "",
|
||||||
|
"add-bio": "",
|
||||||
"add-entry": "Item toevoegen",
|
"add-entry": "Item toevoegen",
|
||||||
"add-to-list": "Toevoegen aan lijst",
|
"add-to-list": "Toevoegen aan lijst",
|
||||||
"add-waypoint": "Routepunt toevoegen",
|
"add-waypoint": "Routepunt toevoegen",
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
"close": "",
|
"close": "",
|
||||||
"comment": "{n, plural, =1 {Opmerking} other {Opmerkingen}}",
|
"comment": "{n, plural, =1 {Opmerking} other {Opmerkingen}}",
|
||||||
"completed": "Voltooid",
|
"completed": "Voltooid",
|
||||||
|
"completed-a-trail": "",
|
||||||
"completion-status": "Voltooiingsstatus",
|
"completion-status": "Voltooiingsstatus",
|
||||||
"confirm": "",
|
"confirm": "",
|
||||||
"confirm-deletion": "",
|
"confirm-deletion": "",
|
||||||
@@ -80,6 +82,9 @@
|
|||||||
"elevation-gain": "Hoogteverschil",
|
"elevation-gain": "Hoogteverschil",
|
||||||
"elevation-loss": "",
|
"elevation-loss": "",
|
||||||
"email": "E-mail",
|
"email": "E-mail",
|
||||||
|
"empty-activities": "",
|
||||||
|
"empty-bio": "",
|
||||||
|
"empty-lists": "",
|
||||||
"english": "Engels",
|
"english": "Engels",
|
||||||
"entry": "Item",
|
"entry": "Item",
|
||||||
"error-creating-user": "Het account kan niet worden aangemaakt",
|
"error-creating-user": "Het account kan niet worden aangemaakt",
|
||||||
@@ -103,6 +108,9 @@
|
|||||||
"filter-categories": "",
|
"filter-categories": "",
|
||||||
"filter-difficulty": "",
|
"filter-difficulty": "",
|
||||||
"focus-map-on": "",
|
"focus-map-on": "",
|
||||||
|
"follow": "",
|
||||||
|
"followers": "",
|
||||||
|
"following": "",
|
||||||
"forgot-your-password": "",
|
"forgot-your-password": "",
|
||||||
"french": "Frans",
|
"french": "Frans",
|
||||||
"german": "Duits",
|
"german": "Duits",
|
||||||
@@ -124,6 +132,7 @@
|
|||||||
"invalid-date": "Ongeldige datum",
|
"invalid-date": "Ongeldige datum",
|
||||||
"invalid-username": "Ongeldige gebruikersnaam",
|
"invalid-username": "Ongeldige gebruikersnaam",
|
||||||
"italian": "Italiaans",
|
"italian": "Italiaans",
|
||||||
|
"joined": "",
|
||||||
"language": "Taal",
|
"language": "Taal",
|
||||||
"latitude": "Breedtegraad",
|
"latitude": "Breedtegraad",
|
||||||
"license": "Licentie",
|
"license": "Licentie",
|
||||||
@@ -179,6 +188,7 @@
|
|||||||
"passwords-must-match": "",
|
"passwords-must-match": "",
|
||||||
"photos": "Foto's",
|
"photos": "Foto's",
|
||||||
"pick-a-trail": "Kies een wandelroute",
|
"pick-a-trail": "Kies een wandelroute",
|
||||||
|
"planned-a-trail": "",
|
||||||
"polish": "Pools",
|
"polish": "Pools",
|
||||||
"portuguese": "Portugees",
|
"portuguese": "Portugees",
|
||||||
"print": "Afdrukken",
|
"print": "Afdrukken",
|
||||||
@@ -211,6 +221,7 @@
|
|||||||
"slope": "",
|
"slope": "",
|
||||||
"sort": "Sorteren",
|
"sort": "Sorteren",
|
||||||
"speed": "",
|
"speed": "",
|
||||||
|
"statistics": "",
|
||||||
"stop-drawing": "",
|
"stop-drawing": "",
|
||||||
"summit-book": "Bergtopboek",
|
"summit-book": "Bergtopboek",
|
||||||
"table": "Tabel",
|
"table": "Tabel",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"about": "Na temat",
|
"about": "Na temat",
|
||||||
"account-delete-confirm": "Usuniesz swoje konto. Wszystkie twoje ścieżki zostaną usunięte. Czy chcesz kontynuować?",
|
"account-delete-confirm": "Usuniesz swoje konto. Wszystkie twoje ścieżki zostaną usunięte. Czy chcesz kontynuować?",
|
||||||
"activity": "",
|
"activity": "",
|
||||||
|
"add-bio": "",
|
||||||
"add-entry": "Dodaj Pozycję",
|
"add-entry": "Dodaj Pozycję",
|
||||||
"add-to-list": "Dodaj do listy",
|
"add-to-list": "Dodaj do listy",
|
||||||
"add-waypoint": "Dodaj Punkt",
|
"add-waypoint": "Dodaj Punkt",
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
"close": "",
|
"close": "",
|
||||||
"comment": "{n, plural, =1 {Komentarz} other {Komentarze}}",
|
"comment": "{n, plural, =1 {Komentarz} other {Komentarze}}",
|
||||||
"completed": "Zakończono",
|
"completed": "Zakończono",
|
||||||
|
"completed-a-trail": "",
|
||||||
"completion-status": "Stan ukończenia",
|
"completion-status": "Stan ukończenia",
|
||||||
"confirm": "",
|
"confirm": "",
|
||||||
"confirm-deletion": "",
|
"confirm-deletion": "",
|
||||||
@@ -80,6 +82,9 @@
|
|||||||
"elevation-gain": "Wzrost Wysokości",
|
"elevation-gain": "Wzrost Wysokości",
|
||||||
"elevation-loss": "",
|
"elevation-loss": "",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
"empty-activities": "",
|
||||||
|
"empty-bio": "",
|
||||||
|
"empty-lists": "",
|
||||||
"english": "Angielski",
|
"english": "Angielski",
|
||||||
"entry": "Pozycja",
|
"entry": "Pozycja",
|
||||||
"error-creating-user": "Błąd tworzenia użytkownika",
|
"error-creating-user": "Błąd tworzenia użytkownika",
|
||||||
@@ -103,6 +108,9 @@
|
|||||||
"filter-categories": "",
|
"filter-categories": "",
|
||||||
"filter-difficulty": "",
|
"filter-difficulty": "",
|
||||||
"focus-map-on": "",
|
"focus-map-on": "",
|
||||||
|
"follow": "",
|
||||||
|
"followers": "",
|
||||||
|
"following": "",
|
||||||
"forgot-your-password": "",
|
"forgot-your-password": "",
|
||||||
"french": "Francuski",
|
"french": "Francuski",
|
||||||
"german": "Niemiecki",
|
"german": "Niemiecki",
|
||||||
@@ -124,6 +132,7 @@
|
|||||||
"invalid-date": "Nieprawidłowa data",
|
"invalid-date": "Nieprawidłowa data",
|
||||||
"invalid-username": "Błędna nazwa użytkownika",
|
"invalid-username": "Błędna nazwa użytkownika",
|
||||||
"italian": "Włoski",
|
"italian": "Włoski",
|
||||||
|
"joined": "",
|
||||||
"language": "Język",
|
"language": "Język",
|
||||||
"latitude": "Szerokość",
|
"latitude": "Szerokość",
|
||||||
"license": "Licencja",
|
"license": "Licencja",
|
||||||
@@ -179,6 +188,7 @@
|
|||||||
"passwords-must-match": "",
|
"passwords-must-match": "",
|
||||||
"photos": "Zdjęcia",
|
"photos": "Zdjęcia",
|
||||||
"pick-a-trail": "Wybierz ścieżkę",
|
"pick-a-trail": "Wybierz ścieżkę",
|
||||||
|
"planned-a-trail": "",
|
||||||
"polish": "Polski",
|
"polish": "Polski",
|
||||||
"portuguese": "Portugalski",
|
"portuguese": "Portugalski",
|
||||||
"print": "",
|
"print": "",
|
||||||
@@ -211,6 +221,7 @@
|
|||||||
"slope": "",
|
"slope": "",
|
||||||
"sort": "Sortowanie",
|
"sort": "Sortowanie",
|
||||||
"speed": "",
|
"speed": "",
|
||||||
|
"statistics": "",
|
||||||
"stop-drawing": "",
|
"stop-drawing": "",
|
||||||
"summit-book": "Logbook",
|
"summit-book": "Logbook",
|
||||||
"table": "Tabela",
|
"table": "Tabela",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"about": "Sobre",
|
"about": "Sobre",
|
||||||
"account-delete-confirm": "Você está prestes a excluir sua conta. Todas as suas trilhas também serão excluídas. Queres prosseguir?",
|
"account-delete-confirm": "Você está prestes a excluir sua conta. Todas as suas trilhas também serão excluídas. Queres prosseguir?",
|
||||||
"activity": "",
|
"activity": "",
|
||||||
|
"add-bio": "",
|
||||||
"add-entry": "Adicionar entrada",
|
"add-entry": "Adicionar entrada",
|
||||||
"add-to-list": "Adicionar à lista",
|
"add-to-list": "Adicionar à lista",
|
||||||
"add-waypoint": "Adicionar ponto de vista",
|
"add-waypoint": "Adicionar ponto de vista",
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
"close": "",
|
"close": "",
|
||||||
"comment": "{n, plural, =1 {Comentário} other {Comentários}}",
|
"comment": "{n, plural, =1 {Comentário} other {Comentários}}",
|
||||||
"completed": "Completada",
|
"completed": "Completada",
|
||||||
|
"completed-a-trail": "",
|
||||||
"completion-status": "Status de conclusão",
|
"completion-status": "Status de conclusão",
|
||||||
"confirm": "",
|
"confirm": "",
|
||||||
"confirm-deletion": "",
|
"confirm-deletion": "",
|
||||||
@@ -80,6 +82,9 @@
|
|||||||
"elevation-gain": "Ganho de elevação",
|
"elevation-gain": "Ganho de elevação",
|
||||||
"elevation-loss": "",
|
"elevation-loss": "",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
"empty-activities": "",
|
||||||
|
"empty-bio": "",
|
||||||
|
"empty-lists": "",
|
||||||
"english": "Inglês",
|
"english": "Inglês",
|
||||||
"entry": "Entrada",
|
"entry": "Entrada",
|
||||||
"error-creating-user": "Erro ao criar usuário",
|
"error-creating-user": "Erro ao criar usuário",
|
||||||
@@ -103,6 +108,9 @@
|
|||||||
"filter-categories": "",
|
"filter-categories": "",
|
||||||
"filter-difficulty": "",
|
"filter-difficulty": "",
|
||||||
"focus-map-on": "",
|
"focus-map-on": "",
|
||||||
|
"follow": "",
|
||||||
|
"followers": "",
|
||||||
|
"following": "",
|
||||||
"forgot-your-password": "",
|
"forgot-your-password": "",
|
||||||
"french": "Francês",
|
"french": "Francês",
|
||||||
"german": "Alemão",
|
"german": "Alemão",
|
||||||
@@ -124,6 +132,7 @@
|
|||||||
"invalid-date": "Data inválida",
|
"invalid-date": "Data inválida",
|
||||||
"invalid-username": "Nome de usuário inválido",
|
"invalid-username": "Nome de usuário inválido",
|
||||||
"italian": "Italiano",
|
"italian": "Italiano",
|
||||||
|
"joined": "",
|
||||||
"language": "Língua",
|
"language": "Língua",
|
||||||
"latitude": "Latitude",
|
"latitude": "Latitude",
|
||||||
"license": "Licença",
|
"license": "Licença",
|
||||||
@@ -179,6 +188,7 @@
|
|||||||
"passwords-must-match": "",
|
"passwords-must-match": "",
|
||||||
"photos": "Fotos",
|
"photos": "Fotos",
|
||||||
"pick-a-trail": "Escolha uma trilha",
|
"pick-a-trail": "Escolha uma trilha",
|
||||||
|
"planned-a-trail": "",
|
||||||
"polish": "Polonês",
|
"polish": "Polonês",
|
||||||
"portuguese": "Português",
|
"portuguese": "Português",
|
||||||
"print": "",
|
"print": "",
|
||||||
@@ -211,6 +221,7 @@
|
|||||||
"slope": "",
|
"slope": "",
|
||||||
"sort": "Ordenar",
|
"sort": "Ordenar",
|
||||||
"speed": "",
|
"speed": "",
|
||||||
|
"statistics": "",
|
||||||
"stop-drawing": "",
|
"stop-drawing": "",
|
||||||
"summit-book": "Livro da cimeira",
|
"summit-book": "Livro da cimeira",
|
||||||
"table": "Tabela",
|
"table": "Tabela",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"about": "关于",
|
"about": "关于",
|
||||||
"account-delete-confirm": "您现在要删除当前账户,所有的路线都会删除无法恢复,确认继续操作吗?",
|
"account-delete-confirm": "您现在要删除当前账户,所有的路线都会删除无法恢复,确认继续操作吗?",
|
||||||
"activity": "",
|
"activity": "",
|
||||||
|
"add-bio": "",
|
||||||
"add-entry": "添加日程",
|
"add-entry": "添加日程",
|
||||||
"add-to-list": "添加到列表",
|
"add-to-list": "添加到列表",
|
||||||
"add-waypoint": "添加坐标",
|
"add-waypoint": "添加坐标",
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
"close": "",
|
"close": "",
|
||||||
"comment": "{n, plural, =1 {评论} other {评论}}",
|
"comment": "{n, plural, =1 {评论} other {评论}}",
|
||||||
"completed": "已完成",
|
"completed": "已完成",
|
||||||
|
"completed-a-trail": "",
|
||||||
"completion-status": "完成状态",
|
"completion-status": "完成状态",
|
||||||
"confirm": "",
|
"confirm": "",
|
||||||
"confirm-deletion": "",
|
"confirm-deletion": "",
|
||||||
@@ -80,6 +82,9 @@
|
|||||||
"elevation-gain": "上升海拔",
|
"elevation-gain": "上升海拔",
|
||||||
"elevation-loss": "",
|
"elevation-loss": "",
|
||||||
"email": "电子邮箱",
|
"email": "电子邮箱",
|
||||||
|
"empty-activities": "",
|
||||||
|
"empty-bio": "",
|
||||||
|
"empty-lists": "",
|
||||||
"english": "英语",
|
"english": "英语",
|
||||||
"entry": "日程",
|
"entry": "日程",
|
||||||
"error-creating-user": "创建用户错误",
|
"error-creating-user": "创建用户错误",
|
||||||
@@ -103,6 +108,9 @@
|
|||||||
"filter-categories": "",
|
"filter-categories": "",
|
||||||
"filter-difficulty": "",
|
"filter-difficulty": "",
|
||||||
"focus-map-on": "",
|
"focus-map-on": "",
|
||||||
|
"follow": "",
|
||||||
|
"followers": "",
|
||||||
|
"following": "",
|
||||||
"forgot-your-password": "",
|
"forgot-your-password": "",
|
||||||
"french": "法语",
|
"french": "法语",
|
||||||
"german": "德语",
|
"german": "德语",
|
||||||
@@ -124,6 +132,7 @@
|
|||||||
"invalid-date": "无效日期",
|
"invalid-date": "无效日期",
|
||||||
"invalid-username": "无效用户名",
|
"invalid-username": "无效用户名",
|
||||||
"italian": "意大利语",
|
"italian": "意大利语",
|
||||||
|
"joined": "",
|
||||||
"language": "语言",
|
"language": "语言",
|
||||||
"latitude": "维度",
|
"latitude": "维度",
|
||||||
"license": "开源协议",
|
"license": "开源协议",
|
||||||
@@ -179,6 +188,7 @@
|
|||||||
"passwords-must-match": "",
|
"passwords-must-match": "",
|
||||||
"photos": "图片",
|
"photos": "图片",
|
||||||
"pick-a-trail": "选择一个路线",
|
"pick-a-trail": "选择一个路线",
|
||||||
|
"planned-a-trail": "",
|
||||||
"polish": "波兰语",
|
"polish": "波兰语",
|
||||||
"portuguese": "葡萄牙语",
|
"portuguese": "葡萄牙语",
|
||||||
"print": "",
|
"print": "",
|
||||||
@@ -211,6 +221,7 @@
|
|||||||
"slope": "",
|
"slope": "",
|
||||||
"sort": "排序",
|
"sort": "排序",
|
||||||
"speed": "",
|
"speed": "",
|
||||||
|
"statistics": "",
|
||||||
"stop-drawing": "",
|
"stop-drawing": "",
|
||||||
"summit-book": "详细日程",
|
"summit-book": "详细日程",
|
||||||
"table": "表格",
|
"table": "表格",
|
||||||
|
|||||||
16
web/src/lib/models/follow.ts
Normal file
16
web/src/lib/models/follow.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import type { User, UserAnonymous } from "./user";
|
||||||
|
|
||||||
|
export class Follow {
|
||||||
|
id?: string;
|
||||||
|
follower: string;
|
||||||
|
followee: string;
|
||||||
|
expand?: {
|
||||||
|
follower: UserAnonymous,
|
||||||
|
followee: UserAnonymous
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(follower: string, followee: string) {
|
||||||
|
this.follower = follower;
|
||||||
|
this.followee = followee;
|
||||||
|
}
|
||||||
|
}
|
||||||
88
web/src/lib/stores/follow_store.ts
Normal file
88
web/src/lib/stores/follow_store.ts
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import type { Follow } from "$lib/models/follow";
|
||||||
|
import { ClientResponseError, type ListResult } from "pocketbase";
|
||||||
|
|
||||||
|
let follows: Follow[] = [];
|
||||||
|
|
||||||
|
export async function follows_index(data: { follower?: string, followee?: string }, page: number = 1, perPage: number = 10, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
|
||||||
|
const r = await f('/api/v1/follow?' + new URLSearchParams({
|
||||||
|
filter: data.follower ? `follower='${data.follower}'` : data.followee ? `followee='${data.followee}'` : '',
|
||||||
|
page: page.toString(),
|
||||||
|
"per-page": perPage.toString()
|
||||||
|
}), {
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!r.ok) {
|
||||||
|
throw new ClientResponseError(await r.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchedFollows: ListResult<Follow> = await r.json();
|
||||||
|
|
||||||
|
const result = page > 1 ? [...follows, ...fetchedFollows.items] : fetchedFollows.items
|
||||||
|
|
||||||
|
follows = result;
|
||||||
|
|
||||||
|
return { ...fetchedFollows, items: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function follows_a_b(a: string, b: string, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
|
||||||
|
const r = await f('/api/v1/follow?' + new URLSearchParams({
|
||||||
|
filter: `follower='${a}'&&followee='${b}'`,
|
||||||
|
}), {
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!r.ok) {
|
||||||
|
throw new ClientResponseError(await r.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
const response: ListResult<Follow> = await r.json();
|
||||||
|
|
||||||
|
return response.items.at(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function follows_counts(id: string, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
|
||||||
|
const r = await f('/api/v1/follow/count/' + id, {
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!r.ok) {
|
||||||
|
throw new ClientResponseError(await r.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
const response: { followers: number, following: number } = await r.json();
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function follows_create(follow: Follow) {
|
||||||
|
let r = await fetch('/api/v1/follow', {
|
||||||
|
method: 'PUT',
|
||||||
|
body: JSON.stringify(follow),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!r.ok) {
|
||||||
|
throw new ClientResponseError(await r.json())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function follows_update(follow: Follow) {
|
||||||
|
let r = await fetch('/api/v1/follow/' + follow.id, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(follow),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!r.ok) {
|
||||||
|
throw new ClientResponseError(await r.json())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function follows_delete(follow: Follow) {
|
||||||
|
const r = await fetch('/api/v1/follow/' + follow.id, {
|
||||||
|
method: 'DELETE',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!r.ok) {
|
||||||
|
throw new ClientResponseError(await r.json())
|
||||||
|
}
|
||||||
|
}
|
||||||
45
web/src/routes/api/v1/follow/+server.ts
Normal file
45
web/src/routes/api/v1/follow/+server.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import type { Follow } from '$lib/models/follow';
|
||||||
|
import type { User } from '$lib/models/user';
|
||||||
|
import { pb } from '$lib/pocketbase';
|
||||||
|
import { error, json, type RequestEvent } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
export async function GET(event: RequestEvent) {
|
||||||
|
const page = event.url.searchParams.get("page") ?? "0";
|
||||||
|
const perPage = event.url.searchParams.get("per-page") ?? "10";
|
||||||
|
const sort = event.url.searchParams.get('sort') ?? ""
|
||||||
|
const filter = event.url.searchParams.get("filter") ?? "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
let r;
|
||||||
|
if (parseInt(perPage) < 0) {
|
||||||
|
r = {
|
||||||
|
items: await pb.collection('follows')
|
||||||
|
.getFullList<Follow>({ sort: sort, filter: filter, requestKey: filter })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r = await pb.collection('follows')
|
||||||
|
.getList<Follow>(parseInt(page), parseInt(perPage), { sort: sort ?? "", filter: filter ?? "", requestKey: filter })
|
||||||
|
}
|
||||||
|
for (const follow of r.items) {
|
||||||
|
const follower = await pb.collection('users_anonymous').getOne<User>(follow.follower, {requestKey: filter})
|
||||||
|
const followee = await pb.collection('users_anonymous').getOne<User>(follow.followee, {requestKey: filter})
|
||||||
|
follow.expand = {
|
||||||
|
follower, followee
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return json(r)
|
||||||
|
} catch (e: any) {
|
||||||
|
throw error(e.status, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function PUT(event: RequestEvent) {
|
||||||
|
const data = await event.request.json();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const r = await pb.collection('follows').create<Follow>(data)
|
||||||
|
return json(r);
|
||||||
|
} catch (e: any) {
|
||||||
|
throw error(e.status, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
33
web/src/routes/api/v1/follow/[id]/+server.ts
Normal file
33
web/src/routes/api/v1/follow/[id]/+server.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import type { Follow } from "$lib/models/follow";
|
||||||
|
import { pb } from "$lib/pocketbase";
|
||||||
|
import { error, json, type RequestEvent } from "@sveltejs/kit";
|
||||||
|
|
||||||
|
export async function GET(event: RequestEvent) {
|
||||||
|
try {
|
||||||
|
const r = await pb.collection('follows')
|
||||||
|
.getOne<Follow>(event.params.id as string)
|
||||||
|
return json(r)
|
||||||
|
} catch (e: any) {
|
||||||
|
throw error(e.status, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(event: RequestEvent) {
|
||||||
|
const data = await event.request.json()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const r = await pb.collection('follows').update<Follow>(event.params.id as string, data)
|
||||||
|
return json(r);
|
||||||
|
} catch (e: any) {
|
||||||
|
throw error(e.status, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function DELETE(event: RequestEvent) {
|
||||||
|
try {
|
||||||
|
const r = await pb.collection('follows').delete(event.params.id as string)
|
||||||
|
return json({ 'acknowledged': r });
|
||||||
|
} catch (e: any) {
|
||||||
|
throw error(e.status, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
12
web/src/routes/api/v1/follow/count/[id]/+server.ts
Normal file
12
web/src/routes/api/v1/follow/count/[id]/+server.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { pb } from "$lib/pocketbase";
|
||||||
|
import { error, json, type RequestEvent } from "@sveltejs/kit";
|
||||||
|
|
||||||
|
export async function GET(event: RequestEvent) {
|
||||||
|
try {
|
||||||
|
const r = await pb.collection('follow_counts')
|
||||||
|
.getOne(event.params.id as string)
|
||||||
|
return json(r)
|
||||||
|
} catch (e: any) {
|
||||||
|
throw error(e.status, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { follows_a_b, follows_counts, follows_index } from "$lib/stores/follow_store";
|
||||||
import { users_show } from "$lib/stores/user_store";
|
import { users_show } from "$lib/stores/user_store";
|
||||||
import { error, type ServerLoad } from "@sveltejs/kit";
|
import { error, type ServerLoad } from "@sveltejs/kit";
|
||||||
import { ClientResponseError } from "pocketbase";
|
import { ClientResponseError } from "pocketbase";
|
||||||
@@ -10,10 +11,13 @@ export const load: ServerLoad = async ({ params, locals, fetch }) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const user = await users_show(params.id, fetch);
|
const user = await users_show(params.id, fetch);
|
||||||
|
|
||||||
const isOwnProfile = params.id === locals.user?.id;
|
const isOwnProfile = params.id === locals.user?.id;
|
||||||
|
|
||||||
return { user, isOwnProfile }
|
|
||||||
|
const follow = await follows_a_b(locals.user?.id, params.id, fetch) ?? null
|
||||||
|
const followCounts = await follows_counts(params.id, fetch)
|
||||||
|
|
||||||
|
return { user, isOwnProfile, ...followCounts, follow: follow }
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof ClientResponseError && e.status == 404) {
|
if (e instanceof ClientResponseError && e.status == 404) {
|
||||||
error(404, {
|
error(404, {
|
||||||
|
|||||||
@@ -1,29 +1,54 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { invalidateAll } from "$app/navigation";
|
||||||
import { page } from "$app/stores";
|
import { page } from "$app/stores";
|
||||||
|
import Button from "$lib/components/base/button.svelte";
|
||||||
import type { SelectItem } from "$lib/components/base/select.svelte";
|
import type { SelectItem } from "$lib/components/base/select.svelte";
|
||||||
|
import { follows_create, follows_delete } from "$lib/stores/follow_store";
|
||||||
|
import { currentUser } from "$lib/stores/user_store";
|
||||||
import { getFileURL } from "$lib/util/file_util";
|
import { getFileURL } from "$lib/util/file_util";
|
||||||
import { onMount } from "svelte";
|
import { _ } from "svelte-i18n";
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
|
|
||||||
|
let followLoading: boolean = false;
|
||||||
|
|
||||||
const profileLinks: SelectItem[] = [
|
const profileLinks: SelectItem[] = [
|
||||||
{ text: "Profile", value: `/profile/${$page.params.id}` },
|
{ text: $_("profile"), value: `/profile/${$page.params.id}` },
|
||||||
{ text: "Trails", value: `/profile/${$page.params.id}/trails` },
|
{
|
||||||
{ text: "Activities", value: `/profile/${$page.params.id}/activities` },
|
text: $_("trail", { values: { n: 2 } }),
|
||||||
{ text: "Stats", value: `/profile/${$page.params.id}/stats` },
|
value: `/profile/${$page.params.id}/trails`,
|
||||||
|
},
|
||||||
|
{ text: $_('statistics'), value: `/profile/${$page.params.id}/stats` },
|
||||||
];
|
];
|
||||||
|
|
||||||
$: activeIndex = profileLinks.findIndex(
|
$: activeIndex = profileLinks.findIndex(
|
||||||
(l) => l.value === $page.url.pathname,
|
(l) => l.value === $page.url.pathname,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
async function follow() {
|
||||||
|
if (!$currentUser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
followLoading = true;
|
||||||
|
|
||||||
|
if (data.follow) {
|
||||||
|
await follows_delete(data.follow);
|
||||||
|
} else {
|
||||||
|
await follows_create({
|
||||||
|
follower: $currentUser.id,
|
||||||
|
followee: data.user.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await invalidateAll();
|
||||||
|
followLoading = false;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="grid grid-cols-1 lg:grid-cols-[356px_minmax(0,_1fr)] gap-4 max-w-6xl mx-auto items-start"
|
class="grid grid-cols-1 lg:grid-cols-[356px_minmax(0,_1fr)] gap-4 max-w-6xl mx-auto items-start"
|
||||||
>
|
>
|
||||||
<div class="border border-input-border rounded-xl space-y-8">
|
<div class="border border-input-border rounded-xl">
|
||||||
{#if data.user}
|
{#if data.user}
|
||||||
<div class="flex items-center gap-x-6 px-6 mt-6">
|
<div class="flex items-center gap-x-6 px-6 my-6">
|
||||||
<img
|
<img
|
||||||
class="rounded-full w-16 aspect-square overflow-hidden"
|
class="rounded-full w-16 aspect-square overflow-hidden"
|
||||||
src={getFileURL(data.user, data.user.avatar) ||
|
src={getFileURL(data.user, data.user.avatar) ||
|
||||||
@@ -35,7 +60,7 @@
|
|||||||
{data.user.username}
|
{data.user.username}
|
||||||
</h4>
|
</h4>
|
||||||
<p class="text-sm">
|
<p class="text-sm">
|
||||||
<span class="text-gray-500">Joined:</span>
|
<span class="text-gray-500">{$_("joined")}:</span>
|
||||||
{new Date(data.user.created ?? "").toLocaleDateString(
|
{new Date(data.user.created ?? "").toLocaleDateString(
|
||||||
undefined,
|
undefined,
|
||||||
{
|
{
|
||||||
@@ -50,16 +75,36 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="flex gap-x-6 text-sm px-6">
|
<div class="flex gap-x-6 text-sm px-6">
|
||||||
<div>
|
<a
|
||||||
<p class="font-semibold">0</p>
|
class:font-bold={$page.url.pathname.endsWith("followers")}
|
||||||
<p>Followers</p>
|
href="/profile/{data.user.id}/users/followers"
|
||||||
|
>
|
||||||
|
<p class="font-semibold">{data.followers}</p>
|
||||||
|
<p>{$_('followers')}</p>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class:font-bold={$page.url.pathname.endsWith("following")}
|
||||||
|
href="/profile/{data.user.id}/users/following"
|
||||||
|
>
|
||||||
|
<p class="font-semibold">{data.following}</p>
|
||||||
|
<p>{$_('following')}</p>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
{#if !data.isOwnProfile}
|
||||||
<p class="font-semibold">0</p>
|
<div class="px-6 mt-4">
|
||||||
<p>Following</p>
|
<Button
|
||||||
|
loading={followLoading}
|
||||||
|
disabled={followLoading}
|
||||||
|
primary={!data.follow}
|
||||||
|
secondary={!!data.follow}
|
||||||
|
icon={data.follow ? "check" : ""}
|
||||||
|
on:click={() => follow()}
|
||||||
|
>
|
||||||
|
{data.follow ? $_("following") : $_("follow")}</Button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
<div>
|
<div class="mt-6 mb-4">
|
||||||
{#each profileLinks as link, i}
|
{#each profileLinks as link, i}
|
||||||
<a
|
<a
|
||||||
class="block mx-2 px-4 py-3 my-1 cursor-pointer hover:bg-menu-item-background-hover focus:bg-menu-item-background-focus transition-colors rounded-md"
|
class="block mx-2 px-4 py-3 my-1 cursor-pointer hover:bg-menu-item-background-hover focus:bg-menu-item-background-focus transition-colors rounded-md"
|
||||||
|
|||||||
@@ -53,8 +53,12 @@
|
|||||||
<p class="whitespace-pre-wrap text-sm">{data.user.bio}</p>
|
<p class="whitespace-pre-wrap text-sm">{data.user.bio}</p>
|
||||||
{:else if data.isOwnProfile}
|
{:else if data.isOwnProfile}
|
||||||
<a class="btn-primary inline-block" href="/settings/profile"
|
<a class="btn-primary inline-block" href="/settings/profile"
|
||||||
>+ Add Bio</a
|
>+ {$_("add-bio")}</a
|
||||||
>
|
>
|
||||||
|
{:else}
|
||||||
|
<p class="w-full text-center text-gray-500 text-sm">
|
||||||
|
{$_("empty-bio", { values: { username: data.user.username } })}
|
||||||
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
@@ -62,6 +66,17 @@
|
|||||||
{$_("list", { values: { n: 2 } })}
|
{$_("list", { values: { n: 2 } })}
|
||||||
</h4>
|
</h4>
|
||||||
<div class="flex gap-x-4 overflow-x-scroll">
|
<div class="flex gap-x-4 overflow-x-scroll">
|
||||||
|
{#if !data.lists.length && data.isOwnProfile}
|
||||||
|
<a class="btn-primary inline-block" href="/lists/edit/new"
|
||||||
|
>+ {$_("new-list")}</a
|
||||||
|
>
|
||||||
|
{:else if !data.lists.length}
|
||||||
|
<p class="w-full text-center text-gray-500 text-sm">
|
||||||
|
{$_("empty-lists", {
|
||||||
|
values: { username: data.user.username },
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
{#each data.lists as list}
|
{#each data.lists as list}
|
||||||
<a
|
<a
|
||||||
href="/lists?list={list.id}"
|
href="/lists?list={list.id}"
|
||||||
@@ -94,7 +109,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<h4 class="text-xl font-semibold">Timeline</h4>
|
<h4 class="text-xl font-semibold">Timeline</h4>
|
||||||
|
{#if !data.activities.items.length && data.isOwnProfile}
|
||||||
|
<a class="btn-primary inline-block" href="/trails/edit/new"
|
||||||
|
>+ {$_("new-trail")}</a
|
||||||
|
>
|
||||||
|
{:else if !data.activities.items.length}
|
||||||
|
<p class="w-full text-center text-gray-500 text-sm">
|
||||||
|
{$_("empty-activities", {
|
||||||
|
values: { username: data.user.username },
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
{#each data.activities.items as activity}
|
{#each data.activities.items as activity}
|
||||||
<a
|
<a
|
||||||
class="block"
|
class="block"
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
<TrailList
|
<TrailList
|
||||||
{pagination}
|
{pagination}
|
||||||
{loading}
|
{loading}
|
||||||
|
fullWidthCards={true}
|
||||||
trails={data.trails.items}
|
trails={data.trails.items}
|
||||||
filter={data.filter}
|
filter={data.filter}
|
||||||
on:update={() => handleFilterUpdate()}
|
on:update={() => handleFilterUpdate()}
|
||||||
|
|||||||
62
web/src/routes/profile/[id]/users/[type]/+page.svelte
Normal file
62
web/src/routes/profile/[id]/users/[type]/+page.svelte
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { page } from "$app/stores";
|
||||||
|
import { follows_index } from "$lib/stores/follow_store.js";
|
||||||
|
import { getFileURL } from "$lib/util/file_util.js";
|
||||||
|
import { _ } from "svelte-i18n";
|
||||||
|
export let data;
|
||||||
|
|
||||||
|
let loading: boolean = false;
|
||||||
|
|
||||||
|
$: pagination = {
|
||||||
|
page: data.follows.page,
|
||||||
|
totalPages: data.follows.totalPages,
|
||||||
|
};
|
||||||
|
|
||||||
|
async function onListScroll(e: Event) {
|
||||||
|
if (
|
||||||
|
window.innerHeight + window.scrollY >=
|
||||||
|
0.8 * document.body.offsetHeight &&
|
||||||
|
pagination.page !== pagination.totalPages &&
|
||||||
|
!loading
|
||||||
|
) {
|
||||||
|
loading = true;
|
||||||
|
await loadNextPage();
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadNextPage() {
|
||||||
|
pagination.page += 1;
|
||||||
|
data.follows = await follows_index(
|
||||||
|
{ followee: $page.params.id },
|
||||||
|
pagination.page,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:window on:scroll={onListScroll} />
|
||||||
|
<div>
|
||||||
|
<h1 class="text-3xl font-semibold mb-4">{$_($page.params.type)}</h1>
|
||||||
|
<ul class="space-y-4">
|
||||||
|
{#each data.follows.items as follow}
|
||||||
|
<a href="/profile/{follow.expand?.follower.id}">
|
||||||
|
<li
|
||||||
|
class="flex items-center gap-x-4 hover:bg-menu-item-background-hover p-4"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="rounded-full w-10 aspect-square overflow-hidden"
|
||||||
|
src={getFileURL(
|
||||||
|
follow.expand?.follower ?? {},
|
||||||
|
follow.expand?.follower.avatar,
|
||||||
|
) ||
|
||||||
|
`https://api.dicebear.com/7.x/initials/svg?seed=${data.user.username}&backgroundType=gradientLinear`}
|
||||||
|
alt="avatar"
|
||||||
|
/>
|
||||||
|
<p class="text-lg font-medium">
|
||||||
|
{follow.expand?.follower.username}
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</a>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
11
web/src/routes/profile/[id]/users/[type]/+page.ts
Normal file
11
web/src/routes/profile/[id]/users/[type]/+page.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { follows_index } from "$lib/stores/follow_store";
|
||||||
|
import { error, type Load } from "@sveltejs/kit";
|
||||||
|
|
||||||
|
export const load: Load = async ({ params, fetch }) => {
|
||||||
|
if (!params.type || (params.type !== "followers" && params.type !== "following")) {
|
||||||
|
throw error(400, "Missing or wrong type");
|
||||||
|
}
|
||||||
|
|
||||||
|
const follows = await follows_index({ [params.type == "followers" ? "followee" : "follower"]: params.id }, 1, 10, fetch)
|
||||||
|
return { follows }
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user