Sign in with Apple

This commit is contained in:
Hillel Coren 2022-07-10 12:30:24 +03:00
parent 047ee8f700
commit 7d2e4ef86d
8 changed files with 143 additions and 73 deletions

View File

@ -25,28 +25,15 @@ class AppToggleButtons extends StatelessWidget {
toggleWidth -= 46 / tabLabels.length; toggleWidth -= 46 / tabLabels.length;
} }
return Padding( final isSelected = tabLabels.length == 4
padding: const EdgeInsets.only(bottom: 20), ? (selectedIndex == 0
child: ToggleButtons( ? [true, false, false, false]
children: [ : selectedIndex == 1
Container( ? [false, true, false, false]
width: toggleWidth, : selectedIndex == 2
height: 40, ? [false, false, true, false]
child: Center(child: Text(tabLabels[0])), : [false, false, false, true])
), : tabLabels.length == 3
Container(
width: toggleWidth,
height: 40,
child: Center(child: Text(tabLabels[1])),
),
if (tabLabels.length == 3)
Container(
width: toggleWidth,
height: 40,
child: Center(child: Text(tabLabels[2])),
),
],
isSelected: tabLabels.length == 3
? (selectedIndex == 0 ? (selectedIndex == 0
? [true, false, false] ? [true, false, false]
: selectedIndex == 1 : selectedIndex == 1
@ -54,7 +41,22 @@ class AppToggleButtons extends StatelessWidget {
: [false, false, true]) : [false, false, true])
: selectedIndex == 0 : selectedIndex == 0
? [true, false] ? [true, false]
: [false, true], : [false, true];
final children = tabLabels
.map((label) => Container(
width: toggleWidth,
height: 40,
child: Center(
child: Text(label[0].toUpperCase() + label.substring(1))),
))
.toList();
return Padding(
padding: const EdgeInsets.only(bottom: 20),
child: ToggleButtons(
children: children,
isSelected: isSelected,
onPressed: (index) => onTabChanged(index), onPressed: (index) => onTabChanged(index),
), ),
); );

View File

@ -65,14 +65,16 @@ class _LoginState extends State<LoginView> {
static const String LOGIN_TYPE_EMAIL = 'email'; static const String LOGIN_TYPE_EMAIL = 'email';
static const String LOGIN_TYPE_GOOGLE = 'google'; static const String LOGIN_TYPE_GOOGLE = 'google';
static const String LOGIN_TYPE_MICROSOFT = 'microsoft'; static const String LOGIN_TYPE_MICROSOFT = 'microsoft';
static const String LOGIN_TYPE_APPLE = 'apple';
String _loginError = ''; String _loginError = '';
String _loginType = LOGIN_TYPE_GOOGLE; String _loginType = LOGIN_TYPE_EMAIL;
List<String> _loginTypes;
bool _tokenLogin = false; bool _tokenLogin = false;
bool _isSelfHosted = false; bool _isSelfHosted = false;
bool _createAccount = false; bool _createAccount = false;
bool _hideGoogle = false;
bool _recoverPassword = false; bool _recoverPassword = false;
bool _autoValidate = false; bool _autoValidate = false;
@ -91,13 +93,15 @@ class _LoginState extends State<LoginView> {
} else if (WebUtils.getHtmlValue('signup') == 'true') { } else if (WebUtils.getHtmlValue('signup') == 'true') {
_createAccount = true; _createAccount = true;
} }
} else if (isApple() || !GoogleOAuth.isEnabled) {
_loginType = LOGIN_TYPE_EMAIL;
_hideGoogle = true;
} else if (isWindows() || isLinux()) {
_loginType = LOGIN_TYPE_EMAIL;
_hideGoogle = true;
} }
_loginTypes = [
LOGIN_TYPE_EMAIL,
if (!kReleaseMode || kIsWeb || isMobileOS()) LOGIN_TYPE_GOOGLE,
if (!kReleaseMode || kIsWeb) LOGIN_TYPE_MICROSOFT,
if (!kReleaseMode || kIsWeb || isMobileOS() || isMacOS())
LOGIN_TYPE_APPLE,
];
} }
@override @override
@ -213,6 +217,8 @@ class _LoginState extends State<LoginView> {
); );
} else if (_loginType == LOGIN_TYPE_MICROSOFT) { } else if (_loginType == LOGIN_TYPE_MICROSOFT) {
viewModel.onMicrosoftSignUpPressed(context, completer, url); viewModel.onMicrosoftSignUpPressed(context, completer, url);
} else if (_loginType == LOGIN_TYPE_APPLE) {
viewModel.onAppleSignUpPressed(context, completer, url);
} else { } else {
viewModel.onGoogleSignUpPressed(context, completer, url); viewModel.onGoogleSignUpPressed(context, completer, url);
} }
@ -288,6 +294,11 @@ class _LoginState extends State<LoginView> {
url: url, url: url,
secret: _isSelfHosted ? _secretController.text : '', secret: _isSelfHosted ? _secretController.text : '',
oneTimePassword: _oneTimePasswordController.text); oneTimePassword: _oneTimePasswordController.text);
} else if (_loginType == LOGIN_TYPE_APPLE) {
viewModel.onAppleLoginPressed(context, completer,
url: url,
secret: _isSelfHosted ? _secretController.text : '',
oneTimePassword: _oneTimePasswordController.text);
} else { } else {
viewModel.onGoogleLoginPressed(context, completer, viewModel.onGoogleLoginPressed(context, completer,
url: url, url: url,
@ -401,49 +412,18 @@ class _LoginState extends State<LoginView> {
}, },
), ),
], ],
if (!_isSelfHosted && if (!_isSelfHosted && _loginTypes.length > 1) ...[
(!kReleaseMode || !_hideGoogle)) ...[
RuledText(localization.selectMethod), RuledText(localization.selectMethod),
if (kIsWeb)
AppToggleButtons( AppToggleButtons(
tabLabels: [ tabLabels: _loginTypes,
'Google', selectedIndex: _loginTypes.indexOf(_loginType),
'Microsoft',
localization.email,
],
selectedIndex: _loginType == LOGIN_TYPE_EMAIL
? 2
: _loginType == LOGIN_TYPE_MICROSOFT
? 1
: 0,
onTabChanged: (index) { onTabChanged: (index) {
setState(() { setState(() {
_loginType = index == 2 _loginType = _loginTypes[index];
? LOGIN_TYPE_EMAIL
: index == 1
? LOGIN_TYPE_MICROSOFT
: LOGIN_TYPE_GOOGLE;
_loginError = ''; _loginError = '';
}); });
}, },
) )
else
AppToggleButtons(
tabLabels: [
'Google',
localization.email,
],
selectedIndex:
_loginType == LOGIN_TYPE_EMAIL ? 1 : 0,
onTabChanged: (index) {
setState(() {
_loginType = index == 1
? LOGIN_TYPE_EMAIL
: LOGIN_TYPE_GOOGLE;
_loginError = '';
});
},
),
], ],
Padding( Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
@ -610,6 +590,8 @@ class _LoginState extends State<LoginView> {
Icon(Icons.mail, color: Colors.white) Icon(Icons.mail, color: Colors.white)
else if (_loginType == LOGIN_TYPE_MICROSOFT) else if (_loginType == LOGIN_TYPE_MICROSOFT)
Icon(MdiIcons.microsoft, color: Colors.white) Icon(MdiIcons.microsoft, color: Colors.white)
else if (_loginType == LOGIN_TYPE_APPLE)
Icon(MdiIcons.apple, color: Colors.white)
else else
ClipOval( ClipOval(
child: Image.asset( child: Image.asset(

View File

@ -60,6 +60,8 @@ class LoginVM {
@required this.onGoogleSignUpPressed, @required this.onGoogleSignUpPressed,
@required this.onMicrosoftLoginPressed, @required this.onMicrosoftLoginPressed,
@required this.onMicrosoftSignUpPressed, @required this.onMicrosoftSignUpPressed,
@required this.onAppleLoginPressed,
@required this.onAppleSignUpPressed,
@required this.onTokenLoginPressed, @required this.onTokenLoginPressed,
}); });
@ -110,6 +112,11 @@ class LoginVM {
final Function(BuildContext, Completer<Null> completer, String url) final Function(BuildContext, Completer<Null> completer, String url)
onMicrosoftSignUpPressed; onMicrosoftSignUpPressed;
final Function(BuildContext, Completer<Null> completer,
{String url, String secret, String oneTimePassword}) onAppleLoginPressed;
final Function(BuildContext, Completer<Null> completer, String url)
onAppleSignUpPressed;
static LoginVM fromStore(Store<AppState> store) { static LoginVM fromStore(Store<AppState> store) {
void _handleLogin({BuildContext context, bool isSignUp = false}) { void _handleLogin({BuildContext context, bool isSignUp = false}) {
final layout = calculateLayout(context); final layout = calculateLayout(context);
@ -266,6 +273,59 @@ class LoginVM {
print('## onMicrosoftSignUpPressed: $error'); print('## onMicrosoftSignUpPressed: $error');
} }
}, },
onAppleLoginPressed: (
BuildContext context,
Completer<Null> completer, {
@required String url,
@required String secret,
@required String oneTimePassword,
}) async {
try {
/*
WebUtils.microsoftLogin((idToken, accessToken) {
store.dispatch(OAuthLoginRequest(
completer: completer,
idToken: idToken,
accessToken: accessToken,
url: _formatApiUrl(url),
secret: secret.trim(),
platform: getPlatform(context),
provider: UserEntity.OAUTH_PROVIDER_MICROSOFT,
oneTimePassword: oneTimePassword,
));
completer.future.then((_) => _handleLogin(context: context));
}, (dynamic error) {
completer.completeError(error);
});
*/
} catch (error) {
completer.completeError(error);
print('## onAppleLoginPressed: $error');
}
},
onAppleSignUpPressed:
(BuildContext context, Completer<Null> completer, String url) async {
try {
/*
WebUtils.microsoftLogin((idToken, accessToken) {
store.dispatch(OAuthSignUpRequest(
url: url,
completer: completer,
idToken: idToken,
provider: UserEntity.OAUTH_PROVIDER_MICROSOFT,
accessToken: accessToken,
));
completer.future
.then((_) => _handleLogin(context: context, isSignUp: true));
}, (dynamic error) {
completer.completeError(error);
});
*/
} catch (error) {
completer.completeError(error);
print('## onAppleSignUpPressed: $error');
}
},
onSignUpPressed: ( onSignUpPressed: (
BuildContext context, BuildContext context,
Completer<Null> completer, { Completer<Null> completer, {

View File

@ -12,6 +12,7 @@ import path_provider_macos
import printing import printing
import sentry_flutter import sentry_flutter
import shared_preferences_macos import shared_preferences_macos
import sign_in_with_apple
import sqflite import sqflite
import url_launcher_macos import url_launcher_macos
@ -23,6 +24,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin")) PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin"))
SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin")) SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SignInWithApplePlugin.register(with: registry.registrar(forPlugin: "SignInWithApplePlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
} }

View File

@ -72,6 +72,7 @@ dependencies:
printing: ^5.8.0 printing: ^5.8.0
image_cropper: ^2.0.2 image_cropper: ^2.0.2
msal_js: ^2.14.0 msal_js: ^2.14.0
sign_in_with_apple: ^4.0.0
# bitsdojo_window: ^0.1.2 # bitsdojo_window: ^0.1.2
# quick_actions: ^0.2.1 # quick_actions: ^0.2.1
# idb_shim: ^1.11.1+1 # idb_shim: ^1.11.1+1

View File

@ -1092,6 +1092,27 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.1" version: "1.0.1"
sign_in_with_apple:
dependency: "direct main"
description:
name: sign_in_with_apple
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
sign_in_with_apple_platform_interface:
dependency: transitive
description:
name: sign_in_with_apple_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
sign_in_with_apple_web:
dependency: transitive
description:
name: sign_in_with_apple_web
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter

View File

@ -71,6 +71,7 @@ dependencies:
printing: ^5.8.0 printing: ^5.8.0
image_cropper: ^2.0.2 image_cropper: ^2.0.2
msal_js: ^2.14.0 msal_js: ^2.14.0
sign_in_with_apple: ^4.0.0
# bitsdojo_window: ^0.1.2 # bitsdojo_window: ^0.1.2
# quick_actions: ^0.2.1 # quick_actions: ^0.2.1
# idb_shim: ^1.11.1+1 # idb_shim: ^1.11.1+1

View File

@ -72,6 +72,7 @@ dependencies:
printing: ^5.8.0 printing: ^5.8.0
image_cropper: ^2.0.2 image_cropper: ^2.0.2
msal_js: ^2.14.0 msal_js: ^2.14.0
sign_in_with_apple: ^4.0.0
# bitsdojo_window: ^0.1.2 # bitsdojo_window: ^0.1.2
# quick_actions: ^0.2.1 # quick_actions: ^0.2.1
# idb_shim: ^1.11.1+1 # idb_shim: ^1.11.1+1