diff --git a/cozy/.gitignore b/cozy/.gitignore new file mode 100644 index 000000000..29a3a5017 --- /dev/null +++ b/cozy/.gitignore @@ -0,0 +1,43 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/cozy/README.md b/cozy/README.md new file mode 100644 index 000000000..741a383bf --- /dev/null +++ b/cozy/README.md @@ -0,0 +1,3 @@ +# cozy + +Cozy config for Cozy integration diff --git a/cozy/analysis_options.yaml b/cozy/analysis_options.yaml new file mode 100644 index 000000000..f9b303465 --- /dev/null +++ b/cozy/analysis_options.yaml @@ -0,0 +1 @@ +include: package:flutter_lints/flutter.yaml diff --git a/cozy/lib/cozy_config_manager/cozy_config_manager.dart b/cozy/lib/cozy_config_manager/cozy_config_manager.dart new file mode 100644 index 000000000..d8503c997 --- /dev/null +++ b/cozy/lib/cozy_config_manager/cozy_config_manager.dart @@ -0,0 +1 @@ +export 'cozy_config_manager_stub.dart' if (dart.library.html) 'cozy_config_manager_web.dart'; \ No newline at end of file diff --git a/cozy/lib/cozy_config_manager/cozy_config_manager_stub.dart b/cozy/lib/cozy_config_manager/cozy_config_manager_stub.dart new file mode 100644 index 000000000..564a19713 --- /dev/null +++ b/cozy/lib/cozy_config_manager/cozy_config_manager_stub.dart @@ -0,0 +1,13 @@ +class CozyConfigManager { + Future injectCozyScript() async { + throw UnimplementedError('Cannot use injectCozyScript in non-web environment'); + } + + Future initialize() async { + throw UnimplementedError('Cannot initialize in non-web environment'); + } + + Future get isInsideCozy async { + return false; + } +} \ No newline at end of file diff --git a/cozy/lib/cozy_config_manager/cozy_config_manager_web.dart b/cozy/lib/cozy_config_manager/cozy_config_manager_web.dart new file mode 100644 index 000000000..03da73f1e --- /dev/null +++ b/cozy/lib/cozy_config_manager/cozy_config_manager_web.dart @@ -0,0 +1,23 @@ +import 'dart:async'; + +import 'package:linagora_design_flutter/cozy_config_manager/cozy_config_manager.dart' as cozy; + +class CozyConfigManager { + bool? _isInsideCozy; + + Future injectCozyScript() async { + await cozy.CozyConfigManager().injectCozyScript(); + } + + Future initialize() async { + await cozy.CozyConfigManager().initialize(); + } + + Future get isInsideCozy async { + return _isInsideCozy ??= await _checkCozyEnvironment(); + } + + Future _checkCozyEnvironment() async { + return await cozy.CozyConfigManager().isInsideCozy; + } +} \ No newline at end of file diff --git a/cozy/pubspec.lock b/cozy/pubspec.lock new file mode 100644 index 000000000..e17ce25da --- /dev/null +++ b/cozy/pubspec.lock @@ -0,0 +1,315 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + auto_size_text: + dependency: transitive + description: + name: auto_size_text + sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + equatable: + dependency: transitive + description: + name: equatable + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" + url: "https://pub.dev" + source: hosted + version: "2.0.7" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_keyboard_visibility: + dependency: transitive + description: + name: flutter_keyboard_visibility + sha256: "98664be7be0e3ffca00de50f7f6a287ab62c763fc8c762e0a21584584a3ff4f8" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + flutter_keyboard_visibility_linux: + dependency: transitive + description: + name: flutter_keyboard_visibility_linux + sha256: "6fba7cd9bb033b6ddd8c2beb4c99ad02d728f1e6e6d9b9446667398b2ac39f08" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + flutter_keyboard_visibility_macos: + dependency: transitive + description: + name: flutter_keyboard_visibility_macos + sha256: c5c49b16fff453dfdafdc16f26bdd8fb8d55812a1d50b0ce25fc8d9f2e53d086 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + flutter_keyboard_visibility_platform_interface: + dependency: transitive + description: + name: flutter_keyboard_visibility_platform_interface + sha256: e43a89845873f7be10cb3884345ceb9aebf00a659f479d1c8f4293fcb37022a4 + url: "https://pub.dev" + source: hosted + version: "2.0.0" + flutter_keyboard_visibility_web: + dependency: transitive + description: + name: flutter_keyboard_visibility_web + sha256: d3771a2e752880c79203f8d80658401d0c998e4183edca05a149f5098ce6e3d1 + url: "https://pub.dev" + source: hosted + version: "2.0.0" + flutter_keyboard_visibility_windows: + dependency: transitive + description: + name: flutter_keyboard_visibility_windows + sha256: fc4b0f0b6be9b93ae527f3d527fb56ee2d918cd88bbca438c478af7bcfd0ef73 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + url: "https://pub.dev" + source: hosted + version: "10.0.5" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + url: "https://pub.dev" + source: hosted + version: "3.0.5" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + linagora_design_flutter: + dependency: "direct main" + description: + path: "." + ref: master + resolved-ref: "083cbf597cce8bedcc7ccda76a0c49eb06a71e9e" + url: "https://github.com/linagora/linagora-design-flutter.git" + source: git + version: "0.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + url: "https://pub.dev" + source: hosted + version: "1.15.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4" + url: "https://pub.dev" + source: hosted + version: "3.12.0" + photo_manager: + dependency: transitive + description: + name: photo_manager + sha256: "0bc7548fd3111eb93a3b0abf1c57364e40aeda32512c100085a48dade60e574f" + url: "https://pub.dev" + source: hosted + version: "3.6.4" + photo_manager_image_provider: + dependency: transitive + description: + name: photo_manager_image_provider + sha256: b6015b67b32f345f57cf32c126f871bced2501236c405aafaefa885f7c821e4f + url: "https://pub.dev" + source: hosted + version: "2.2.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + url: "https://pub.dev" + source: hosted + version: "14.2.5" +sdks: + dart: ">=3.5.4 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/cozy/pubspec.yaml b/cozy/pubspec.yaml new file mode 100644 index 000000000..22730da58 --- /dev/null +++ b/cozy/pubspec.yaml @@ -0,0 +1,24 @@ +name: cozy +description: "Cozy integration module" +publish_to: 'none' +version: 0.1.0 + +environment: + sdk: ^3.5.4 + +dependencies: + flutter: + sdk: flutter + + linagora_design_flutter: + git: + url: https://github.com/linagora/linagora-design-flutter.git + ref: master + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^4.0.0 + +flutter: + uses-material-design: true diff --git a/docs/adr/0060-cozy-integration-env.md b/docs/adr/0060-cozy-integration-env.md new file mode 100644 index 000000000..8c0c291ee --- /dev/null +++ b/docs/adr/0060-cozy-integration-env.md @@ -0,0 +1,21 @@ +# 60. Cozy integration env + +Date: 2025-04-15 + +## Status + +Accepted + +## Context + +- Cozy script needs to be loaded optionally + +## Decision + +- `COZY_INTEGRATION` is added to env + - Set it to `true` if you want to load cozy script + - Set it to `false` or left it as is if you don't want to load cozy script + +## Consequences + +- Cozy script can be loaded optionally diff --git a/docs/adr/0061-cozy-integration-set-up.md b/docs/adr/0061-cozy-integration-set-up.md new file mode 100644 index 000000000..2f9a2053b --- /dev/null +++ b/docs/adr/0061-cozy-integration-set-up.md @@ -0,0 +1,72 @@ +# 61. Cozy integration set up + +Date: 2025-04-21 + +## Status + +Accepted + +## Context + +- How to run Cozy locally? + +## Steps +### Install cozy-stack CLI + +1. Install CouchDB +```bash +docker run -d \ + --name cozy-stack-couch \ + -p 5984:5984 \ + -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password \ + -v $HOME/.cozy-stack-couch:/opt/couchdb/data \ + couchdb:3.3 +curl -X PUT http://admin:password@127.0.0.1:5984/{_users,_replicator} +``` +2. Install cozy-stack CLI +- Install Go and add to path +- Clone cozy-stack +```bash +git clone git@github.com:cozy/cozy-stack.git +cd cozy-stack +make +``` +- Create cozy configuration file +```bash +mkdir -p ~/.cozy +cp cozy.example.yaml $HOME/.cozy/cozy.yml +``` +- Find CouchDB config inside `cozy.yml` and edit as follow: +```yaml +couchdb: + url: +``` +3. Run cozy-stack +- Start cozy-stack +```bash +cozy-stack serve +``` +- Create an instance +```bash +cozy-stack instances add tmail.localhost:8080 --passphrase cozy --apps home,store,drive,photos,settings,contacts,notes,passwords --email tmail@cozy.localhost --locale en --public-name Tmail --context-name dev +``` +- The created instance will be available at http://tmail.localhost:8080/ +- The password is `cozy` + +### Run Cozy locally +1. On the cozy-twakemail side +- Clone `https://github.com/cozy/cozy-twakemail` +- `yarn install` +- In `src/components/AppLayout.jsx`, replace `flag('mail.embedded-app-url')` with `http://localhost:2023` +- `yarn build` +- `cozy-stack serve --appdir tmail:build/ --disable-csp` + +2. On the tmail side +- Config tmail to run on basic auth +- isInsideCozy will be `false` if run on localhost, so in cozy_config_web.dart, return true on isInsideCozy +- in `env.file`, COZY_INTEGRATION=true +- `flutter run -d chrome --web-port 2023 --web-browser-flag "--disable-web-security" --profile` + +3. Access Cozy +- tmail cozy: http://tmail.localhost:8080/ +- The password is `cozy` \ No newline at end of file diff --git a/docs/configuration/cozy_integration_configuration.md b/docs/configuration/cozy_integration_configuration.md new file mode 100644 index 000000000..627f8e7f8 --- /dev/null +++ b/docs/configuration/cozy_integration_configuration.md @@ -0,0 +1,12 @@ +## Configuration Cozy Integration + +### Context +- Cozy script load is optional +### How to config +In [env.file]: +- If you want to load Cozy script: +```COZY_INTEGRATION=true``` +- If you don't want to load Cozy script: +```COZY_INTEGRATION=false``` + or +```COZY_INTEGRATION=``` \ No newline at end of file diff --git a/env.file b/env.file index a072f6896..77b662611 100644 --- a/env.file +++ b/env.file @@ -7,4 +7,5 @@ FCM_AVAILABLE=supported IOS_FCM=supported FORWARD_WARNING_MESSAGE= PLATFORM=other -WS_ECHO_PING= \ No newline at end of file +WS_ECHO_PING= +COZY_INTEGRATION= \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index bb5eb473b..ed342e4a1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -17,6 +17,7 @@ import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/utils/app_utils.dart'; import 'package:tmail_ui_user/main/utils/asset_preloader.dart'; +import 'package:tmail_ui_user/main/utils/cozy_integration.dart'; import 'package:url_strategy/url_strategy.dart'; import 'package:worker_manager/worker_manager.dart'; @@ -31,13 +32,14 @@ Future runTmail() async { ThemeUtils.setSystemLightUIStyle(); await Future.wait([ - MainBindings().dependencies(), - HiveCacheConfig.instance.setUp(), - Executor().warmUp(log: BuildUtils.isDebugMode), - AppUtils.loadEnvFile(), - if (PlatformInfo.isWeb) - AssetPreloader.preloadHtmlEditorAssets(), + MainBindings().dependencies(), + HiveCacheConfig.instance.setUp(), + Executor().warmUp(log: BuildUtils.isDebugMode), + AppUtils.loadEnvFile(), + if (PlatformInfo.isWeb) + AssetPreloader.preloadHtmlEditorAssets(), ]); + await CozyIntegration.integrateCozy(); await HiveCacheConfig.instance.initializeEncryptionKey(); setPathUrlStrategy(); diff --git a/lib/main/utils/app_config.dart b/lib/main/utils/app_config.dart index 2efb63681..a5b3e5526 100644 --- a/lib/main/utils/app_config.dart +++ b/lib/main/utils/app_config.dart @@ -78,4 +78,6 @@ class AppConfig { static bool get isSaasPlatForm => _platformEnv.toLowerCase() == saasPlatform; static bool get isWebSocketEchoPingEnabled => dotenv.get('WS_ECHO_PING', fallback: 'false') == 'true'; + + static bool get isCozyIntegrationEnabled => dotenv.get('COZY_INTEGRATION', fallback: 'false') == 'true'; } \ No newline at end of file diff --git a/lib/main/utils/cozy_integration.dart b/lib/main/utils/cozy_integration.dart new file mode 100644 index 000000000..e2a78de4b --- /dev/null +++ b/lib/main/utils/cozy_integration.dart @@ -0,0 +1,20 @@ +import 'package:core/utils/app_logger.dart'; +import 'package:core/utils/platform_info.dart'; +import 'package:cozy/cozy_config_manager/cozy_config_manager.dart'; +import 'package:tmail_ui_user/main/utils/app_config.dart'; + +class CozyIntegration { + const CozyIntegration._(); + + static Future integrateCozy() async { + if (!PlatformInfo.isWeb || !AppConfig.isCozyIntegrationEnabled) return; + + try { + final cozyConfig = CozyConfigManager(); + await cozyConfig.injectCozyScript(); + await cozyConfig.initialize(); + } catch (e) { + logError('CozyIntegration::integrateCozy:Exception = $e'); + } + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 917cef4ac..10b25bbed 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -312,6 +312,13 @@ packages: relative: true source: path version: "1.0.0+1" + cozy: + dependency: "direct main" + description: + path: cozy + relative: true + source: path + version: "0.1.0" cross_file: dependency: transitive description: @@ -1399,7 +1406,7 @@ packages: description: path: "." ref: master - resolved-ref: "0f666ca14fd4f0710775ca3de03cde73b21de7d6" + resolved-ref: "083cbf597cce8bedcc7ccda76a0c49eb06a71e9e" url: "https://github.com/linagora/linagora-design-flutter.git" source: git version: "0.0.1" diff --git a/pubspec.yaml b/pubspec.yaml index 1b2146bc6..ed5938942 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,6 +51,9 @@ dependencies: server_settings: path: server_settings + cozy: + path: cozy + ### Dependencies from git ### rich_text_composer: git: diff --git a/scripts/prebuild.sh b/scripts/prebuild.sh index d8ed0dde0..f7d32bee4 100755 --- a/scripts/prebuild.sh +++ b/scripts/prebuild.sh @@ -15,6 +15,11 @@ for mod in "${modules[@]}"; do ) done +# For Cozy +cd cozy +flutter pub get +cd ../ + # For the parent module flutter pub get dart run build_runner build --delete-conflicting-outputs &&