feat: support sign in with Apple (#6049)

* feat: support sign in with Apple

* feat: support sign in with Apple

* feat: optimize sign in on desktop

* feat: expand third party sign in buttons on android

* fix: revert text color and font size in button
This commit is contained in:
Lucas.Xu 2024-08-27 10:49:31 +08:00 committed by GitHub
parent e5ad0f6d1d
commit 1b185ba3cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 525 additions and 280 deletions

View File

@ -12,7 +12,7 @@ import 'util.dart';
extension AppFlowyAuthTest on WidgetTester {
Future<void> tapGoogleLoginInButton() async {
await tapButton(
find.byKey(const Key('signInWithGoogleButton')),
find.byKey(signInWithGoogleButtonKey),
);
}
@ -36,7 +36,7 @@ extension AppFlowyAuthTest on WidgetTester {
}
void expectToSeeGoogleLoginButton() {
expect(find.byKey(const Key('signInWithGoogleButton')), findsOneWidget);
expect(find.byKey(signInWithGoogleButtonKey), findsOneWidget);
}
void assertSwitchValue(Finder finder, bool value) {

View File

@ -372,6 +372,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AppFlowy;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -383,6 +384,8 @@
STRIP_STYLE = "non-global";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
@ -511,6 +514,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AppFlowy;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -522,6 +526,8 @@
STRIP_STYLE = "non-global";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@ -545,6 +551,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AppFlowy;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -556,6 +563,8 @@
STRIP_STYLE = "non-global";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;

View File

@ -2,10 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSCameraUsageDescription</key>
<string>AppFlowy requires access to the camera.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>AppFlowy requires access to the photo library.</string>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
@ -20,8 +16,6 @@
<array>
<string>en</string>
</array>
<key>FLTEnableImpeller</key>
<false />
<key>CFBundleName</key>
<string>AppFlowy</string>
<key>CFBundlePackageType</key>
@ -43,8 +37,19 @@
</array>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>FLTEnableImpeller</key>
<false/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSCameraUsageDescription</key>
<string>AppFlowy requires access to the camera.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>AppFlowy requires access to the photo library.</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
@ -54,22 +59,15 @@
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict>
</plist>

View File

@ -4,5 +4,9 @@
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
</dict>
</plist>

View File

@ -65,7 +65,7 @@ class _MobileBottomSheetRenameWidgetState
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
),
textColor: Colors.white,
fontColor: Colors.white,
fillColor: Theme.of(context).primaryColor,
onPressed: () {
widget.onRename(controller.text);

View File

@ -16,14 +16,11 @@ class AppFlowyCloudPage extends StatelessWidget {
appBar: FlowyAppBar(
titleText: LocaleKeys.settings_menu_cloudSettings.tr(),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: SettingCloud(
body: SettingCloud(
restartAppFlowy: () async {
await runAppFlowy();
},
),
),
);
}
}

View File

@ -33,7 +33,9 @@ class UserSessionSettingGroup extends StatelessWidget {
);
},
builder: (context, state) {
return const ThirdPartySignInButtons();
return const ThirdPartySignInButtons(
expanded: true,
);
},
),
),

View File

@ -372,7 +372,7 @@ class _NewTaskItemState extends State<NewTaskItem> {
? Theme.of(context).disabledColor
: Theme.of(context).colorScheme.primaryContainer,
fontColor: Theme.of(context).colorScheme.onPrimary,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
onPressed: _textEditingController.text.isEmpty
? null
: () {

View File

@ -121,6 +121,8 @@ extension ProviderTypePBExtension on ProviderTypePB {
return ProviderTypePB.Google;
case 'discord':
return ProviderTypePB.Discord;
case 'apple':
return ProviderTypePB.Apple;
default:
throw UnimplementedError();
}

View File

@ -1,16 +1,16 @@
import 'package:flutter/foundation.dart';
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/startup/tasks/appflowy_cloud_task.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-error/code.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
show UserProfilePB;
import 'package:appflowy_result/appflowy_result.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@ -208,6 +208,8 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
}
SignInState _stateFromCode(FlowyError error) {
Log.error('SignInState _stateFromCode: ${error.msg}');
switch (error.code) {
case ErrorCode.EmailFormatInvalid:
return state.copyWith(

View File

@ -1,11 +1,12 @@
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/user/presentation/router.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-error/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.dart';
import 'package:flowy_infra_ui/style_widget/snap_bar.dart';
import 'package:flutter/material.dart';
import 'package:toastification/toastification.dart';
void handleOpenWorkspaceError(BuildContext context, FlowyError error) {
Log.error(error);
@ -15,24 +16,24 @@ void handleOpenWorkspaceError(BuildContext context, FlowyError error) {
getIt<AuthRouter>().pushWorkspaceErrorScreen(context, userFolder, error);
break;
case ErrorCode.InvalidEncryptSecret:
showSnapBar(
case ErrorCode.HttpError:
showToastNotification(
context,
error.msg,
message: error.msg,
type: ToastificationType.error,
);
break;
case ErrorCode.HttpError:
showSnapBar(
context,
error.msg,
);
default:
showSnapBar(
showToastNotification(
context,
error.msg,
onClosed: () {
message: error.msg,
type: ToastificationType.error,
callbacks: ToastificationCallbacks(
onDismissed: (_) {
getIt<AuthService>().signOut();
runAppFlowy();
},
),
);
}
}

View File

@ -1,5 +1,3 @@
import 'package:flutter/material.dart';
import 'package:appflowy/core/frameless_window.dart';
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
@ -10,6 +8,7 @@ import 'package:appflowy/user/presentation/widgets/widgets.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DesktopSignInScreen extends StatelessWidget {
@ -45,14 +44,14 @@ class DesktopSignInScreen extends StatelessWidget {
if (isAuthEnabled) ...[
const _OrDivider(),
const VSpace(10),
const VSpace(20),
const ThirdPartySignInButtons(),
],
const VSpace(20),
// anonymous sign in
const SignInAnonymousButtonV2(),
const VSpace(10),
const VSpace(16),
// sign in agreement
const SignInAgreement(),

View File

@ -1,4 +1,4 @@
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
@ -8,6 +8,7 @@ import 'package:appflowy/user/application/sign_in_bloc.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
@ -23,26 +24,26 @@ class MobileSignInScreen extends StatelessWidget {
return BlocBuilder<SignInBloc, SignInState>(
builder: (context, state) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 50, horizontal: 40),
padding: const EdgeInsets.symmetric(vertical: 38, horizontal: 40),
child: Column(
children: [
const Spacer(flex: 4),
_buildLogo(),
const VSpace(spacing * 2),
_buildWelcomeText(),
const VSpace(spacing),
_buildAppNameText(colorScheme),
const VSpace(spacing * 2),
const SignInWithMagicLinkButtons(),
const VSpace(spacing),
if (isAuthEnabled) _buildThirdPartySignInButtons(colorScheme),
const VSpace(spacing),
const SignInAnonymousButtonV2(),
const VSpace(spacing),
const VSpace(spacing * 1.5),
const SignInAgreement(),
const VSpace(spacing),
_buildSettingsButton(context),
if (!isAuthEnabled) const Spacer(flex: 2),
const Spacer(flex: 2),
const Spacer(),
Expanded(child: _buildSettingsButton(context)),
],
),
),
@ -51,19 +52,10 @@ class MobileSignInScreen extends StatelessWidget {
);
}
Widget _buildWelcomeText() {
return FlowyText(
LocaleKeys.welcomeTo.tr(),
textAlign: TextAlign.center,
fontSize: 32,
fontWeight: FontWeight.w700,
);
}
Widget _buildLogo() {
return const FlowySvg(
FlowySvgs.flowy_logo_xl,
size: Size.square(64),
size: Size.square(56),
blendMode: null,
);
}
@ -72,7 +64,7 @@ class MobileSignInScreen extends StatelessWidget {
return FlowyText(
LocaleKeys.appName.tr(),
textAlign: TextAlign.center,
fontSize: 32,
fontSize: 28,
color: const Color(0xFF00BCF0),
fontWeight: FontWeight.w700,
);
@ -89,6 +81,7 @@ class MobileSignInScreen extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 8),
child: FlowyText(
LocaleKeys.signIn_or.tr(),
fontSize: 12,
color: colorScheme.onSecondary,
),
),
@ -96,23 +89,45 @@ class MobileSignInScreen extends StatelessWidget {
],
),
const VSpace(16),
const ThirdPartySignInButtons(),
// expand third-party sign in buttons on Android by default.
// on iOS, the github and discord buttons are collapsed by default.
ThirdPartySignInButtons(
expanded: Platform.isAndroid,
),
],
);
}
Widget _buildSettingsButton(BuildContext context) {
return FlowyButton(
return Row(
mainAxisSize: MainAxisSize.min,
children: [
FlowyButton(
useIntrinsicWidth: true,
text: FlowyText(
LocaleKeys.signIn_settings.tr(),
textAlign: TextAlign.center,
fontSize: 12.0,
fontWeight: FontWeight.w500,
// fontWeight: FontWeight.w500,
color: Colors.grey,
decoration: TextDecoration.underline,
),
onTap: () {
context.push(MobileLaunchSettingsPage.routeName);
},
),
const HSpace(24),
SignInAnonymousButtonV2(
child: FlowyText(
LocaleKeys.signIn_anonymous.tr(),
textAlign: TextAlign.center,
fontSize: 12.0,
// fontWeight: FontWeight.w500,
color: Colors.grey,
decoration: TextDecoration.underline,
),
),
],
);
}
}

View File

@ -1,5 +1,3 @@
import 'package:flutter/material.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/sign_in_bloc.dart';
import 'package:appflowy/user/presentation/router.dart';
@ -7,6 +5,7 @@ import 'package:appflowy/user/presentation/screens/sign_in_screen/desktop_sign_i
import 'package:appflowy/user/presentation/screens/sign_in_screen/mobile_loading_screen.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/mobile_sign_in_screen.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../helpers/helpers.dart';
@ -21,16 +20,7 @@ class SignInScreen extends StatelessWidget {
return BlocProvider(
create: (context) => getIt<SignInBloc>(),
child: BlocConsumer<SignInBloc, SignInState>(
listener: (context, state) {
final successOrFail = state.successOrFail;
if (successOrFail != null) {
handleUserProfileResult(
successOrFail,
context,
getIt<AuthRouter>(),
);
}
},
listener: _showSignInError,
builder: (context, state) {
final isLoading = context.read<SignInBloc>().state.isSubmitting;
if (PlatformExtension.isMobile) {
@ -43,4 +33,15 @@ class SignInScreen extends StatelessWidget {
),
);
}
void _showSignInError(BuildContext context, SignInState state) {
final successOrFail = state.successOrFail;
if (successOrFail != null) {
handleUserProfileResult(
successOrFail,
context,
getIt<AuthRouter>(),
);
}
}
}

View File

@ -34,11 +34,19 @@ class _SignInWithMagicLinkButtonsState
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 48.0,
height: PlatformExtension.isMobile ? 38.0 : 48.0,
child: FlowyTextField(
autoFocus: false,
controller: controller,
borderRadius: BorderRadius.circular(4.0),
hintText: LocaleKeys.signIn_pleaseInputYourEmail.tr(),
hintStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontSize: 14.0,
color: Theme.of(context).hintColor,
),
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontSize: 14.0,
),
keyboardType: TextInputType.emailAddress,
onSubmitted: (_) => _sendMagicLink(context, controller.text),
),
@ -88,14 +96,14 @@ class _ConfirmButton extends StatelessWidget {
if (PlatformExtension.isMobile) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 56),
minimumSize: const Size(double.infinity, 32),
maximumSize: const Size(double.infinity, 38),
),
onPressed: onTap,
child: FlowyText(
name,
fontSize: 14,
color: Theme.of(context).colorScheme.onPrimary,
fontWeight: FontWeight.w500,
),
);
} else {
@ -108,6 +116,7 @@ class _ConfirmButton extends StatelessWidget {
text: FlowyText.medium(
name,
textAlign: TextAlign.center,
color: Theme.of(context).colorScheme.onPrimary,
),
radius: Corners.s6Border,
),

View File

@ -21,7 +21,11 @@ class SignInAgreement extends StatelessWidget {
),
TextSpan(
text: '${LocaleKeys.web_termOfUse.tr()} ',
style: const TextStyle(color: Colors.blue, fontSize: 12),
style: const TextStyle(
color: Colors.grey,
fontSize: 12,
decoration: TextDecoration.underline,
),
mouseCursor: SystemMouseCursors.click,
recognizer: TapGestureRecognizer()
..onTap = () => afLaunchUrlString('https://appflowy.io/terms'),
@ -32,7 +36,11 @@ class SignInAgreement extends StatelessWidget {
),
TextSpan(
text: LocaleKeys.web_privacyPolicy.tr(),
style: const TextStyle(color: Colors.blue, fontSize: 12),
style: const TextStyle(
color: Colors.grey,
fontSize: 12,
decoration: TextDecoration.underline,
),
mouseCursor: SystemMouseCursors.click,
recognizer: TapGestureRecognizer()
..onTap = () => afLaunchUrlString('https://appflowy.io/privacy'),

View File

@ -89,8 +89,11 @@ class SignInAnonymousButton extends StatelessWidget {
class SignInAnonymousButtonV2 extends StatelessWidget {
const SignInAnonymousButtonV2({
super.key,
this.child,
});
final Widget? child;
@override
Widget build(BuildContext context) {
return BlocBuilder<SignInBloc, SignInState>(
@ -126,7 +129,8 @@ class SignInAnonymousButtonV2 extends StatelessWidget {
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: onTap,
child: FlowyText(
child: child ??
FlowyText(
text,
color: Colors.blue,
fontSize: 12,

View File

@ -20,7 +20,7 @@ class MobileSignInOrLogoutButton extends StatelessWidget {
return GestureDetector(
onTap: onPressed,
child: Container(
height: 48,
height: 38,
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(
Radius.circular(4),
@ -54,7 +54,7 @@ class MobileSignInOrLogoutButton extends StatelessWidget {
FlowyText(
labelText,
fontSize: 14.0,
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w400,
),
],
),

View File

@ -0,0 +1,137 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/base/gesture.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
enum ThirdPartySignInButtonType {
apple,
google,
github,
discord,
anonymous;
FlowySvgData get icon {
switch (this) {
case ThirdPartySignInButtonType.apple:
return FlowySvgs.m_apple_icon_xl;
case ThirdPartySignInButtonType.google:
return FlowySvgs.m_google_icon_xl;
case ThirdPartySignInButtonType.github:
return FlowySvgs.m_github_icon_xl;
case ThirdPartySignInButtonType.discord:
return FlowySvgs.m_discord_icon_xl;
case ThirdPartySignInButtonType.anonymous:
return FlowySvgs.m_discord_icon_xl;
}
}
String get labelText {
switch (this) {
case ThirdPartySignInButtonType.apple:
return LocaleKeys.signIn_signInWithApple.tr();
case ThirdPartySignInButtonType.google:
return LocaleKeys.signIn_signInWithGoogle.tr();
case ThirdPartySignInButtonType.github:
return LocaleKeys.signIn_signInWithGithub.tr();
case ThirdPartySignInButtonType.discord:
return LocaleKeys.signIn_signInWithDiscord.tr();
case ThirdPartySignInButtonType.anonymous:
return 'Anonymous session';
}
}
// https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple
Color backgroundColor(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
switch (this) {
case ThirdPartySignInButtonType.apple:
return isDarkMode ? Colors.white : Colors.black;
case ThirdPartySignInButtonType.google:
case ThirdPartySignInButtonType.github:
case ThirdPartySignInButtonType.discord:
case ThirdPartySignInButtonType.anonymous:
return isDarkMode ? Colors.black : Colors.grey.shade100;
}
}
Color textColor(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
switch (this) {
case ThirdPartySignInButtonType.apple:
return isDarkMode ? Colors.black : Colors.white;
case ThirdPartySignInButtonType.google:
case ThirdPartySignInButtonType.github:
case ThirdPartySignInButtonType.discord:
case ThirdPartySignInButtonType.anonymous:
return isDarkMode ? Colors.white : Colors.black;
}
}
BlendMode? get blendMode {
switch (this) {
case ThirdPartySignInButtonType.apple:
case ThirdPartySignInButtonType.github:
return BlendMode.srcIn;
default:
return null;
}
}
}
class MobileThirdPartySignInButton extends StatelessWidget {
const MobileThirdPartySignInButton({
super.key,
this.height = 38,
this.fontSize = 14.0,
required this.onPressed,
required this.type,
});
final VoidCallback onPressed;
final double height;
final double fontSize;
final ThirdPartySignInButtonType type;
@override
Widget build(BuildContext context) {
final style = Theme.of(context);
return AnimatedGestureDetector(
scaleFactor: 1.0,
onTapUp: onPressed,
child: Container(
height: height,
decoration: BoxDecoration(
color: type.backgroundColor(context),
borderRadius: const BorderRadius.all(
Radius.circular(4),
),
border: Border.all(
color: style.colorScheme.outline,
width: 0.5,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (type != ThirdPartySignInButtonType.anonymous)
FlowySvg(
type.icon,
size: Size.square(fontSize),
blendMode: type.blendMode,
color: type.textColor(context),
),
const HSpace(8.0),
FlowyText(
type.labelText,
fontSize: fontSize,
color: type.textColor(context),
),
],
),
),
);
}
}

View File

@ -1,126 +1,167 @@
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/user/application/sign_in_bloc.dart';
import 'package:appflowy/user/presentation/presentation.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/sign_in_or_logout_button.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.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';
class ThirdPartySignInButtons extends StatelessWidget {
import 'third_party_sign_in_button.dart';
@visibleForTesting
const Key signInWithGoogleButtonKey = Key('signInWithGoogleButton');
class ThirdPartySignInButtons extends StatefulWidget {
/// Used in DesktopSignInScreen, MobileSignInScreen and SettingThirdPartyLogin
const ThirdPartySignInButtons({super.key});
const ThirdPartySignInButtons({
super.key,
this.expanded = false,
});
final bool expanded;
@override
State<ThirdPartySignInButtons> createState() =>
_ThirdPartySignInButtonsState();
}
class _ThirdPartySignInButtonsState extends State<ThirdPartySignInButtons> {
bool expanded = false;
@override
void initState() {
super.initState();
expanded = widget.expanded;
}
@override
Widget build(BuildContext context) {
// Get themeMode from AppearanceSettingsCubit
// When user changes themeMode, it changes the state in AppearanceSettingsCubit, but the themeMode for the MaterialApp won't change, it only got updated(get value from AppearanceSettingsCubit) when user open the app again. Thus, we should get themeMode from AppearanceSettingsCubit rather than MediaQuery.
final themeModeFromCubit =
context.watch<AppearanceSettingsCubit>().state.themeMode;
final isDarkMode = themeModeFromCubit == ThemeMode.system
? MediaQuery.of(context).platformBrightness == Brightness.dark
: themeModeFromCubit == ThemeMode.dark;
return BlocBuilder<SignInBloc, SignInState>(
builder: (context, state) {
final (googleText, githubText, discordText) = switch (state.loginType) {
LoginType.signIn => (
LocaleKeys.signIn_signInWithGoogle.tr(),
LocaleKeys.signIn_signInWithGithub.tr(),
LocaleKeys.signIn_signInWithDiscord.tr()
),
LoginType.signUp => (
LocaleKeys.signIn_signUpWithGoogle.tr(),
LocaleKeys.signIn_signUpWithGithub.tr(),
LocaleKeys.signIn_signUpWithDiscord.tr()
),
};
if (PlatformExtension.isDesktopOrWeb) {
const padding = 16.0;
return Column(
children: [
_ThirdPartySignInButton(
key: const Key('signInWithGoogleButton'),
icon: FlowySvgs.google_mark_xl,
labelText: googleText,
_DesktopSignInButton(
key: signInWithGoogleButtonKey,
type: ThirdPartySignInButtonType.google,
onPressed: () {
_signInWithGoogle(context);
},
),
const VSpace(8),
_ThirdPartySignInButton(
icon: isDarkMode
? FlowySvgs.github_mark_white_xl
: FlowySvgs.github_mark_black_xl,
labelText: githubText,
const VSpace(padding),
_DesktopSignInButton(
type: ThirdPartySignInButtonType.github,
onPressed: () {
_signInWithGithub(context);
},
),
const VSpace(8),
_ThirdPartySignInButton(
icon: isDarkMode
? FlowySvgs.discord_mark_white_xl
: FlowySvgs.discord_mark_blurple_xl,
labelText: discordText,
const VSpace(padding),
_DesktopSignInButton(
type: ThirdPartySignInButtonType.discord,
onPressed: () {
_signInWithDiscord(context);
},
),
],
);
} else {
const padding = 8.0;
return BlocBuilder<SignInBloc, SignInState>(
builder: (context, state) {
return Column(
children: [
if (Platform.isIOS) ...[
MobileThirdPartySignInButton(
type: ThirdPartySignInButtonType.apple,
onPressed: () {
_signInWithApple(context);
},
),
const VSpace(padding),
],
MobileThirdPartySignInButton(
type: ThirdPartySignInButtonType.google,
onPressed: () {
_signInWithGoogle(context);
},
),
if (expanded) ...[
const VSpace(padding),
MobileThirdPartySignInButton(
type: ThirdPartySignInButtonType.github,
onPressed: () {
_signInWithGithub(context);
},
),
const VSpace(padding),
MobileThirdPartySignInButton(
type: ThirdPartySignInButtonType.discord,
onPressed: () {
_signInWithDiscord(context);
},
),
],
if (!expanded) ...[
const VSpace(padding * 2),
GestureDetector(
onTap: () {
setState(() {
expanded = !expanded;
});
},
child: FlowyText(
LocaleKeys.signIn_continueAnotherWay.tr(),
color: Theme.of(context).colorScheme.onSurface,
decoration: TextDecoration.underline,
fontSize: 14,
),
),
],
],
);
},
);
}
}
class _ThirdPartySignInButton extends StatelessWidget {
/// Build button based on current Platform(mobile or desktop).
const _ThirdPartySignInButton({
super.key,
required this.icon,
required this.labelText,
required this.onPressed,
});
final FlowySvgData icon;
final String labelText;
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
if (PlatformExtension.isMobile) {
return MobileSignInOrLogoutButton(
icon: icon,
labelText: labelText,
onPressed: onPressed,
);
} else {
return _DesktopSignInButton(
icon: icon,
labelText: labelText,
onPressed: onPressed,
void _signInWithApple(BuildContext context) {
context.read<SignInBloc>().add(
const SignInEvent.signedInWithOAuth('apple'),
);
}
void _signInWithGoogle(BuildContext context) {
context.read<SignInBloc>().add(
const SignInEvent.signedInWithOAuth('google'),
);
}
void _signInWithGithub(BuildContext context) {
context
.read<SignInBloc>()
.add(const SignInEvent.signedInWithOAuth('github'));
}
void _signInWithDiscord(BuildContext context) {
context
.read<SignInBloc>()
.add(const SignInEvent.signedInWithOAuth('discord'));
}
}
class _DesktopSignInButton extends StatelessWidget {
const _DesktopSignInButton({
required this.icon,
required this.labelText,
super.key,
required this.type,
required this.onPressed,
});
final FlowySvgData icon;
final String labelText;
final ThirdPartySignInButtonType type;
final VoidCallback onPressed;
@override
@ -139,8 +180,8 @@ class _DesktopSignInButton extends StatelessWidget {
// Some icons are not square, so we just use a fixed width here.
width: 24,
child: FlowySvg(
icon,
blendMode: null,
type.icon,
blendMode: type.blendMode,
),
),
),
@ -148,7 +189,7 @@ class _DesktopSignInButton extends StatelessWidget {
padding: const EdgeInsets.only(left: 8),
alignment: Alignment.centerLeft,
child: FlowyText(
labelText,
type.labelText,
fontSize: 14,
),
),
@ -177,19 +218,3 @@ class _DesktopSignInButton extends StatelessWidget {
);
}
}
void _signInWithGoogle(BuildContext context) {
context.read<SignInBloc>().add(
const SignInEvent.signedInWithOAuth('google'),
);
}
void _signInWithGithub(BuildContext context) {
context.read<SignInBloc>().add(const SignInEvent.signedInWithOAuth('github'));
}
void _signInWithDiscord(BuildContext context) {
context
.read<SignInBloc>()
.add(const SignInEvent.signedInWithOAuth('discord'));
}

View File

@ -1,7 +1,7 @@
export 'magic_link_sign_in_buttons.dart';
export 'sign_in_anonymous_button.dart';
export 'sign_in_or_logout_button.dart';
export 'third_party_sign_in_button.dart';
// export 'switch_sign_in_sign_up_button.dart';
export 'third_party_sign_in_buttons.dart';
export 'sign_in_agreement.dart';

View File

@ -107,7 +107,6 @@ class SingleSettingAction extends StatelessWidget {
radius: Corners.s8Border,
hoverColor: hoverColor(context),
fontColor: fontColor(context),
textColor: fontColor(context),
fontHoverColor: fontHoverColor(context),
borderColor: borderColor(context),
fontSize: 12,

View File

@ -447,7 +447,7 @@ class _IncludeTimePickerState extends State<_IncludeTimePicker> {
LocaleKeys.button_confirm.tr(),
constraints: const BoxConstraints.tightFor(height: 42),
mainAxisAlignment: MainAxisAlignment.center,
textColor: Theme.of(context).colorScheme.onPrimary,
fontColor: Theme.of(context).colorScheme.onPrimary,
fillColor: Theme.of(context).primaryColor,
onPressed: () {
if (isStartDay) {

View File

@ -303,12 +303,14 @@ void showToastNotification(
required String message,
String? description,
ToastificationType type = ToastificationType.success,
ToastificationCallbacks? callbacks,
double bottomPadding = 100,
}) {
if (PlatformExtension.isMobile) {
toastification.showCustom(
alignment: Alignment.bottomCenter,
autoCloseDuration: const Duration(milliseconds: 3000),
callbacks: callbacks ?? const ToastificationCallbacks(),
builder: (_, __) => _MToast(
message: message,
type: type,

View File

@ -297,7 +297,6 @@ class FlowyTextButton extends StatelessWidget {
this.padding = const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
this.hoverColor,
this.fillColor,
this.textColor,
this.heading,
this.radius,
this.mainAxisAlignment = MainAxisAlignment.start,
@ -353,7 +352,6 @@ class FlowyTextButton extends StatelessWidget {
final Widget? heading;
final Color? hoverColor;
final Color? fillColor;
final Color? textColor;
final BorderRadius? radius;
final MainAxisAlignment mainAxisAlignment;
final String? tooltip;
@ -376,9 +374,10 @@ class FlowyTextButton extends StatelessWidget {
children.add(FlowyText(
text,
overflow: overflow,
color: textColor,
color: fontColor ?? Theme.of(context).colorScheme.onPrimary,
textAlign: TextAlign.center,
lineHeight: lineHeight,
fontSize: fontSize,
));
Widget child = Row(

View File

@ -38,6 +38,7 @@ class FlowyTextField extends StatefulWidget {
final bool isDense;
final bool readOnly;
final Color? enableBorderColor;
final BorderRadius? borderRadius;
const FlowyTextField({
super.key,
@ -74,6 +75,7 @@ class FlowyTextField extends StatefulWidget {
this.isDense = true,
this.readOnly = false,
this.enableBorderColor,
this.borderRadius,
});
@override
@ -180,7 +182,7 @@ class FlowyTextFieldState extends State<FlowyTextField> {
(widget.maxLines == null || widget.maxLines! > 1) ? 12 : 0,
),
enabledBorder: OutlineInputBorder(
borderRadius: Corners.s8Border,
borderRadius: widget.borderRadius ?? Corners.s8Border,
borderSide: BorderSide(
color: widget.enableBorderColor ??
Theme.of(context).colorScheme.outline,
@ -202,7 +204,7 @@ class FlowyTextFieldState extends State<FlowyTextField> {
suffixText: widget.showCounter ? _suffixText() : "",
counterText: "",
focusedBorder: OutlineInputBorder(
borderRadius: Corners.s8Border,
borderRadius: widget.borderRadius ?? Corners.s8Border,
borderSide: BorderSide(
color: widget.readOnly
? widget.enableBorderColor ??
@ -214,13 +216,13 @@ class FlowyTextFieldState extends State<FlowyTextField> {
borderSide: BorderSide(
color: Theme.of(context).colorScheme.error,
),
borderRadius: Corners.s8Border,
borderRadius: widget.borderRadius ?? Corners.s8Border,
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).colorScheme.error,
),
borderRadius: Corners.s8Border,
borderRadius: widget.borderRadius ?? Corners.s8Border,
),
prefixIcon: widget.prefixIcon,
suffixIcon: widget.suffixIcon,

View File

@ -51,11 +51,9 @@ class RoundedTextButton extends StatelessWidget {
radius: borderRadius ?? Corners.s6Border,
fontColor: textColor ?? Theme.of(context).colorScheme.onPrimary,
fillColor: fillColor ?? Theme.of(context).colorScheme.primary,
textColor: textColor,
hoverColor:
hoverColor ?? Theme.of(context).colorScheme.primaryContainer,
padding: padding,
),
),
);

View File

@ -0,0 +1,3 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M36.2874 31.1721C35.677 32.5696 34.9544 33.856 34.1173 35.0387C32.9761 36.651 32.0418 37.767 31.3217 38.3867C30.2055 39.404 29.0095 39.925 27.7289 39.9546C26.8095 39.9546 25.7007 39.6953 24.4101 39.1694C23.1152 38.646 21.9252 38.3867 20.8372 38.3867C19.696 38.3867 18.4722 38.646 17.1631 39.1694C15.852 39.6953 14.7959 39.9694 13.9883 39.9966C12.7602 40.0484 11.5361 39.5126 10.3143 38.3867C9.5344 37.7127 8.55895 36.5572 7.3904 34.9202C6.13664 33.1721 5.10588 31.145 4.29836 28.8339C3.43353 26.3377 3 23.9205 3 21.5803C3 18.8997 3.58452 16.5877 4.75531 14.6502C5.67545 13.0939 6.89956 11.8663 8.43163 10.9651C9.9637 10.0639 11.6191 9.60465 13.4018 9.57527C14.3773 9.57527 15.6564 9.87427 17.2461 10.4619C18.8312 11.0515 19.849 11.3505 20.2953 11.3505C20.6289 11.3505 21.7595 11.0009 23.6763 10.3039C25.4889 9.65749 27.0188 9.38984 28.272 9.49527C31.668 9.76687 34.2194 11.0935 35.9162 13.4835C32.8789 15.3072 31.3765 17.8614 31.4064 21.1381C31.4338 23.6904 32.3682 25.8143 34.2045 27.5007C35.0366 28.2833 35.966 28.8883 37 29.3179C36.7758 29.9623 36.5391 30.5796 36.2874 31.1721ZM28.4988 0.800228C28.4988 2.80068 27.7612 4.6685 26.2912 6.39734C24.5172 8.45259 22.3715 9.64021 20.0446 9.4528C20.0149 9.21281 19.9978 8.96023 19.9978 8.6948C19.9978 6.77437 20.8414 4.71912 22.3396 3.03868C23.0876 2.18784 24.0388 1.48038 25.1924 0.916026C26.3435 0.360092 27.4324 0.0526484 28.4564 0C28.4863 0.26743 28.4988 0.534877 28.4988 0.800202V0.800228Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,3 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M33.7359 8.64277C31.2096 7.48358 28.5005 6.62955 25.6679 6.1404C25.6163 6.13096 25.5648 6.15455 25.5382 6.20174C25.1898 6.82142 24.8039 7.62985 24.5336 8.26527C21.4871 7.80916 18.4561 7.80916 15.472 8.26527C15.2017 7.61572 14.8018 6.82142 14.4518 6.20174C14.4252 6.15613 14.3737 6.13254 14.3221 6.1404C11.4911 6.62798 8.78201 7.48202 6.25412 8.64277C6.23224 8.6522 6.21348 8.66795 6.20103 8.68838C1.06243 16.3653 -0.345244 23.8536 0.345315 31.249C0.348439 31.2852 0.36875 31.3198 0.396872 31.3418C3.78717 33.8316 7.07126 35.3431 10.2944 36.3449C10.3459 36.3607 10.4006 36.3418 10.4334 36.2993C11.1958 35.2582 11.8755 34.1603 12.4582 33.0058C12.4926 32.9382 12.4598 32.858 12.3895 32.8313C11.3115 32.4223 10.285 31.9237 9.29757 31.3575C9.21947 31.3119 9.21322 31.2002 9.28507 31.1467C9.49285 30.991 9.7007 30.829 9.8991 30.6655C9.935 30.6356 9.98502 30.6293 10.0272 30.6482C16.5141 33.6098 23.5368 33.6098 29.9471 30.6482C29.9894 30.6277 30.0394 30.634 30.0768 30.6639C30.2753 30.8275 30.4831 30.991 30.6924 31.1467C30.7643 31.2002 30.7596 31.3119 30.6815 31.3575C29.6941 31.9347 28.6676 32.4223 27.588 32.8297C27.5177 32.8564 27.4865 32.9382 27.5209 33.0058C28.1161 34.1587 28.7957 35.2565 29.5441 36.2978C29.5753 36.3418 29.6316 36.3607 29.6831 36.3449C32.9219 35.3431 36.2059 33.8316 39.5962 31.3418C39.6259 31.3198 39.6447 31.2868 39.6478 31.2506C40.4743 22.7007 38.2635 15.2738 33.7874 8.68994C33.7765 8.66795 33.7578 8.6522 33.7359 8.64277ZM13.4269 26.746C11.4739 26.746 9.86471 24.953 9.86471 22.751C9.86471 20.549 11.4427 18.7561 13.4269 18.7561C15.4267 18.7561 17.0203 20.5648 16.989 22.751C16.989 24.953 15.411 26.746 13.4269 26.746ZM26.5975 26.746C24.6446 26.746 23.0354 24.953 23.0354 22.751C23.0354 20.549 24.6133 18.7561 26.5975 18.7561C28.5973 18.7561 30.1909 20.5648 30.1597 22.751C30.1597 24.953 28.5973 26.746 26.5975 26.746Z" fill="#5865F2"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,10 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.993 1C15.2746 1.00245 10.7109 2.6736 7.11804 5.71464C3.52513 8.75568 1.13726 12.9683 0.381404 17.5993C-0.374455 22.2302 0.550977 26.9775 2.99224 30.9922C5.43351 35.007 9.23141 38.0274 13.7068 39.5135C14.6942 39.6967 15.0661 39.0848 15.0661 38.5645C15.0661 38.0441 15.0463 36.5355 15.0397 34.8862C9.51058 36.0807 8.34223 32.553 8.34223 32.553C7.44044 30.2623 6.13714 29.6601 6.13714 29.6601C4.33357 28.4362 6.27209 28.4591 6.27209 28.4591C8.26983 28.5999 9.31971 30.4979 9.31971 30.4979C11.0904 33.5183 13.9701 32.6446 15.1023 32.1341C15.28 30.8546 15.7967 29.9841 16.3661 29.49C11.9493 28.9925 7.30879 27.2974 7.30879 19.725C7.28142 17.7611 8.01434 15.8619 9.35591 14.4203C9.15186 13.9229 8.47057 11.9136 9.55008 9.1844C9.55008 9.1844 11.2187 8.65427 15.0167 11.2101C18.2744 10.3242 21.7115 10.3242 24.9692 11.2101C28.7639 8.65427 30.4293 9.1844 30.4293 9.1844C31.5121 11.9071 30.8308 13.9164 30.6267 14.4203C31.9726 15.8621 32.707 17.7646 32.6771 19.7315C32.6771 27.3203 28.0267 28.9925 23.6034 29.4801C24.3143 30.0954 24.9495 31.2964 24.9495 33.142C24.9495 35.7862 24.9264 37.9132 24.9264 38.5645C24.9264 39.0913 25.2852 39.7066 26.2923 39.5135C30.7682 38.0273 34.5665 35.0063 37.0077 30.9908C39.4489 26.9754 40.3739 22.2274 39.6172 17.596C38.8604 12.9647 36.4714 8.75199 32.8773 5.71147C29.2832 2.67095 24.7185 1.0009 19.9995 1H19.993Z" fill="#191717"/>
<path d="M7.64403 29.3754C7.60125 29.4736 7.44327 29.503 7.31491 29.4343C7.18656 29.3656 7.09112 29.238 7.1372 29.1365C7.18327 29.0351 7.33796 29.0089 7.46631 29.0776C7.59467 29.1463 7.6934 29.2772 7.64403 29.3754Z" fill="#191717"/>
<path d="M8.45041 30.2688C8.38226 30.3029 8.30428 30.3125 8.22983 30.2957C8.15538 30.279 8.0891 30.2371 8.04231 30.1772C7.91396 30.0397 7.88761 29.8499 7.98635 29.7648C8.08508 29.6797 8.26282 29.719 8.39118 29.8565C8.51953 29.9939 8.54915 30.1837 8.45041 30.2688Z" fill="#191717"/>
<path d="M9.23364 31.4043C9.11186 31.4894 8.90451 31.4043 8.78932 31.2341C8.75747 31.2036 8.73214 31.167 8.71483 31.1265C8.69753 31.0861 8.6886 31.0425 8.6886 30.9985C8.6886 30.9545 8.69753 30.911 8.71483 30.8705C8.73214 30.83 8.75747 30.7934 8.78932 30.7629C8.91109 30.6811 9.11845 30.7629 9.23364 30.9298C9.34883 31.0967 9.35212 31.3192 9.23364 31.4043Z" fill="#191717"/>
<path d="M10.2968 32.5039C10.1881 32.625 9.96764 32.5922 9.78662 32.4286C9.60561 32.265 9.56281 32.0425 9.67142 31.9246C9.78003 31.8068 10.0005 31.8396 10.1881 31.9999C10.3757 32.1603 10.4119 32.3861 10.2968 32.5039Z" fill="#191717"/>
<path d="M11.7876 33.1453C11.7382 33.2991 11.5144 33.3678 11.2906 33.3023C11.0668 33.2369 10.9187 33.0536 10.9615 32.8965C11.0043 32.7395 11.2314 32.6675 11.4585 32.7395C11.6856 32.8115 11.8304 32.9849 11.7876 33.1453Z" fill="#191717"/>
<path d="M13.4136 33.2565C13.4136 33.4169 13.2293 33.5543 12.9924 33.5576C12.7554 33.5609 12.5612 33.43 12.5612 33.2696C12.5612 33.1093 12.7455 32.9718 12.9825 32.9686C13.2194 32.9653 13.4136 33.0929 13.4136 33.2565Z" fill="#191717"/>
<path d="M14.9274 33.0046C14.957 33.1649 14.7924 33.3318 14.5555 33.3711C14.3185 33.4104 14.1111 33.3155 14.0815 33.1584C14.0519 33.0013 14.223 32.8311 14.4534 32.7886C14.6838 32.7461 14.8977 32.8442 14.9274 33.0046Z" fill="#191717"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,13 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_10_316)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M39.2 20.4546C39.2 19.0364 39.0727 17.6728 38.8364 16.3637H20V24.1H30.7636C30.3 26.6 28.8909 28.7182 26.7727 30.1364V35.1546H33.2364C37.0182 31.6728 39.2 26.5455 39.2 20.4546Z" fill="#4285F4"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20 40C25.4 40 29.9273 38.2091 33.2364 35.1545L26.7727 30.1364C24.9818 31.3364 22.6909 32.0454 20 32.0454C14.7909 32.0454 10.3818 28.5273 8.80909 23.8H2.12727V28.9818C5.41818 35.5182 12.1818 40 20 40Z" fill="#34A853"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.80909 23.8C8.40909 22.6 8.18182 21.3182 8.18182 20C8.18182 18.6818 8.40909 17.4 8.80909 16.2V11.0182H2.12727C0.772727 13.7182 0 16.7727 0 20C0 23.2273 0.772727 26.2818 2.12727 28.9818L8.80909 23.8Z" fill="#FBBC05"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20 7.95455C22.9364 7.95455 25.5727 8.96364 27.6455 10.9455L33.3818 5.20909C29.9182 1.98182 25.3909 0 20 0C12.1818 0 5.41818 4.48182 2.12727 11.0182L8.80909 16.2C10.3818 11.4727 14.7909 7.95455 20 7.95455Z" fill="#EA4335"/>
</g>
<defs>
<clipPath id="clip0_10_316">
<rect width="40" height="40" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -36,6 +36,7 @@
"loginButtonText": "Login",
"loginStartWithAnonymous": "Continue with an anonymous session",
"continueAnonymousUser": "Continue with an anonymous session",
"anonymous": "Anonymous",
"buttonText": "Sign In",
"signingInText": "Signing in...",
"forgotPassword": "Forgot Password?",
@ -50,6 +51,8 @@
"signInWithGoogle": "Continue with Google",
"signInWithGithub": "Continue with Github",
"signInWithDiscord": "Continue with Discord",
"signInWithApple": "Continue with Apple",
"continueAnotherWay": "Continue another way",
"signUpWithGoogle": "Sign up with Google",
"signUpWithGithub": "Sign up with Github",
"signUpWithDiscord": "Sign up with Discord",
@ -2367,7 +2370,7 @@
"continueWithGoogle": "Continue with Google",
"continueWithGithub": "Continue with GitHub",
"continueWithDiscord": "Continue with Discord",
"signInAgreement": "By clicking \"Continue\" above, you confirm that\nyou have read, understood, and agreed to\nAppFlowy's",
"signInAgreement": "By clicking \"Continue\" above, you agreed to AppFlowy's",
"and": "and",
"termOfUse": "Terms",
"privacyPolicy": "Privacy Policy",