From 76c1e8a7f5206d572fca898036b245e15d30f94e Mon Sep 17 00:00:00 2001
From: chimnayajith <chinmaajith30@gmail.com>
Date: Thu, 30 Jan 2025 16:31:56 +0530
Subject: [PATCH] home: Display current organization's name and icon atop menu

Fixes #1037
---
 assets/l10n/app_en.arb                        |  4 ++
 lib/generated/l10n/zulip_localizations.dart   |  6 ++
 .../l10n/zulip_localizations_ar.dart          |  3 +
 .../l10n/zulip_localizations_en.dart          |  3 +
 .../l10n/zulip_localizations_ja.dart          |  3 +
 .../l10n/zulip_localizations_nb.dart          |  3 +
 .../l10n/zulip_localizations_pl.dart          |  3 +
 .../l10n/zulip_localizations_ru.dart          |  3 +
 .../l10n/zulip_localizations_sk.dart          |  3 +
 lib/widgets/home.dart                         | 67 +++++++++++++++++++
 test/widgets/home_test.dart                   | 17 +++++
 11 files changed, 115 insertions(+)

diff --git a/assets/l10n/app_en.arb b/assets/l10n/app_en.arb
index 10600275530..740ba114d0f 100644
--- a/assets/l10n/app_en.arb
+++ b/assets/l10n/app_en.arb
@@ -23,6 +23,10 @@
   "@switchAccountButton": {
     "description": "Label for main-menu button leading to the choose-account page."
   },
+  "organizationsButtonLabel": "Organizations",
+  "@organizationsButtonLabel": {
+    "description": "Button text to view and switch between different organizations."
+  },
   "tryAnotherAccountMessage": "Your account at {url} is taking a while to load.",
   "@tryAnotherAccountMessage": {
     "description": "Message that appears on the loading screen after waiting for some time.",
diff --git a/lib/generated/l10n/zulip_localizations.dart b/lib/generated/l10n/zulip_localizations.dart
index 501eb577bfa..ebd3c3fad66 100644
--- a/lib/generated/l10n/zulip_localizations.dart
+++ b/lib/generated/l10n/zulip_localizations.dart
@@ -141,6 +141,12 @@ abstract class ZulipLocalizations {
   /// **'Switch account'**
   String get switchAccountButton;
 
+  /// Button text to view and switch between different organizations.
+  ///
+  /// In en, this message translates to:
+  /// **'Organizations'**
+  String get organizationsButtonLabel;
+
   /// Message that appears on the loading screen after waiting for some time.
   ///
   /// In en, this message translates to:
diff --git a/lib/generated/l10n/zulip_localizations_ar.dart b/lib/generated/l10n/zulip_localizations_ar.dart
index 721b20ac02c..d676dd168ce 100644
--- a/lib/generated/l10n/zulip_localizations_ar.dart
+++ b/lib/generated/l10n/zulip_localizations_ar.dart
@@ -26,6 +26,9 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
   @override
   String get switchAccountButton => 'Switch account';
 
+  @override
+  String get organizationsButtonLabel => 'Organizations';
+
   @override
   String tryAnotherAccountMessage(Object url) {
     return 'Your account at $url is taking a while to load.';
diff --git a/lib/generated/l10n/zulip_localizations_en.dart b/lib/generated/l10n/zulip_localizations_en.dart
index 6936cfe7364..36c7ab32de6 100644
--- a/lib/generated/l10n/zulip_localizations_en.dart
+++ b/lib/generated/l10n/zulip_localizations_en.dart
@@ -26,6 +26,9 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
   @override
   String get switchAccountButton => 'Switch account';
 
+  @override
+  String get organizationsButtonLabel => 'Organizations';
+
   @override
   String tryAnotherAccountMessage(Object url) {
     return 'Your account at $url is taking a while to load.';
diff --git a/lib/generated/l10n/zulip_localizations_ja.dart b/lib/generated/l10n/zulip_localizations_ja.dart
index c4314716454..aae509a0ac2 100644
--- a/lib/generated/l10n/zulip_localizations_ja.dart
+++ b/lib/generated/l10n/zulip_localizations_ja.dart
@@ -26,6 +26,9 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
   @override
   String get switchAccountButton => 'Switch account';
 
+  @override
+  String get organizationsButtonLabel => 'Organizations';
+
   @override
   String tryAnotherAccountMessage(Object url) {
     return 'Your account at $url is taking a while to load.';
diff --git a/lib/generated/l10n/zulip_localizations_nb.dart b/lib/generated/l10n/zulip_localizations_nb.dart
index fc530fccaaa..f46f072d084 100644
--- a/lib/generated/l10n/zulip_localizations_nb.dart
+++ b/lib/generated/l10n/zulip_localizations_nb.dart
@@ -26,6 +26,9 @@ class ZulipLocalizationsNb extends ZulipLocalizations {
   @override
   String get switchAccountButton => 'Switch account';
 
+  @override
+  String get organizationsButtonLabel => 'Organizations';
+
   @override
   String tryAnotherAccountMessage(Object url) {
     return 'Your account at $url is taking a while to load.';
diff --git a/lib/generated/l10n/zulip_localizations_pl.dart b/lib/generated/l10n/zulip_localizations_pl.dart
index f817d400a8e..ef8d34bbc5e 100644
--- a/lib/generated/l10n/zulip_localizations_pl.dart
+++ b/lib/generated/l10n/zulip_localizations_pl.dart
@@ -26,6 +26,9 @@ class ZulipLocalizationsPl extends ZulipLocalizations {
   @override
   String get switchAccountButton => 'Przełącz konto';
 
+  @override
+  String get organizationsButtonLabel => 'Organizations';
+
   @override
   String tryAnotherAccountMessage(Object url) {
     return 'Twoje konto na $url wymaga jeszcze chwili na załadowanie.';
diff --git a/lib/generated/l10n/zulip_localizations_ru.dart b/lib/generated/l10n/zulip_localizations_ru.dart
index f6d8f1e41c4..346a74d7229 100644
--- a/lib/generated/l10n/zulip_localizations_ru.dart
+++ b/lib/generated/l10n/zulip_localizations_ru.dart
@@ -26,6 +26,9 @@ class ZulipLocalizationsRu extends ZulipLocalizations {
   @override
   String get switchAccountButton => 'Сменить учетную запись';
 
+  @override
+  String get organizationsButtonLabel => 'Organizations';
+
   @override
   String tryAnotherAccountMessage(Object url) {
     return 'Ваша учетная запись на $url загружается медленно.';
diff --git a/lib/generated/l10n/zulip_localizations_sk.dart b/lib/generated/l10n/zulip_localizations_sk.dart
index d6e04126d37..4420073fcd6 100644
--- a/lib/generated/l10n/zulip_localizations_sk.dart
+++ b/lib/generated/l10n/zulip_localizations_sk.dart
@@ -26,6 +26,9 @@ class ZulipLocalizationsSk extends ZulipLocalizations {
   @override
   String get switchAccountButton => 'Zmeniť účet';
 
+  @override
+  String get organizationsButtonLabel => 'Organizations';
+
   @override
   String tryAnotherAccountMessage(Object url) {
     return 'Načítavanie vášho konta na adrese $url chvílu trvá.';
diff --git a/lib/widgets/home.dart b/lib/widgets/home.dart
index ad70b57c32b..8e00cda30f2 100644
--- a/lib/widgets/home.dart
+++ b/lib/widgets/home.dart
@@ -310,6 +310,7 @@ void _showMainMenu(BuildContext context, {
             crossAxisAlignment: CrossAxisAlignment.stretch,
             mainAxisSize: MainAxisSize.min,
             children: [
+              _OrganizationHeader(),
               Flexible(child: InsetShadowBox(
                 top: 8, bottom: 8,
                 color: designVariables.bgBotBar,
@@ -326,6 +327,72 @@ void _showMainMenu(BuildContext context, {
     });
 }
 
+class _OrganizationHeader extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    final store = PerAccountStoreWidget.of(context);
+    final designVariables = DesignVariables.of(context);
+    final zulipLocalizations = ZulipLocalizations.of(context);
+
+    String organizationName = store.realmName;
+    Uri? organizationIcon = store.tryResolveUrl(store.realmIcon);
+    final buttonStyle = TextButton.styleFrom(
+      splashFactory: NoSplash.splashFactory,
+      overlayColor: Colors.transparent
+    );
+
+    return Padding(
+      padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+        children: [
+          Expanded(
+            child: Row(
+              children: [
+                Image.network(
+                  organizationIcon.toString(),
+                  width: 28,
+                  height: 28,
+                  fit: BoxFit.contain,
+                  errorBuilder: (context, error, stackTrace) {
+                    return const Icon(Icons.broken_image, size: 28);
+                  },
+                ),
+                const SizedBox(width: 8),
+                Expanded(
+                  child: Text(
+                    organizationName,
+                    style: const TextStyle(
+                      fontWeight: FontWeight.bold,
+                      fontSize: 20,
+                    ),
+                    overflow: TextOverflow.ellipsis,
+                    maxLines: 1,
+                  ),
+                ),
+              ],
+            ),
+          ),
+          TextButton(
+            onPressed: () {
+              Navigator.of(context).push(MaterialWidgetRoute(page: const ChooseAccountPage()));
+            },
+            style: buttonStyle,
+            child: Text(
+              zulipLocalizations.organizationsButtonLabel,
+              style: TextStyle(
+                fontSize: 19,
+                fontWeight: FontWeight.w500,
+                color: designVariables.icon,
+              ),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}
+
 abstract class _MenuButton extends StatelessWidget {
   const _MenuButton();
 
diff --git a/test/widgets/home_test.dart b/test/widgets/home_test.dart
index 5bb789727f6..aa3a359fd94 100644
--- a/test/widgets/home_test.dart
+++ b/test/widgets/home_test.dart
@@ -248,6 +248,23 @@ void main () {
       check(find.byType(BottomSheet)).findsNothing();
     });
 
+    testWidgets('organization header shows realm info and navigation works', (tester) async {
+      await prepare(tester);
+      final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
+      await tapOpenMenu(tester);
+
+      check(find.text(store.realmName)).findsOne();
+      check(find.byType(Image)).findsOne();
+
+      final organizationsButton = find.text('Organizations');
+      check(organizationsButton).findsOne();
+
+      await tester.tap(organizationsButton);
+      await tester.pump(Duration.zero);
+      await tester.pump(const Duration(milliseconds: 250)); // wait for animation
+
+      check(find.byType(ChooseAccountPage)).findsOne();
+    });
     testWidgets('_MyProfileButton', (tester) async {
       await prepare(tester);
       await tapOpenMenu(tester);