Skip to content

Commit 16e1c67

Browse files
authored
fix: connecting a new account [#121] (#136)
1 parent 7bf65ed commit 16e1c67

File tree

8 files changed

+76
-24
lines changed

8 files changed

+76
-24
lines changed

packages/clerk_auth/lib/src/clerk_api/api.dart

+12
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,18 @@ class Api with Logging {
440440
);
441441
}
442442

443+
/// Delete an [ExternalAccount]
444+
///
445+
Future<ApiResponse> deleteExternalAccount({
446+
required ExternalAccount account,
447+
}) async {
448+
return await _fetchApiResponse(
449+
'/me/external_accounts/${account.id}',
450+
withSession: true,
451+
method: HttpMethod.delete,
452+
);
453+
}
454+
443455
/// After signing in via oauth, transfer the [SignUp] into an authenticated [User]
444456
///
445457
Future<ApiResponse> transfer() async {

packages/clerk_auth/lib/src/clerk_auth/auth.dart

+6
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,12 @@ class Auth {
236236
update();
237237
}
238238

239+
/// Delete an external account
240+
Future<void> deleteExternalAccount({required ExternalAccount account}) async {
241+
await _api.deleteExternalAccount(account: account).then(_housekeeping);
242+
update();
243+
}
244+
239245
/// Progressively attempt sign in
240246
///
241247
/// Can be repeatedly called with updated parameters

packages/clerk_auth/lib/src/models/client/external_account.dart

+3
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ class ExternalAccount {
7878
/// is verified?
7979
bool get isVerified => verification.status.isVerified;
8080

81+
/// is in error?
82+
bool get isInError => verification.errorMessage is String;
83+
8184
/// is expired?
8285
bool get isExpired => verification.status.isExpired;
8386

packages/clerk_auth/lib/src/models/client/verification.dart

+8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class Verification {
1717
required this.attempts,
1818
required this.expireAt,
1919
this.externalVerificationRedirectUrl,
20+
this.errorMessage,
2021
this.nonce,
2122
});
2223

@@ -39,10 +40,17 @@ class Verification {
3940
@JsonKey(fromJson: intToDateTime, toJson: dateTimeToInt)
4041
final DateTime expireAt;
4142

43+
/// error message
44+
@JsonKey(readValue: _extractErrorMessage)
45+
final String? errorMessage;
46+
4247
/// fromJson
4348
static Verification fromJson(Map<String, dynamic> json) =>
4449
_$VerificationFromJson(json);
4550

4651
/// toJson
4752
Map<String, dynamic> toJson() => _$VerificationToJson(this);
4853
}
54+
55+
String? _extractErrorMessage(Map<dynamic, dynamic> map, String field) =>
56+
map['error']?['long_message'] as String?;

packages/clerk_auth/lib/src/models/client/verification.g.dart

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/clerk_flutter/lib/src/widgets/control/clerk_error_listener.dart

+6-4
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,20 @@ class ClerkErrorListener extends StatefulWidget {
3535
final Widget child;
3636

3737
/// Default Error Handler
38-
static Future<void> defaultErrorHandler(
38+
static void defaultErrorHandler(
3939
BuildContext context,
4040
AuthError error,
41-
) async {
41+
) {
4242
final localizations = ClerkAuth.localizationsOf(context);
4343
final message = error.localizedMessage(localizations);
4444

4545
final messenger = ScaffoldMessenger.maybeOf(context);
4646
if (messenger == null) {
4747
if (kDebugMode) {
48-
debugPrint('Warning: no ScaffoldMessenger found '
49-
'to display error: $message');
48+
debugPrint(
49+
'Warning: no ScaffoldMessenger found '
50+
'to display error: $message',
51+
);
5052
}
5153
return;
5254
}

packages/clerk_flutter/lib/src/widgets/user/clerk_user_profile.dart

+32-15
Original file line numberDiff line numberDiff line change
@@ -210,12 +210,6 @@ class _ClerkUserProfileState extends State<ClerkUserProfile>
210210
child: _ExternalAccountList(
211211
user: user,
212212
env: auth.env,
213-
onAddNew: () => ClerkPage.show(
214-
context,
215-
builder: (context) => ConnectAccountPanel(
216-
onDone: (context) => Navigator.of(context).pop(),
217-
),
218-
),
219213
),
220214
),
221215
],
@@ -234,12 +228,36 @@ class _ExternalAccountList extends StatelessWidget {
234228
const _ExternalAccountList({
235229
required this.user,
236230
required this.env,
237-
required this.onAddNew,
238231
});
239232

240233
final clerk.User user;
241234
final clerk.Environment env;
242-
final VoidCallback onAddNew;
235+
236+
void _onAddNew(BuildContext context) {
237+
ClerkPage.show(
238+
context,
239+
builder: (context) => ConnectAccountPanel(
240+
onDone: (context) async {
241+
Navigator.of(context).pop();
242+
243+
final auth = ClerkAuth.of(context);
244+
if (auth.user?.externalAccounts case final accounts?) {
245+
for (final account in accounts) {
246+
if (account.verification.errorMessage case String errorMessage) {
247+
auth.addError(
248+
clerk.AuthError(
249+
message: errorMessage,
250+
code: clerk.AuthErrorCode.serverErrorResponse,
251+
),
252+
);
253+
await auth.deleteExternalAccount(account: account);
254+
}
255+
}
256+
}
257+
},
258+
),
259+
);
260+
}
243261

244262
@override
245263
Widget build(BuildContext context) {
@@ -281,7 +299,7 @@ class _ExternalAccountList extends StatelessWidget {
281299
),
282300
GestureDetector(
283301
behavior: HitTestBehavior.opaque,
284-
onTap: onAddNew,
302+
onTap: () => _onAddNew(context),
285303
child: Row(
286304
mainAxisAlignment: MainAxisAlignment.start,
287305
children: [
@@ -320,27 +338,26 @@ class _IdentifierList extends StatelessWidget {
320338
crossAxisAlignment: CrossAxisAlignment.stretch,
321339
children: [
322340
if (identifiers case List<clerk.UserIdentifyingData> identifiers) //
323-
for (final ident in identifiers) //
341+
for (final uid in identifiers) //
324342
Padding(
325343
padding: bottomPadding16,
326344
child: Row(
327345
crossAxisAlignment: CrossAxisAlignment.start,
328346
children: [
329347
Expanded(
330348
child: Text(
331-
format?.call(ident.identifier) ?? ident.identifier,
349+
format?.call(uid.identifier) ?? uid.identifier,
332350
maxLines: 1,
333351
overflow: TextOverflow.ellipsis,
334352
),
335353
),
336-
if (ident.isUnverified) //
354+
if (uid.isUnverified) //
337355
_RowLabel(
338356
label: localizations.unverified,
339357
color: ClerkColors.incarnadine,
340-
onTap: () =>
341-
onIdentifierUnverified.call(ident.identifier),
358+
onTap: () => onIdentifierUnverified.call(uid.identifier),
342359
),
343-
if (user.isPrimary(ident)) //
360+
if (user.isPrimary(uid)) //
344361
_RowLabel(label: localizations.primary),
345362
],
346363
),

packages/clerk_flutter/lib/src/widgets/user/connect_account_panel.dart

+7-5
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@ class ConnectAccountPanel extends StatelessWidget {
3131
padding: horizontalPadding32 + bottomPadding32,
3232
child: ClerkChangeObserver<DateTime>(
3333
onChange: onDone,
34-
accumulateData: () =>
35-
auth.client.user?.externalAccounts?.map(
36-
(o) => o.updatedAt,
37-
) ??
38-
const [],
34+
accumulateData: () {
35+
final accounts =
36+
auth.client.user?.externalAccounts ?? const [];
37+
return accounts
38+
.where((a) => a.isVerified || a.isInError)
39+
.map((a) => a.updatedAt);
40+
},
3941
builder: (context) => ClerkSSOPanel(
4042
onStrategyChosen: (strategy) =>
4143
auth.ssoConnect(context, strategy),

0 commit comments

Comments
 (0)