splash_screen.dart 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import 'package:appflowy/env/env.dart';
  2. import 'package:appflowy/generated/flowy_svgs.g.dart';
  3. import 'package:appflowy/startup/startup.dart';
  4. import 'package:appflowy/user/application/auth/auth_service.dart';
  5. import 'package:appflowy/user/application/splash_bloc.dart';
  6. import 'package:appflowy/user/domain/auth_state.dart';
  7. import 'package:appflowy/user/presentation/helpers/helpers.dart';
  8. import 'package:appflowy/user/presentation/router.dart';
  9. import 'package:appflowy/util/platform_extension.dart';
  10. import 'package:appflowy_backend/dispatch/dispatch.dart';
  11. import 'package:appflowy_backend/log.dart';
  12. import 'package:flutter/material.dart';
  13. import 'package:flutter_bloc/flutter_bloc.dart';
  14. // [[diagram: splash screen]]
  15. // ┌────────────────┐1.get user ┌──────────┐ ┌────────────┐ 2.send UserEventCheckUser
  16. // │ SplashScreen │──────────▶│SplashBloc│────▶│ISplashUser │─────┐
  17. // └────────────────┘ └──────────┘ └────────────┘ │
  18. // │
  19. // ▼
  20. // ┌───────────┐ ┌─────────────┐ ┌────────┐
  21. // │HomeScreen │◀───────────│BlocListener │◀────────────────│RustSDK │
  22. // └───────────┘ └─────────────┘ └────────┘
  23. // 4. Show HomeScreen or SignIn 3.return AuthState
  24. class SplashScreen extends StatelessWidget {
  25. const SplashScreen({
  26. super.key,
  27. required this.autoRegister,
  28. });
  29. static const routeName = '/SplashScreen';
  30. final bool autoRegister;
  31. @override
  32. Widget build(BuildContext context) {
  33. if (!autoRegister) {
  34. return _buildChild(context);
  35. } else {
  36. return FutureBuilder<void>(
  37. future: _registerIfNeeded(),
  38. builder: (context, snapshot) {
  39. if (snapshot.connectionState != ConnectionState.done) {
  40. return Container();
  41. }
  42. return _buildChild(context);
  43. },
  44. );
  45. }
  46. }
  47. BlocProvider<SplashBloc> _buildChild(BuildContext context) {
  48. return BlocProvider(
  49. create: (context) {
  50. return getIt<SplashBloc>()..add(const SplashEvent.getUser());
  51. },
  52. child: Scaffold(
  53. body: BlocListener<SplashBloc, SplashState>(
  54. listener: (context, state) {
  55. state.auth.map(
  56. authenticated: (r) => _handleAuthenticated(context, r),
  57. unauthenticated: (r) => _handleUnauthenticated(context, r),
  58. initial: (r) => {},
  59. );
  60. },
  61. child: const Body(),
  62. ),
  63. ),
  64. );
  65. }
  66. /// Handles the authentication flow once a user is authenticated.
  67. Future<void> _handleAuthenticated(
  68. BuildContext context,
  69. Authenticated authenticated,
  70. ) async {
  71. final userProfile = authenticated.userProfile;
  72. /// After a user is authenticated, this function checks if encryption is required.
  73. final result = await UserEventCheckEncryptionSign().send();
  74. result.fold(
  75. (check) async {
  76. /// If encryption is needed, the user is navigated to the encryption screen.
  77. /// Otherwise, it fetches the current workspace for the user and navigates them
  78. if (check.isNeedSecret) {
  79. getIt<AuthRouter>().pushEncryptionScreen(context, userProfile);
  80. } else {
  81. final result = await FolderEventGetCurrentWorkspace().send();
  82. result.fold(
  83. (workspaceSetting) {
  84. getIt<SplashRouter>().pushHomeScreen(
  85. context,
  86. userProfile,
  87. workspaceSetting,
  88. );
  89. },
  90. (error) => handleOpenWorkspaceError(context, error),
  91. );
  92. }
  93. },
  94. (err) {
  95. Log.error(err);
  96. },
  97. );
  98. }
  99. void _handleUnauthenticated(BuildContext context, Unauthenticated result) {
  100. Log.debug(
  101. '_handleUnauthenticated -> Supabase is enabled: $isSupabaseEnabled',
  102. );
  103. // if the env is not configured, we will skip to the 'skip login screen'.
  104. if (isSupabaseEnabled) {
  105. getIt<SplashRouter>().pushSignInScreen(context);
  106. } else {
  107. getIt<SplashRouter>().pushSkipLoginScreen(context);
  108. }
  109. }
  110. Future<void> _registerIfNeeded() async {
  111. final result = await UserEventGetUserProfile().send();
  112. if (!result.isLeft()) {
  113. await getIt<AuthService>().signUpAsGuest();
  114. }
  115. }
  116. }
  117. class Body extends StatelessWidget {
  118. const Body({super.key});
  119. @override
  120. Widget build(BuildContext context) {
  121. return Container(
  122. alignment: Alignment.center,
  123. child: PlatformExtension.isMobile
  124. ? const FlowySvg(
  125. FlowySvgs.flowy_logo_xl,
  126. blendMode: null,
  127. )
  128. : const _DesktopSplashBody(),
  129. );
  130. }
  131. }
  132. class _DesktopSplashBody extends StatelessWidget {
  133. const _DesktopSplashBody();
  134. @override
  135. Widget build(BuildContext context) {
  136. final size = MediaQuery.of(context).size;
  137. return SingleChildScrollView(
  138. child: Stack(
  139. alignment: Alignment.center,
  140. children: [
  141. Image(
  142. fit: BoxFit.cover,
  143. width: size.width,
  144. height: size.height,
  145. image: const AssetImage(
  146. 'assets/images/appflowy_launch_splash.jpg',
  147. ),
  148. ),
  149. const CircularProgressIndicator.adaptive(),
  150. ],
  151. ),
  152. );
  153. }
  154. }