diff --git a/app/Devnull/Account.php b/app/Devnull/Account.php index 725e7446..0d78e649 100644 --- a/app/Devnull/Account.php +++ b/app/Devnull/Account.php @@ -153,12 +153,13 @@ public function rememberUnreadRemotes(int $count): void } /** - * Signs this phone out: who it was, the badges, its pastéis and Remotes, every answer kept for a quick start, - * and your unsent drafts. + * Signs this phone out: who it was, the badges, its pastéis and Remotes, every answer and image kept for a + * quick start, and your unsent drafts. */ public function forget(): void { app(ResponseCache::class)->flush(); + ImageCache::clear(); app(Stash::class)->forget($this->user()?->id); app(Outbox::class)->forgetAll($this->user()?->id); diff --git a/app/Devnull/DownloadImages.php b/app/Devnull/DownloadImages.php index 9057ad5a..f3eddae8 100644 --- a/app/Devnull/DownloadImages.php +++ b/app/Devnull/DownloadImages.php @@ -66,6 +66,11 @@ public static function save(array $urls, ?float $until = null): int $batch, )); + // Signing out empties the cache (see Account::forget), and this thread can't tell it happened otherwise. + if (app(Account::class)->user() === null) { + break; + } + foreach ($batch as $index => $url) { $response = $responses[$index] ?? null; diff --git a/app/Devnull/ImageCache.php b/app/Devnull/ImageCache.php index baf6751d..cd41cd70 100644 --- a/app/Devnull/ImageCache.php +++ b/app/Devnull/ImageCache.php @@ -3,6 +3,7 @@ namespace App\Devnull; use Carbon\CarbonImmutable; +use Illuminate\Support\Facades\File; /** * Remote images kept on the device: avatars, commit images and link preview @@ -117,6 +118,18 @@ public static function takePrune(): ?int return $cache->startedAt - self::hours() * 3600; } + /** + * Deletes every image on the device, on sign-out, so the next person to + * sign in never sees what the last one was shown. What's drawn from now on + * comes from its URL. + */ + public static function clear(): void + { + File::deleteDirectory(self::directory()); + + app(self::class)->sources = []; + } + /** * Every image URL in an answer, wherever it's nested: people's avatars, * commits' images and link previews' images. diff --git a/tests/Feature/ImageCacheTest.php b/tests/Feature/ImageCacheTest.php index df867b63..dea23d03 100644 --- a/tests/Feature/ImageCacheTest.php +++ b/tests/Feature/ImageCacheTest.php @@ -1,5 +1,6 @@ Http::response('png bytes', 200, ['Content-Type' => 'image/png']), 'devnull.test/api/v1/log/following*' => Http::response(pageJson([commitJson(['author' => userJson(['avatar_url' => 'https://avatars.githubusercontent.com/u/2?v=4'])])])), @@ -275,7 +278,28 @@ function imagesQueued(): array expect(File::exists(ImageCache::path('https://avatars.githubusercontent.com/u/3?v=4')))->toBeFalse(); }); +it('empties the image folder on sign-out, and draws what it held from the URL again', function () { + signIn(); + $url = 'https://avatars.githubusercontent.com/u/2?v=4'; + $path = cachedImage($url); + + expect(ImageCache::src($url))->toBe($path); + + app(Account::class)->forget(); + + expect(File::isDirectory(ImageCache::directory()))->toBeFalse() + ->and(ImageCache::src($url))->toBe($url); +}); + +it('keeps no image that lands once nobody is signed in', function () { + Http::fake(['avatars.githubusercontent.com/*' => Http::response('png bytes', 200, ['Content-Type' => 'image/png'])]); + + expect(DownloadImages::save(['https://avatars.githubusercontent.com/u/2?v=4']))->toBe(0) + ->and(File::exists(ImageCache::path('https://avatars.githubusercontent.com/u/2?v=4')))->toBeFalse(); +}); + it('gives images it waits on only the time that is left, and starts none once it is up', function () { + signIn(); Http::fake(['avatars.githubusercontent.com/*' => Http::response('png bytes', 200, ['Content-Type' => 'image/png'])]); expect(DownloadImages::save(['https://avatars.githubusercontent.com/u/2?v=4'], until: microtime(true) - 1))->toBe(0);