Repository: zulip/zulip

Issue:
UI redesign: marketing /hello
New design for the main page
<img width="1109" alt="image" src="https://user-images.githubusercontent.com/1903309/212569694-bea47388-af85-480a-a2ce-f1d7fef0395a.png">
Repl it project, which you can fork (like a google doc copy) https://replit.com/@VladKorobov/zulip-hello-page
Replit demo: https://zulip-hello-page.vladkorobov.repl.co (switch light/dark theme in your OS or in webdev tools)

Video walk through: https://share.cleanshot.com/Bfqf275f

**Notes**:
- Favicon is updated
- Links are not populated ( clarify with @alya )
- Css isn't optimized or has any styleguide
- Desktop menu and mobile menu are 2 components
- Probably separate Menu and Footer into separate components
- Expect some changes about links/wording

Archived code: 
[zulip-hello-page.zip](https://github.com/zulip/zulip/files/10421371/zulip-hello-page.12.zip)



Discussion:
Hello @zulip/design, @zulip/server-misc members, this issue was labeled with the "area: portico", "redesign" labels, so you may want to check it out!

<!-- areaLabelAddition -->

Hi @terpimost, 
I would like to collaborate and contribute to this UI redesign. 

PS: I am done with the installation and reading the important guides. 
Hey there !!! @terpimost @amanagr @technicalpickles 
After seeing the current interface app, I can assure you that I can give it a great makeover.
please assign this to me 

Since this issue doesn't have a "help wanted" label, this is not open for new contributors.
@Advide2001 and @Rish-it I'm not sure what exactly you are suggesting but I'm open to talk about that here https://chat.zulip.org/#narrow/pm-with/22433-Vlad-Korobov
@terpimost I noticed that we need to change the "R&D org leads" privacy icon from a hash to a lock in the mobile screenshots. Could you please make that tweak?

![Screen Shot 2023-04-17 at 2 56 27 PM](https://user-images.githubusercontent.com/2090066/232619705-5f4a2be0-b772-4b6b-81ab-eb5a231a22d2.png)

@amanagr You have been unassigned from this issue because you have not made any updates for over 14 days. Please feel free to reclaim the issue if you decide to pick up again. Thanks!


Resolving patch:
diff --git a/tools/lib/provision_inner.py b/tools/lib/provision_inner.py
--- a/tools/lib/provision_inner.py
+++ b/tools/lib/provision_inner.py
@@ -65,6 +65,12 @@ def build_timezones_data_paths() -> List[str]:
     return paths
 
 
+def build_landing_page_images_paths() -> List[str]:
+    paths = ["tools/setup/generate_landing_page_images.py"]
+    paths += glob.glob("static/images/landing-page/hello/original/*")
+    return paths
+
+
 def compilemessages_paths() -> List[str]:
     paths = ["zerver/management/commands/compilemessages.py"]
     paths += glob.glob("locale/*/LC_MESSAGES/*.po")
@@ -159,6 +165,16 @@ def need_to_run_build_timezone_data() -> bool:
     )
 
 
+def need_to_regenerate_landing_page_images() -> bool:
+    if not os.path.exists("static/images/landing-page/hello/generated"):
+        return True
+
+    return is_digest_obsolete(
+        "landing_page_images_hash",
+        build_landing_page_images_paths(),
+    )
+
+
 def need_to_run_compilemessages() -> bool:
     if not os.path.exists("locale/language_name_map.json"):
         # User may have cleaned their Git checkout.
@@ -227,6 +243,13 @@ def main(options: argparse.Namespace) -> int:
     else:
         print("No need to run `tools/setup/build_timezone_values`.")
 
+    if options.is_force or need_to_regenerate_landing_page_images():
+        run(["tools/setup/generate_landing_page_images.py"])
+        write_new_digest(
+            "landing_page_images_hash",
+            build_landing_page_images_paths(),
+        )
+
     if not options.is_build_release_tarball_only:
         # The following block is skipped when we just need the development
         # environment to build a release tarball.
diff --git a/tools/setup/generate_landing_page_images.py b/tools/setup/generate_landing_page_images.py
new file mode 100755
--- /dev/null
+++ b/tools/setup/generate_landing_page_images.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+"""Generates versions of landing page images to be served in different conditions."""
+
+import glob
+import os
+import sys
+from pathlib import Path
+from typing import Tuple
+
+from PIL import Image
+
+ZULIP_PATH = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+if ZULIP_PATH not in sys.path:
+    sys.path.append(ZULIP_PATH)
+
+LANDING_IMAGES_DIR = os.path.join(ZULIP_PATH, "static", "images", "landing-page", "hello")
+ORIGINAL_IMAGES_DIR = os.path.join(LANDING_IMAGES_DIR, "original")
+GENERATED_IMAGES_DIR = os.path.join(LANDING_IMAGES_DIR, "generated")
+
+
+def get_x_size(size: Tuple[float, float], x: int) -> Tuple[int, int]:
+    return int(x / 3 * size[0]), int(x / 3 * size[1])
+
+
+def generate_landing_page_images() -> None:
+    if not os.path.exists(GENERATED_IMAGES_DIR):
+        os.mkdir(GENERATED_IMAGES_DIR)
+
+    for image_file_path in glob.glob(f"{ORIGINAL_IMAGES_DIR}/*"):
+        file_name = Path(image_file_path).stem
+        with Image.open(image_file_path) as image:
+            size_2x = get_x_size(image.size, 2)
+            size_1x = get_x_size(image.size, 1)
+
+            ## Generate WEBP images.
+            image.save(f"{GENERATED_IMAGES_DIR}/{file_name}-3x.webp", quality=50)
+            image_2x = image.resize(size_2x)
+            image_2x.save(f"{GENERATED_IMAGES_DIR}/{file_name}-2x.webp", quality=50)
+            image_1x = image.resize(size_1x)
+            image_1x.save(f"{GENERATED_IMAGES_DIR}/{file_name}-1x.webp", quality=70)
+
+            ## Generate JPG images.
+            # Convert from RGBA to RGB since jpg doesn't support transparency.
+            rgb_image = image.convert("RGB")
+            rgb_image.save(f"{GENERATED_IMAGES_DIR}/{file_name}-3x.jpg", quality=19, optimize=True)
+            rgb_image_2x = rgb_image.resize(size_2x)
+            rgb_image_2x.save(
+                f"{GENERATED_IMAGES_DIR}/{file_name}-2x.jpg", quality=50, optimize=True
+            )
+            rgb_image_1x = rgb_image.resize(size_1x)
+            rgb_image_1x.save(
+                f"{GENERATED_IMAGES_DIR}/{file_name}-1x.jpg", quality=70, optimize=True
+            )
+
+
+if __name__ == "__main__":
+    generate_landing_page_images()


Test patch:
diff --git a/zerver/tests/test_docs.py b/zerver/tests/test_docs.py
--- a/zerver/tests/test_docs.py
+++ b/zerver/tests/test_docs.py
@@ -253,7 +253,7 @@ def test_corporate_portico_endpoints(self) -> None:
         # Test the i18n version of one of these pages.
         self._test("/en/history/", ["Zulip released as open source!"])
         self._test("/values/", ["designed our company"])
-        self._test("/hello/", ["Chat for distributed teams"])
+        self._test("/hello/", ["your mission-critical communications with Zulip"])
         self._test("/communities/", ["Open communities directory"])
         self._test("/development-community/", ["Zulip development community"])
         self._test("/features/", ["Beautiful messaging"])
diff --git a/zerver/tests/test_home.py b/zerver/tests/test_home.py
--- a/zerver/tests/test_home.py
+++ b/zerver/tests/test_home.py
@@ -544,7 +544,7 @@ def test_terms_of_service_first_time_template(self) -> None:
             result = self.client_post("/accounts/accept_terms/")
             self.assertEqual(result.status_code, 200)
             self.assert_in_response("I agree to the", result)
-            self.assert_in_response("Chat for distributed teams", result)
+            self.assert_in_response("your mission-critical communications with Zulip", result)
 
     def test_accept_terms_of_service(self) -> None:
         self.login("hamlet")
@@ -1045,7 +1045,7 @@ def test_subdomain_homepage(self) -> None:
             with patch("zerver.views.home.get_subdomain", return_value=""):
                 result = self._get_home_page()
             self.assertEqual(result.status_code, 200)
-            self.assert_in_response("Chat for distributed teams", result)
+            self.assert_in_response("your mission-critical communications with Zulip", result)
 
             with patch("zerver.views.home.get_subdomain", return_value="subdomain"):
                 result = self._get_home_page()