3fda5649c3
Build Docker images with Sentry / Build release image with Sentry (push) Failing after 1m59s
Release / Release (android, ubuntu-latest) (push) Failing after 38m31s
Release / Release (ios, macos-14) (push) Has been cancelled
Integration tests / Run integration tests for mobile apps (push) Failing after 15s
Build dev binaries / Build app (ios, macos-14) (push) Waiting to run
Build dev binaries / Build app (android, ubuntu-latest) (push) Failing after 34m13s
Tier 1+2 - web/UI user-facing:
- Replace all visible "Twake Mail" / "Linagora" with "Workavia Mail"
- Swap 3 logo SVGs (icon kept, wordmark replaced via SVG <text>)
- Splash: replace icon_twp.png with HTML <div>Workavia</div>
- Remove power_by_linagora.svg, replace with AGPL SourceLinkWidget
- Patch web/index.html, manifest.json, login/logout-callback HTMLs
- Patch all 16 ARB locales + scribe ARB + app_localizations.dart
- Patch 4 web/i18n/*.json (FR/EN/DE/VI smart banner titles)
- App grid reduced to "Mail" + "Calendar"
- User-Agent: Twake-Mail -> Workavia-Mail
Tier 3+4 - mobile native:
- Bundle ID: com.linagora.{android,ios}.teammail -> com.workavia.mail
- URL schemes consolidated: teammail.mobile/twakemail.mobile -> workaviamail.mobile
- Android: applicationId, namespace, AndroidManifest, strings.xml
- Android: move Kotlin sources com.linagora.android.tmail -> com.workavia.mail
- Android: empty branding.xml + android12branding.xml (vector text not patchable)
- iOS: Info.plist (CFBundleDisplayName/Name, schemes), Runner.entitlements
- iOS: TwakeMailNSE + TeamMailShareExtension entitlements + Info.plist
- iOS: Runner.xcodeproj (12x PRODUCT_BUNDLE_IDENTIFIER, 9x APP_GROUP_ID)
- iOS: TokenRefreshManager.swift redirect URLs
- iOS: fastlane Appfile + Matchfile
- Dart: oidc_constant, app_config keychain, notification MethodChannel
Known follow-ups (require user action):
- Apple Team ID KUT463DS29 (still Linagora) -> needs Workavia team for App Store
- Android branding.xml emptied: need PNG/path-converted wordmark for splash
- iOS sub-bundle paths (TwakeMailNSE/, TeamMailShareExtension/) on disk unchanged
- iOS App Store URL emptied; Play Store ID points to com.workavia.mail
281 lines
11 KiB
Dart
281 lines
11 KiB
Dart
import 'package:core/presentation/extensions/color_extension.dart';
|
|
import 'package:core/presentation/state/success.dart';
|
|
import 'package:core/presentation/utils/theme_utils.dart';
|
|
import 'package:core/presentation/views/responsive/responsive_widget.dart';
|
|
import 'package:core/presentation/views/text/slogan_builder.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
|
import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart';
|
|
import 'package:tmail_ui_user/features/base/widget/application_logo_with_text_widget.dart';
|
|
import 'package:tmail_ui_user/features/base/widget/application_version_widget.dart';
|
|
import 'package:tmail_ui_user/features/login/presentation/base_login_view.dart';
|
|
import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
|
|
import 'package:tmail_ui_user/features/login/presentation/privacy_link_widget.dart';
|
|
import 'package:tmail_ui_user/features/login/presentation/source_link_widget.dart';
|
|
import 'package:tmail_ui_user/features/login/presentation/widgets/login_message_widget.dart';
|
|
import 'package:tmail_ui_user/features/login/presentation/widgets/try_again_button.dart';
|
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
|
|
|
class LoginView extends BaseLoginView {
|
|
|
|
const LoginView({Key? key}) : super(key: key);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: AppColor.primaryLightColor,
|
|
body: Center(child: SingleChildScrollView(
|
|
child: ResponsiveWidget(
|
|
responsiveUtils: controller.responsiveUtils,
|
|
mobile: _buildMobileForm(context),
|
|
desktop: _buildWebForm(context),
|
|
))),
|
|
);
|
|
}
|
|
|
|
Widget _buildMobileForm(BuildContext context) {
|
|
return Stack(
|
|
children: [
|
|
ConstrainedBox(
|
|
constraints: const BoxConstraints(minWidth: 200, maxWidth: 720, minHeight: 720),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.max,
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 67),
|
|
child: ApplicationLogoWidthTextWidget()
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 67),
|
|
child: Text(
|
|
AppLocalizations.of(context).signIn,
|
|
style: ThemeUtils.defaultTextStyleInterFont.copyWith(fontSize: 32, color: AppColor.colorNameEmail, fontWeight: FontWeight.w900)
|
|
)
|
|
),
|
|
Obx(() => LoginMessageWidget(
|
|
formType: controller.loginFormType.value,
|
|
viewState: controller.viewState.value,
|
|
)),
|
|
Obx(() {
|
|
switch (controller.loginFormType.value) {
|
|
case LoginFormType.credentialForm:
|
|
return buildInputCredentialForm(context);
|
|
case LoginFormType.retry:
|
|
return TryAgainButton(
|
|
onRetry: controller.retryCheckOidc,
|
|
responsiveUtils: controller.responsiveUtils,
|
|
);
|
|
default:
|
|
return const SizedBox.shrink();
|
|
}
|
|
}),
|
|
_buildLoadingProgress(context),
|
|
const Padding(
|
|
padding: EdgeInsets.only(top: 16),
|
|
child: PrivacyLinkWidget(),
|
|
),
|
|
const ApplicationVersionWidget(padding: EdgeInsets.only(top: 8)),
|
|
],
|
|
)
|
|
),
|
|
const Positioned.fill(
|
|
bottom: 24,
|
|
child: Align(
|
|
alignment: Alignment.bottomCenter,
|
|
child: SourceLinkWidget()))
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildWebForm(BuildContext context) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(top: 60, bottom: 60),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.only(right: 86),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
mainAxisSize: MainAxisSize.max,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
AppLocalizations.of(context).jmapBasedMailSolution,
|
|
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
|
|
fontSize: 36,
|
|
color: AppColor.colorNameEmail,
|
|
fontWeight: FontWeight.w900,
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 24),
|
|
child: SloganBuilder(
|
|
arrangedByHorizontal: true,
|
|
logo: controller.imagePaths.icJMAPStandard,
|
|
sizeLogo: 48.0,
|
|
paddingText: const EdgeInsets.only(left: 12),
|
|
text: AppLocalizations.of(context).jmapStandard,
|
|
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
|
|
fontSize: 24,
|
|
fontWeight: FontWeight.w400,
|
|
color: AppColor.colorNameEmail,
|
|
),
|
|
)
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 16),
|
|
child: SloganBuilder(
|
|
arrangedByHorizontal: true,
|
|
logo: controller.imagePaths.icEncrypted,
|
|
sizeLogo: 48.0,
|
|
paddingText: const EdgeInsets.only(left: 12),
|
|
text: AppLocalizations.of(context).encryptedMailbox,
|
|
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
|
|
fontSize: 24,
|
|
fontWeight: FontWeight.w400,
|
|
color: AppColor.colorNameEmail,
|
|
),
|
|
)
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 16),
|
|
child: SloganBuilder(
|
|
arrangedByHorizontal: true,
|
|
logo: controller.imagePaths.icTeam,
|
|
sizeLogo: 48.0,
|
|
paddingText: const EdgeInsets.only(left: 12),
|
|
text: AppLocalizations.of(context).manageEmailAsATeam,
|
|
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
|
|
fontSize: 24,
|
|
fontWeight: FontWeight.w400,
|
|
color: AppColor.colorNameEmail,
|
|
),
|
|
)
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 16),
|
|
child: SloganBuilder(
|
|
arrangedByHorizontal: true,
|
|
logo: controller.imagePaths.icIntegration,
|
|
sizeLogo: 48.0,
|
|
paddingText: const EdgeInsets.only(left: 12),
|
|
text: AppLocalizations.of(context).multipleIntegrations,
|
|
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
|
|
fontSize: 24,
|
|
fontWeight: FontWeight.w400,
|
|
color: AppColor.colorNameEmail,
|
|
),
|
|
)
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 44),
|
|
child: SvgPicture.asset(
|
|
controller.imagePaths.icLoginGraphic,
|
|
fit: BoxFit.fill,
|
|
alignment: Alignment.center
|
|
)
|
|
)
|
|
],
|
|
)
|
|
),
|
|
Column(
|
|
children: [
|
|
Container(
|
|
height: 684,
|
|
width: 458,
|
|
padding: const EdgeInsets.symmetric(horizontal: 31),
|
|
clipBehavior: Clip.antiAlias,
|
|
decoration: const ShapeDecoration(
|
|
color: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.all(Radius.circular(20)),
|
|
),
|
|
shadows: [
|
|
BoxShadow(
|
|
color: AppColor.loginViewShadowColor,
|
|
blurRadius: 40,
|
|
offset: Offset(0, 2),
|
|
spreadRadius: 2,
|
|
)
|
|
],
|
|
),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 66),
|
|
child: ApplicationLogoWidthTextWidget()
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 67),
|
|
child: Text(
|
|
AppLocalizations.of(context).signIn,
|
|
style: ThemeUtils.defaultTextStyleInterFont.copyWith(fontSize: 32, color: AppColor.colorNameEmail, fontWeight: FontWeight.w900)
|
|
)
|
|
),
|
|
Obx(() => LoginMessageWidget(
|
|
formType: controller.loginFormType.value,
|
|
viewState: controller.viewState.value,
|
|
)),
|
|
Obx(() {
|
|
switch (controller.loginFormType.value) {
|
|
case LoginFormType.credentialForm:
|
|
return buildInputCredentialForm(context);
|
|
case LoginFormType.retry:
|
|
return TryAgainButton(
|
|
onRetry: controller.retryCheckOidc,
|
|
responsiveUtils: controller.responsiveUtils,
|
|
);
|
|
default:
|
|
return const SizedBox.shrink();
|
|
}
|
|
}),
|
|
_buildLoadingProgress(context),
|
|
const Padding(
|
|
padding: EdgeInsets.only(top: 16),
|
|
child: PrivacyLinkWidget()
|
|
),
|
|
const ApplicationVersionWidget(padding: EdgeInsets.only(top: 8)),
|
|
],
|
|
)
|
|
),
|
|
const Padding(
|
|
padding: EdgeInsets.only(top: 44, bottom: 10),
|
|
child: SourceLinkWidget()
|
|
)
|
|
]
|
|
)
|
|
],
|
|
)
|
|
);
|
|
}
|
|
|
|
Widget _buildLoadingProgress(BuildContext context) {
|
|
return Obx(() => controller.viewState.value.fold(
|
|
(failure) {
|
|
switch (controller.loginFormType.value) {
|
|
case LoginFormType.credentialForm:
|
|
return buildLoginButton(context);
|
|
default:
|
|
return const SizedBox.shrink();
|
|
}
|
|
},
|
|
(success) {
|
|
if (success is LoadingState) {
|
|
return buildLoadingCircularProgress();
|
|
} else {
|
|
switch (controller.loginFormType.value) {
|
|
case LoginFormType.credentialForm:
|
|
return buildLoginButton(context);
|
|
default:
|
|
return const SizedBox.shrink();
|
|
}
|
|
}
|
|
}
|
|
));
|
|
}
|
|
} |