Skip to content

PWA#121

Merged
breadddevv merged 11 commits intoPlanetaryOrbit:mainfrom
infyieldtaker:main
Apr 4, 2026
Merged

PWA#121
breadddevv merged 11 commits intoPlanetaryOrbit:mainfrom
infyieldtaker:main

Conversation

@infyieldtaker
Copy link
Copy Markdown
Contributor

@infyieldtaker infyieldtaker commented Apr 4, 2026

Added PWA for iOS and made it so the new phone UI shows, as my last PR which was closed by me did not support it.

What is a PWA?
A Progressive Web App is just a way to let users "install" Orbit like a native app.
When they tap "Add to Home Screen":

  1. The browser's URL bar and navigation buttons disappear (Full screen).
  2. The app gets its own icon in the app drawer/home screen.
  3. It runs in its own window instead of a browser tab.
IMG_1889

Summary by CodeRabbit

  • New Features

    • Add installable PWA support with app manifest, icons, theme/background colors, and registration for offline/app-like use.
    • Add meta tags for improved mobile/iOS standalone behavior and safe-area handling.
  • UI Improvements

    • Hide sidebar and expand main content in standalone mode for a native-app layout.
    • Adjust bottom navigation and padding to better suit mobile safe areas and standalone displays.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 4, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 30470f1d-4f2f-49c4-8d6b-482711742715

📥 Commits

Reviewing files that changed from the base of the PR and between 9a37748 and 76930e7.

📒 Files selected for processing (3)
  • components/sidebar.tsx
  • next.config.js
  • pages/_document.tsx

📝 Walkthrough

Walkthrough

Adds PWA support (config, manifest, document meta), introduces CSS rules for display-mode: standalone, and a tiny sidebar comment plus file newline; no exported API signatures changed.

Changes

Cohort / File(s) Summary
PWA Config & Export
next.config.js
Wraps Next config with next-pwa via withPWA(nextConfig), adds PWA options (dest: 'public', register, skipWaiting), sets experimental.turbopack: {}, and leaves existing headers logic intact.
Document Meta Tags
pages/_document.tsx
Adds iOS/web-app meta tags (apple/mobile-web-app-capable, viewport-fit=cover, apple-touch-icon, theme-related metas) to support standalone/installed behavior; removed a few comment-only header notes.
Web App Manifest
public/manifest.json
New manifest providing app identity, start_url, display: "standalone", colors, and icons (192/512, maskable).
Standalone Display Styles
styles/globals.scss
Introduces @media all and (display-mode: standalone) to hide sidebar elements and force main/content full width, adjusts bottom nav safe-area padding and backdrop blur, and streamlines some base selectors and removed certain dark-mode rules.
Sidebar Minor Edit
components/sidebar.tsx
Non-functional edit: added an inline comment // Detect PWA Mode above an existing useEffect and ensured file ends with a trailing newline. No signature/behavior changes detected.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 I hopped in the manifest, snug and bright,
hid the sidebar when the app stands upright.
Safe-area padding, icons in sight,
offline dreams tucked in a service worker's night.
Hip-hop PWA—springy, light! 🥕✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'PWA' is too vague and generic. While it refers to a real component of the changes, it fails to meaningfully summarize the main changeset, which involves PWA integration alongside significant UI refactoring (sidebar redesign, mobile bottom navigation, etc.). Use a more descriptive title that captures the primary change, such as 'Add PWA support with mobile bottom navigation redesign' or 'Implement PWA standalone mode with refactored mobile sidebar'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/sidebar.tsx (1)

120-132: ⚠️ Potential issue | 🟡 Minor

Add error handling for logout.

The logout function doesn't handle API failures. If the logout request fails, the user is still redirected to /login with potentially stale session state.

🔧 Add error handling
 const logout = async () => {
-  await axios.post("/api/auth/logout");
-  setLogin({
-    userId: 1,
-    username: "",
-    displayname: "",
-    canMakeWorkspace: false,
-    thumbnail: "",
-    workspaces: [],
-    isOwner: false,
-  });
-  router.push("/login");
+  try {
+    await axios.post("/api/auth/logout");
+  } catch (error) {
+    console.error("Logout failed:", error);
+  } finally {
+    setLogin({
+      userId: 1,
+      username: "",
+      displayname: "",
+      canMakeWorkspace: false,
+      thumbnail: "",
+      workspaces: [],
+      isOwner: false,
+    });
+    router.push("/login");
+  }
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/sidebar.tsx` around lines 120 - 132, The logout function currently
calls axios.post("/api/auth/logout") and immediately clears local state via
setLogin and router.push("/login") even if the API fails; wrap the API call in a
try/catch inside logout, only call setLogin(...) and router.push("/login") after
a successful response from axios.post("/api/auth/logout"), and in the catch log
the error and surface a user-facing error (e.g., via console.error or a toast)
so stale session state isn't wiped or the user redirected on failure; update the
logout function and related flow accordingly.
🧹 Nitpick comments (4)
next.config.js (1)

2-7: Consider potential manifest conflict.

next-pwa may auto-generate a manifest.json at build time, potentially overwriting the manually authored public/manifest.json. To preserve your custom manifest, add publicExcludes: ['!manifest.json'] to the config, or verify the generated manifest matches your requirements.

🔧 Prevent manifest overwrite
 const withPWA = require('next-pwa')({
   dest: 'public',
   register: true,
   skipWaiting: true,
   disable: process.env.NODE_ENV === 'development',
+  publicExcludes: ['!manifest.json'],
 });

Also applies to: 62-62

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@next.config.js` around lines 2 - 7, The PWA config created in the withPWA
initialization may overwrite your handwritten public/manifest.json; update the
next-pwa options used when constructing withPWA by adding a publicExcludes entry
to preserve the file (e.g., include "publicExcludes: ['!manifest.json']") or
otherwise ensure the generated manifest matches your custom manifest, keeping
this change in the same object passed to require('next-pwa') that initializes
withPWA.
components/sidebar.tsx (1)

279-279: Consider extracting duplicated active route logic.

The isActive calculation using page.href.replace("[id]", workspace.groupId.toString()) appears three times (lines 279, 405, 501). Consider extracting this to a helper function for maintainability.

♻️ Extract helper function
// Add near the top of the component or as a utility
const getResolvedHref = (href: string) => 
  href.replace("[id]", workspace.groupId.toString());

const isActivePage = (pageHref: string) => 
  router.asPath === getResolvedHref(pageHref);

Note: Currently no href contains "[id]" (they all use template literals with workspace.groupId), so the replace call is effectively a no-op. Consider removing it if not needed.

Also applies to: 405-405, 501-501

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/sidebar.tsx` at line 279, The duplicated active-route logic (the
isActive calculation that compares router.asPath to page.href.replace("[id]",
workspace.groupId.toString())) should be extracted into a small helper to
improve maintainability: add a getResolvedHref(href: string) that substitutes
"[id]" with workspace.groupId.toString() and an isActivePage(pageHref: string)
that returns router.asPath === getResolvedHref(pageHref), then replace the three
inline calculations with isActivePage(page.href); also verify whether any hrefs
actually contain "[id]"—if not, simplify getResolvedHref to return the original
href (or remove the replace call) to avoid a no-op.
public/manifest.json (1)

9-21: Add purpose: "any" to at least one icon for broader compatibility.

The 192x192 icon is marked as maskable only, but maskable icons may be cropped on some platforms. Best practice is to include at least one icon with purpose: "any" (or both "any maskable") to ensure proper display across all devices.

🔧 Suggested fix
   "icons": [
     {
       "src": "/icons/icon-192x192.png",
       "sizes": "192x192",
       "type": "image/png",
-      "purpose": "maskable"
+      "purpose": "any"
     },
     {
       "src": "/icons/icon-512x512.png",
       "sizes": "512x512",
-      "type": "image/png"
+      "type": "image/png",
+      "purpose": "any maskable"
     }
   ]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@public/manifest.json` around lines 9 - 21, The icons array currently only
marks the 192x192 icon as purpose "maskable", which can cause cropping on some
platforms; update one or both icon objects (e.g., the entries with "src":
"/icons/icon-192x192.png" and/or "/icons/icon-512x512.png") to include purpose
"any" or "any maskable" so at least one icon has purpose "any" for broader
compatibility; simply add or replace the purpose value on the chosen icon
object(s) in the manifest.
styles/globals.scss (1)

62-71: Potential z-index conflict with bottom navigation.

The CSS sets z-index: 99999 on nav.fixed.bottom-0, but in components/sidebar.tsx (line 399), the nav element uses z-[99990]. The CSS override takes precedence, but consider aligning these values for consistency and maintainability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/globals.scss` around lines 62 - 71, The nav.fixed.bottom-0 rule sets
z-index: 99999 which conflicts with the nav in components/sidebar.tsx that uses
the Tailwind class z-[99990]; update one to match the other for consistency —
either change the stylesheet's z-index in nav.fixed.bottom-0 to 99990 or change
the Tailwind class in the Sidebar nav to z-[99999], or better yet centralize the
value using a shared CSS variable or a single source (e.g., prefer the Tailwind
class and remove/avoid the high-specificity override in globals.scss) so both
the global rule and the Sidebar nav reference the same z-index.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@next.config.js`:
- Around line 2-7: Remove the withPWA setup that requires next-pwa (the const
withPWA = require('next-pwa')({...}) block) and replace it with a PWA solution
compatible with Next.js 16/Turbopack: either integrate `@serwist/next` by
installing and initializing its plugin in next.config.js, or remove the plugin
entirely and add a Next.js-native app/manifest.ts and related meta in your app
layout; ensure you delete the require('next-pwa') call and any usage of withPWA
so next.config.js exports the plain config or the `@serwist/next-enhanced` config
instead.

In `@pages/_document.tsx`:
- Around line 7-15: Add a <link rel="manifest" href="/manifest.json" /> element
to the Head output in the custom Document render (the Head block in
pages/_document.tsx) alongside the existing PWA meta tags so browsers can
discover the app manifest; place it near the apple-mobile-web-app and viewport
meta tags and ensure the href points to the actual manifest file path.

In `@styles/globals.scss`:
- Around line 43-50: The current media rule hides any element using utility
classes `.lg\:flex` and `.w-64` as well as any element matching
`[class*="Sidebar"]` and `aside`, which is too broad; narrow the rule to only
target the sidebar container by replacing the global selectors with a specific
sidebar class (e.g., `.sidebar`, `.AppSidebar`, or whatever unique class wraps
the sidebar) and apply the utility-class combinators scoped to that container
(for example target `.sidebar .lg\:flex` and `.sidebar .w-64` and/or
`.sidebar[class*="Sidebar"]`) so only sidebar elements are hidden in
`display-mode: standalone`.

---

Outside diff comments:
In `@components/sidebar.tsx`:
- Around line 120-132: The logout function currently calls
axios.post("/api/auth/logout") and immediately clears local state via setLogin
and router.push("/login") even if the API fails; wrap the API call in a
try/catch inside logout, only call setLogin(...) and router.push("/login") after
a successful response from axios.post("/api/auth/logout"), and in the catch log
the error and surface a user-facing error (e.g., via console.error or a toast)
so stale session state isn't wiped or the user redirected on failure; update the
logout function and related flow accordingly.

---

Nitpick comments:
In `@components/sidebar.tsx`:
- Line 279: The duplicated active-route logic (the isActive calculation that
compares router.asPath to page.href.replace("[id]",
workspace.groupId.toString())) should be extracted into a small helper to
improve maintainability: add a getResolvedHref(href: string) that substitutes
"[id]" with workspace.groupId.toString() and an isActivePage(pageHref: string)
that returns router.asPath === getResolvedHref(pageHref), then replace the three
inline calculations with isActivePage(page.href); also verify whether any hrefs
actually contain "[id]"—if not, simplify getResolvedHref to return the original
href (or remove the replace call) to avoid a no-op.

In `@next.config.js`:
- Around line 2-7: The PWA config created in the withPWA initialization may
overwrite your handwritten public/manifest.json; update the next-pwa options
used when constructing withPWA by adding a publicExcludes entry to preserve the
file (e.g., include "publicExcludes: ['!manifest.json']") or otherwise ensure
the generated manifest matches your custom manifest, keeping this change in the
same object passed to require('next-pwa') that initializes withPWA.

In `@public/manifest.json`:
- Around line 9-21: The icons array currently only marks the 192x192 icon as
purpose "maskable", which can cause cropping on some platforms; update one or
both icon objects (e.g., the entries with "src": "/icons/icon-192x192.png"
and/or "/icons/icon-512x512.png") to include purpose "any" or "any maskable" so
at least one icon has purpose "any" for broader compatibility; simply add or
replace the purpose value on the chosen icon object(s) in the manifest.

In `@styles/globals.scss`:
- Around line 62-71: The nav.fixed.bottom-0 rule sets z-index: 99999 which
conflicts with the nav in components/sidebar.tsx that uses the Tailwind class
z-[99990]; update one to match the other for consistency — either change the
stylesheet's z-index in nav.fixed.bottom-0 to 99990 or change the Tailwind class
in the Sidebar nav to z-[99999], or better yet centralize the value using a
shared CSS variable or a single source (e.g., prefer the Tailwind class and
remove/avoid the high-specificity override in globals.scss) so both the global
rule and the Sidebar nav reference the same z-index.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3d697013-6bf4-4b5d-952c-1b9cd38e4627

📥 Commits

Reviewing files that changed from the base of the PR and between 5595c51 and 9a37748.

📒 Files selected for processing (5)
  • components/sidebar.tsx
  • next.config.js
  • pages/_document.tsx
  • public/manifest.json
  • styles/globals.scss

Comment thread next.config.js
Comment on lines +2 to +7
const withPWA = require('next-pwa')({
dest: 'public',
register: true,
skipWaiting: true,
disable: process.env.NODE_ENV === 'development',
});
Copy link
Copy Markdown
Contributor

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

🌐 Web query:

Is next-pwa compatible with Next.js 16 and Turbopack?

💡 Result:

No, next-pwa is not compatible with Next.js 16 and Turbopack. next-pwa (both the original shadowwalker/next-pwa v5.6.0 last release Aug 2022, and the maintained fork @ducanh2912/next-pwa v10.2.9 Sep 2024) relies on webpack plugins like workbox-webpack-plugin. Its peer dependency explicitly requires webpack >=5.9.0. Next.js 16 uses Turbopack as the default bundler, which does not support webpack plugins. Users must use --webpack flag for builds to make next-pwa work, but this disables Turbopack compatibility. Evidence includes unmaintained status concerns (GitHub issue #508), build errors with Turbopack/Turbo (issues #419, #5199, #512), and recommendations for alternatives like @serwist/next which is designed for Next.js 16 (LogRocket blog, Next.js PWA guides). Next.js has built-in PWA support via app/manifest.ts, but for advanced service workers, Serwist is preferred over next-pwa.

Citations:


Remove next-pwa and replace with alternative PWA solution—incompatible with Next.js 16 and Turbopack.

next-pwa relies on webpack plugins that Turbopack does not support. Both the original package (v5.6.0, unmaintained since Aug 2022) and the maintained fork @ducanh2912/next-pwa have the same incompatibility. Using --webpack flag as a workaround disables Turbopack entirely, defeating its performance benefits. Replace with @serwist/next (actively maintained, designed for Next.js 16) or leverage Next.js's built-in PWA support via app/manifest.ts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@next.config.js` around lines 2 - 7, Remove the withPWA setup that requires
next-pwa (the const withPWA = require('next-pwa')({...}) block) and replace it
with a PWA solution compatible with Next.js 16/Turbopack: either integrate
`@serwist/next` by installing and initializing its plugin in next.config.js, or
remove the plugin entirely and add a Next.js-native app/manifest.ts and related
meta in your app layout; ensure you delete the require('next-pwa') call and any
usage of withPWA so next.config.js exports the plain config or the
`@serwist/next-enhanced` config instead.

Comment thread pages/_document.tsx
Comment on lines +7 to +15
{/* PWA / iOS Standalone Mode Tags */}
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Orbit" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />

{/* Favicon / Touch Icon (Crucial for iOS to treat it as an app) */}
<link rel="apple-touch-icon" href="/favicon.png" />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add manifest link for PWA installation.

The manifest.json file exists but is not linked in the document head. Without the link tag, browsers won't recognize the app as installable.

🔧 Add manifest link
         {/* PWA / iOS Standalone Mode Tags */}
         <meta name="apple-mobile-web-app-capable" content="yes" />
         <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
         <meta name="apple-mobile-web-app-title" content="Orbit" />
         <meta name="mobile-web-app-capable" content="yes" />
-        <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
         
         {/* Favicon / Touch Icon (Crucial for iOS to treat it as an app) */}
         <link rel="apple-touch-icon" href="/favicon.png" />
+        <link rel="manifest" href="/manifest.json" />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{/* PWA / iOS Standalone Mode Tags */}
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Orbit" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
{/* Favicon / Touch Icon (Crucial for iOS to treat it as an app) */}
<link rel="apple-touch-icon" href="/favicon.png" />
{/* PWA / iOS Standalone Mode Tags */}
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Orbit" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
{/* Favicon / Touch Icon (Crucial for iOS to treat it as an app) */}
<link rel="apple-touch-icon" href="/favicon.png" />
<link rel="manifest" href="/manifest.json" />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pages/_document.tsx` around lines 7 - 15, Add a <link rel="manifest"
href="/manifest.json" /> element to the Head output in the custom Document
render (the Head block in pages/_document.tsx) alongside the existing PWA meta
tags so browsers can discover the app manifest; place it near the
apple-mobile-web-app and viewport meta tags and ensure the href points to the
actual manifest file path.

Comment thread styles/globals.scss
Comment on lines +43 to +50
@media all and (display-mode: standalone) {
aside,
.lg\:flex,
.w-64,
[class*="Sidebar"] {
display: none !important;
width: 0 !important;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Overly broad selectors may hide unintended elements.

Hiding .lg\:flex and .w-64 globally in standalone mode will affect any element using these utility classes, not just sidebar-related ones. This could break layouts elsewhere in the app.

🔧 Use more specific selectors
 `@media` all and (display-mode: standalone) {
-  aside, 
-  .lg\:flex, 
-  .w-64, 
-  [class*="Sidebar"] {
+  aside[class*="sidebar"],
+  [class*="Sidebar"],
+  .sidebar-container {
     display: none !important;
     width: 0 !important;
   }

Alternatively, add a specific class to the sidebar container and target only that class.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/globals.scss` around lines 43 - 50, The current media rule hides any
element using utility classes `.lg\:flex` and `.w-64` as well as any element
matching `[class*="Sidebar"]` and `aside`, which is too broad; narrow the rule
to only target the sidebar container by replacing the global selectors with a
specific sidebar class (e.g., `.sidebar`, `.AppSidebar`, or whatever unique
class wraps the sidebar) and apply the utility-class combinators scoped to that
container (for example target `.sidebar .lg\:flex` and `.sidebar .w-64` and/or
`.sidebar[class*="Sidebar"]`) so only sidebar elements are hidden in
`display-mode: standalone`.

@breadddevv breadddevv merged commit a5a59f3 into PlanetaryOrbit:main Apr 4, 2026
1 check was pending
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants