diff --git a/db/federation/create.go b/db/federation/create.go index a929da56..afeae539 100644 --- a/db/federation/create.go +++ b/db/federation/create.go @@ -466,13 +466,20 @@ func ProcessCreateOrUpdateActivity(app core.App, actor *core.Record, activity pu } func processCreateOrUpdateTrailActivity(activity pub.Activity, app core.App, actor *core.Record) error { - - // no need to do anything if the actor is local - if actor.GetBool("isLocal") { - return nil + trail, err := util.TrailFromActivity(activity, app, actor) + if err != nil { + return err } - trail, err := util.TrailFromActivity(activity, app, actor) + trailAuthor, err := app.FindRecordById("activitypub_actors", trail.GetString("author")) + if err != nil { + return err + } + + _, err = util.InsertIntoFeed(app, actor.Id, trailAuthor.Id, trail.Id, util.TrailFeed) + if err != nil { + return err + } trailObject, _ := pub.ToObject(activity.Object) @@ -792,13 +799,20 @@ func processCreateOrUpdateSummitLogActivity(activity pub.Activity, app core.App, } func processCreateOrUpdateListActivity(activity pub.Activity, app core.App, actor *core.Record) error { - - // no need to do anything if the actor is local - if actor.GetBool("isLocal") { - return nil + list, err := util.ListFromActivity(activity, app, actor) + if err != nil { + return err } - _, err := util.ListFromActivity(activity, app, actor) + listAuthor, err := app.FindRecordById("activitypub_actors", list.GetString("author")) + if err != nil { + return err + } + + _, err = util.InsertIntoFeed(app, actor.Id, listAuthor.Id, list.Id, util.ListFeed) + if err != nil { + return err + } return err } diff --git a/db/main.go b/db/main.go index bf7a6f52..3e021acb 100644 --- a/db/main.go +++ b/db/main.go @@ -120,6 +120,8 @@ func setupEventHandlers(app *pocketbase.PocketBase, client meilisearch.ServiceMa app.OnRecordUpdate("integrations").BindFunc(updateIntegrationHandler()) app.OnRecordAfterUpdateSuccess("integrations").BindFunc(createUpdateIntegrationSuccessHandler()) + app.OnRecordsListRequest("feed").BindFunc(listFeedHandler()) + app.OnRecordCreateRequest().BindFunc(sanitizeHTML()) app.OnRecordUpdateRequest().BindFunc(sanitizeHTML()) @@ -242,6 +244,11 @@ func createTrailHandler(client meilisearch.ServiceManager) func(e *core.RecordEv return err } + _, err = util.InsertIntoFeed(e.App, author.Id, author.Id, record.Id, util.TrailFeed) + if err != nil { + return err + } + return nil } } @@ -616,6 +623,11 @@ func createListHandler(client meilisearch.ServiceManager) func(e *core.RecordEve return err } + _, err = util.InsertIntoFeed(e.App, author.Id, author.Id, record.Id, util.ListFeed) + if err != nil { + return err + } + return nil } } @@ -877,6 +889,31 @@ func changeUserEmailHandler() func(e *core.RecordRequestEmailChangeRequestEvent) } } +func listFeedHandler() func(e *core.RecordsListRequestEvent) error { + return func(e *core.RecordsListRequestEvent) error { + + for _, r := range e.Records { + var item *core.Record + var err error + switch r.GetString("type") { + case string(util.TrailFeed): + item, err = e.App.FindRecordById("trails", r.GetString("item")) + case string(util.ListFeed): + item, err = e.App.FindRecordById("lists", r.GetString("item")) + case string(util.SummitLogFeed): + item, err = e.App.FindRecordById("summit_logs", r.GetString("item")) + } + + if err != nil { + return err + } + + r.MergeExpand(map[string]any{"item": item}) + } + return e.Next() + } +} + func onBeforeServeHandler(client meilisearch.ServiceManager) func(se *core.ServeEvent) error { return func(se *core.ServeEvent) error { registerRoutes(se, client) diff --git a/db/migrations/1752231390_created_feed.go b/db/migrations/1752231390_created_feed.go new file mode 100644 index 00000000..6e18f65e --- /dev/null +++ b/db/migrations/1752231390_created_feed.go @@ -0,0 +1,115 @@ +package migrations + +import ( + "encoding/json" + + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + jsonData := `{ + "createRule": null, + "deleteRule": null, + "fields": [ + { + "autogeneratePattern": "[a-z0-9]{15}", + "hidden": false, + "id": "text3208210256", + "max": 15, + "min": 15, + "name": "id", + "pattern": "^[a-z0-9]+$", + "presentable": false, + "primaryKey": true, + "required": true, + "system": true, + "type": "text" + }, + { + "cascadeDelete": true, + "collectionId": "pbc_1295301207", + "hidden": false, + "id": "relation1148540665", + "maxSelect": 1, + "minSelect": 0, + "name": "actor", + "presentable": false, + "required": true, + "system": false, + "type": "relation" + }, + { + "cascadeDelete": true, + "collectionId": "pbc_1295301207", + "hidden": false, + "id": "relation3182418120", + "maxSelect": 1, + "minSelect": 0, + "name": "author", + "presentable": false, + "required": true, + "system": false, + "type": "relation" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text521872670", + "max": 15, + "min": 15, + "name": "item", + "pattern": "^[a-z0-9]+$", + "presentable": false, + "primaryKey": false, + "required": true, + "system": false, + "type": "text" + }, + { + "hidden": false, + "id": "autodate2990389176", + "name": "created", + "onCreate": true, + "onUpdate": false, + "presentable": false, + "system": false, + "type": "autodate" + }, + { + "hidden": false, + "id": "autodate3332085495", + "name": "updated", + "onCreate": true, + "onUpdate": true, + "presentable": false, + "system": false, + "type": "autodate" + } + ], + "id": "pbc_72164123", + "indexes": [], + "listRule": null, + "name": "feed", + "system": false, + "type": "base", + "updateRule": null, + "viewRule": null + }` + + collection := &core.Collection{} + if err := json.Unmarshal([]byte(jsonData), &collection); err != nil { + return err + } + + return app.Save(collection) + }, func(app core.App) error { + collection, err := app.FindCollectionByNameOrId("pbc_72164123") + if err != nil { + return err + } + + return app.Delete(collection) + }) +} diff --git a/db/migrations/1752232212_updated_feed.go b/db/migrations/1752232212_updated_feed.go new file mode 100644 index 00000000..4393275c --- /dev/null +++ b/db/migrations/1752232212_updated_feed.go @@ -0,0 +1,46 @@ +package migrations + +import ( + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + collection, err := app.FindCollectionByNameOrId("pbc_72164123") + if err != nil { + return err + } + + // add field + if err := collection.Fields.AddMarshaledJSONAt(4, []byte(`{ + "hidden": false, + "id": "select2363381545", + "maxSelect": 1, + "name": "type", + "presentable": false, + "required": true, + "system": false, + "type": "select", + "values": [ + "trail", + "list", + "summit_log" + ] + }`)); err != nil { + return err + } + + return app.Save(collection) + }, func(app core.App) error { + collection, err := app.FindCollectionByNameOrId("pbc_72164123") + if err != nil { + return err + } + + // remove field + collection.Fields.RemoveById("select2363381545") + + return app.Save(collection) + }) +} diff --git a/db/migrations/1752233467_updated_feed.go b/db/migrations/1752233467_updated_feed.go new file mode 100644 index 00000000..e2684adb --- /dev/null +++ b/db/migrations/1752233467_updated_feed.go @@ -0,0 +1,40 @@ +package migrations + +import ( + "encoding/json" + + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + collection, err := app.FindCollectionByNameOrId("pbc_72164123") + if err != nil { + return err + } + + // update collection data + if err := json.Unmarshal([]byte(`{ + "listRule": "actor.user = @request.auth.id" + }`), &collection); err != nil { + return err + } + + return app.Save(collection) + }, func(app core.App) error { + collection, err := app.FindCollectionByNameOrId("pbc_72164123") + if err != nil { + return err + } + + // update collection data + if err := json.Unmarshal([]byte(`{ + "listRule": null + }`), &collection); err != nil { + return err + } + + return app.Save(collection) + }) +} diff --git a/db/migrations/1752239359_create_initial_feed.go b/db/migrations/1752239359_create_initial_feed.go new file mode 100644 index 00000000..1a05bd3b --- /dev/null +++ b/db/migrations/1752239359_create_initial_feed.go @@ -0,0 +1,100 @@ +package migrations + +import ( + "pocketbase/util" + + "github.com/pocketbase/dbx" + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + trails, err := app.FindAllRecords("trails") + if err != nil { + return err + } + + for _, t := range trails { + trailAuthor, err := app.FindRecordById("activitypub_actors", t.GetString("author")) + if err != nil { + return err + } + + feed, err := util.InsertIntoFeed(app, trailAuthor.Id, trailAuthor.Id, t.Id, util.TrailFeed) + if err != nil { + return err + } + + feed.SetRaw("created", t.GetDateTime("created")) + err = app.Save(feed) + if err != nil { + return err + } + + followers, err := app.FindRecordsByFilter("follows", "followee={:author}", "", -1, 0, dbx.Params{"author": trailAuthor.Id}) + if err != nil { + return err + } + for _, f := range followers { + feed, err = util.InsertIntoFeed(app, f.GetString("follower"), trailAuthor.Id, t.Id, util.TrailFeed) + if err != nil { + return err + } + + feed.SetRaw("created", t.GetDateTime("created")) + err = app.Save(feed) + if err != nil { + return err + } + } + } + + lists, err := app.FindAllRecords("lists") + if err != nil { + return err + } + + for _, t := range lists { + listAuthor, err := app.FindRecordById("activitypub_actors", t.GetString("author")) + if err != nil { + return err + } + + feed, err := util.InsertIntoFeed(app, listAuthor.Id, listAuthor.Id, t.Id, util.ListFeed) + if err != nil { + return err + } + + feed.SetRaw("created", t.GetDateTime("created")) + err = app.Save(feed) + if err != nil { + return err + } + + followers, err := app.FindRecordsByFilter("follows", "followee={:author}", "", -1, 0, dbx.Params{"author": listAuthor.Id}) + if err != nil { + return err + } + for _, f := range followers { + feed, err = util.InsertIntoFeed(app, f.GetString("follower"), listAuthor.Id, t.Id, util.ListFeed) + if err != nil { + return err + } + + feed.SetRaw("created", t.GetDateTime("created")) + err = app.Save(feed) + if err != nil { + return err + } + } + } + return nil + }, func(app core.App) error { + _, err := app.DB(). + NewQuery("DELETE FROM feed;"). + Execute() + + return err + }) +} diff --git a/db/util/activitypub.go b/db/util/activitypub.go index bfb1281f..197f25fd 100644 --- a/db/util/activitypub.go +++ b/db/util/activitypub.go @@ -176,6 +176,11 @@ func TrailFromActivity(activity pub.Activity, app core.App, actor *core.Record) } else { return nil, err } + } else { + // this trail exists already + // nothing more to do + + return record, nil } var distance, duration, elevation_gain, elevation_loss float64 @@ -442,6 +447,11 @@ func ListFromActivity(activity pub.Activity, app core.App, actor *core.Record) ( } else { return nil, err } + } else { + // this list exists already + // nothing more to do + + return record, nil } record.Set("name", l.Name.First().Value) diff --git a/db/util/feed.go b/db/util/feed.go new file mode 100644 index 00000000..34df48a7 --- /dev/null +++ b/db/util/feed.go @@ -0,0 +1,27 @@ +package util + +import "github.com/pocketbase/pocketbase/core" + +type FeedType string + +const ( + ListFeed FeedType = "list" + TrailFeed FeedType = "trail" + SummitLogFeed FeedType = "summit_log" +) + +func InsertIntoFeed(app core.App, actorId string, authorId string, itemId string, feedType FeedType) (*core.Record, error) { + collection, err := app.FindCollectionByNameOrId("feed") + if err != nil { + return nil, err + } + + record := core.NewRecord(collection) + + record.Set("actor", actorId) + record.Set("author", authorId) + record.Set("item", itemId) + record.Set("type", string(feedType)) + + return record, app.Save(record) +} diff --git a/web/src/lib/assets/svgs/empty_states/empty_state_feed_dark.svg b/web/src/lib/assets/svgs/empty_states/empty_state_feed_dark.svg new file mode 100644 index 00000000..398931e4 --- /dev/null +++ b/web/src/lib/assets/svgs/empty_states/empty_state_feed_dark.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/web/src/lib/assets/svgs/empty_states/empty_state_feed_light.svg b/web/src/lib/assets/svgs/empty_states/empty_state_feed_light.svg new file mode 100644 index 00000000..85240086 --- /dev/null +++ b/web/src/lib/assets/svgs/empty_states/empty_state_feed_light.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/web/src/lib/components/empty_states/empty_state_feed.svelte b/web/src/lib/components/empty_states/empty_state_feed.svelte new file mode 100644 index 00000000..71e08e2a --- /dev/null +++ b/web/src/lib/components/empty_states/empty_state_feed.svelte @@ -0,0 +1,20 @@ + + +
+ Empty State showing a wanderer going into the distance + +

{$_("empty-feed")}...

+

+ {$_('empty-feed-explanation')} +

+
diff --git a/web/src/lib/components/nav_bar.svelte b/web/src/lib/components/nav_bar.svelte index 3473ec3d..1df00084 100644 --- a/web/src/lib/components/nav_bar.svelte +++ b/web/src/lib/components/nav_bar.svelte @@ -185,7 +185,7 @@ -