Skip to content

Commit efd1f1b

Browse files
authored
Replace default GitHub avatar (#1359)
1 parent 6cb8db7 commit efd1f1b

File tree

12 files changed

+122
-7
lines changed

12 files changed

+122
-7
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace App\Console\Commands;
4+
5+
use App\Jobs\UpdateUserIdenticonStatus;
6+
use App\Models\User;
7+
use Illuminate\Console\Command;
8+
9+
class BackfillIdenticons extends Command
10+
{
11+
/**
12+
* The name and signature of the console command.
13+
*
14+
* @var string
15+
*/
16+
protected $signature = 'app:backfill-identicons';
17+
18+
/**
19+
* The console command description.
20+
*
21+
* @var string
22+
*/
23+
protected $description = 'Backfills the github_has_identicon column for users';
24+
25+
/**
26+
* Execute the console command.
27+
*/
28+
public function handle()
29+
{
30+
User::whereNotNull('github_id')
31+
->chunk(100, function ($users) {
32+
foreach ($users as $user) {
33+
UpdateUserIdenticonStatus::dispatch($user);
34+
}
35+
36+
sleep(2);
37+
});
38+
}
39+
}

app/Http/Controllers/Auth/RegisterController.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use App\Http\Controllers\Controller;
66
use App\Http\Requests\RegisterRequest;
77
use App\Jobs\RegisterUser;
8+
use App\Jobs\UpdateUserIdenticonStatus;
89
use App\Models\User;
910
use App\Providers\AppServiceProvider;
1011
use Illuminate\Auth\Events\Registered;
@@ -71,6 +72,10 @@ protected function create(RegisterRequest $request): User
7172
{
7273
$this->dispatchSync(RegisterUser::fromRequest($request));
7374

74-
return User::findByEmailAddress($request->emailAddress());
75+
$user = User::findByEmailAddress($request->emailAddress());
76+
77+
$this->dispatch(new UpdateUserIdenticonStatus($user));
78+
79+
return $user;
7580
}
7681
}

app/Http/Controllers/HomeController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public function show()
1616
$communityMembers = Cache::remember(
1717
'communityMembers',
1818
now()->addMinutes(5),
19-
fn () => User::notBanned()->inRandomOrder()->take(100)->get()->chunk(20)
19+
fn () => User::notBanned()->withAvatar()->inRandomOrder()->take(100)->get()->chunk(20)
2020
);
2121

2222
$totalUsers = Cache::remember('totalUsers', now()->addDay(), fn () => number_format(User::notBanned()->count()));

app/Jobs/UnVerifyAuthor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use Illuminate\Contracts\Queue\ShouldQueue;
77
use Illuminate\Foundation\Queue\Queueable;
88

9-
class UnVerifyAuthor implements ShouldQueue
9+
final class UnVerifyAuthor implements ShouldQueue
1010
{
1111
use Queueable;
1212

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace App\Jobs;
4+
5+
use Illuminate\Contracts\Queue\ShouldQueue;
6+
use Illuminate\Foundation\Bus\Dispatchable;
7+
use Illuminate\Foundation\Queue\Queueable;
8+
use Illuminate\Queue\InteractsWithQueue;
9+
use Illuminate\Queue\SerializesModels;
10+
use App\Models\User;
11+
use App\Social\GithubUserApi;
12+
13+
final class UpdateUserIdenticonStatus implements ShouldQueue
14+
{
15+
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
16+
17+
public function __construct(protected User $user) {}
18+
19+
public function handle(GithubUserApi $github): void
20+
{
21+
$hasIdenticon = $github->hasIdenticon($this->user->githubId());
22+
23+
$this->user->update(['github_has_identicon' => $hasIdenticon]);
24+
}
25+
}

app/Jobs/VerifyAuthor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use Illuminate\Contracts\Queue\ShouldQueue;
77
use Illuminate\Foundation\Queue\Queueable;
88

9-
class VerifyAuthor implements ShouldQueue
9+
final class VerifyAuthor implements ShouldQueue
1010
{
1111
use Queueable;
1212

app/Models/User.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ final class User extends Authenticatable implements MustVerifyEmail
5858
'remember_token',
5959
'bio',
6060
'banned_reason',
61+
'github_has_identicon'
6162
];
6263

6364
/**
@@ -75,6 +76,7 @@ protected function casts(): array
7576
return [
7677
'allowed_notifications' => 'array',
7778
'author_verified_at' => 'datetime',
79+
'github_has_identicon' => 'boolean',
7880
];
7981
}
8082

@@ -113,6 +115,11 @@ public function githubUsername(): string
113115
return $this->github_username ?? '';
114116
}
115117

118+
public function hasIdenticon(): bool
119+
{
120+
return (bool) $this->github_has_identicon;
121+
}
122+
116123
public function twitter(): ?string
117124
{
118125
return $this->twitter;
@@ -411,6 +418,11 @@ public function scopeModerators(Builder $query)
411418
]);
412419
}
413420

421+
public function scopeWithAvatar(Builder $query)
422+
{
423+
return $query->where('github_has_identicon', false);
424+
}
425+
414426
public function scopeNotBanned(Builder $query)
415427
{
416428
return $query->whereNull('banned_at');

app/Social/GithubUserApi.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use Illuminate\Http\Client\ConnectionException;
66
use Illuminate\Support\Facades\Http;
77

8-
class GithubUserApi
8+
final class GithubUserApi
99
{
1010
public function find(int|string $id): ?GitHubUser
1111
{
@@ -14,4 +14,22 @@ public function find(int|string $id): ?GitHubUser
1414

1515
return $response->failed() ? null : new GitHubUser($response->json());
1616
}
17+
18+
public function hasIdenticon(int|string $id): bool
19+
{
20+
$response = Http::retry(3, 300, fn ($exception) => $exception instanceof ConnectionException)
21+
->get("https://avatars.githubusercontent.com/u/{$id}?v=4&s=40");
22+
23+
if ($response->failed()) {
24+
return true;
25+
}
26+
27+
if (! $info = getimagesizefromstring($response->body())) {
28+
return true;
29+
}
30+
31+
[$width, $height] = $info;
32+
33+
return ! ($width === 420 && $height === 420);
34+
}
1735
}

database/factories/UserFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public function definition(): array
2323
'remember_token' => Str::random(10),
2424
'github_id' => $this->faker->unique()->numberBetween(10000, 99999),
2525
'github_username' => $this->faker->unique()->userName(),
26+
'github_has_identicon' => $this->faker->boolean(),
2627
'twitter' => $this->faker->unique()->userName(),
2728
'bluesky' => $this->faker->unique()->userName(),
2829
'website' => 'https://laravel.io',
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
public function up(): void
10+
{
11+
Schema::table('users', function (Blueprint $table) {
12+
$table->boolean('github_has_identicon')->after('github_username')->default(false);
13+
});
14+
}
15+
};

0 commit comments

Comments
 (0)