Skip to content

Commit

Permalink
feat: support sign-in and sign-up on Web (#5712)
Browse files Browse the repository at this point in the history
  • Loading branch information
qinluhe authored Jul 11, 2024
1 parent 80afcf4 commit fe0fa9b
Show file tree
Hide file tree
Showing 49 changed files with 934 additions and 160 deletions.
4 changes: 2 additions & 2 deletions frontend/appflowy_flutter/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ SPEC CHECKSUMS:
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c
fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
Expand All @@ -191,4 +191,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: d0d9b4ff572d8695c38eb3f9b490f55cdfc57eca

COCOAPODS: 1.11.3
COCOAPODS: 1.15.2
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ class AboutSettingGroup extends StatelessWidget {
trailing: const Icon(
Icons.chevron_right,
),
onTap: () => afLaunchUrlString('https://appflowy.io/privacy/app'),
onTap: () => afLaunchUrlString('https://appflowy.io/privacy'),
),
MobileSettingItem(
name: LocaleKeys.settings_mobile_termsAndConditions.tr(),
trailing: const Icon(
Icons.chevron_right,
),
onTap: () => afLaunchUrlString('https://appflowy.io/terms/app'),
onTap: () => afLaunchUrlString('https://appflowy.io/terms'),
),
if (kDebugMode)
MobileSettingItem(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,8 @@ class DesktopSignInScreen extends StatelessWidget {
const SignInAnonymousButtonV2(),
const VSpace(10),

SwitchSignInSignUpButton(
onTap: () {
final type = state.loginType == LoginType.signIn
? LoginType.signUp
: LoginType.signIn;
context
.read<SignInBloc>()
.add(SignInEvent.switchLoginType(type));
},
),
// sign in agreement
const SignInAgreement(),

// loading status
const VSpace(indicatorMinHeight),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,7 @@ class MobileSignInScreen extends StatelessWidget {
const VSpace(spacing),
const SignInAnonymousButtonV2(),
const VSpace(spacing),
SwitchSignInSignUpButton(
onTap: () {
final type = state.loginType == LoginType.signIn
? LoginType.signUp
: LoginType.signIn;
context.read<SignInBloc>().add(
SignInEvent.switchLoginType(type),
);
},
),
const SignInAgreement(),
const VSpace(spacing),
_buildSettingsButton(context),
if (!isAuthEnabled) const Spacer(flex: 2),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import 'package:flutter/material.dart';

import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/user/application/sign_in_bloc.dart';
import 'package:appflowy/workspace/presentation/home/toast.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:string_validator/string_validator.dart';
import 'package:toastification/toastification.dart';

class SignInWithMagicLinkButtons extends StatefulWidget {
const SignInWithMagicLinkButtons({super.key});
Expand Down Expand Up @@ -53,18 +53,19 @@ class _SignInWithMagicLinkButtonsState

void _sendMagicLink(BuildContext context, String email) {
if (!isEmail(email)) {
return showSnackBarMessage(
return showToastNotification(
context,
LocaleKeys.signIn_invalidEmail.tr(),
duration: const Duration(seconds: 8),
message: LocaleKeys.signIn_invalidEmail.tr(),
type: ToastificationType.error,
);
}

context.read<SignInBloc>().add(SignInEvent.signedWithMagicLink(email));
showSnackBarMessage(
context,
LocaleKeys.signIn_magicLinkSent.tr(),
duration: const Duration(seconds: 1000),

showConfirmDialog(
context: context,
title: LocaleKeys.signIn_magicLinkSent.tr(),
description: LocaleKeys.signIn_magicLinkSentDescription.tr(),
);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import 'package:appflowy/core/helpers/url_launcher.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

class SignInAgreement extends StatelessWidget {
const SignInAgreement({
super.key,
});

@override
Widget build(BuildContext context) {
return RichText(
textAlign: TextAlign.center,
text: TextSpan(
children: [
TextSpan(
text: '${LocaleKeys.web_signInAgreement.tr()} ',
style: const TextStyle(color: Colors.grey, fontSize: 12),
),
TextSpan(
text: '${LocaleKeys.web_termOfUse.tr()} ',
style: const TextStyle(color: Colors.blue, fontSize: 12),
mouseCursor: SystemMouseCursors.click,
recognizer: TapGestureRecognizer()
..onTap = () => afLaunchUrlString('https://appflowy.io/terms'),
),
TextSpan(
text: '${LocaleKeys.web_and.tr()} ',
style: const TextStyle(color: Colors.grey, fontSize: 12),
),
TextSpan(
text: LocaleKeys.web_privacyPolicy.tr(),
style: const TextStyle(color: Colors.blue, fontSize: 12),
mouseCursor: SystemMouseCursors.click,
recognizer: TapGestureRecognizer()
..onTap = () => afLaunchUrlString('https://appflowy.io/privacy'),
),
],
),
);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export 'magic_link_sign_in_buttons.dart';
export 'sign_in_anonymous_button.dart';
export 'sign_in_or_logout_button.dart';
export 'switch_sign_in_sign_up_button.dart';

// export 'switch_sign_in_sign_up_button.dart';
export 'third_party_sign_in_buttons.dart';
export 'sign_in_agreement.dart';
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,55 @@ class SpaceCancelOrConfirmButton extends StatelessWidget {
}
}

class ConfirmDeletionPopup extends StatefulWidget {
const ConfirmDeletionPopup({
class SpaceOkButton extends StatelessWidget {
const SpaceOkButton({
super.key,
required this.onConfirm,
required this.confirmButtonName,
this.confirmButtonColor,
});

final VoidCallback onConfirm;
final String confirmButtonName;
final Color? confirmButtonColor;

@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
DecoratedBox(
decoration: ShapeDecoration(
color: confirmButtonColor ?? Theme.of(context).colorScheme.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: FlowyButton(
useIntrinsicWidth: true,
margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 9.0),
radius: BorderRadius.circular(8),
text: FlowyText.regular(
confirmButtonName,
color: Colors.white,
),
onTap: onConfirm,
),
),
],
);
}
}

enum ConfirmPopupStyle {
onlyOk,
cancelAndOk,
}

class ConfirmPopup extends StatefulWidget {
const ConfirmPopup({
super.key,
this.style = ConfirmPopupStyle.cancelAndOk,
required this.title,
required this.description,
required this.onConfirm,
Expand All @@ -234,12 +280,13 @@ class ConfirmDeletionPopup extends StatefulWidget {
final String title;
final String description;
final VoidCallback onConfirm;
final ConfirmPopupStyle style;

@override
State<ConfirmDeletionPopup> createState() => _ConfirmDeletionPopupState();
State<ConfirmPopup> createState() => _ConfirmPopupState();
}

class _ConfirmDeletionPopupState extends State<ConfirmDeletionPopup> {
class _ConfirmPopupState extends State<ConfirmPopup> {
final focusNode = FocusNode();

@override
Expand All @@ -262,46 +309,70 @@ class _ConfirmDeletionPopupState extends State<ConfirmDeletionPopup> {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Flexible(
child: FlowyText(
widget.title,
fontSize: 14.0,
overflow: TextOverflow.ellipsis,
),
),
const HSpace(6.0),
FlowyButton(
useIntrinsicWidth: true,
text: const FlowySvg(FlowySvgs.upgrade_close_s),
onTap: () => Navigator.of(context).pop(),
),
],
),
const VSpace(8.0),
FlowyText.regular(
widget.description,
fontSize: 12.0,
color: Theme.of(context).hintColor,
maxLines: 3,
lineHeight: 1.4,
),
_buildTitle(),
const VSpace(6.0),
_buildDescription(),
const VSpace(20.0),
SpaceCancelOrConfirmButton(
onCancel: () => Navigator.of(context).pop(),
onConfirm: () {
widget.onConfirm();
Navigator.of(context).pop();
},
confirmButtonName: LocaleKeys.space_delete.tr(),
confirmButtonColor: Theme.of(context).colorScheme.error,
),
_buildStyledButton(context),
],
),
),
);
}

Widget _buildTitle() {
return Row(
children: [
Expanded(
child: FlowyText(
widget.title,
fontSize: 14.0,
overflow: TextOverflow.ellipsis,
),
),
const HSpace(6.0),
FlowyButton(
useIntrinsicWidth: true,
text: const FlowySvg(FlowySvgs.upgrade_close_s),
onTap: () => Navigator.of(context).pop(),
),
],
);
}

Widget _buildDescription() {
return FlowyText.regular(
widget.description,
fontSize: 12.0,
color: Theme.of(context).hintColor,
maxLines: 3,
lineHeight: 1.4,
);
}

Widget _buildStyledButton(BuildContext context) {
switch (widget.style) {
case ConfirmPopupStyle.onlyOk:
return SpaceOkButton(
onConfirm: () {
widget.onConfirm();
Navigator.of(context).pop();
},
confirmButtonName: LocaleKeys.button_ok.tr(),
confirmButtonColor: Theme.of(context).colorScheme.primary,
);
case ConfirmPopupStyle.cancelAndOk:
return SpaceCancelOrConfirmButton(
onCancel: () => Navigator.of(context).pop(),
onConfirm: () {
widget.onConfirm();
Navigator.of(context).pop();
},
confirmButtonName: LocaleKeys.space_delete.tr(),
confirmButtonColor: Theme.of(context).colorScheme.error,
);
}
}
}

class SpacePopup extends StatelessWidget {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,10 +289,11 @@ void showToastNotification(
BuildContext context, {
required String message,
String? description,
ToastificationType type = ToastificationType.success,
}) {
toastification.show(
context: context,
type: ToastificationType.success,
type: type,
style: ToastificationStyle.flat,
title: FlowyText(message),
description: description != null
Expand Down Expand Up @@ -329,7 +330,7 @@ Future<void> showConfirmDeletionDialog({
),
child: SizedBox(
width: 440,
child: ConfirmDeletionPopup(
child: ConfirmPopup(
title: title,
description: description,
onConfirm: onConfirm,
Expand All @@ -339,3 +340,30 @@ Future<void> showConfirmDeletionDialog({
},
);
}

Future<void> showConfirmDialog({
required BuildContext context,
required String title,
required String description,
VoidCallback? onConfirm,
}) {
return showDialog(
context: context,
builder: (_) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
child: SizedBox(
width: 440,
child: ConfirmPopup(
title: title,
description: description,
onConfirm: () => onConfirm?.call(),
style: ConfirmPopupStyle.onlyOk,
),
),
);
},
);
}
Loading

0 comments on commit fe0fa9b

Please sign in to comment.