Alex Wallen 243f80b6d5
[chore] svg improvements (#3145)
* chore: remove use of svgWidget()

* refactor: redundant code in svg

* feat: add generator to generate flowy_svgs.g.dart

* chore: reference new package in pubspec

* chore: remove svg widget from flowy_infra

* refactor: update usage in flowy_infra_ui

* refactor: usage in appflowy_flutter

* fix: error with script not running

* fix: error with script not running

* fix: use unix style file path

* feat: use generation script for flowy svgs

* feat: add task

* fix: add required missing semicolon

* fix: rebase errors

* feat: update generate build script

* fix: solve duplicate entries in the generated file

* fix: compilation errors

* fix: replace all spaces with an underscore

* feat: use FlowySvgs

* feat: reduce assets and simplify

* refactor: do not return empty svg widget

* fix: rebase errors

* fix: analyzer warnings

* chore: remove flowy_icons from tracking

* chore: fix generate flowy icons script linux

* chore: macos/linux script

* chore: add rsync

---------

Co-authored-by: Mathias Mogensen <mathiasrieckm@gmail.com>
Co-authored-by: Mathias Mogensen <mathias@appflowy.io>
2023-08-14 10:34:01 -10:00

423 lines
12 KiB
Dart

import 'package:appflowy/core/config/kv.dart';
import 'package:appflowy/core/config/kv_keys.dart';
import 'package:appflowy/core/frameless_window.dart';
import 'package:appflowy/startup/entry_point.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/historical_user_bloc.dart';
import 'package:appflowy/user/application/sign_in_bloc.dart';
import 'package:appflowy/user/presentation/router.dart';
import 'package:appflowy/user/presentation/widgets/background.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:flowy_infra_ui/widget/rounded_button.dart';
import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
import 'package:flowy_infra_ui/style_widget/snap_bar.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
show UserProfilePB;
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:dartz/dartz.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
class SignInScreen extends StatelessWidget {
const SignInScreen({
super.key,
required this.router,
});
final AuthRouter router;
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<SignInBloc>(),
child: BlocConsumer<SignInBloc, SignInState>(
listener: (context, state) {
state.successOrFail.fold(
() => null,
(result) => _handleSuccessOrFail(result, context),
);
},
builder: (_, __) => Scaffold(
appBar: const PreferredSize(
preferredSize: Size(double.infinity, 60),
child: MoveWindowDetector(),
),
body: SignInForm(router: router),
),
),
);
}
void _handleSuccessOrFail(
Either<UserProfilePB, FlowyError> result,
BuildContext context,
) {
result.fold(
(user) => router.pushHomeScreen(context, user),
(error) => showSnapBar(context, error.msg),
);
}
}
class SignInForm extends StatelessWidget {
const SignInForm({
super.key,
required this.router,
});
final AuthRouter router;
@override
Widget build(BuildContext context) {
final isSubmitting = context.read<SignInBloc>().state.isSubmitting;
const indicatorMinHeight = 4.0;
return Align(
alignment: Alignment.center,
child: AuthFormContainer(
children: [
// Email.
FlowyLogoTitle(
title: LocaleKeys.signIn_loginTitle.tr(),
logoSize: const Size(60, 60),
),
const VSpace(30),
// Email and password. don't support yet.
/*
...[
const EmailTextField(),
const VSpace(5),
const PasswordTextField(),
const VSpace(20),
const LoginButton(),
const VSpace(10),
const VSpace(10),
SignUpPrompt(router: router),
],
*/
const SignInAsGuestButton(),
// third-party sign in.
const VSpace(20),
const OrContinueWith(),
const VSpace(10),
const ThirdPartySignInButtons(),
const VSpace(20),
// loading status
...isSubmitting
? [
const VSpace(indicatorMinHeight),
const LinearProgressIndicator(
value: null,
minHeight: indicatorMinHeight,
),
]
: [
const VSpace(indicatorMinHeight * 2.0)
], // add the same space when there's no loading status.
// ConstrainedBox(
// constraints: const BoxConstraints(maxHeight: 140),
// child: HistoricalUserList(
// didOpenUser: () async {
// await FlowyRunner.run(
// FlowyApp(),
// integrationEnv(),
// );
// },
// ),
// ),
const VSpace(20),
],
),
);
}
}
class SignUpPrompt extends StatelessWidget {
const SignUpPrompt({
Key? key,
required this.router,
}) : super(key: key);
final AuthRouter router;
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FlowyText.medium(
LocaleKeys.signIn_dontHaveAnAccount.tr(),
color: Theme.of(context).hintColor,
),
TextButton(
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.bodyMedium,
),
onPressed: () => router.pushSignUpScreen(context),
child: Text(
LocaleKeys.signUp_buttonText.tr(),
style: TextStyle(color: Theme.of(context).colorScheme.primary),
),
),
ForgetPasswordButton(router: router),
],
);
}
}
class LoginButton extends StatelessWidget {
const LoginButton({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return RoundedTextButton(
title: LocaleKeys.signIn_loginButtonText.tr(),
height: 48,
borderRadius: Corners.s10Border,
onPressed: () => context
.read<SignInBloc>()
.add(const SignInEvent.signedInWithUserEmailAndPassword()),
);
}
}
class SignInAsGuestButton extends StatelessWidget {
const SignInAsGuestButton({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => HistoricalUserBloc()
..add(
const HistoricalUserEvent.initial(),
),
child: BlocListener<HistoricalUserBloc, HistoricalUserState>(
listenWhen: (previous, current) =>
previous.openedHistoricalUser != current.openedHistoricalUser,
listener: (context, state) async {
await FlowyRunner.run(
FlowyApp(),
integrationEnv(),
);
},
child: BlocBuilder<HistoricalUserBloc, HistoricalUserState>(
builder: (context, state) {
if (state.historicalUsers.isEmpty) {
return RoundedTextButton(
title: LocaleKeys.signIn_loginAsGuestButtonText.tr(),
height: 48,
borderRadius: Corners.s6Border,
onPressed: () {
getIt<KeyValueStorage>().set(KVKeys.loginType, 'local');
context
.read<SignInBloc>()
.add(const SignInEvent.signedInAsGuest());
},
);
} else {
return RoundedTextButton(
title: LocaleKeys.signIn_continueAnonymousUser.tr(),
height: 48,
borderRadius: Corners.s6Border,
onPressed: () {
final bloc = context.read<HistoricalUserBloc>();
final user = bloc.state.historicalUsers.first;
bloc.add(HistoricalUserEvent.openHistoricalUser(user));
},
);
}
},
),
),
);
}
}
class ForgetPasswordButton extends StatelessWidget {
const ForgetPasswordButton({
Key? key,
required this.router,
}) : super(key: key);
final AuthRouter router;
@override
Widget build(BuildContext context) {
return TextButton(
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.bodyMedium,
),
onPressed: () {
throw UnimplementedError();
},
child: Text(
LocaleKeys.signIn_forgotPassword.tr(),
style: TextStyle(color: Theme.of(context).colorScheme.primary),
),
);
}
}
class PasswordTextField extends StatelessWidget {
const PasswordTextField({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocBuilder<SignInBloc, SignInState>(
buildWhen: (previous, current) =>
previous.passwordError != current.passwordError,
builder: (context, state) {
return RoundedInputField(
obscureText: true,
obscureIcon: const FlowySvg(FlowySvgs.hide_m),
obscureHideIcon: const FlowySvg(FlowySvgs.show_m),
hintText: LocaleKeys.signIn_passwordHint.tr(),
errorText: context
.read<SignInBloc>()
.state
.passwordError
.fold(() => "", (error) => error),
onChanged: (value) => context
.read<SignInBloc>()
.add(SignInEvent.passwordChanged(value)),
);
},
);
}
}
class EmailTextField extends StatelessWidget {
const EmailTextField({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocBuilder<SignInBloc, SignInState>(
buildWhen: (previous, current) =>
previous.emailError != current.emailError,
builder: (context, state) {
return RoundedInputField(
hintText: LocaleKeys.signIn_emailHint.tr(),
errorText: context
.read<SignInBloc>()
.state
.emailError
.fold(() => "", (error) => error),
onChanged: (value) =>
context.read<SignInBloc>().add(SignInEvent.emailChanged(value)),
);
},
);
}
}
class OrContinueWith extends StatelessWidget {
const OrContinueWith({super.key});
@override
Widget build(BuildContext context) {
return const Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Flexible(
child: Divider(
color: Colors.white,
height: 10,
),
),
FlowyText.regular(' Or continue with '),
Flexible(
child: Divider(
color: Colors.white,
height: 10,
),
),
],
);
}
}
class ThirdPartySignInButton extends StatelessWidget {
const ThirdPartySignInButton({
Key? key,
required this.icon,
required this.onPressed,
}) : super(key: key);
final FlowySvgData icon;
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return FlowyIconButton(
height: 48,
width: 48,
iconPadding: const EdgeInsets.all(8.0),
radius: Corners.s10Border,
onPressed: onPressed,
icon: FlowySvg(
icon,
),
);
}
}
class ThirdPartySignInButtons extends StatelessWidget {
final MainAxisAlignment mainAxisAlignment;
const ThirdPartySignInButtons({
this.mainAxisAlignment = MainAxisAlignment.center,
super.key,
});
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: mainAxisAlignment,
children: [
ThirdPartySignInButton(
icon: FlowySvgs.google_mark_xl,
onPressed: () {
getIt<KeyValueStorage>().set(KVKeys.loginType, 'supabase');
context.read<SignInBloc>().add(
const SignInEvent.signedInWithOAuth('google'),
);
},
),
// const SizedBox(width: 20),
// ThirdPartySignInButton(
// icon: 'login/github-mark',
// onPressed: () {
// getIt<KeyValueStorage>().set(KVKeys.loginType, 'supabase');
// context
// .read<SignInBloc>()
// .add(const SignInEvent.signedInWithOAuth('github'));
// },
// ),
// const SizedBox(width: 20),
// ThirdPartySignInButton(
// icon: 'login/discord-mark',
// onPressed: () {
// getIt<KeyValueStorage>().set(KVKeys.loginType, 'supabase');
// context
// .read<SignInBloc>()
// .add(const SignInEvent.signedInWithOAuth('discord'));
// },
// ),
],
);
}
}