diff --git a/CHANGELOG.md b/CHANGELOG.md index a293915f5..ab228964f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,10 @@ - Update AccountTransformer, fix follower/following count visibility bug ([542d1106](https://github.com/pixelfed/pixelfed/commit/542d1106)) - Update ProfileMigration model, add target relation ([3f053997](https://github.com/pixelfed/pixelfed/commit/3f053997)) - Update ApiV1Controller, update Notifications endpoint to filter notifications with missing activities ([a933615b](https://github.com/pixelfed/pixelfed/commit/a933615b)) +- Update ApiV1Controller, fix public timeline scope, properly support both local + remote parameters ([d6eac655](https://github.com/pixelfed/pixelfed/commit/d6eac655)) +- Update ApiV1Controller, handle public feed parameter bug to gracefully fallback to min_id=1 when max_id=0 ([e3826c58](https://github.com/pixelfed/pixelfed/commit/e3826c58)) +- Update ApiV1Controller, fix hashtag feed to include private posts from accounts you follow or your own, and your own unlisted posts ([3b5500b3](https://github.com/pixelfed/pixelfed/commit/3b5500b3)) +- ([](https://github.com/pixelfed/pixelfed/commit/)) - ([](https://github.com/pixelfed/pixelfed/commit/)) ## [v0.11.12 (2024-02-16)](https://github.com/pixelfed/pixelfed/compare/v0.11.11...v0.11.12) diff --git a/app/Http/Controllers/Api/ApiV1Controller.php b/app/Http/Controllers/Api/ApiV1Controller.php index e6aa2cb7b..c9967c192 100644 --- a/app/Http/Controllers/Api/ApiV1Controller.php +++ b/app/Http/Controllers/Api/ApiV1Controller.php @@ -2627,6 +2627,9 @@ class ApiV1Controller extends Controller $napi = $request->has(self::PF_API_ENTITY_KEY); $min = $request->input('min_id'); $max = $request->input('max_id'); + if ($max == 0) { + $min = 1; + } $minOrMax = $request->anyFilled(['max_id', 'min_id']); $limit = $request->input('limit') ?? 20; if ($limit > 40) { @@ -2634,8 +2637,8 @@ class ApiV1Controller extends Controller } $user = $request->user(); - $remote = $request->has('remote'); - $local = $request->has('local'); + $remote = $request->has('remote') && $request->boolean('remote'); + $local = $request->boolean('local'); $userRoleKey = $remote ? 'can-view-network-feed' : 'can-view-public-feed'; if ($user->has_roles && ! UserRoleService::can($userRoleKey, $user->id)) { return []; @@ -2646,7 +2649,36 @@ class ApiV1Controller extends Controller $hideNsfw = config('instance.hide_nsfw_on_public_feeds'); $amin = SnowflakeService::byDate(now()->subDays(config('federation.network_timeline_days_falloff'))); - if ($remote) { + if ($local && $remote) { + $feed = Status::select( + 'id', + 'uri', + 'type', + 'scope', + 'created_at', + 'profile_id', + 'in_reply_to_id', + 'reblog_of_id' + ) + ->when($minOrMax, function ($q, $minOrMax) use ($min, $max) { + $dir = $min ? '>' : '<'; + $id = $min ?? $max; + + return $q->where('id', $dir, $id); + }) + ->whereNull(['in_reply_to_id', 'reblog_of_id']) + ->when($hideNsfw, function ($q, $hideNsfw) { + return $q->where('is_nsfw', false); + }) + ->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']) + ->whereScope('public') + ->where('id', '>', $amin) + ->orderByDesc('id') + ->limit(($limit * 2)) + ->pluck('id') + ->values() + ->toArray(); + } elseif ($remote && ! $local) { if (config('instance.timeline.network.cached')) { Cache::remember('api:v1:timelines:network:cache_check', 10368000, function () { if (NetworkTimelineService::count() == 0) { @@ -2799,6 +2831,9 @@ class ApiV1Controller extends Controller if ($remote) { $baseUrl .= 'remote=1&'; } + if ($local) { + $baseUrl .= 'local=1&'; + } $minId = $res->map(function ($s) { return ['id' => $s['id']]; })->min('id'); @@ -3600,7 +3635,7 @@ class ApiV1Controller extends Controller 'min_id' => 'nullable|integer|min:0|max:'.PHP_INT_MAX, 'max_id' => 'nullable|integer|min:0|max:'.PHP_INT_MAX, 'limit' => 'sometimes|integer|min:1', - 'only_media' => 'sometimes|boolean', + 'only_media' => 'sometimes', '_pe' => 'sometimes', ]); @@ -3635,7 +3670,7 @@ class ApiV1Controller extends Controller if ($limit > 40) { $limit = 40; } - $onlyMedia = $request->input('only_media', true); + $onlyMedia = $request->boolean('only_media', true); $pe = $request->has(self::PF_API_ENTITY_KEY); $pid = $request->user()->profile_id; @@ -3661,20 +3696,32 @@ class ApiV1Controller extends Controller } $res = StatusHashtag::whereHashtagId($tag->id) - ->whereStatusVisibility('public') + ->whereIn('status_visibility', ['public', 'private', 'unlisted']) ->where('status_id', $dir, $id) ->orderBy('status_id', 'desc') ->limit(100) ->pluck('status_id') ->map(function ($i) use ($pe) { - return $pe ? StatusService::get($i) : StatusService::getMastodon($i); + return $pe ? StatusService::get($i, false) : StatusService::getMastodon($i, false); }) - ->filter(function ($i) use ($onlyMedia) { - if (! $i) { + ->filter(function ($i) use ($onlyMedia, $pid) { + if (! $i || ! isset($i['account'], $i['account']['id'])) { return false; } - if ($onlyMedia && ! isset($i['media_attachments']) || ! count($i['media_attachments'])) { - return false; + if ($i['visibility'] === 'unlisted') { + if ((int) $i['account']['id'] !== $pid) { + return false; + } + } + if ($i['visibility'] === 'private') { + if ((int) $i['account']['id'] !== $pid) { + return FollowerService::follows($pid, $i['account']['id'], true); + } + } + if ($onlyMedia == true) { + if (! isset($i['media_attachments']) || ! count($i['media_attachments'])) { + return false; + } } return $i && isset($i['account'], $i['url']);