diff --git a/database.sql b/database.sql index c0c5f78358..ce9d74cc68 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2022.05-rc (Siberian Iris) --- DB_UPDATE_VERSION 1467 +-- DB_UPDATE_VERSION 1468 -- ------------------------------------------ @@ -386,6 +386,22 @@ CREATE TABLE IF NOT EXISTS `application` ( UNIQUE INDEX `client_id` (`client_id`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='OAuth application'; +-- +-- TABLE application-marker +-- +CREATE TABLE IF NOT EXISTS `application-marker` ( + `application-id` int unsigned NOT NULL COMMENT '', + `uid` mediumint unsigned NOT NULL COMMENT 'Owner User id', + `timeline` varchar(64) NOT NULL COMMENT 'Marker (home, notifications)', + `last_read_id` varchar(255) COMMENT 'Marker id for the timeline', + `version` smallint unsigned COMMENT 'Version number', + `updated_at` datetime COMMENT 'creation time', + PRIMARY KEY(`application-id`,`uid`,`timeline`), + INDEX `uid_id` (`uid`), + FOREIGN KEY (`application-id`) REFERENCES `application` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE, + FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Timeline marker'; + -- -- TABLE application-token -- diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index a89649921d..b09ecca8af 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -84,6 +84,8 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en - [`GET /api/v1/lists/:id/accounts`](https://docs.joinmastodon.org/methods/timelines/lists/) - [`POST /api/v1/lists/:id/accounts`](https://docs.joinmastodon.org/methods/timelines/lists/) - [`DELETE /api/v1/lists/:id/accounts`](https://docs.joinmastodon.org/methods/timelines/lists/) +- [`POST /api/v1/markers`](https://docs.joinmastodon.org/methods/timelines/markers/) +- [`GET /api/v1/markers`](https://docs.joinmastodon.org/methods/timelines/markers/) - [`POST /api/v1/media`](https://docs.joinmastodon.org/methods/statuses/media/) - [`GET /api/v1/media/:id`](https://docs.joinmastodon.org/methods/statuses/media/) - [`PUT /api/v1/media/:id`](https://docs.joinmastodon.org/methods/statuses/media/) @@ -156,7 +158,6 @@ They refer to features that don't exist in Friendica yet. - [`GET /api/v1/announcements`](https://docs.joinmastodon.org/methods/announcements/) - [`GET /api/v1/endorsements`](https://docs.joinmastodon.org/methods/accounts/endorsements/) - [`GET /api/v1/filters`](https://docs.joinmastodon.org/methods/accounts/filters/) -- [`GET /api/v1/markers`](https://docs.joinmastodon.org/methods/timelines/markers/) ## Non supportable endpoints diff --git a/doc/database.md b/doc/database.md index baa9205354..fcff590e13 100644 --- a/doc/database.md +++ b/doc/database.md @@ -11,6 +11,7 @@ Database Tables | [addon](help/database/db_addon) | registered addons | | [apcontact](help/database/db_apcontact) | ActivityPub compatible contacts - used in the ActivityPub implementation | | [application](help/database/db_application) | OAuth application | +| [application-marker](help/database/db_application-marker) | Timeline marker | | [application-token](help/database/db_application-token) | OAuth user token | | [attach](help/database/db_attach) | file attachments | | [cache](help/database/db_cache) | Stores temporary data | diff --git a/doc/database/db_application-marker.md b/doc/database/db_application-marker.md new file mode 100644 index 0000000000..fa83bf58c6 --- /dev/null +++ b/doc/database/db_application-marker.md @@ -0,0 +1,34 @@ +Table application-marker +=========== + +Timeline marker + +Fields +------ + +| Field | Description | Type | Null | Key | Default | Extra | +| -------------- | ---------------------------- | ------------------ | ---- | --- | ------- | ----- | +| application-id | | int unsigned | NO | PRI | NULL | | +| uid | Owner User id | mediumint unsigned | NO | PRI | NULL | | +| timeline | Marker (home, notifications) | varchar(64) | NO | PRI | NULL | | +| last_read_id | Marker id for the timeline | varchar(255) | YES | | NULL | | +| version | Version number | smallint unsigned | YES | | NULL | | +| updated_at | creation time | datetime | YES | | NULL | | + +Indexes +------------ + +| Name | Fields | +| ------- | ----------------------------- | +| PRIMARY | application-id, uid, timeline | +| uid_id | uid | + +Foreign Keys +------------ + +| Field | Target Table | Target Field | +|-------|--------------|--------------| +| application-id | [application](help/database/db_application) | id | +| uid | [user](help/database/db_user) | uid | + +Return to [database documentation](help/database) diff --git a/src/Module/Api/Mastodon/Markers.php b/src/Module/Api/Mastodon/Markers.php index 43949f74c7..4b2a4f46ce 100644 --- a/src/Module/Api/Mastodon/Markers.php +++ b/src/Module/Api/Mastodon/Markers.php @@ -21,10 +21,11 @@ namespace Friendica\Module\Api\Mastodon; -use Friendica\App\Router; use Friendica\Core\System; +use Friendica\Database\DBA; use Friendica\DI; use Friendica\Module\BaseApi; +use Friendica\Util\DateTimeFormat; /** * @see https://docs.joinmastodon.org/methods/timelines/markers/ @@ -34,8 +35,33 @@ class Markers extends BaseApi protected function post(array $request = []) { self::checkAllowedScope(self::SCOPE_WRITE); + $uid = self::getCurrentUserID(); + $application = self::getCurrentApplication(); - $this->response->unsupported(Router::POST, $request); + $timeline = ''; + $last_read_id = ''; + foreach (['home', 'notifications'] as $name) { + if (!empty($request[$name])) { + $timeline = $name; + $last_read_id = $request[$name]['last_read_id'] ?? ''; + } + } + + if (empty($timeline) || empty($last_read_id) || empty($application['id'])) { + DI::mstdnError()->UnprocessableEntity(); + } + + $condition = ['application-id' => $application['id'], 'uid' => $uid, 'timeline' => $timeline]; + $marker = DBA::selectFirst('application-marker', [], $condition); + if (!empty($marker['version'])) { + $version = $marker['version'] + 1; + } else { + $version = 1; + } + + $fields = ['last_read_id' => $last_read_id, 'version' => $version, 'updated_at' => DateTimeFormat::utcNow()]; + DBA::update('application-marker', $fields, $condition, true); + System::jsonExit($this->fethTimelines($application['id'], $uid)); } /** @@ -44,7 +70,23 @@ class Markers extends BaseApi protected function rawContent(array $request = []) { self::checkAllowedScope(self::SCOPE_READ); + $uid = self::getCurrentUserID(); + $application = self::getCurrentApplication(); - System::jsonExit([]); + System::jsonExit($this->fethTimelines($application['id'], $uid)); + } + + private function fethTimelines(int $application_id, int $uid) + { + $values = []; + $markers = DBA::select('application-marker', [], ['application-id' => $application_id, 'uid' => $uid]); + while ($marker = DBA::fetch($markers)) { + $values[$marker['timeline']] = [ + 'last_read_id' => $marker['last_read_id'], + 'version' => $marker['version'], + 'updated_at' => $marker['updated_at'] + ]; + } + return $values; } } diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 642e1667bd..011a298a57 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -55,7 +55,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1467); + define('DB_UPDATE_VERSION', 1468); } return [ @@ -449,6 +449,21 @@ return [ "client_id" => ["UNIQUE", "client_id"] ] ], + "application-marker" => [ + "comment" => "Timeline marker", + "fields" => [ + "application-id" => ["type" => "int unsigned", "not null" => "1", "primary" => "1", "foreign" => ["application" => "id"], "comment" => ""], + "uid" => ["type" => "mediumint unsigned", "not null" => "1", "primary" => "1", "foreign" => ["user" => "uid"], "comment" => "Owner User id"], + "timeline" => ["type" => "varchar(64)", "not null" => "1", "primary" => "1", "comment" => "Marker (home, notifications)"], + "last_read_id" => ["type" => "varchar(255)", "comment" => "Marker id for the timeline"], + "version" => ["type" => "smallint unsigned", "comment" => "Version number"], + "updated_at" => ["type" => "datetime", "comment" => "creation time"], + ], + "indexes" => [ + "PRIMARY" => ["application-id", "uid", "timeline"], + "uid_id" => ["uid"], + ] + ], "application-token" => [ "comment" => "OAuth user token", "fields" => [ diff --git a/static/routes.config.php b/static/routes.config.php index f2a6415482..4c6f5b6bb4 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -237,7 +237,7 @@ return [ '/lists' => [Module\Api\Mastodon\Lists::class, [R::GET, R::POST]], '/lists/{id:\d+}' => [Module\Api\Mastodon\Lists::class, [R::GET, R::PUT, R::DELETE]], '/lists/{id:\d+}/accounts' => [Module\Api\Mastodon\Lists\Accounts::class, [R::GET, R::POST, R::DELETE]], - '/markers' => [Module\Api\Mastodon\Markers::class, [R::GET, R::POST]], // Dummy, not supported + '/markers' => [Module\Api\Mastodon\Markers::class, [R::GET, R::POST]], '/media/{id:\d+}' => [Module\Api\Mastodon\Media::class, [R::GET, R::PUT ]], '/mutes' => [Module\Api\Mastodon\Mutes::class, [R::GET ]], '/notifications' => [Module\Api\Mastodon\Notifications::class, [R::GET ]],