Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion lib/config/app_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ abstract class AppConfig {
static const String sourceCodeUrl =
'https://github.com/linagora/twake-on-matrix';
static String supportUrl = 'https://twake.app/support';
static String cozyExternalBridgeVersion = '0.16.1';
static String cozyExternalBridgeVersion = '1.2.1';
static List<String> cozyExternalBridgeAllowlist = ['.twake.app', '.lin-saas.com','.lin-saas.dev'];
static bool renderHtml = true;
static bool hideRedactedEvents = false;
static bool hideUnknownEvents = true;
Expand Down Expand Up @@ -193,6 +194,11 @@ abstract class AppConfig {
defaultValue: ConfigurationSaas.homeserver,
);

static const String _cozyExternalBridgeAllowlistEnv = String.fromEnvironment(
'COZY_EXTERNAL_BRIDGE_ALLOWLIST',
defaultValue: '.twake.app,.lin-saas.com,.lin-saas.dev',
Comment on lines 102 to +199
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A const should be defined for these default values, for reuse.

);

static void loadEnvironment() {
twakeWorkplaceHomeserver = _twakeWorkplaceHomeserverEnv;

Expand All @@ -213,6 +219,14 @@ abstract class AppConfig {
homeserver = _homeserverEnv;

Logs().i('[Public Platform] AppConfig():: HOME_SERVER $_homeserverEnv');

cozyExternalBridgeAllowlist = _cozyExternalBridgeAllowlistEnv
.split(',')
.map((i) => i.trim())
.where((i) => i.isNotEmpty)
.toList();

Logs().i('[Public Platform] AppConfig():: COZY_EXTERNAL_BRIDGE_ALLOWLIST $_cozyExternalBridgeAllowlistEnv');
}

static bool get isSaasPlatForm => _platformEnv == 'saas';
Expand Down Expand Up @@ -302,5 +316,11 @@ abstract class AppConfig {
json['cozy_external_bridge_version'].isNotEmpty) {
cozyExternalBridgeVersion = json['cozy_external_bridge_version'];
}
if (json['cozy_external_bridge_allowlist'] is List &&
json['cozy_external_bridge_allowlist'].isNotEmpty) {
cozyExternalBridgeAllowlist = json['cozy_external_bridge_allowlist']
.whereType<String>()
.toList();
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goal is to be able to add new approved domain name for bridge in env var or in config.json. It will help us:

  • ease development experience by adding for example in config.json 'localhost:8080'
  • allow to deploy tchat in a container for any domain name

However, for config.json, it seems that it is loaded after CozyBridge initialization. Any thought on this?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a method to initialize config early, put only the cozy config initialization there, then call as soon as the app start.

}
}
2 changes: 1 addition & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ void main() async {
if (PlatformInfos.isWeb) {
CozyConfigManager()
.injectCozyScript(AppConfig.cozyExternalBridgeVersion)
.then((value) => CozyConfigManager().initialize());
.then((value) => CozyConfigManager().initialize(validUrlSuffixes: AppConfig.cozyExternalBridgeAllowlist));
}
Comment on lines 68 to 71
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

CozyConfigManager().initialize() is fire-and-forget — race condition with initial widget builds.

injectCozyScript(...).then(initialize) is not awaited. startGui(clients) executes immediately after, so isInContainer can be false during the first widget tree render (including AdaptiveScaffoldAppBar). Unlike the previous FutureBuilder(isInsideCozy) approach, which automatically rebuilt when the future resolved, the new synchronous isInContainer reads have no rebuild trigger, meaning the app bar may remain visible inside a Cozy container for the entire session if no parent rebuild occurs.

🐛 Proposed fix — await the initialization chain before rendering
   if (PlatformInfos.isWeb) {
-    CozyConfigManager()
-        .injectCozyScript(AppConfig.cozyExternalBridgeVersion)
-        .then((value) => CozyConfigManager().initialize(validUrlSuffixes: AppConfig.cozyExternalBridgeAllowlist));
+    await CozyConfigManager()
+        .injectCozyScript(AppConfig.cozyExternalBridgeVersion)
+        .then((_) => CozyConfigManager().initialize(
+              validUrlSuffixes: AppConfig.cozyExternalBridgeAllowlist,
+            ));
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/main.dart` around lines 68 - 71, The app is starting the UI before
CozyConfigManager finishes setup, causing isInContainer reads (used by
AdaptiveScaffoldAppBar) to be stale; change the startup sequence so
injectCozyScript(...) completes and CozyConfigManager().initialize(...) is
awaited before calling startGui(clients). Locate the chain that calls
CozyConfigManager().injectCozyScript and CozyConfigManager().initialize and make
startGui(clients) run only after the Future returned by
injectCozyScript().then(...) resolves (or use async/await), ensuring
isInContainer is set before the first widget build.


// Started in foreground mode.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,11 @@ class _AdaptiveScaffoldAppBarState extends State<AdaptiveScaffoldAppBar> {
},
);

return FutureBuilder(
future: CozyConfigManager().isInsideCozy,
builder: (context, snapshot) {
if (!snapshot.hasData || !snapshot.data!) return child;
if(CozyConfigManager().isInContainer) {
return const SizedBox();
}

return const SizedBox();
},
);
return child;
Comment on lines +53 to +57
Copy link

@coderabbitai coderabbitai bot Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for CozyConfigManager class definition
fd "cozy_config_manager" --type f

Repository: linagora/twake-on-matrix

Length of output: 50


🏁 Script executed:

#!/bin/bash
# Search for CozyConfigManager class definition and factory constructor
rg -n "class CozyConfigManager" --type dart -A 10

Repository: linagora/twake-on-matrix

Length of output: 50


🏁 Script executed:

#!/bin/bash
# Search for isInContainer property and initialize method
rg -n "isInContainer" --type dart -B 2 -A 2

Repository: linagora/twake-on-matrix

Length of output: 1478


🏁 Script executed:

#!/bin/bash
# Search for initialize method in CozyConfigManager context
rg -n "initialize\(\)" --type dart -B 2 -A 5 | head -100

Repository: linagora/twake-on-matrix

Length of output: 4901


🏁 Script executed:

#!/bin/bash
# Find where CozyConfigManager is imported from
rg -n "import.*[Cc]ozy|from.*cozy" --type dart | head -20

Repository: linagora/twake-on-matrix

Length of output: 422


🏁 Script executed:

#!/bin/bash
# Search for CozyConfigManager.initialize() calls
rg -n "CozyConfigManager\(\)\.initialize" --type dart -B 2 -A 2

Repository: linagora/twake-on-matrix

Length of output: 359


🏁 Script executed:

#!/bin/bash
# Search main.dart for initialize setup
fd "main.dart" --type f

Repository: linagora/twake-on-matrix

Length of output: 81


🏁 Script executed:

#!/bin/bash
# Read main.dart to understand the initialization sequence and race condition
cat -n lib/main.dart | sed -n '50,100p'

Repository: linagora/twake-on-matrix

Length of output: 2355


Race condition in main.dart: CozyConfigManager().initialize() is not awaited before GUI rendering.

The initialization chain on lines 68–70 of main.dart is fire-and-forget (chained with .then()), while startGui() on line 77 is called immediately without waiting. This means the widget at line 53–57 will read CozyConfigManager().isInContainer during the first build, but the initialize() call likely hasn't completed yet, causing the check to read an uninitialized value.

Fix: Add await to the initialization chain in main.dart:

if (PlatformInfos.isWeb) {
  await CozyConfigManager()
      .injectCozyScript(AppConfig.cozyExternalBridgeVersion)
      .then((value) => CozyConfigManager().initialize(validUrlSuffixes: AppConfig.cozyExternalBridgeAllowlist));
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/widgets/layouts/adaptive_layout/adaptive_scaffold_appbar.dart` around
lines 53 - 57, The code races because CozyConfigManager().initialize(...)
(called via .then()) isn’t awaited before startGui() runs, so
adaptive_scaffold_appbar's check CozyConfigManager().isInContainer can read an
uninitialized value; fix by awaiting the initialization sequence on the
PlatformInfos.isWeb branch — await
CozyConfigManager().injectCozyScript(AppConfig.cozyExternalBridgeVersion) and
then await CozyConfigManager().initialize(validUrlSuffixes:
AppConfig.cozyExternalBridgeAllowlist) (instead of chaining with .then()), and
only call startGui() after those awaits complete.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hoangdat I agree it is an issue but I suppose it was built on purpose to not await it right? Any thoughts?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

}
}

Expand Down
4 changes: 2 additions & 2 deletions lib/widgets/local_notifications_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ extension LocalNotificationsExtension on MatrixState {
method: ThumbnailMethod.crop,
);
if (kIsWeb) {
final isInsideCozy = await CozyConfigManager().isInsideCozy;
final isInContainer = CozyConfigManager().isInContainer;
_audioPlayer.play();
if (isInsideCozy) {
if (isInContainer) {
CozyConfigManager().sendNotification(title, body);
return;
}
Expand Down
4 changes: 2 additions & 2 deletions lib/widgets/matrix.dart
Original file line number Diff line number Diff line change
Expand Up @@ -528,8 +528,8 @@ class MatrixState extends State<Matrix>

Future<void> _requestNotificationPermission() async {
try {
final isInsideCozy = await CozyConfigManager().isInsideCozy;
if (isInsideCozy) {
final isInContainer = CozyConfigManager().isInContainer;
if (isInContainer) {
CozyConfigManager().requestNotificationPermission();
} else {
html.Notification.requestPermission();
Expand Down
4 changes: 2 additions & 2 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1745,8 +1745,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: master
resolved-ref: "66d09a271f20243badd92352a8bb5866b430020d"
ref: "feat/update-cozy-external-bridge-setup"
resolved-ref: "4331015b1f10c4b2033493e756ebf86398185d8d"
url: "git@github.com:linagora/linagora-design-flutter.git"
source: git
version: "0.0.1"
Expand Down
8 changes: 7 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ dependencies:
linagora_design_flutter:
git:
url: git@github.com:linagora/linagora-design-flutter.git
ref: master
ref: feat/update-cozy-external-bridge-setup
flutter_matrix_html:
git:
url: https://github.com/linagora/flutter_matrix_html.git
Expand Down Expand Up @@ -327,6 +327,12 @@ dependency_overrides:
git:
url: https://github.com/linagora/overflow_view.git
ref: master

# Fix dependency conflict between flutter_emoji_mart and linagora_design_flutter
linagora_design_flutter:
git:
url: git@github.com:linagora/linagora-design-flutter.git
ref: feat/update-cozy-external-bridge-setup

cider:
link_template:
Expand Down
Loading