mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: enable apple sign in on desktop version
This commit is contained in:
parent
d89804f3e4
commit
39d6e3f1fe
@ -12,7 +12,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class DesktopSignInScreen extends StatelessWidget {
|
||||
const DesktopSignInScreen({super.key});
|
||||
const DesktopSignInScreen({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -20,38 +22,32 @@ class DesktopSignInScreen extends StatelessWidget {
|
||||
return BlocBuilder<SignInBloc, SignInState>(
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
preferredSize:
|
||||
Size.fromHeight(PlatformExtension.isWindows ? 40 : 60),
|
||||
child: PlatformExtension.isWindows
|
||||
? const WindowTitleBar()
|
||||
: const MoveWindowDetector(),
|
||||
),
|
||||
appBar: _buildAppBar(),
|
||||
body: Center(
|
||||
child: AuthFormContainer(
|
||||
children: [
|
||||
const Spacer(),
|
||||
|
||||
const VSpace(20),
|
||||
|
||||
// logo and title
|
||||
FlowyLogoTitle(
|
||||
title: LocaleKeys.welcomeText.tr(),
|
||||
logoSize: const Size(60, 60),
|
||||
),
|
||||
const VSpace(20),
|
||||
|
||||
// magic link sign in
|
||||
const SignInWithMagicLinkButtons(),
|
||||
|
||||
// third-party sign in.
|
||||
const VSpace(20),
|
||||
|
||||
// third-party sign in.
|
||||
if (isAuthEnabled) ...[
|
||||
const _OrDivider(),
|
||||
const VSpace(20),
|
||||
const ThirdPartySignInButtons(),
|
||||
const VSpace(20),
|
||||
],
|
||||
const VSpace(20),
|
||||
|
||||
// anonymous sign in
|
||||
const SignInAnonymousButtonV2(),
|
||||
const VSpace(16),
|
||||
|
||||
// sign in agreement
|
||||
const SignInAgreement(),
|
||||
@ -64,6 +60,12 @@ class DesktopSignInScreen extends StatelessWidget {
|
||||
)
|
||||
: const VSpace(indicatorMinHeight),
|
||||
const VSpace(20),
|
||||
|
||||
const Spacer(),
|
||||
|
||||
// anonymous sign in
|
||||
const SignInAnonymousButtonV2(),
|
||||
const VSpace(16),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -71,6 +73,15 @@ class DesktopSignInScreen extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSize _buildAppBar() {
|
||||
return PreferredSize(
|
||||
preferredSize: Size.fromHeight(PlatformExtension.isWindows ? 40 : 60),
|
||||
child: PlatformExtension.isWindows
|
||||
? const WindowTitleBar()
|
||||
: const MoveWindowDetector(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _OrDivider extends StatelessWidget {
|
||||
|
@ -1,7 +1,9 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/animated_gesture.dart';
|
||||
import 'package:appflowy/user/presentation/widgets/widgets.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';
|
||||
|
||||
@ -12,6 +14,21 @@ enum ThirdPartySignInButtonType {
|
||||
discord,
|
||||
anonymous;
|
||||
|
||||
String get provider {
|
||||
switch (this) {
|
||||
case ThirdPartySignInButtonType.apple:
|
||||
return 'apple';
|
||||
case ThirdPartySignInButtonType.google:
|
||||
return 'google';
|
||||
case ThirdPartySignInButtonType.github:
|
||||
return 'github';
|
||||
case ThirdPartySignInButtonType.discord:
|
||||
return 'discord';
|
||||
case ThirdPartySignInButtonType.anonymous:
|
||||
throw UnsupportedError('Anonymous session does not have a provider');
|
||||
}
|
||||
}
|
||||
|
||||
FlowySvgData get icon {
|
||||
switch (this) {
|
||||
case ThirdPartySignInButtonType.apple:
|
||||
@ -135,3 +152,69 @@ class MobileThirdPartySignInButton extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DesktopSignInButton extends StatelessWidget {
|
||||
const DesktopSignInButton({
|
||||
super.key,
|
||||
required this.type,
|
||||
required this.onPressed,
|
||||
});
|
||||
|
||||
final ThirdPartySignInButtonType type;
|
||||
final VoidCallback onPressed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final style = Theme.of(context);
|
||||
// In desktop, the width of button is limited by [AuthFormContainer]
|
||||
return SizedBox(
|
||||
height: 48,
|
||||
width: AuthFormContainer.width,
|
||||
child: OutlinedButton.icon(
|
||||
// In order to align all the labels vertically in a relatively centered position to the button, we use a fixed width container to wrap the icon(align to the right), then use another container to align the label to left.
|
||||
icon: Container(
|
||||
width: AuthFormContainer.width / 4,
|
||||
alignment: Alignment.centerRight,
|
||||
child: SizedBox(
|
||||
// Some icons are not square, so we just use a fixed width here.
|
||||
width: 24,
|
||||
child: FlowySvg(
|
||||
type.icon,
|
||||
blendMode: type.blendMode,
|
||||
),
|
||||
),
|
||||
),
|
||||
label: Container(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: FlowyText(
|
||||
type.labelText,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
style: ButtonStyle(
|
||||
overlayColor: WidgetStateProperty.resolveWith<Color?>(
|
||||
(states) {
|
||||
if (states.contains(WidgetState.hovered)) {
|
||||
return style.colorScheme.onSecondaryContainer;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
shape: WidgetStateProperty.all(
|
||||
const RoundedRectangleBorder(
|
||||
borderRadius: Corners.s6Border,
|
||||
),
|
||||
),
|
||||
side: WidgetStateProperty.all(
|
||||
BorderSide(
|
||||
color: style.dividerColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,22 +1,21 @@
|
||||
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_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 'third_party_sign_in_button.dart';
|
||||
|
||||
typedef _SignInCallback = void Function(ThirdPartySignInButtonType signInType);
|
||||
|
||||
@visibleForTesting
|
||||
const Key signInWithGoogleButtonKey = Key('signInWithGoogleButton');
|
||||
|
||||
class ThirdPartySignInButtons extends StatefulWidget {
|
||||
class ThirdPartySignInButtons extends StatelessWidget {
|
||||
/// Used in DesktopSignInScreen, MobileSignInScreen and SettingThirdPartyLogin
|
||||
const ThirdPartySignInButtons({
|
||||
super.key,
|
||||
@ -26,195 +25,175 @@ class ThirdPartySignInButtons extends StatefulWidget {
|
||||
final bool expanded;
|
||||
|
||||
@override
|
||||
State<ThirdPartySignInButtons> createState() =>
|
||||
_ThirdPartySignInButtonsState();
|
||||
Widget build(BuildContext context) {
|
||||
if (PlatformExtension.isDesktopOrWeb) {
|
||||
return _DesktopThirdPartySignIn(
|
||||
onSignIn: (type) => _signIn(context, type.provider),
|
||||
);
|
||||
} else {
|
||||
return _MobileThirdPartySignIn(
|
||||
isExpanded: expanded,
|
||||
onSignIn: (type) => _signIn(context, type.provider),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _signIn(BuildContext context, String provider) {
|
||||
context.read<SignInBloc>().add(
|
||||
SignInEvent.signedInWithOAuth(provider),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ThirdPartySignInButtonsState extends State<ThirdPartySignInButtons> {
|
||||
bool expanded = false;
|
||||
class _DesktopThirdPartySignIn extends StatefulWidget {
|
||||
const _DesktopThirdPartySignIn({
|
||||
required this.onSignIn,
|
||||
});
|
||||
|
||||
final _SignInCallback onSignIn;
|
||||
|
||||
@override
|
||||
State<_DesktopThirdPartySignIn> createState() =>
|
||||
_DesktopThirdPartySignInState();
|
||||
}
|
||||
|
||||
class _DesktopThirdPartySignInState extends State<_DesktopThirdPartySignIn> {
|
||||
static const padding = 12.0;
|
||||
|
||||
bool isExpanded = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
DesktopSignInButton(
|
||||
key: signInWithGoogleButtonKey,
|
||||
type: ThirdPartySignInButtonType.google,
|
||||
onPressed: () => widget.onSignIn(ThirdPartySignInButtonType.google),
|
||||
),
|
||||
const VSpace(padding),
|
||||
DesktopSignInButton(
|
||||
type: ThirdPartySignInButtonType.apple,
|
||||
onPressed: () => widget.onSignIn(ThirdPartySignInButtonType.apple),
|
||||
),
|
||||
...isExpanded ? _buildExpandedButtons() : _buildCollapsedButtons(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildExpandedButtons() {
|
||||
return [
|
||||
const VSpace(padding * 1.5),
|
||||
DesktopSignInButton(
|
||||
type: ThirdPartySignInButtonType.github,
|
||||
onPressed: () => widget.onSignIn(ThirdPartySignInButtonType.github),
|
||||
),
|
||||
const VSpace(padding),
|
||||
DesktopSignInButton(
|
||||
type: ThirdPartySignInButtonType.discord,
|
||||
onPressed: () => widget.onSignIn(ThirdPartySignInButtonType.discord),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<Widget> _buildCollapsedButtons() {
|
||||
return [
|
||||
const VSpace(padding),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
isExpanded = !isExpanded;
|
||||
});
|
||||
},
|
||||
child: FlowyText(
|
||||
LocaleKeys.signIn_continueAnotherWay.tr(),
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
decoration: TextDecoration.underline,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class _MobileThirdPartySignIn extends StatefulWidget {
|
||||
const _MobileThirdPartySignIn({
|
||||
required this.isExpanded,
|
||||
required this.onSignIn,
|
||||
});
|
||||
|
||||
final bool isExpanded;
|
||||
final _SignInCallback onSignIn;
|
||||
|
||||
@override
|
||||
State<_MobileThirdPartySignIn> createState() =>
|
||||
_MobileThirdPartySignInState();
|
||||
}
|
||||
|
||||
class _MobileThirdPartySignInState extends State<_MobileThirdPartySignIn> {
|
||||
static const padding = 8.0;
|
||||
|
||||
bool isExpanded = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
expanded = widget.expanded;
|
||||
isExpanded = widget.isExpanded;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (PlatformExtension.isDesktopOrWeb) {
|
||||
const padding = 16.0;
|
||||
return Column(
|
||||
children: [
|
||||
_DesktopSignInButton(
|
||||
key: signInWithGoogleButtonKey,
|
||||
type: ThirdPartySignInButtonType.google,
|
||||
onPressed: () {
|
||||
_signInWithGoogle(context);
|
||||
},
|
||||
return Column(
|
||||
children: [
|
||||
// only display apple sign in button on iOS
|
||||
if (Platform.isIOS) ...[
|
||||
MobileThirdPartySignInButton(
|
||||
type: ThirdPartySignInButtonType.apple,
|
||||
onPressed: () => widget.onSignIn(ThirdPartySignInButtonType.apple),
|
||||
),
|
||||
const VSpace(padding),
|
||||
_DesktopSignInButton(
|
||||
type: ThirdPartySignInButtonType.github,
|
||||
onPressed: () {
|
||||
_signInWithGithub(context);
|
||||
},
|
||||
),
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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({
|
||||
super.key,
|
||||
required this.type,
|
||||
required this.onPressed,
|
||||
});
|
||||
|
||||
final ThirdPartySignInButtonType type;
|
||||
final VoidCallback onPressed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final style = Theme.of(context);
|
||||
// In desktop, the width of button is limited by [AuthFormContainer]
|
||||
return SizedBox(
|
||||
height: 48,
|
||||
width: AuthFormContainer.width,
|
||||
child: OutlinedButton.icon(
|
||||
// In order to align all the labels vertically in a relatively centered position to the button, we use a fixed width container to wrap the icon(align to the right), then use another container to align the label to left.
|
||||
icon: Container(
|
||||
width: AuthFormContainer.width / 4,
|
||||
alignment: Alignment.centerRight,
|
||||
child: SizedBox(
|
||||
// Some icons are not square, so we just use a fixed width here.
|
||||
width: 24,
|
||||
child: FlowySvg(
|
||||
type.icon,
|
||||
blendMode: type.blendMode,
|
||||
),
|
||||
),
|
||||
MobileThirdPartySignInButton(
|
||||
type: ThirdPartySignInButtonType.google,
|
||||
onPressed: () => widget.onSignIn(ThirdPartySignInButtonType.google),
|
||||
),
|
||||
label: Container(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: FlowyText(
|
||||
type.labelText,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
style: ButtonStyle(
|
||||
overlayColor: WidgetStateProperty.resolveWith<Color?>(
|
||||
(states) {
|
||||
if (states.contains(WidgetState.hovered)) {
|
||||
return style.colorScheme.onSecondaryContainer;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
shape: WidgetStateProperty.all(
|
||||
const RoundedRectangleBorder(
|
||||
borderRadius: Corners.s6Border,
|
||||
),
|
||||
),
|
||||
side: WidgetStateProperty.all(
|
||||
BorderSide(
|
||||
color: style.dividerColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
),
|
||||
...isExpanded ? _buildExpandedButtons() : _buildCollapsedButtons(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildExpandedButtons() {
|
||||
return [
|
||||
const VSpace(padding),
|
||||
MobileThirdPartySignInButton(
|
||||
type: ThirdPartySignInButtonType.github,
|
||||
onPressed: () => widget.onSignIn(ThirdPartySignInButtonType.github),
|
||||
),
|
||||
const VSpace(padding),
|
||||
MobileThirdPartySignInButton(
|
||||
type: ThirdPartySignInButtonType.discord,
|
||||
onPressed: () => widget.onSignIn(ThirdPartySignInButtonType.discord),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<Widget> _buildCollapsedButtons() {
|
||||
return [
|
||||
const VSpace(padding * 2),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
isExpanded = !isExpanded;
|
||||
});
|
||||
},
|
||||
child: FlowyText(
|
||||
LocaleKeys.signIn_continueAnotherWay.tr(),
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
decoration: TextDecoration.underline,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AuthFormContainer extends StatelessWidget {
|
||||
const AuthFormContainer({super.key, required this.children});
|
||||
const AuthFormContainer({
|
||||
super.key,
|
||||
required this.children,
|
||||
});
|
||||
|
||||
final List<Widget> children;
|
||||
|
||||
@ -11,14 +14,10 @@ class AuthFormContainer extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: width,
|
||||
child: ScrollConfiguration(
|
||||
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: children,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: children,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user