From f8b6485390eb21e46e86331452f09c6c498252bf Mon Sep 17 00:00:00 2001 From: Edison Jwa Date: Wed, 20 May 2026 10:13:19 +0900 Subject: [PATCH] fix(android): show password action only for locked channels --- apps/chanora_flutter/lib/main.dart | 15 ++++++++------- apps/chanora_flutter/lib/src/rust/api.dart | 13 +++++++++++-- .../lib/src/rust/frb_generated.dart | 7 +++++-- core/chanora_core/src/lib.rs | 4 ++++ crates/chanora_bridge/src/api.rs | 3 +++ crates/chanora_bridge/src/frb_generated.rs | 4 ++++ crates/chanora_protocol/src/adapter.rs | 1 + crates/chanora_protocol/src/dto.rs | 2 ++ 8 files changed, 38 insertions(+), 11 deletions(-) diff --git a/apps/chanora_flutter/lib/main.dart b/apps/chanora_flutter/lib/main.dart index 920e925..2a0ea5a 100644 --- a/apps/chanora_flutter/lib/main.dart +++ b/apps/chanora_flutter/lib/main.dart @@ -2152,13 +2152,14 @@ class _SnapshotView extends StatelessWidget { : Row( mainAxisSize: MainAxisSize.min, children: [ - IconButton( - icon: const Icon(Icons.lock_outline), - tooltip: l10n.channelPasswordTitle, - onPressed: hasJoinPending - ? null - : () => onJoinChannelWithPassword(ch), - ), + if (ch.hasPassword) + IconButton( + icon: const Icon(Icons.lock_outline), + tooltip: l10n.channelPasswordTitle, + onPressed: hasJoinPending + ? null + : () => onJoinChannelWithPassword(ch), + ), IconButton( icon: Icon( ch.id == pendingVoiceChannelId diff --git a/apps/chanora_flutter/lib/src/rust/api.dart b/apps/chanora_flutter/lib/src/rust/api.dart index 3c208b4..573918d 100644 --- a/apps/chanora_flutter/lib/src/rust/api.dart +++ b/apps/chanora_flutter/lib/src/rust/api.dart @@ -314,16 +314,24 @@ class BridgeChannel { /// Server-side ordering hint. final PlatformInt64 order; + /// True when the server marks the channel as password-protected. + final bool hasPassword; + const BridgeChannel({ required this.id, required this.parent, required this.name, required this.order, + required this.hasPassword, }); @override int get hashCode => - id.hashCode ^ parent.hashCode ^ name.hashCode ^ order.hashCode; + id.hashCode ^ + parent.hashCode ^ + name.hashCode ^ + order.hashCode ^ + hasPassword.hashCode; @override bool operator ==(Object other) => @@ -333,7 +341,8 @@ class BridgeChannel { id == other.id && parent == other.parent && name == other.name && - order == other.order; + order == other.order && + hasPassword == other.hasPassword; } /// Client as seen by Dart. diff --git a/apps/chanora_flutter/lib/src/rust/frb_generated.dart b/apps/chanora_flutter/lib/src/rust/frb_generated.dart index c95d41f..24572d1 100644 --- a/apps/chanora_flutter/lib/src/rust/frb_generated.dart +++ b/apps/chanora_flutter/lib/src/rust/frb_generated.dart @@ -1157,13 +1157,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { BridgeChannel dco_decode_bridge_channel(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 4) - throw Exception('unexpected arr length: expect 4 but see ${arr.length}'); + if (arr.length != 5) + throw Exception('unexpected arr length: expect 5 but see ${arr.length}'); return BridgeChannel( id: dco_decode_u_64(arr[0]), parent: dco_decode_u_64(arr[1]), name: dco_decode_String(arr[2]), order: dco_decode_i_64(arr[3]), + hasPassword: dco_decode_bool(arr[4]), ); } @@ -1518,11 +1519,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_parent = sse_decode_u_64(deserializer); var var_name = sse_decode_String(deserializer); var var_order = sse_decode_i_64(deserializer); + var var_hasPassword = sse_decode_bool(deserializer); return BridgeChannel( id: var_id, parent: var_parent, name: var_name, order: var_order, + hasPassword: var_hasPassword, ); } diff --git a/core/chanora_core/src/lib.rs b/core/chanora_core/src/lib.rs index bec9344..b3d2933 100644 --- a/core/chanora_core/src/lib.rs +++ b/core/chanora_core/src/lib.rs @@ -2021,12 +2021,14 @@ mod tests { parent: chanora_protocol::ChannelId(0), name: "a".into(), order: 0, + has_password: false, }, ChannelInfo { id: chanora_protocol::ChannelId(2), parent: chanora_protocol::ChannelId(0), name: "b".into(), order: 1, + has_password: false, }, ], clients: vec![ClientInfo { @@ -2057,12 +2059,14 @@ mod tests { parent: chanora_protocol::ChannelId(0), name: "b".into(), order: 1, + has_password: false, }, ChannelInfo { id: chanora_protocol::ChannelId(1), parent: chanora_protocol::ChannelId(0), name: "a".into(), order: 0, + has_password: false, }, ], clients: vec![], diff --git a/crates/chanora_bridge/src/api.rs b/crates/chanora_bridge/src/api.rs index e802532..5a36930 100644 --- a/crates/chanora_bridge/src/api.rs +++ b/crates/chanora_bridge/src/api.rs @@ -355,6 +355,8 @@ pub struct BridgeChannel { pub name: String, /// Server-side ordering hint. pub order: i64, + /// True when the server marks the channel as password-protected. + pub has_password: bool, } /// Client as seen by Dart. @@ -406,6 +408,7 @@ impl From for BridgeSnapshot { parent: c.parent.0, name: c.name, order: c.order, + has_password: c.has_password, }) .collect(), clients: s diff --git a/crates/chanora_bridge/src/frb_generated.rs b/crates/chanora_bridge/src/frb_generated.rs index b03783b..a7b9430 100644 --- a/crates/chanora_bridge/src/frb_generated.rs +++ b/crates/chanora_bridge/src/frb_generated.rs @@ -1275,11 +1275,13 @@ impl SseDecode for crate::api::BridgeChannel { let mut var_parent = ::sse_decode(deserializer); let mut var_name = ::sse_decode(deserializer); let mut var_order = ::sse_decode(deserializer); + let mut var_has_password = ::sse_decode(deserializer); return crate::api::BridgeChannel { id: var_id, parent: var_parent, name: var_name, order: var_order, + has_password: var_has_password, }; } } @@ -1800,6 +1802,7 @@ impl flutter_rust_bridge::IntoDart for crate::api::BridgeChannel { self.parent.into_into_dart().into_dart(), self.name.into_into_dart().into_dart(), self.order.into_into_dart().into_dart(), + self.has_password.into_into_dart().into_dart(), ] .into_dart() } @@ -2180,6 +2183,7 @@ impl SseEncode for crate::api::BridgeChannel { ::sse_encode(self.parent, serializer); ::sse_encode(self.name, serializer); ::sse_encode(self.order, serializer); + ::sse_encode(self.has_password, serializer); } } diff --git a/crates/chanora_protocol/src/adapter.rs b/crates/chanora_protocol/src/adapter.rs index a5b58fd..6fd0880 100644 --- a/crates/chanora_protocol/src/adapter.rs +++ b/crates/chanora_protocol/src/adapter.rs @@ -864,6 +864,7 @@ fn build_snapshot(con: &Connection) -> Result { parent: ChannelId(c.parent.0), name: sanitize(&c.name), order: c.order.0 as i64, + has_password: c.has_password.unwrap_or(false), }) .collect(); diff --git a/crates/chanora_protocol/src/dto.rs b/crates/chanora_protocol/src/dto.rs index 6ba4e85..7002ddc 100644 --- a/crates/chanora_protocol/src/dto.rs +++ b/crates/chanora_protocol/src/dto.rs @@ -23,6 +23,8 @@ pub struct ChannelInfo { pub name: String, /// Server-side ordering hint. pub order: i64, + /// True when the server marks the channel as password-protected. + pub has_password: bool, } /// One connected client on the server.