Skip to content

Commit 2aa260e

Browse files
committed
Add GitHub account connection and disconnection functionality
1 parent a4dd3c8 commit 2aa260e

File tree

9 files changed

+164
-2
lines changed

9 files changed

+164
-2
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace App\Actions;
4+
5+
use App\Jobs\UpdateUserIdenticonStatus;
6+
use App\Models\User;
7+
use Laravel\Socialite\Two\User as SocialiteUser;
8+
use function dispatch;
9+
10+
final class ConnectGitHubAccount
11+
{
12+
public function __invoke(User $user, SocialiteUser $socialiteUser): void
13+
{
14+
$user->update([
15+
'github_id' => $socialiteUser->getId(),
16+
'github_username' => $socialiteUser->getNickname(),
17+
]);
18+
19+
dispatch(new UpdateUserIdenticonStatus($user));
20+
}
21+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace App\Actions;
4+
5+
use App\Models\User;
6+
7+
final class DisconnectGitHubAccount
8+
{
9+
public function __invoke(User $user): void
10+
{
11+
$user->update([
12+
'github_id' => null,
13+
'github_username' => null,
14+
'github_has_identicon' => false,
15+
]);
16+
}
17+
}

app/Http/Controllers/Auth/GitHubController.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Http\Controllers\Auth;
44

5+
use App\Actions\ConnectGitHubAccount;
56
use App\Http\Controllers\Controller;
67
use App\Jobs\UpdateProfile;
78
use App\Models\User;
@@ -28,7 +29,7 @@ public function redirectToProvider()
2829
/**
2930
* Obtain the user information from GitHub.
3031
*/
31-
public function handleProviderCallback()
32+
public function handleProviderCallback(ConnectGitHubAccount $connectGitHubAccount)
3233
{
3334
try {
3435
$socialiteUser = $this->getSocialiteUser();
@@ -42,6 +43,26 @@ public function handleProviderCallback()
4243
return $socialiteUser;
4344
}
4445

46+
$isConnectingAttempt = session()->pull('settings.github.connect.intended', false);
47+
if ($isConnectingAttempt) {
48+
$currentUser = auth()->user();
49+
$githubId = $socialiteUser->getId();
50+
51+
// Check if the GitHub account is already connected to another user
52+
$existingUser = User::where('github_id', $githubId)->where('id', '!=', $currentUser->id)->first();
53+
if ($existingUser) {
54+
$this->error('This GitHub account is already connected to another user.');
55+
56+
return redirect(route('settings.profile'));
57+
}
58+
59+
$connectGitHubAccount($currentUser, $socialiteUser);
60+
61+
$this->success('Your GitHub account has been connected.');
62+
63+
return redirect(route('settings.profile'));
64+
}
65+
4566
try {
4667
$user = User::findByGitHubId($socialiteUser->getId());
4768
} catch (ModelNotFoundException $exception) {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace App\Http\Controllers\Settings;
4+
5+
use App\Actions\DisconnectGitHubAccount;
6+
use App\Http\Controllers\Controller;
7+
use Illuminate\Auth\Middleware\Authenticate;
8+
use Illuminate\Http\RedirectResponse;
9+
10+
final class GitHubAccountController extends Controller
11+
{
12+
public function __construct()
13+
{
14+
$this->middleware(Authenticate::class);
15+
}
16+
17+
public function connect(): RedirectResponse
18+
{
19+
session()->put('settings.github.connect.intended', true);
20+
21+
return redirect(route('login.github'));
22+
}
23+
24+
public function disconnect(DisconnectGitHubAccount $disconnectGitHubAccount): RedirectResponse
25+
{
26+
$disconnectGitHubAccount(auth()->user());
27+
28+
$this->success('Your GitHub account has been disconnected.');
29+
30+
return redirect(route('settings.profile'));
31+
}
32+
}

app/Models/User.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ public function githubUsername(): string
118118
return $this->github_username ?? '';
119119
}
120120

121+
public function hasConnectedGitHubAccount(): bool
122+
{
123+
return ! is_null($this->githubId());
124+
}
125+
121126
public function hasIdenticon(): bool
122127
{
123128
return (bool) $this->github_has_identicon;

resources/svg/github-line.svg

Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
@title('GitHub')
2+
3+
<section aria-labelledby="github_settings_heading" class="mt-6">
4+
<div class="shadow-sm sm:rounded-md sm:overflow-hidden">
5+
<div class="bg-white py-6 px-4 space-y-6 sm:p-6">
6+
<div>
7+
<h2 id="github_settings_heading" class="text-lg leading-6 font-medium text-gray-900">
8+
GitHub Account
9+
</h2>
10+
11+
<p class="mt-1 text-sm leading-5 text-gray-500">
12+
Connect your GitHub account to keep your profile information in sync.
13+
</p>
14+
</div>
15+
16+
@if (Auth::user()->hasConnectedGitHubAccount())
17+
<div class="flex items-center justify-between flex-wrap gap-4">
18+
<div class="space-y-1">
19+
<span class="block text-sm font-medium text-gray-700">
20+
Connected as
21+
</span>
22+
23+
<a href="https://github.com/{{ Auth::user()->githubUsername() }}" class="text-lio-700 font-semibold"
24+
target="_blank" rel="noopener">
25+
{{ '@' . Auth::user()->githubUsername() }}
26+
</a>
27+
</div>
28+
29+
<x-forms.form method="POST" action="{{ route('settings.github.disconnect') }}">
30+
<x-buttons.danger-button type="submit">
31+
Disconnect GitHub
32+
</x-buttons.danger-button>
33+
</x-forms.form>
34+
</div>
35+
@else
36+
<div class="flex items-center justify-between flex-wrap gap-4">
37+
<p class="text-sm text-gray-600">
38+
Connecting your GitHub account will automatically populate your GitHub username and use your
39+
GitHub profile image.
40+
</p>
41+
</div>
42+
@endif
43+
</div>
44+
45+
@if (!Auth::user()->hasConnectedGitHubAccount())
46+
<x-forms.form id="github_settings_form" method="POST" action="{{ route('settings.github.connect') }}">
47+
<div class="px-4 py-3 bg-gray-50 text-right sm:px-6">
48+
<x-buttons.primary-button type="submit">
49+
Connect GitHub
50+
</x-buttons.primary-button>
51+
</div>
52+
</x-forms.form>
53+
@endif
54+
</div>
55+
</section>

resources/views/users/settings/settings.blade.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@
2525
<x-heroicon-o-key class="text-gray-400 shrink-0 -ml-1 mr-3 h-6 w-6" />
2626
<span class="truncate">Password</span>
2727
</a>
28-
<a href="#api_token_settings_heading" class="text-gray-600 hover:bg-gray-50 hover:text-gray-900 flex items-center px-3 py-2 text-sm font-medium rounded-md">
28+
<a href="#github_settings_heading"
29+
class="text-gray-600 hover:bg-gray-50 hover:text-gray-900 flex items-center px-3 py-2 text-sm font-medium rounded-md">
30+
<x-icon-github-line class="text-gray-400 shrink-0 -ml-1 mr-3 h-6 w-6" />
31+
<span class="truncate">GitHub</span>
32+
</a>
33+
<a href="#api_token_settings_heading"
34+
class="text-gray-600 hover:bg-gray-50 hover:text-gray-900 flex items-center px-3 py-2 text-sm font-medium rounded-md">
2935
<x-heroicon-o-code-bracket class="text-gray-400 shrink-0 -ml-1 mr-3 h-6 w-6" />
3036
<span class="truncate">API Tokens</span>
3137
</a>
@@ -45,6 +51,7 @@
4551
<div class="mt-10 lg:mt-0 sm:px-6 lg:px-0 lg:col-span-3">
4652
@include('users.settings.profile')
4753
@include('users.settings.password')
54+
@include('users.settings.github')
4855
@include('users.settings.api_tokens')
4956
@include('users.settings.notification_settings')
5057
@include('users.settings.blocked')

routes/web.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use App\Http\Controllers\ReplyAbleController;
2020
use App\Http\Controllers\ReplyController;
2121
use App\Http\Controllers\Settings\ApiTokenController;
22+
use App\Http\Controllers\Settings\GitHubAccountController;
2223
use App\Http\Controllers\Settings\NotificationSettingsController;
2324
use App\Http\Controllers\Settings\PasswordController;
2425
use App\Http\Controllers\Settings\ProfileController as ProfileSettingsController;
@@ -79,6 +80,8 @@
7980
Route::put('settings', [ProfileSettingsController::class, 'update'])->name('settings.profile.update');
8081
Route::delete('settings', [ProfileSettingsController::class, 'destroy'])->name('settings.profile.delete');
8182
Route::put('settings/password', [PasswordController::class, 'update'])->name('settings.password.update');
83+
Route::post('settings/github/connect', [GitHubAccountController::class, 'connect'])->name('settings.github.connect');
84+
Route::post('settings/github/disconnect', [GitHubAccountController::class, 'disconnect'])->name('settings.github.disconnect');
8285
Route::put('settings/users/{username}/unblock', UnblockUserSettingsController::class)->name('settings.users.unblock');
8386
Route::post('settings/api-tokens', [ApiTokenController::class, 'store'])->name('settings.api-tokens.store');
8487
Route::delete('settings/api-tokens', [ApiTokenController::class, 'destroy'])->name('settings.api-tokens.delete');

0 commit comments

Comments
 (0)