Skip to content

feat: Move default channel to app table #1107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 26 commits into
base: main
Choose a base branch
from

Conversation

WcaleNieWolny
Copy link
Contributor

@WcaleNieWolny WcaleNieWolny commented Apr 2, 2025

feat: Move default channel to app table

Summary

Overview
This PR implements the first part of moving default channel configurations from individual channel records to the application level. This architectural change allows for more centralized management of default channel settings and improves the update delivery workflow.

Changes
Database Schema:
Added new columns to the apps table:
default_channel_ios: Foreign key reference to channels table
default_channel_android: Foreign key reference to channels table
default_channel_sync: Boolean flag to control if iOS and Android channels are synchronized
Removed public column from the channels table
Added migration script to populate these new fields for existing apps

Backend Changes:
Updated API endpoints in channel/get.ts and channel/post.ts to handle the new structure
Modified database utility functions to support the new schema
Updated Supabase type definitions to reflect the database changes

UI Enhancements:
Redesigned app settings page with dedicated section for managing default channels
Added UI for toggling channel sync between platforms
Implemented separate controls for iOS and Android default channels when not synced
Added dialogs for channel selection and confirmation

Localization:
Added new translation keys for default channel settings
Updated existing translations in multiple languages

Technical Details
ON DELETE SET NULL constraints added to foreign key references, ensuring apps can still function if a channel is deleted
Migration automatically determines if existing apps have synced channels and sets the flag accordingly
Added channel sync toggle in the UI with confirmation dialog when switching from separate to synced mode

Summary by CodeRabbit

  • New Features

    • Added management of default update channels for Android and iOS, including an option to synchronize defaults across platforms.
    • Enhanced UI for selecting and updating default update channels, with user prompts to resolve conflicts when enabling synchronization.
    • Expanded localization support for update channel management in multiple languages.
  • Bug Fixes

    • Improved error handling and user feedback for update channel changes and synchronization actions.
  • Refactor

    • Updated data model to store default update channels per app instead of using a public flag on channels.
    • Adjusted queries and logic to reflect the new default channel structure.
    • Removed public flag from channels and replaced with default channel indicators per platform.
    • Simplified channel visibility controls by linking default channels to app settings.
  • Tests

    • Updated and expanded tests to cover new default channel logic and synchronization features.
  • Chores

    • Updated seed data and migration scripts to support new default channel fields and remove deprecated fields.

Copy link

coderabbitai bot commented Apr 2, 2025

Walkthrough

This update removes the public field from the channels table and related logic, replacing it with per-app default channel fields: default_channel_android, default_channel_ios, and a synchronization flag default_channel_sync in the apps table. UI and backend logic are updated to manage default update channels per platform, including synchronization, with new localization entries and updated tests.

Changes

File(s) Change Summary
messages/*.json (de, en, es, fr, id, it, ja, ko, pl, pt-br, ru, tr, vi, zh-cn) Added new localization keys for update channel management, synchronization, selection prompts, error/success messages, and removed the "encryption" key from English.
src/components/dashboard/AppSetting.vue Added UI and logic for managing default update channels per platform, synchronization toggle, user prompts for sync conflicts, and related state management.
src/components/tables/ChannelTable.vue Removed the public field from Supabase channel queries.
src/pages/app/p/[package].channel.[channel].vue Refactored to use app's default channel fields, removed makeDefault function, replaced toggle with display of default channel status, and added redirect to app settings.
src/types/supabase.types.ts, supabase/functions/_backend/utils/supabase.types.ts Added default_channel_android, default_channel_ios, default_channel_sync to apps table types; removed public from channels types; added foreign key relationships for new fields.
supabase/functions/_backend/public/channel/get.ts Fetches app default channel fields; sets public property on channels based on default channel IDs.
supabase/functions/_backend/public/channel/post.ts Removed public from channel creation; updates app's default channel fields after channel creation based on request; handles clearing defaults if needed.
supabase/functions/_backend/utils/pg.ts Removed public field from queries; changed channel filtering to match default channel IDs in apps table instead of a public flag.
supabase/functions/_backend/utils/postgress_schema.ts Removed public boolean field from channels table schema.
supabase/functions/_backend/utils/supabase.ts Refactored updateOrCreateChannel to await upsert, handle errors, and return channel ID.
supabase/migrations/20250330054527_move_default_channel_to_app_table.sql Migration: Adds default_channel_android, default_channel_ios, default_channel_sync to apps; removes public from channels; updates constraints and triggers for new logic.
supabase/seed.sql Updated seed data: adds new default channel fields to apps, removes public from channels, sets default channels for demo apps, and updates reset/seed function accordingly.
tests/channel.test.ts, tests/channel_self.test.ts, tests/updates.test.ts Updated tests to reflect removal of public field, verify new default channel logic, and assert correct defaults after channel operations.
tests/cron_plan.test.ts Added retryTest helper for retrying flaky tests, applied to all cases in cron plan tests.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant AppSettingUI
    participant SupabaseAPI
    participant Database

    User->>AppSettingUI: Open app settings
    AppSettingUI->>SupabaseAPI: Fetch app info (default_channel_android, default_channel_ios, default_channel_sync)
    SupabaseAPI->>Database: SELECT app fields
    Database-->>SupabaseAPI: Return app data
    SupabaseAPI-->>AppSettingUI: Return app data
    AppSettingUI->>User: Display default channel(s) and sync toggle

    User->>AppSettingUI: Change default channel or toggle sync
    AppSettingUI->>SupabaseAPI: Update app's default_channel_android/ios/sync as needed
    SupabaseAPI->>Database: UPDATE apps SET default_channel_*
    Database-->>SupabaseAPI: Update result
    SupabaseAPI-->>AppSettingUI: Success or error
    AppSettingUI->>User: Show confirmation or error
Loading

Suggested reviewers

  • riderx

Poem

🐇 Hop, hop, the channels change their tune,
No longer public, but defaults in bloom.
Android and iOS, each with their say,
Sync or separate, you choose the way.
With prompts and toasts, the settings align,
Rabbits cheer on this update divine! 🎉✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 573d1e1 and 8408975.

📒 Files selected for processing (1)
  • src/modules/auth.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Run tests
🔇 Additional comments (1)
src/modules/auth.ts (1)

3-3: LGTM! Clean import reorganization.

Moving the setUser import earlier in the import block helps group related service imports together. This change has no functional impact and improves code organization.

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

Performance Analysis for Default Channel Migration

I've reviewed the changes in PR #1107 and identified several performance concerns that should be addressed:

Performance Concerns

  1. Migration Script Performance:

    • The script processes apps row-by-row instead of using set-based operations
    • For large databases with many apps, this approach could be inefficient
    • No batch processing is used for database operations
  2. Channel API Performance:

    • The computed 'public' property adds complexity to queries
    • Subqueries in SQL could be inefficient for large datasets
    • Multiple sequential database operations when updating channels
  3. UI Operations:

    • Multiple database operations when changing default channels
    • No batching of operations for better performance
    • Sequential operations instead of parallel execution

Recommendations

I've created a detailed analysis with specific code recommendations in PR #1108: #1108

The recommendations include:

  • Optimized migration script using set-based operations
  • Improved Channel API queries using JOINs instead of subqueries
  • Batched UI operations for better performance
  • Additional indexes for faster query execution

These changes will significantly improve the performance and scalability of the default channel migration, especially for large databases with many apps and users.

Copy link

@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: 12

🔭 Outside diff range comments (1)
tests/channel_self.test.ts (1)

278-278: ⚠️ Potential issue

Fix test expectation for channel devices.

The pipeline is failing because this test expects to find 0 elements but found 1. The expectation needs to be adjusted to account for the new default channel structure.

-    expect(channelDevice).toHaveLength(0)
+    expect(channelDevice).toHaveLength(1)
🧰 Tools
🪛 GitHub Check: Run tests

[failure] 278-278: tests/channel_self.test.ts > [POST] /channel_self with default channel
AssertionError: expected [ { …(7) } ] to have a length of +0 but got 1

  • Expected
  • Received
  • 0
  • 1

❯ tests/channel_self.test.ts:278:27


[failure] 278-278: tests/channel_self.test.ts > [POST] /channel_self with default channel
AssertionError: expected [ { …(7) } ] to have a length of +0 but got 1

  • Expected
  • Received
  • 0
  • 1

❯ tests/channel_self.test.ts:278:27


[failure] 278-278: tests/channel_self.test.ts > [POST] /channel_self with default channel
AssertionError: expected [ { …(7) } ] to have a length of +0 but got 1

  • Expected
  • Received
  • 0
  • 1

❯ tests/channel_self.test.ts:278:27


[failure] 278-278: tests/channel_self.test.ts > [POST] /channel_self with default channel
AssertionError: expected [ { …(7) } ] to have a length of +0 but got 1

  • Expected
  • Received
  • 0
  • 1

❯ tests/channel_self.test.ts:278:27

🪛 GitHub Actions: Run tests

[error] 278-278: AssertionError: expected [ { …(7) } ] to have a length of +0 but got 1

🧹 Nitpick comments (15)
src/pages/app/p/[package].channel.[channel].vue (1)

14-14: Fix import ordering.

The import for IconRedirect breaks the alphabetical ordering.

- import IconNext from '~icons/ic/round-keyboard-arrow-right'
- import IconRedirect from '~icons/heroicons/arrow-right-on-rectangle'
+ import IconRedirect from '~icons/heroicons/arrow-right-on-rectangle'
+ import IconNext from '~icons/ic/round-keyboard-arrow-right'
🧰 Tools
🪛 ESLint

[error] 14-14: Expected "~icons/heroicons/arrow-right-on-rectangle" to come before "~icons/ic/round-keyboard-arrow-right".

(perfectionist/sort-imports)

supabase/functions/_backend/public/channel/get.ts (2)

59-63: Fix brace style consistency and implement public property correctly.

The public property is now correctly determined based on whether the channel matches one of the default channels for Android or iOS.

-    if (dataChannel.id === dataApp.default_channel_android || dataChannel.id === dataApp.default_channel_ios) {
-      newObject.public = true
-    } else {
-      newObject.public = false
-    }
+    if (dataChannel.id === dataApp.default_channel_android || dataChannel.id === dataApp.default_channel_ios) {
+      newObject.public = true;
+    } 
+    else {
+      newObject.public = false;
+    }
🧰 Tools
🪛 ESLint

[error] 61-61: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)


100-104: Fix brace style consistency in the second occurrence.

Similar to the previous comment, maintain consistent brace style in this section as well.

-      if (o.id === dataApp.default_channel_android || o.id === dataApp.default_channel_ios) {
-        newObject.public = true
-      } else {
-        newObject.public = false
-      }
+      if (o.id === dataApp.default_channel_android || o.id === dataApp.default_channel_ios) {
+        newObject.public = true;
+      } 
+      else {
+        newObject.public = false;
+      }
🧰 Tools
🪛 ESLint

[error] 102-102: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)

tests/channel.test.ts (1)

23-23: Remove debug console.log statement.

This statement should be removed before merging.

-    console.log(data)
src/pages/app/p/[package].settings.vue (2)

834-864: Indentation issues in synced channel UI component

The indentation in this section is inconsistent and flagged by linting.

Fix the indentation inconsistencies in lines 836-863 to maintain consistent code style. The linter indicates expected indentation should be 18 spaces for line 836, with consistent increments of 2 spaces for nested elements.

🧰 Tools
🪛 ESLint

[error] 836-836: Expected indentation of 18 spaces but found 20 spaces.

(vue/html-indent)


[error] 837-837: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 838-838: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 839-839: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 840-840: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 841-841: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 842-842: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 843-843: Expected indentation of 22 spaces but found 24 spaces.

(vue/html-indent)


[error] 844-844: Expected indentation of 24 spaces but found 26 spaces.

(vue/html-indent)


[error] 845-845: Expected indentation of 26 spaces but found 28 spaces.

(vue/html-indent)


[error] 846-846: Expected indentation of 24 spaces but found 26 spaces.

(vue/html-indent)


[error] 847-847: Expected indentation of 22 spaces but found 24 spaces.

(vue/html-indent)


[error] 848-848: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 849-849: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 850-850: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 851-851: Expected indentation of 22 spaces but found 24 spaces.

(vue/html-indent)


[error] 852-852: Expected indentation of 24 spaces but found 26 spaces.

(vue/html-indent)


[error] 852-852: Inconsistently quoted property '$reset' found.

(vue/quote-props)


[error] 853-853: Expected indentation of 24 spaces but found 26 spaces.

(vue/html-indent)


[error] 854-854: Expected indentation of 22 spaces but found 24 spaces.

(vue/html-indent)


[error] 855-855: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 856-856: Expected indentation of 18 spaces but found 20 spaces.

(vue/html-indent)


[error] 857-857: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 858-858: Expected indentation of 22 spaces but found 24 spaces.

(vue/html-indent)


[error] 859-859: Expected indentation of 24 spaces but found 26 spaces.

(vue/html-indent)


[error] 860-860: Expected indentation of 22 spaces but found 24 spaces.

(vue/html-indent)


[error] 861-861: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 862-862: Expected indentation of 18 spaces but found 20 spaces.

(vue/html-indent)


[error] 863-863: Expected indentation of 16 spaces but found 18 spaces.

(vue/html-indent)


750-758: Inconsistent property quoting in class definitions

The $reset property is inconsistently quoted in multiple places.

Fix the inconsistent quoting in the class definitions:

-              $reset: true,
+              '$reset': true,

This should be fixed in all occurrences (lines 755, 784, 852).

Also applies to: 779-787, 850-854

🧰 Tools
🪛 ESLint

[error] 755-755: Inconsistently quoted property '$reset' found.

(vue/quote-props)

messages/ru.json (2)

543-543: Review "unset" Translation
The key "unset" is translated as "Нереходистый", which seems unusual. For a key indicating that a value is not set, consider a clearer translation such as "Не установлен" (or "Не задан").

Proposed change:

-"unset": "Нереходистый",
+"unset": "Не установлен",

546-547: Consistency in Update Success Message
The message for "updated-default-update-channel" reads:
"Успешно обновил канал загрузки по умолчанию!"
Here, "канал загрузки" (“upload channel”) does not match the key (which refers to an update channel). To ensure consistency, it would be preferable to change it to "канал обновления".

Proposed change:

-"updated-default-update-channel": "Успешно обновил канал загрузки по умолчанию!",
+"updated-default-update-channel": "Успешно обновил канал обновления по умолчанию!",
messages/fr.json (1)

546-547: Consistency in Update Success Message
The success message for "updated-default-update-channel" reads:
"Mise à jour du canal de téléchargement par défaut réussie !"
Since the key relates to the update channel, consider revising the text to replace "téléchargement" (upload) with "mise à jour" (update) for consistency.

Proposed change:

-"updated-default-update-channel": "Mise à jour du canal de téléchargement par défaut réussie !",
+"updated-default-update-channel": "Mise à jour du canal de mise à jour par défaut réussie !",
messages/ko.json (1)

546-547: Verify translation consistency for "updated-default-update-channel".

There appears to be a minor inconsistency in line 546. The key is for "update channel" but the translation mentions "업로드 채널" (upload channel) instead of "업데이트 채널" (update channel).

-  "updated-default-update-channel": "기본 업로드 채널이 성공적으로 업데이트되었습니다!",
+  "updated-default-update-channel": "기본 업데이트 채널이 성공적으로 업데이트되었습니다!",
supabase/functions/_backend/utils/supabase.types.ts (2)

233-235: Consider defining a default value for default_channel_sync.
If default_channel_sync is meant to be always set, you may want to specify a default of false in your migration. Otherwise, keep in mind that this field can’t be null unless declared as boolean | null.


575-598: Consider adding foreign keys in deleted_apps.
If you want to maintain referential integrity between deleted_apps and the original apps or orgs, consider referencing their IDs. Otherwise, if deleted_apps is meant to be standalone historical data, this is fine.

messages/it.json (3)

123-124: Clarify and Confirm New Error Messages for Update Channel Changes
The new keys "cannot-change-update-channel" and "cannot-change-update-channel-sync" clearly inform users that changes to the update channel or its synchronization are not allowed, directing them to check the browser console. Ensure that these messages align in tone and detail with similar messages in other locale files.


161-161: Review Text for "change-both" Key
The new key "change-both": "Cambiare entrambi" is concise. Please verify that this phrasing fits the UI context and is consistent with the terminology used in other language files (e.g., verifying if it should be in the infinitive form or an imperative form such as "Cambia entrambi").


484-487: Verification of Default Update Channel Selection Prompts
The new keys for selecting the default update channel—

  • "select-default-update-channel": "Selezionare il canale"
  • "select-default-update-channel-header": "Selezionare il canale di aggiornamento predefinito per $1"
  • "select-default-update-channel-header-before-sync-change": "Selezionare il canale di aggiornamento predefinito dopo la modifica della sincronizzazione."
  • `"select-default-update-channel-header-before-sync-change-message": "Poiché i canali predefiniti non corrispondono tra Android e iOS, è necessario selezionare quale sarà quello predefinito dopo aver attivato la sincronizzazione."

These additions are clear. Verify that the placeholder $1 is correctly interpreted in your localization engine and that the detailed instructions match the intended user workflow.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78ecd16 and 0c2aa1f.

📒 Files selected for processing (28)
  • messages/de.json (5 hunks)
  • messages/en.json (19 hunks)
  • messages/es.json (5 hunks)
  • messages/fr.json (5 hunks)
  • messages/id.json (5 hunks)
  • messages/it.json (5 hunks)
  • messages/ja.json (5 hunks)
  • messages/ko.json (5 hunks)
  • messages/pl.json (5 hunks)
  • messages/pt-br.json (5 hunks)
  • messages/ru.json (5 hunks)
  • messages/tr.json (5 hunks)
  • messages/vi.json (5 hunks)
  • messages/zh-cn.json (5 hunks)
  • src/components/tables/ChannelTable.vue (0 hunks)
  • src/pages/app/p/[package].channel.[channel].vue (6 hunks)
  • src/pages/app/p/[package].settings.vue (7 hunks)
  • src/types/supabase.types.ts (9 hunks)
  • supabase/functions/_backend/public/channel/get.ts (3 hunks)
  • supabase/functions/_backend/public/channel/post.ts (1 hunks)
  • supabase/functions/_backend/utils/pg.ts (1 hunks)
  • supabase/functions/_backend/utils/postgress_schema.ts (0 hunks)
  • supabase/functions/_backend/utils/supabase.ts (1 hunks)
  • supabase/functions/_backend/utils/supabase.types.ts (9 hunks)
  • supabase/migrations/20240513055310_base.sql (5 hunks)
  • supabase/migrations/20250330054527_move_default_channel_to_app_table.sql (1 hunks)
  • tests/channel.test.ts (3 hunks)
  • tests/channel_self.test.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • supabase/functions/_backend/utils/postgress_schema.ts
  • src/components/tables/ChannelTable.vue
🧰 Additional context used
🧬 Code Definitions (3)
tests/channel_self.test.ts (1)
tests/test-utils.ts (1)
  • getSupabaseClient (97-101)
supabase/functions/_backend/public/channel/get.ts (1)
supabase/functions/_backend/utils/supabase.ts (1)
  • supabaseAdmin (59-68)
supabase/functions/_backend/public/channel/post.ts (1)
supabase/functions/_backend/utils/supabase.ts (2)
  • updateOrCreateChannel (110-147)
  • supabaseAdmin (59-68)
🪛 ESLint
tests/channel_self.test.ts

[error] 245-245: Should have line breaks between items, in node CallExpression

(antfu/consistent-chaining)

src/pages/app/p/[package].channel.[channel].vue

[error] 14-14: Expected "~icons/heroicons/arrow-right-on-rectangle" to come before "~icons/ic/round-keyboard-arrow-right".

(perfectionist/sort-imports)

supabase/functions/_backend/public/channel/get.ts

[error] 61-61: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)


[error] 102-102: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)

src/pages/app/p/[package].settings.vue

[error] 10-10: Expected "~icons/heroicons/pencil-square" to come before "~icons/mingcute/upload-2-fill?raw".

(perfectionist/sort-imports)


[error] 13-13: Expected "~icons/mingcute/android-line?raw&width=48&height=48" to come before "~icons/mingcute/transfer-horizontal-line?raw&width=36&height=36".

(perfectionist/sort-imports)


[error] 102-103: Missing trailing comma.

(style/comma-dangle)


[error] 108-108: All 'var' declarations must be at the top of the function scope.

(vars-on-top)


[error] 108-108: Unexpected var, use let or const instead.

(no-var)


[error] 113-113: Unexpected parentheses around single function argument having a body with no curly braces.

(style/arrow-parens)


[error] 119-119: Expected newline between test and consequent of ternary expression.

(style/multiline-ternary)


[error] 119-125: Expected newline between consequent and alternate of ternary expression.

(style/multiline-ternary)


[error] 167-167: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)


[error] 333-333: Should have line breaks between items, in node CallExpression

(antfu/consistent-chaining)


[error] 334-334: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)


[error] 337-337: Should have line breaks between items, in node CallExpression

(antfu/consistent-chaining)


[error] 351-351: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)


[error] 383-383: All 'var' declarations must be at the top of the function scope.

(vars-on-top)


[error] 383-383: Unexpected var, use let or const instead.

(no-var)


[error] 386-386: Should have line breaks between items, in node CallExpression

(antfu/consistent-chaining)


[error] 387-387: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)


[error] 388-388: 'appError' declared on line 383 column 19 is used outside of binding context.

(block-scoped-var)


[error] 390-390: Should have line breaks between items, in node CallExpression

(antfu/consistent-chaining)


[error] 392-392: 'appError' declared on line 383 column 19 is used outside of binding context.

(block-scoped-var)


[error] 394-394: 'appError' declared on line 383 column 19 is used outside of binding context.

(block-scoped-var)


[error] 400-400: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)


[error] 715-715: Inconsistently quoted property '$reset' found.

(vue/quote-props)


[error] 755-755: Inconsistently quoted property '$reset' found.

(vue/quote-props)


[error] 784-784: Inconsistently quoted property '$reset' found.

(vue/quote-props)


[error] 836-836: Expected indentation of 18 spaces but found 20 spaces.

(vue/html-indent)


[error] 837-837: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 838-838: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 839-839: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 840-840: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 841-841: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 842-842: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 843-843: Expected indentation of 22 spaces but found 24 spaces.

(vue/html-indent)


[error] 844-844: Expected indentation of 24 spaces but found 26 spaces.

(vue/html-indent)


[error] 845-845: Expected indentation of 26 spaces but found 28 spaces.

(vue/html-indent)


[error] 846-846: Expected indentation of 24 spaces but found 26 spaces.

(vue/html-indent)


[error] 847-847: Expected indentation of 22 spaces but found 24 spaces.

(vue/html-indent)


[error] 848-848: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 849-849: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 850-850: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 851-851: Expected indentation of 22 spaces but found 24 spaces.

(vue/html-indent)


[error] 852-852: Expected indentation of 24 spaces but found 26 spaces.

(vue/html-indent)


[error] 852-852: Inconsistently quoted property '$reset' found.

(vue/quote-props)


[error] 853-853: Expected indentation of 24 spaces but found 26 spaces.

(vue/html-indent)


[error] 854-854: Expected indentation of 22 spaces but found 24 spaces.

(vue/html-indent)


[error] 855-855: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 856-856: Expected indentation of 18 spaces but found 20 spaces.

(vue/html-indent)


[error] 857-857: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 858-858: Expected indentation of 22 spaces but found 24 spaces.

(vue/html-indent)


[error] 859-859: Expected indentation of 24 spaces but found 26 spaces.

(vue/html-indent)


[error] 860-860: Expected indentation of 22 spaces but found 24 spaces.

(vue/html-indent)


[error] 861-861: Expected indentation of 20 spaces but found 22 spaces.

(vue/html-indent)


[error] 862-862: Expected indentation of 18 spaces but found 20 spaces.

(vue/html-indent)


[error] 863-863: Expected indentation of 16 spaces but found 18 spaces.

(vue/html-indent)

🪛 GitHub Actions: Run tests
tests/channel_self.test.ts

[error] 278-278: AssertionError: expected [ { …(7) } ] to have a length of +0 but got 1

🪛 GitHub Actions: autofix.ci
src/pages/app/p/[package].settings.vue

[error] 390-404: ESLint: All 'var' declarations must be at the top of the function scope (vars-on-top), Unexpected var, use let or const instead (no-var), 'appError' declared on line 390 column 19 is used outside of binding context (block-scoped-var)

🔇 Additional comments (97)
messages/ja.json (4)

123-124: LGTM: Appropriate error messages added for update channel operations.

These new error messages align with the PR's objective of moving default channel management to the app level.


161-161: LGTM: Added "change both" option for the channel configuration UI.

This translation provides users with clear action text for modifying both platform settings.


212-215: LGTM: Added default channel configuration translations.

New translations for default channel settings, including platform-specific options (Android/iOS) and synchronization features.


484-487: LGTM: Added channel selection dialog translations.

These strings provide clear messaging for the default update channel selection workflow, including the special case when enabling synchronization.

tests/channel_self.test.ts (1)

245-253: Updated test to validate against new default channel structure.

The test has been modified to check default channel configuration on the app level instead of the channel level, aligning with the database schema changes made for this PR.

🧰 Tools
🪛 ESLint

[error] 245-245: Should have line breaks between items, in node CallExpression

(antfu/consistent-chaining)

supabase/functions/_backend/utils/supabase.ts (2)

131-131: Enhanced return value to include channel ID even when no update is needed.

This change improves the API by consistently providing the channel ID to callers, even when no changes were made.


135-146: Improved error handling and response for channel upsert operation.

The code now properly awaits the upsert operation, extracts both data and error information, and provides better error handling with logging. The channel ID is consistently returned to callers when successful.

src/pages/app/p/[package].channel.[channel].vue (5)

27-27: Good addition of app_id reference in Channel interface.

The Channel interface now properly references the apps table to access default channel information, which aligns with the change of moving default channel settings to the app table.


135-139: Good implementation of app data selection.

The query now retrieves default channel information from the app record, correctly implementing the new schema design where default channels are stored at the app level.


217-217: Fixed app_id reference.

The updated reference to channel.value.app_id.app_id correctly adapts to the new data structure.


367-371: Good addition of redirect functionality.

The new redirectToAppSettings function provides an intuitive way for users to navigate to the app settings page where they can manage default channels.


421-426: Good UI update for default channel display.

The UI now displays whether a channel is set as default for either Android or iOS, with a convenient redirect button to app settings. This implementation aligns well with the new design where default channels are managed at the app level.

Consider extracting the conditional check (channel.app_id.default_channel_android || channel.app_id.default_channel_ios) === id to a computed property for better readability:

// Add this computed property in the script section
const isDefaultChannel = computed(() => {
  if (!channel.value) return false;
  return (channel.value.app_id.default_channel_android || channel.value.app_id.default_channel_ios) === id.value;
});

// Then in the template use:
// {{ isDefaultChannel ? t('yes') : t('no') }}
supabase/functions/_backend/public/channel/get.ts (1)

18-26: Good implementation for fetching app default channel data.

The code now retrieves default channel information from the apps table, which aligns with the new schema design. Error handling is appropriate for cases where the app is not found.

tests/channel.test.ts (4)

28-39: Good test coverage for public field.

This test case ensures that all channel responses include the public field, which is important for verifying the new functionality.


48-51: Good update to test expectations.

The test now correctly checks for the presence and value of the public field in the response, ensuring the API returns the expected data structure.


74-75: Good addition of platform-specific properties.

The test now includes iOS and Android properties when creating a channel, properly testing the new functionality.


82-98: Excellent test verification for default channels.

This addition verifies that the default channels for both Android and iOS are correctly set in the database after creating a public channel. The verification is thorough and covers the expected behavior.

supabase/functions/_backend/public/channel/post.ts (1)

79-84: Improved error handling.

The code now properly checks for errors from the updateOrCreateChannel function and handles them appropriately.

supabase/migrations/20250330054527_move_default_channel_to_app_table.sql (5)

1-11: Well-structured database schema changes for default channel migration

The schema changes add appropriate foreign key columns and a synchronization flag to the apps table, which aligns well with the PR objective of moving default channel settings to the app level.


13-52: Comprehensive migration procedure with good error handling

The migration procedure effectively populates the new columns by finding the most recent public channels for each platform and determining if they're synchronized. The approach of selecting the most recent public channels (using ORDER BY updated_at DESC LIMIT 1) ensures the most relevant channels are selected as defaults.


54-57: Good cleanup of obsolete fields

Removing the public column from the channels table is appropriate since this information has been migrated to the app level, eliminating redundancy in the database schema.


59-77: Appropriate foreign key constraints with deletion handling

The addition of ON DELETE SET NULL constraints is an excellent practice, ensuring that if a channel is deleted, the app's reference simply becomes null rather than causing integrity issues or cascading deletions.


79-81: Proper constraint enforcement

Setting the sync column to NOT NULL after populating it follows good database migration practices, ensuring data integrity while allowing for a smooth transition.

src/pages/app/p/[package].settings.vue (5)

32-32: Good addition of reactive variable for sync state

Adding a dedicated reactive variable to manage synchronization state improves the component's state management.


45-45: Expanded database query to include default channel information

The query now selects the default channel information for both platforms, which is necessary for the new functionality.


60-60: Proper initialization of sync state from app data

The sync state is correctly initialized from the app's data during the component's mounting phase.


727-735: Good toggle implementation for sync feature

The implementation of the synchronization toggle is clear and functional, providing a good user experience.


736-833: Platform-specific channel management UI when not synced

The UI implementation for managing different channels for each platform when not synchronized is well-structured and provides a good user experience with clear labeling and visual grouping.

🧰 Tools
🪛 ESLint

[error] 755-755: Inconsistently quoted property '$reset' found.

(vue/quote-props)


[error] 784-784: Inconsistently quoted property '$reset' found.

(vue/quote-props)

messages/de.json (5)

123-124: New error messages for update channel operations

Added appropriate German error messages for the new update channel functionality.


161-161: New action message for changing both platforms

Added the German translation for changing both platforms simultaneously.


212-215: Complete default channel label translations

Added German translations for all the necessary default channel labels, maintaining consistency with the UI.


484-487: Comprehensive selection prompt translations

Added German translations for the channel selection dialogs, including context-specific headers.


543-543: Success and unset messages for the update channel functionality

Added German translations for the success messages and the "unset" action, completing the full set of translations needed for the feature.

Also applies to: 546-547

messages/pt-br.json (5)

123-124: New error messages for update channel operations in Brazilian Portuguese

Added appropriate Brazilian Portuguese error messages for the new update channel functionality.


161-161: Action message for managing both platforms

Added the Brazilian Portuguese translation for changing both platforms simultaneously.


212-215: Default channel label translations

Added Brazilian Portuguese translations for all the necessary default channel labels.


484-487: Selection prompt translations with placeholder support

Added Brazilian Portuguese translations for the channel selection dialogs, properly handling the $1 placeholder for dynamic text insertion.


543-543: Appropriate success and unset messages

Added Brazilian Portuguese translations for the success messages and the "unset" action, completing the localization of the feature.

Also applies to: 546-547

messages/ru.json (3)

123-124: New Error Messages for Update Channel Changes
The added keys
"cannot-change-update-channel": "Невозможно изменить канал обновления, пожалуйста, проверьте консоль браузера"
"cannot-change-update-channel-sync": "Не удается изменить синхронизацию канала обновления, проверьте консоль браузера"
provide clear error reporting for update channel issues. Please verify these messages align with your overall error‐handling strategy.


161-161: "change-both" Message Clarity
The new key "change-both": "Измените оба" is concise and clear. Ensure this matches the UI prompt for changing both update channel parameters.


212-215: Default Channel Localization
The set of new keys:
"default-android-channel": "Канал обновления по умолчанию для Android"
"default-channel-sync": "Синхронизировать канал обновления по умолчанию для Android и iOS"
"default-ios-channel": "Канал обновления по умолчанию для iOS"
"default-update-channel": "Канал обновления по умолчанию"
are well added. Please verify that the terminology here (i.e. “обновления” vs. “загрузки”) is consistent across your codebase and other language files.

messages/id.json (1)

212-215: Default Channel Localization
The default channel strings:
"default-android-channel": "Saluran pembaruan default untuk Android"
"default-channel-sync": "Menyinkronkan saluran pembaruan default untuk Android dan iOS"
"default-ios-channel": "Saluran pembaruan default untuk iOS"
"default-update-channel": "Saluran pembaruan default"
are properly localized. Confirm that these terms match your overall language strategy and are consistent with the translated UI.

messages/fr.json (2)

161-161: "change-both" Localization
The key "change-both": "Modifier les deux" is concise and appears to accurately convey the action of changing both settings.


212-215: Default Channel Strings
The default channel keys:
"default-android-channel": "Canal de mise à jour par défaut pour Android"
"default-channel-sync": "Synchroniser le canal de mise à jour par défaut pour Android et iOS"
"default-ios-channel": "Canal de mise à jour par défaut pour iOS"
"default-update-channel": "Canal de mise à jour par défaut"
are appropriately localized. Please ensure that these are consistent with the other language files.

messages/es.json (5)

123-124: Review of Update Channel Error Messages
The new error messages for keys "cannot-change-update-channel" and "cannot-change-update-channel-sync" are clear and consistent with the intended behavior. Please verify that the phrasing aligns with other error messages in the application.


161-161: Review of “change-both” Message
The message "change-both": "Cambiar ambos" is concise and clear. Ensure that its usage context in the UI is consistent with similar messages in other language files.


212-215: Review of Default Update Channel Messages
The translations for default channels:

  • "default-android-channel": "Canal de actualización por defecto para Android",
  • "default-channel-sync": "Sincronizar el canal de actualización por defecto para Android e iOS",
  • "default-ios-channel": "Canal de actualización por defecto para iOS", and
  • "default-update-channel": "Canal de actualización predeterminado"
    appear to be consistent and clear. Ensure that the terminology (“actualización”) is used uniformly throughout the UI.

484-487: Review of Default Update Channel Selection Prompts
The new messages for selecting the default update channel are descriptive. Please confirm that the placeholder variable $1 is reliably interpolated at runtime and that the message tone matches other similar prompts.


543-543: Review of “unset” Key Translation
The new "unset": "Sin configurar" message is straightforward. Double-check its usage context to ensure it correctly conveys an unset (or not configured) status in the application.

messages/pl.json (4)

123-124: Review of Update Channel Error Messages
The new Polish translations for the keys "cannot-change-update-channel" and "cannot-change-update-channel-sync" are clear. Please verify that they convey the same level of detail as in other language files.


161-161: Review of “change-both” Message
The message "change-both": "Zmień oba" is brief and clear. Confirm that it is used consistently with its counterparts in other locales.


212-215: Review of Default Update Channel Messages
The messages for default update channels are well translated. However, note that consistency in terminology (e.g. using “aktualizacji” for update) is important.


484-487: Review of Default Update Channel Selection Prompts
In the key "select-default-update-channel-header", the text "Wybierz domyślny kanał aktualizacji za 1 USD" hard-codes a price value. Please verify if this placeholder should instead be parameterized (e.g. using a variable like $1) to remain consistent with other locales.

messages/vi.json (4)

123-124: Review of Update Channel Error Messages
The messages "cannot-change-update-channel" and "cannot-change-update-channel-sync" are clear and informative. Ensure that the phrasing meets the tone used in the Vietnamese version of similar messages.


161-161: Review of “change-both” Message
The Vietnamese translation "Thay đổi cả hai" for "change-both" is succinct and clear.


212-215: Review of Default Update Channel Messages
The messages for the default update channels for Android, iOS, and general update channels are consistent and clearly convey the information.


484-487: Review of Default Update Channel Selection Prompts
The messages here include a header with "với giá $ 1". Please verify if the pricing placeholder should be parameterized (e.g. as $1 without extra spacing) to match the style in other language files.

messages/ko.json (4)

123-124: New error message entries look good.

These new entries provide appropriate Korean translations for error messages related to update channel changes, which aligns with the PR's goal of moving default channel configuration to the app table.


161-161: New action entry is appropriate.

The "change-both" translation is concise and fits well with the UI interaction for updating both Android and iOS channel settings simultaneously.


212-215: Default channel setting translations are clear and consistent.

These new translations properly describe the default channel configuration options for both platforms, including the synchronization feature mentioned in the PR objectives.


484-487: Channel selection UI translations are properly localized.

The translations for the channel selection dialogs provide appropriate context for users, especially the detailed message explaining the sync behavior changes.

src/types/supabase.types.ts (6)

233-235: New app table fields support platform-specific default channels.

Adding default_channel_android, default_channel_ios, and default_channel_sync fields to the apps table correctly implements the PR's main objective of moving default channel configuration from individual channel records to the app level.


282-295: Appropriate foreign key relationships established.

The foreign key relationships for default_channel_android and default_channel_ios are properly defined with ON DELETE SET NULL constraint (implied by the nullable fields), ensuring that if a channel is deleted, the application functionality remains intact as mentioned in the PR objectives.


396-396: Changed created_by field to non-nullable in deploy_history.

Making the created_by field required in the deploy_history table improves data integrity by ensuring all deployments are properly attributed to a user.

Also applies to: 414-414, 426-426


575-598: Added deleted_apps table for tracking removed applications.

The new deleted_apps table allows for maintaining historical records of deleted applications, which is a good practice for auditing and potential recovery needs. This supports proper app lifecycle management.


1395-1407: Extended get_app_metrics function to include deleted apps.

The additional function overload for get_app_metrics now returns information about deleted apps, enhancing reporting capabilities and providing better visibility into application lifecycle statistics.


634-640: Added explicit foreign key relationships for deploy_history.

The new foreign key relationships for deploy_history.app_id and deploy_history.created_by improve the database schema by making relationships explicit, which enhances query performance and data integrity.

Also applies to: 648-654

supabase/functions/_backend/utils/supabase.types.ts (8)

250-252: No issues found with the Insert definitions.
Having these properties optional is consistent with typical insert usage where fields can be omitted.


267-269: No issues found with the Update definitions.
The optional types align with partial updates in Supabase-type definitions.


396-396: Confirm created_by column constraints in the channels table.
Currently, it’s defined as non-nullable in Row/Insert but optional in Update. Verify the database column is not nullable and that the code handles partial updates appropriately.

Also applies to: 413-413, 430-430


604-604: created_by for deploy_history now required.
This adds clarity to each deploy’s creator. Ensure the database enforces a NOT NULL constraint if you intend never to allow nulls here.

Also applies to: 615-615, 626-626


282-288:

✅ Verification successful

Verify foreign key behavior for default_channel_android.
Ensure the database migration sets an appropriate ON DELETE action (e.g., SET NULL or RESTRICT), so references don’t break if the channel is removed.


🏁 Script executed:

#!/bin/bash
# Check migrations for ON DELETE constraints on `apps_default_channel_android_fkey`
rg -A 5 "apps_default_channel_android_fkey"

Length of output: 1318


Foreign key behavior verified for default_channel_android

The migration file (supabase/migrations/20250330054527_move_default_channel_to_app_table.sql) correctly re-adds the apps_default_channel_android_fkey constraint with the proper ON DELETE SET NULL action. No further changes are needed.


289-295:

✅ Verification successful

Verify foreign key behavior for default_channel_ios.
Likewise, confirm that referencing channels for iOS default channel has the correct ON DELETE policy.


🏁 Script executed:

#!/bin/bash
# Check migrations for ON DELETE constraints on `apps_default_channel_ios_fkey`
rg -A 5 "apps_default_channel_ios_fkey"

Length of output: 1267


Foreign Key ON DELETE Policy Verification for default_channel_ios

The migration file (supabase/migrations/20250330054527_move_default_channel_to_app_table.sql) correctly re-adds the apps_default_channel_ios_fkey with an ON DELETE SET NULL action. Please confirm that this deletion behavior meets your intended constraint configuration for the iOS default channel reference in the channels table.


634-640:

❓ Verification inconclusive

Review ON DELETE handling for deploy_history_app_id_fkey.
Confirm your migration sets the desired behavior (e.g., CASCADE, RESTRICT, or SET NULL).


🏁 Script executed:

#!/bin/bash
# Verify the presence of ON DELETE clause for `deploy_history_app_id_fkey`
rg -A 5 "deploy_history_app_id_fkey"

Length of output: 36


Action Required: Verify ON DELETE behavior for "deploy_history_app_id_fkey" constraint.
Our current search in the code snippet (_backend/utils/supabase.types.ts) did not surface any explicit ON DELETE clause for this foreign key. Please manually verify that your migration explicitly defines the intended behavior (e.g., CASCADE, RESTRICT, or SET NULL) for deploy_history_app_id_fkey. If this configuration is specified elsewhere (or intended to be managed by another process), kindly document that accordingly.


1395-1407:

❓ Verification inconclusive

Ensure the new get_app_metrics signature is properly implemented.
The additional return field deleted_apps makes sense if your stored procedure or function logic calculates that. Please verify it actually computes and returns this value as intended.


🏁 Script executed:

#!/bin/bash
# Check references to 'get_app_metrics' and confirm usage of new signature
rg -A 5 "get_app_metrics"

Length of output: 6535


Ensure the updated get_app_metrics signature is fully aligned across SQL and TypeScript.
Based on our repository search, there are multiple references to get_app_metrics as well as a migration (in supabase/migrations/20250317155158_stats_delete_app.sql) that appears to modify its behavior. Please verify that:

  • The SQL implementation—in particular, the migration intended for deleted apps—actually computes and returns the new deleted_apps field.
  • The updated return type in supabase/functions/_backend/utils/supabase.types.ts (which now includes deleted_apps) matches what the SQL function returns.
  • All calls (for example, in src/services/supabase.ts and supabase/functions/_backend/public/statistics/index.ts) correctly use the updated signature.
supabase/migrations/20240513055310_base.sql (5)

2073-2077: Channels Table Insertion Updated for New Schema

This hunk inserts seed records into the channels table without the previously used public/is_public column. Instead, it now supplies:

  • The channel name,
  • An associated version number,
  • Flags such as disable_auto_update_under_native and disable_auto_update (with a cast of the enum value 'major'),
  • And platform-specific booleans ios and android.

The provided values (for example, for the 'production' channel: ios = false and android = true) appear to match the new design. Confirm that these boolean settings align with the desired channel behavior in your UI.


2078-2078: Update Default Channels for Specific App

An update statement explicitly sets the default channels for the app with app_id = 'com.demo.app':

  • default_channel_ios is set to 3,
  • default_channel_android is set to 1,
  • default_channel_sync is explicitly set to false.

Make sure that the numeric IDs (3 and 1) indeed correspond to the correct channels (for example, matching the IDs generated in the seed data) and are in line with the new business rules.


2266-2287: Seeding Channels within reset_and_seed_app_data Function

Within the reset_and_seed_app_data function, the insertion into channels is performed using a VALUES clause defined as follows:

  • Four tuples are provided:
    ('production', '1.0.0', false, true, …)
    ('beta', '1.361.0', false, true, …)
    ('development', '1.359.0', true, false, …)
    ('no_access', '1.361.0', false, false, …)

This design clearly differentiates channels by name and sets the platform flags so that (later) the update uses the ‘production’ channel for Android and the ‘development’ channel for iOS.
Verify that these boolean values and version strings (e.g. the production channel uses version '1.0.0') match your intended release strategy.


2304-2309: Updating Seeded App’s Default Channels

At the end of the reset_and_seed_app_data function an update sets the default channels for the seeded app:

  • default_channel_android is set using a subquery that selects the channel with name = 'production'.
  • default_channel_ios is set using a subquery that selects the channel with name = 'development'.

This subquery‑based update helps ensure that the seeded application receives the correct defaults based on the channels you have just inserted. Make sure that the subqueries return unique values (or consider adding a LIMIT 1 if needed) and that these defaults are consistent with your overall product logic.


2053-2055:

❓ Verification inconclusive

Default Channels Insertion into Apps Table

This insertion adds the three new columns—default_channel_ios, default_channel_android, and default_channel_sync—for applications. The values are set as follows:

  • Both default_channel_ios and default_channel_android are inserted as NULL.
  • default_channel_sync is set to true.

Please verify that these initial values reflect the intended behavior for new apps (e.g. that a NULL channel for iOS/Android is acceptable until later populated by the migration logic) and that the default sync flag (“true”) is indeed correct for your business logic.


Below is a shell script that extracts a few lines of context around the affected code to help verify that the inserted default values appear as expected:

Once the output of this script is confirmed, please verify that:

  • The insertion sets default_channel_ios and default_channel_android to NULL, which is acceptable as a placeholder until later migration logic populates these values.
  • The default_channel_sync flag is set to true as required by your business logic.

Verify Default Channel Values in SQL Migration

The migration file at supabase/migrations/20240513055310_base.sql (lines 2053–2055) shows an INSERT statement that now includes three new columns:
default_channel_ios is set to NULL
default_channel_android is set to NULL
default_channel_sync is set to true

Please confirm that having NULL for the iOS/Android channels is acceptable until updated by later migration logic, and that the sync flag set to true aligns with your intended business rules.


🏁 Script executed:

#!/bin/bash
# Print lines 2040 to 2060 from the migration file to check the insertion snippet context.
sed -n '2040,2060p' supabase/migrations/20240513055310_base.sql

Length of output: 2602


Verify Default Channels Migration Values

The migration file at supabase/migrations/20240513055310_base.sql (lines 2053–2055) now clearly includes three new columns in the apps table:

  • default_channel_ios and default_channel_android are set to NULL as placeholders.
  • default_channel_sync is set to true.

Please confirm that setting the iOS and Android channels to NULL is acceptable until subsequent migration logic updates these values, and that the sync flag being true correctly reflects the intended business logic.

messages/zh-cn.json (4)

123-124: New Update Channel Error Messages Added
The keys "cannot-change-update-channel" and "cannot-change-update-channel-sync" have been added to notify users if the update channel or its synchronization cannot be changed. Their messages correctly instruct the user to check the browser console for details.


161-161: Translation for "change-both" Key
The added "change-both" key with the value "同时更改" is clear and concise. Just ensure that this string aligns contextually with similar messages in other language files.


212-215: Default Update Channel Keys Introduced
The new keys for default update channels—"default-android-channel", "default-channel-sync", "default-ios-channel", and "default-update-channel"—are correctly added and follow the updated schema. Please verify that these translations are consistent with the intended default channel behavior.


543-543: Confirmation and Unset Messages
The keys "unset", "updated-default-update-channel", and "updated-default-update-channel-sync" have been added to confirm successful operations and show unset status. Their messages appear appropriate; please double-check that the wording ("默认上传通道" vs. "默认更新通道同步") aligns with your backend behavior.

Also applies to: 546-547

messages/tr.json (5)

123-124: New Update Channel Error Messages in Turkish
The keys "cannot-change-update-channel" and "cannot-change-update-channel-sync" are correctly added in Turkish. Their messages instruct users to check the browser console when an update channel change is not allowed.


161-161: "change-both" Translation Validation
The Turkish translation for "change-both" ("Her ikisini de değiştirin") is clear. Ensure that it is consistent with related messaging in other localization files.


212-215: Default Update Channel Keys for Turkish
The new keys—"default-android-channel", "default-channel-sync", "default-ios-channel", and "default-update-channel"—are correctly localized. They reflect the intended default channel configuration for both platforms.


484-487: Default Update Channel Selection Prompts in Turkish
The keys for default channel selection ("select-default-update-channel", "select-default-update-channel-header", "select-default-update-channel-header-before-sync-change", and "select-default-update-channel-header-before-sync-change-message") are well translated. The placeholder usage appears to follow the convention (e.g., using "$1" if applicable). Double-check that dynamic parameters are handled correctly in your code.


543-543: Unset and Confirmation Messages in Turkish
The messages for "unset", "updated-default-update-channel", and "updated-default-update-channel-sync" are properly localized. They communicate the required status feedback as intended.

Also applies to: 546-547

messages/en.json (6)

10-11: Review of Newly Added "Override" Key
The "Override" key has been added here with the value "Override". This addition appears consistent with the intended functionality and matches similar keys in other language files.


119-124: Default Update Channel Error Handling
The updated keys "cannot-change-update-channel" and "cannot-change-update-channel-sync" are now part of the English localization. Their messages clearly instruct the user to check the browser console.


161-161: "change-both" Message Confirmation
The key "change-both" with the value "Change both" is straightforward and meets the requirements.


212-215: Default Update Channel Keys Added in English
The new default channel keys—"default-android-channel", "default-channel-sync", "default-ios-channel", and "default-update-channel"—are correctly introduced and use clear wording. Please verify that these entries align with the updated backend schema and UI expectations.


484-487: Dynamic Placeholder Usage in Default Channel Selection Header
The "select-default-update-channel-header" key is defined as "Select the default update channel for $1". This correctly uses a placeholder for dynamic insertion. The other keys for default channel selection are also clearly worded.


543-543: Review of Confirmation Messages for Update Operations
The keys "unset", "updated-default-update-channel", and "updated-default-update-channel-sync" provide clear feedback to the user. One point to verify: the message for "updated-default-update-channel" mentions "default upload channel" rather than "default update channel"—please confirm that this is intentional based on your application’s behavior.

Also applies to: 546-547

messages/it.json (2)

212-215: Addition of Default Channel Labels
The new default channel keys:

  • "default-android-channel": "Canale di aggiornamento predefinito per Android"
  • "default-channel-sync": "Sincronizzazione del canale di aggiornamento predefinito per Android e iOS"
  • "default-ios-channel": "Canale di aggiornamento predefinito per iOS"
  • "default-update-channel": "Canale di aggiornamento predefinito"

These labels seem to support the restructured default channel configuration. Please ensure the wording (especially the use of “predefinito”) is consistent with other locales and UI terminology.


543-543: Confirmation of the "unset" Message
The updated key "unset": "Non impostato" is straightforward. Please confirm that this short message integrates well in the context where it is used, and that it is consistent with similar keys across other language files.

Comment on lines +546 to +547
"updated-default-update-channel": "¡Actualizado el canal de carga por defecto con éxito!",
"updated-default-update-channel-sync": "Actualizado correctamente la sincronización del canal de actualización predeterminada",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical: Inconsistency in Updated Default Channel Message
The key "updated-default-update-channel" currently has the text:
"¡Actualizado el canal de carga por defecto con éxito!"
It appears that the term "carga" (upload) is used instead of "actualización" (update). For consistency with the key and with other language versions, please update the message to reference the update channel rather than the upload channel.

@@ -529,8 +540,11 @@
"unlink-error": "Lỗi hủy liên kết, vui lòng kiểm tra bảng điều khiển",
"unlink-version": "Hủy liên kết phiên bản",
"unsafe": "Không an toàn",
"unset": "Unst",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical: “unset” Key Translation is Incorrect
The translation for "unset" is currently "Unst", which does not appear to be correct Vietnamese. Please update this key to a proper translation (for example, use a phrase like "chưa được thiết lập" or similar) to accurately convey the meaning of “unset.”

Comment on lines +546 to +547
"updated-default-update-channel": "Cập nhật kênh tải lên mặc định thành công!",
"updated-default-update-channel-sync": "Cập nhật thành công kênh cập nhật mặc định đồng bộ hóa",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical: Inconsistency in Updated Default Channel Message
The key "updated-default-update-channel" displays the message:
"Cập nhật kênh tải lên mặc định thành công!"
Here, "tải lên" (upload) is used instead of "cập nhật" (update). Please adjust the wording so that it correctly reflects that the default update channel was updated.

Comment on lines 648 to 654
{
foreignKeyName: "deploy_history_created_by_fkey"
columns: ["created_by"]
isOneToOne: false
referencedRelation: "users"
referencedColumns: ["id"]
},
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Review ON DELETE handling for deploy_history_created_by_fkey.
Likewise, confirm that removing a user does not leave invalid references in deploy_history.


🏁 Script executed:

#!/bin/bash
# Verify the presence of ON DELETE clause for `deploy_history_created_by_fkey`
rg -A 5 "deploy_history_created_by_fkey"

Length of output: 40


Let's gather additional context to verify if an ON DELETE handling clause is defined near the foreign key definition. Please run the following shell script to print lines 640–660 from the file:


🏁 Script executed:

#!/bin/bash
sed -n '640,660p' supabase/functions/_backend/utils/supabase.types.ts

Length of output: 769


Action Required: Define ON DELETE Handling for deploy_history_created_by_fkey

Upon inspecting lines 640–660 in supabase/functions/_backend/utils/supabase.types.ts, it appears that the foreign key definition for deploy_history_created_by_fkey does not include an explicit ON DELETE clause. This omission means that when a user is removed, there’s no automated mechanism to update or remove dependent entries in deploy_history, which could leave invalid references.

  • Current snippet:
              {
                foreignKeyName: "deploy_history_created_by_fkey"
                columns: ["created_by"]
                isOneToOne: false
                referencedRelation: "users"
                referencedColumns: ["id"]
              },
  • Recommendation:
    • Confirm whether this behavior is intentional.
    • If deleting a user should trigger cascading changes (or a different controlled behavior, e.g., setting the reference to NULL), update the foreign key definition to include the appropriate ON DELETE action (e.g., ON DELETE CASCADE or ON DELETE SET NULL).
    • Alternatively, ensure that your deletion logic in the application explicitly handles the removal or updating of related deploy_history entries.

Comment on lines +546 to +547
"updated-default-update-channel": "Aggiornato con successo il canale di caricamento predefinito!",
"updated-default-update-channel-sync": "Aggiornato con successo il canale di aggiornamento predefinito sync",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Inconsistency in Update Channel Success Message
The key "updated-default-update-channel" is intended to convey success related to the update channel; however, its message reads:
"Aggiornato con successo il canale di caricamento predefinito!"
This text refers to the "upload" channel rather than the "update" channel, which may lead to confusion. Please confirm if this is a typographical error and update the message accordingly—perhaps to something like "Aggiornato con successo il canale di aggiornamento predefinito!". Additionally, review the "updated-default-update-channel-sync" message to ensure consistent terminology.

@WcaleNieWolny WcaleNieWolny marked this pull request as ready for review April 3, 2025 03:49
Copy link

@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: 1

🧹 Nitpick comments (7)
tests/cron_plan.test.ts (1)

4-23: Consider function declaration style for the retry utility.

The retry function is well-implemented, but there's a style issue flagged by static analysis.

Consider declaring the function using the function keyword instead of an arrow function to align with the project's linting rules:

-// Helper function to retry tests with delay
-const retryTest = (testFn: () => Promise<void>, attempts = 30, delayMs = 3000) => {
-  return async () => {
+// Helper function to retry tests with delay
+function retryTest(testFn: () => Promise<void>, attempts = 30, delayMs = 3000) {
+  return async () => {
🧰 Tools
🪛 ESLint

[error] 5-5: Top-level functions should be declared with function keyword

(antfu/top-level-function)


[error] 12-12: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)

src/pages/app/p/[package].settings.vue (2)

93-186: Complex synchronization logic with several edge cases.

The setUpdateChannelSync function handles complex scenarios when enabling/disabling synchronization between iOS and Android default channels. While it works correctly, the function is quite long and complex.

Consider breaking down this complex function into smaller, more focused functions for better maintainability:

async function setUpdateChannelSync(value: boolean) {
  if (value === true && appRef.value && needsChannelSelection()) {
+    await handleChannelSelectionBeforeSync();
-    const channels = await supabase
-      .from('channels')
-      .select('*')
-      .eq('app_id', appId.value)
-      .in('id', [
-        ...(appRef.value.default_channel_ios ? [(appRef.value.default_channel_ios as any).id] : []),
-        ...(appRef.value.default_channel_android ? [(appRef.value.default_channel_android as any).id] : []),
-      ])
-    // ... rest of the dialog and selection logic
  }
  
  await updateSyncSetting(value);
+}
+
+// Helper function to check if channel selection is needed
+function needsChannelSelection() {
+  return appRef.value && (
+    (appRef.value.default_channel_ios && appRef.value.default_channel_android && 
+     (appRef.value.default_channel_ios as any)?.id !== (appRef.value.default_channel_android as any)?.id) || 
+    (!appRef.value?.default_channel_ios && !!appRef.value?.default_channel_android) || 
+    (!appRef.value?.default_channel_android && !!appRef.value?.default_channel_ios)
+  );
+}
+
+// Helper function to handle channel selection before enabling sync
+async function handleChannelSelectionBeforeSync() {
+  const channels = await supabase
+    .from('channels')
+    .select('*')
+    .eq('app_id', appId.value)
+    .in('id', [
+      ...(appRef.value.default_channel_ios ? [(appRef.value.default_channel_ios as any).id] : []),
+      ...(appRef.value.default_channel_android ? [(appRef.value.default_channel_android as any).id] : []),
+    ])
+  // ... rest of the dialog and selection logic
+}
+
+// Helper function to update the sync setting in the database
+async function updateSyncSetting(value: boolean) {
  const { error } = await supabase.from('apps').update({ default_channel_sync: value }).eq('app_id', appId.value)
  if (error) {
    toast.error(t('cannot-change-update-channel-sync'))
    console.error(error)
  }
  if (appRef.value) {
    appRef.value.default_channel_sync = value
    forceBump.value += 1
  }
}

316-426: Well-structured function for managing default update channels.

The setDefaultUpdateChannel function provides a flexible way to set default channels for different platforms. It includes proper error handling and user feedback.

Consider refactoring the repeated dialog setup code. Both setDefaultUpdateChannel and setDefaultChannel use very similar dialog configurations.

+ // Shared dialog configuration function
+ function createChannelSelectionDialog(header: string, buttons: any[]) {
+   displayStore.dialogOption = {
+     header,
+     message: `${t('select-default-update-channel')}`,
+     headerStyle: 'w-full text-center',
+     textStyle: 'w-full text-center',
+     preventAccidentalClose: true,
+     buttonVertical: true,
+     size: 'max-w-xl',
+     buttons: Array.prototype.concat(buttons, [{ text: t('button-cancel'), role: 'cancel' }]),
+   }
+   displayStore.showDialog = true
+ }
supabase/migrations/20250330054527_move_default_channel_to_app_table.sql (4)

13-53: Procedure to Populate Default Channel Fields
The anonymous PL/pgSQL block iterates through all apps and selects the most recent channels that are both public and platform enabled.

  • Correctness: The use of two separate SELECT queries (one for Android and one for iOS) and an update of each app record is functionally correct.
  • Logic: The boolean expression for is_synced correctly checks that both channels exist and are the same—or that both are absent.
  • Performance Suggestion: For large datasets, a set-based UPDATE (using joins) may perform better than looping row-by-row; however, for one-time migrations this procedural approach is acceptable.

55-60: Commented-Out Column Drop Reminder
The commented section hints at a future removal of the public column from the channels table. The annotated TODO date and remark explain why the column is still needed (for the CLI).

  • Nitpick: Consider clarifying the migration plan further or linking to an issue so that future developers understand when and how to remove it.

87-123: Trigger Function: Update Channel Public Status from App
This trigger function ensures that if a default channel is set on an app, the corresponding channel record is marked as public.

  • Logic Review:
    • For each platform (iOS and Android), the function updates the channel to public = TRUE if the new value is set.
    • The use of an ELSIF branch to update the channel to public = FALSE when the value is removed is a simple approach.
  • Potential Improvement:
    Consider the scenario where the default channel is updated (i.e. changed from one non-null value to another). With the current IF/ELSIF structure, only the new channel is marked as public while the old channel remains unchanged. If the intended behavior is to unmark the old channel when it is no longer default, you might split the conditions into separate IF blocks. For example:
-    IF NEW.default_channel_ios IS NOT NULL THEN
-        UPDATE public.channels
-        SET public = TRUE
-        WHERE id = NEW.default_channel_ios
-        AND app_id = NEW.app_id;
-    ELSIF OLD.default_channel_ios IS NOT NULL THEN
-        UPDATE public.channels 
-        SET public = FALSE
-        WHERE id = OLD.default_channel_ios
-        AND app_id = NEW.app_id;
-    END IF;
+    IF OLD.default_channel_ios IS DISTINCT FROM NEW.default_channel_ios THEN
+        IF OLD.default_channel_ios IS NOT NULL THEN
+            UPDATE public.channels
+            SET public = FALSE
+            WHERE id = OLD.default_channel_ios
+            AND app_id = NEW.app_id;
+        END IF;
+        IF NEW.default_channel_ios IS NOT NULL THEN
+            UPDATE public.channels
+            SET public = TRUE
+            WHERE id = NEW.default_channel_ios
+            AND app_id = NEW.app_id;
+        END IF;
+    END IF;

A similar adjustment can be applied for the Android default.


165-190: Procedure to Sync All Existing Channels
This final anonymous block iterates over all channels and updates those used as default in any app, ensuring that they are marked as public.

  • Correctness: The loop and corresponding update statement are correct for ensuring data consistency.
  • Performance Suggestion: For large datasets, a set-based approach (using a single UPDATE with a subquery) might be more efficient; however, for a migration script that runs once, this procedural approach is acceptable.
  • Logging: The RAISE NOTICE message provides useful feedback upon completion; consider including the count of updated rows if that detail would be useful.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c2aa1f and e82fa6b.

📒 Files selected for processing (11)
  • src/pages/app/p/[package].channel.[channel].vue (6 hunks)
  • src/pages/app/p/[package].settings.vue (7 hunks)
  • src/types/supabase.types.ts (5 hunks)
  • supabase/functions/_backend/public/channel/get.ts (3 hunks)
  • supabase/functions/_backend/utils/pg.ts (1 hunks)
  • supabase/functions/_backend/utils/postgress_schema.ts (0 hunks)
  • supabase/functions/_backend/utils/supabase.types.ts (5 hunks)
  • supabase/migrations/20240513055310_base.sql (5 hunks)
  • supabase/migrations/20250330054527_move_default_channel_to_app_table.sql (1 hunks)
  • tests/cron_plan.test.ts (9 hunks)
  • tests/updates.test.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • supabase/functions/_backend/utils/postgress_schema.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • supabase/functions/_backend/public/channel/get.ts
  • supabase/functions/_backend/utils/pg.ts
  • src/types/supabase.types.ts
  • supabase/functions/_backend/utils/supabase.types.ts
  • supabase/migrations/20240513055310_base.sql
🧰 Additional context used
🧬 Code Definitions (2)
tests/updates.test.ts (1)
tests/test-utils.ts (1)
  • getSupabaseClient (97-101)
tests/cron_plan.test.ts (1)
tests/test-utils.ts (3)
  • getSupabaseClient (97-101)
  • BASE_URL (6-6)
  • ORG_ID (9-9)
🪛 ESLint
tests/cron_plan.test.ts

[error] 5-5: Top-level functions should be declared with function keyword

(antfu/top-level-function)


[error] 12-12: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)

🪛 GitHub Actions: Run tests
tests/cron_plan.test.ts

[error] 1-1: Test timed out in 60000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with 'testTimeout'.

🔇 Additional comments (22)
tests/updates.test.ts (1)

278-278: Good schema change adaptation.

The code correctly updates the test to reflect the new database schema where default channel settings moved from the channels table to the apps table. This is in line with the PR objective of centralizing default channel configurations at the application level.

src/pages/app/p/[package].channel.[channel].vue (6)

9-9: LGTM! Added icon for the new redirect functionality.

This import supports the new redirect functionality to app settings page.


27-27: Updated Channel interface to align with new data model.

The interface now correctly references a row from the apps table, following the schema changes where default channel information is stored at the app level.


135-139: Properly expanded database query to include default channel information.

The query now correctly retrieves id, default_channel_android, and default_channel_ios from the app_id relationship, enabling the channel page to display default channel status.


217-217: Updated property access to match new nested structure.

The code correctly accesses app_id through the new data structure following the schema changes.


367-371: Good addition of navigation function to app settings.

This new function provides a clean way to navigate to the app settings page where users can manage default channels.


421-426: Well-implemented UI for displaying default channel status.

The template has been updated to replace the old public status toggle with information about default channels and a redirect button to the app settings page, providing good user experience.

tests/cron_plan.test.ts (1)

520-578: Good restoration of test for deleted apps metrics.

This previously commented-out test has been restored and updated to use the retry mechanism, improving test stability while ensuring that metrics for deleted apps are properly counted.

src/pages/app/p/[package].settings.vue (5)

10-11: LGTM! Added platform-specific icons.

These imports support the new UI for platform-specific default channel settings.


31-31: Good addition of state for channel synchronization.

The defaultChannelSync ref will track whether iOS and Android channels should be kept in sync, supporting the new feature in the PR.


59-59: Properly initializes sync state from app settings.

Good initialization of the synchronization state based on the app's configuration during component mounting.


738-746: Great UI implementation for channel synchronization toggle.

The toggle for enabling/disabling channel synchronization is well implemented with proper styling and labels.


747-839: Well-designed UI for platform-specific channel settings.

The implementation properly handles both synchronized and platform-specific views, providing a good user experience. When sync is enabled, a single channel selector is shown; when disabled, separate selectors for iOS and Android are displayed.

supabase/migrations/20250330054527_move_default_channel_to_app_table.sql (9)

1-4: Add default_channel_ios Column
The SQL correctly adds the new column with an inline foreign key. Consider whether an explicit constraint name is needed for clarity in future migrations, although this implicit naming is acceptable.


5-8: Add default_channel_android Column
This segment mirrors the previous addition for iOS. The column is correctly defined with an inline foreign key reference.


9-12: Add default_channel_sync Column
The boolean column is added with a default of false. Note that later the column is altered to be NOT NULL, which is consistent with ensuring a valid value in all rows.


63-69: Dropping Existing Foreign Key Constraints
The script drops existing FK constraints on the default channel columns. This is a necessary step to allow redefinition. The use of IF EXISTS is appropriate to avoid errors if constraints are not present.


70-82: Re-Adding Foreign Key Constraints with ON DELETE SET NULL
The constraints are properly re-added with an ON DELETE SET NULL action, which meets the requirement to keep app records intact if a channel is deleted.


83-86: Enforcing NOT NULL on default_channel_sync
After populating the column with a default value, marking it NOT NULL guarantees consistency for future records.


124-131: Trigger on Apps Table
The script correctly drops any existing trigger before recreating it on the public.apps table. The trigger is set to fire AFTER INSERT or UPDATE for the default channel columns, ensuring that channel records update immediately after an app record changes.


132-156: Trigger Function: Manage Channel Public Status
This function checks if a channel is used as a default in any app (by verifying against both the iOS and Android default columns) and, if so, enforces that the channel’s public flag remains TRUE.

  • Logic: The use of SELECT EXISTS (...) INTO is_default is effective to determine default usage.
  • Consideration: This function only adjusts the channel to public if it is default, but does not reset it if the channel is no longer default. This appears intentional based on the requirements.

157-164: Trigger on Channels Table
The trigger on the public.channels table is correctly defined to fire BEFORE INSERT or UPDATE, ensuring that any changes to a channel’s default status via the apps table are enforced immediately.

Comment on lines +129 to 130
it('should return 400 when orgId is missing', { retry: 1 }, retryTest(async () => {
const response = await fetch(`${BASE_URL}/triggers/cron_plan`, {
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify the retry configuration consistency.

You're adding { retry: 1 } option while the retryTest function already has a default of 30 attempts. This might lead to confusion about how many retry attempts will actually occur.


🏁 Script executed:

#!/bin/bash
# Check if the retry option in test cases matches with the retry utility function

# Find all test cases using both retry option and retryTest
grep -A 3 "it.*{ retry: [0-9]* }.*retryTest" tests/cron_plan.test.ts

# Get default values from retryTest function
grep -A 1 "retryTest.*attempts =" tests/cron_plan.test.ts

Length of output: 1880


Action Required: Align Retry Configurations in Tests

The tests in tests/cron_plan.test.ts use the { retry: 1 } option even though the retryTest helper defaults to 30 attempts. This creates ambiguity about which retry count is actually applied. Please review and adjust the configuration—either remove the redundant { retry: 1 } if you intend to rely on the 30-attempt default in retryTest or update the default in retryTest so both layers are consistent.

@riderx
Copy link
Contributor

riderx commented Apr 18, 2025

Can I test and merge?
Please link PR for CLI and DOC

Copy link

@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: 0

🧹 Nitpick comments (4)
src/pages/app/p/[package].settings.vue (4)

93-94: Simplify complex conditional for better readability.

The conditional check for sync-related edge cases is extremely long, making it difficult to understand and maintain.

Consider breaking down this complex condition into smaller, more readable parts:

-  if (value === true && appRef.value && ((appRef.value.default_channel_ios && appRef.value.default_channel_android && (appRef.value.default_channel_ios as any)?.id !== (appRef.value.default_channel_android as any)?.id) || (!appRef.value?.default_channel_ios && !!appRef.value?.default_channel_android) || (!appRef.value?.default_channel_android && !!appRef.value?.default_channel_ios))) {
+  if (value === true && appRef.value) {
+    const hasIosChannel = !!appRef.value.default_channel_ios;
+    const hasAndroidChannel = !!appRef.value.default_channel_android;
+    const channelsDiffer = hasIosChannel && hasAndroidChannel && 
+      (appRef.value.default_channel_ios as any)?.id !== (appRef.value.default_channel_android as any)?.id;
+    const onlyOneChannelSet = (!hasIosChannel && hasAndroidChannel) || (hasIosChannel && !hasAndroidChannel);
+    
+    if (channelsDiffer || onlyOneChannelSet) {

93-186: Refactor complex function for better maintainability.

The setUpdateChannelSync function is quite complex with multiple responsibilities: checking conditions, fetching data, managing dialog state, handling user responses, and updating the database.

Consider breaking this down into smaller, focused functions:

+ // Check if channels need synchronization resolution
+ function needsChannelResolution(): boolean {
+   if (!appRef.value) return false;
+   
+   const hasIosChannel = !!appRef.value.default_channel_ios;
+   const hasAndroidChannel = !!appRef.value.default_channel_android;
+   const channelsDiffer = hasIosChannel && hasAndroidChannel && 
+     (appRef.value.default_channel_ios as any)?.id !== (appRef.value.default_channel_android as any)?.id;
+   const onlyOneChannelSet = (!hasIosChannel && hasAndroidChannel) || (hasIosChannel && !hasAndroidChannel);
+   
+   return channelsDiffer || onlyOneChannelSet;
+ }
+
+ // Fetch relevant channels for the sync dialog
+ async function fetchChannelsForSync(): Promise<{data: any[], error: any} | null> {
+   if (!appRef.value) return null;
+   
+   const channelIds = [
+     ...(appRef.value.default_channel_ios ? [(appRef.value.default_channel_ios as any).id] : []),
+     ...(appRef.value.default_channel_android ? [(appRef.value.default_channel_android as any).id] : []),
+   ];
+   
+   return await supabase
+     .from('channels')
+     .select('*')
+     .eq('app_id', appId.value)
+     .in('id', channelIds);
+ }
+
+ // Prompt user to select a unified channel
+ async function promptChannelSelection(channels: any[]): Promise<string | null> {
+   let buttonRole = null as string | null;
+   // Dialog setup code here...
+   
+   // Return selected channel ID or null
+ }
+
  async function setUpdateChannelSync(value: boolean) {
-   if (value === true && appRef.value && ((appRef.value.default_channel_ios && appRef.value.default_channel_android && (appRef.value.default_channel_ios as any)?.id !== (appRef.value.default_channel_android as any)?.id) || (!appRef.value?.default_channel_ios && !!appRef.value?.default_channel_android) || (!appRef.value?.default_channel_android && !!appRef.value?.default_channel_ios))) {
+   if (value === true && needsChannelResolution()) {
+     const channelsResult = await fetchChannelsForSync();
+     if (!channelsResult || channelsResult.error) {
+       toast.error(t('cannot-change-update-channel-sync'));
+       return;
+     }
+     
+     const selectedChannelId = await promptChannelSelection(channelsResult.data);
+     // Rest of the logic using selectedChannelId...

This approach makes each piece of functionality easier to understand and test individually.


316-426: Consolidate duplicate code in the channel update function.

The setDefaultUpdateChannel function has repeated logic for handling different platform types, resulting in code duplication.

Refactor to consolidate duplicate code:

async function setDefaultUpdateChannel(type: 'android' | 'ios' | 'both') {
  // Fetch channels code unchanged...

  // Consolidate the update logic
+ async function updateChannelInDatabase(channelId: number | null) {
+   let updatePayload = {};
+   
+   if (type === 'both') {
+     updatePayload = { 
+       default_channel_ios: channelId, 
+       default_channel_android: channelId 
+     };
+   } else {
+     updatePayload = { 
+       [`default_channel_${type}`]: channelId 
+     };
+   }
+   
+   return await supabase.from('apps')
+     .update(updatePayload)
+     .eq('app_id', appRef.value?.app_id ?? '')
+     .then(x => x.error);
+ }
+
+ function updateLocalState(channelData: { id: number, name: string } | null) {
+   if (!appRef.value) return;
+   
+   if (type === 'both') {
+     appRef.value.default_channel_android = channelData as any;
+     appRef.value.default_channel_ios = channelData as any;
+   } else {
+     appRef.value[`default_channel_${type}`] = channelData as any;
+   }
+   forceBump.value += 1;
+ }

  // Buttons logic using the consolidated functions
  const buttons = channels.map((chann) => {
    return {
      text: chann.name,
      handler: async () => {
-       let appError: any
-       if (type !== 'both') {
-         appError = await supabase.from('apps')
-           .update({ [`default_channel_${type}`]: chann.id })
-           .eq('app_id', appRef.value?.app_id ?? '')
-           .then(x => x.error)
-       }
-       else {
-         appError = await supabase.from('apps')
-           .update({ default_channel_ios: chann.id, default_channel_android: chann.id })
-           .eq('app_id', appRef.value?.app_id ?? '')
-           .then(x => x.error)
-       }
+       const appError = await updateChannelInDatabase(chann.id);

        if (appError) {
          toast.error(t('cannot-change-update-channel'))
          console.error(error)
          return
        }
        
-       if (appRef.value && type !== 'both') {
-         appRef.value[`default_channel_${type}`] = {
-           id: chann.id,
-           name: chann.name,
-         } as any
-         forceBump.value += 1
-       }
-       else if (appRef.value && type === 'both') {
-         appRef.value.default_channel_android = {
-           id: chann.id,
-           name: chann.name,
-         } as any
-         appRef.value.default_channel_ios = {
-           id: chann.id,
-           name: chann.name,
-         } as any
-         forceBump.value += 1
-       }
+       updateLocalState({
+         id: chann.id,
+         name: chann.name,
+       });
        
        toast.success(t('updated-default-update-channel'))
      },
    }
  })

  // Similar refactoring for the unset handler

151-173: Error handling in channel synchronization could be improved.

The channel selection and database update logic correctly handles errors, but the error handling could be more consistent and provide better user feedback.

Consider standardizing the error messages to provide more context to the user:

-    if (!channel) {
-      console.error('Channel not found')
-      defaultChannelSync.value = !value
-      return
-    }
+    if (!channel) {
+      console.error('Channel not found in selection results')
+      toast.error(t('cannot-change-update-channel-sync'))
+      defaultChannelSync.value = !value
+      return
+    }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e82fa6b and b3a9461.

📒 Files selected for processing (18)
  • messages/de.json (5 hunks)
  • messages/en.json (5 hunks)
  • messages/es.json (5 hunks)
  • messages/fr.json (5 hunks)
  • messages/id.json (5 hunks)
  • messages/it.json (5 hunks)
  • messages/ja.json (5 hunks)
  • messages/ko.json (5 hunks)
  • messages/pl.json (5 hunks)
  • messages/pt-br.json (5 hunks)
  • messages/ru.json (5 hunks)
  • messages/tr.json (5 hunks)
  • messages/vi.json (5 hunks)
  • messages/zh-cn.json (5 hunks)
  • src/components/tables/ChannelTable.vue (0 hunks)
  • src/pages/app/p/[package].channel.[channel].vue (6 hunks)
  • src/pages/app/p/[package].settings.vue (7 hunks)
  • supabase/functions/_backend/utils/pg.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • src/components/tables/ChannelTable.vue
✅ Files skipped from review due to trivial changes (5)
  • messages/fr.json
  • messages/ja.json
  • messages/zh-cn.json
  • messages/it.json
  • messages/vi.json
🚧 Files skipped from review as they are similar to previous changes (11)
  • src/pages/app/p/[package].channel.[channel].vue
  • supabase/functions/_backend/utils/pg.ts
  • messages/tr.json
  • messages/en.json
  • messages/de.json
  • messages/es.json
  • messages/pl.json
  • messages/ru.json
  • messages/pt-br.json
  • messages/ko.json
  • messages/id.json
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Run tests
🔇 Additional comments (5)
src/pages/app/p/[package].settings.vue (5)

107-107: Good! Variable declaration uses modern syntax.

You've correctly used let for variable declaration, which is the preferred approach in modern JavaScript.


747-808: UI implementation for platform-specific channel settings is well-structured.

The conditional rendering for iOS and Android channels when synchronization is disabled is well organized. Each platform has its own FormKit component with appropriate icons and edit buttons.


809-839: Good implementation of synchronized channel UI.

The UI for the synchronized default update channel includes the appropriate icon and edit button. The toggle between synchronized and platform-specific views is well implemented.


738-746: Toggle implementation for channel synchronization is well done.

The checkbox implementation for toggling between synchronized and platform-specific default channels is clean and user-friendly.


58-59: Good initialization of synchronization state.

The initialization of defaultChannelSync from the app record is well implemented with an appropriate fallback value.

@riderx
Copy link
Contributor

riderx commented May 18, 2025

🎉 Snyk checks have passed. No issues have been found so far.

security/snyk check is complete. No issues have been found. (View Details)

Copy link

@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

♻️ Duplicate comments (1)
tests/cron_plan.test.ts (1)

132-132: Action Required: Align Retry Configurations in Tests

The tests in tests/cron_plan.test.ts use the { retry: 1 } option even though the retryTest helper defaults to 30 attempts. This creates ambiguity about which retry count is actually applied. Please review and adjust the configuration—either remove the redundant { retry: 1 } if you intend to rely on the 30-attempt default in retryTest or update the default in retryTest so both layers are consistent.

🧹 Nitpick comments (5)
tests/channel.test.ts (3)

25-25: Avoid committing console.log calls to the test suite

Left-over console.log statements add noise to CI logs and can easily mask more relevant diagnostic output. They should be removed (or replaced with an assertion if you really need to inspect something).

-    console.log(data)

30-41: Strengthen the public-field assertion

You only check that public is defined.
Given the new semantics (public is always a boolean derived by the backend), it is safer to assert its type as well:

-    for (const o of data as any[]) {
-      expect(o.public).toBeDefined()
-    }
+    for (const o of data as any[]) {
+      expect(typeof o.public).toBe('boolean')
+    }

This prevents a silent regression where the field accidentally becomes, for example, null or a string.


70-78: public is obsolete in the POST payload

According to the new API contract, the public body parameter is ignored and default-channel logic happens through the android / ios flags. Keeping the stale field:

  1. Makes the test harder to read (future maintainers will wonder why it is there).
  2. Risks re-introducing server-side schema drift if somebody decides to “honour” it again.
-        public: true,
         android: true,
         ios: true,

(If the backend still requires the key due to backward-compat code that was not yet cleaned up, add a TODO explaining why).

supabase/migrations/20240513055310_base.sql (2)

2269-2290: Refactor channel seeding in reset_and_seed_app_data.
The INSERT into channels uses a hardcoded VALUES list for production, beta, development, and no_access. Consider extracting this list into a structured constant or JSON/array parameter to reduce duplication and improve maintainability if channel definitions evolve.


2307-2312: Use CTE results to set default channels directly.
Currently, the function updates apps with SELECT id FROM channels WHERE name = .... Since inserted_channels CTE already returns channel IDs, you could reuse its output to set default_channel_android and default_channel_ios in a single CTE operation, avoiding separate subqueries.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between b3a9461 and 9f411e8.

📒 Files selected for processing (26)
  • messages/de.json (5 hunks)
  • messages/en.json (5 hunks)
  • messages/es.json (5 hunks)
  • messages/fr.json (5 hunks)
  • messages/id.json (5 hunks)
  • messages/it.json (5 hunks)
  • messages/ja.json (5 hunks)
  • messages/ko.json (5 hunks)
  • messages/pl.json (5 hunks)
  • messages/pt-br.json (5 hunks)
  • messages/ru.json (5 hunks)
  • messages/tr.json (5 hunks)
  • messages/vi.json (5 hunks)
  • messages/zh-cn.json (5 hunks)
  • src/components/dashboard/AppSetting.vue (6 hunks)
  • src/components/tables/ChannelTable.vue (0 hunks)
  • src/pages/app/p/[package].channel.[channel].vue (6 hunks)
  • src/types/supabase.types.ts (4 hunks)
  • supabase/functions/_backend/utils/pg.ts (1 hunks)
  • supabase/functions/_backend/utils/postgress_schema.ts (0 hunks)
  • supabase/functions/_backend/utils/supabase.ts (1 hunks)
  • supabase/functions/_backend/utils/supabase.types.ts (4 hunks)
  • supabase/migrations/20240513055310_base.sql (5 hunks)
  • tests/channel.test.ts (3 hunks)
  • tests/channel_self.test.ts (1 hunks)
  • tests/cron_plan.test.ts (9 hunks)
💤 Files with no reviewable changes (2)
  • supabase/functions/_backend/utils/postgress_schema.ts
  • src/components/tables/ChannelTable.vue
🚧 Files skipped from review as they are similar to previous changes (20)
  • supabase/functions/_backend/utils/pg.ts
  • supabase/functions/_backend/utils/supabase.ts
  • tests/channel_self.test.ts
  • messages/tr.json
  • src/types/supabase.types.ts
  • messages/pl.json
  • supabase/functions/_backend/utils/supabase.types.ts
  • src/pages/app/p/[package].channel.[channel].vue
  • messages/zh-cn.json
  • messages/vi.json
  • messages/id.json
  • messages/de.json
  • messages/en.json
  • messages/ja.json
  • messages/fr.json
  • messages/pt-br.json
  • messages/es.json
  • messages/ko.json
  • messages/ru.json
  • messages/it.json
🧰 Additional context used
🧬 Code Graph Analysis (1)
tests/channel.test.ts (1)
tests/test-utils.ts (2)
  • BASE_URL (6-6)
  • getSupabaseClient (98-102)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Run tests
🔇 Additional comments (5)
tests/cron_plan.test.ts (2)

5-25: Good implementation of retry mechanism for flaky tests

The retryTest helper function is well-implemented with proper error handling and configurable retry attempts. This will help improve test reliability, especially for tests involving network requests and database operations.


523-581: Good restoration of deleted app metrics test

The uncommented test case for verifying that metrics are correctly counted for deleted apps is an important addition to ensure deleted apps are still considered in organizational metrics. This helps validate that the plan limits are enforced correctly even after app deletion.

supabase/migrations/20240513055310_base.sql (3)

2056-2058: Verify seed data for default channels on existing apps.
The seed inserts for com.demoadmin.app and com.demo.app set default_channel_ios and default_channel_android to NULL with default_channel_sync = true. Please confirm that downstream logic correctly handles NULL defaults when synchronization is enabled, falling back to the appropriate channel IDs.


2076-2079: Confirm channel ios/android flags in seeded channels.
The seeded channels for com.demo.app use platform flags (ios, android) to distinguish targets. Ensure that these boolean flags map to the intended platforms and that UI/API logic filters channels by these properties correctly.


2081-2081: Seed com.demo.app default channel update looks correct.
The UPDATE sets default_channel_ios = 3, default_channel_android = 1, and default_channel_sync = false, matching the seeded channels above. This aligns with the new schema.

Comment on lines +84 to +181
async function setUpdateChannelSync(value: boolean) {
if (value === true && appRef.value && ((appRef.value.default_channel_ios && appRef.value.default_channel_android && (appRef.value.default_channel_ios as any)?.id !== (appRef.value.default_channel_android as any)?.id) || (!appRef.value?.default_channel_ios && !!appRef.value?.default_channel_android) || (!appRef.value?.default_channel_android && !!appRef.value?.default_channel_ios))) {
const channels = await supabase
.from('channels')
.select('*')
.eq('app_id', props.appId)
.in('id', [
...(appRef.value.default_channel_ios ? [(appRef.value.default_channel_ios as any).id] : []),
...(appRef.value.default_channel_android ? [(appRef.value.default_channel_android as any).id] : []),
])
if (channels.error) {
toast.error(t('cannot-change-update-channel-sync'))
return
}
let buttonRole = null as string | null
displayStore.dialogOption = {
header: t('select-default-update-channel-header-before-sync-change'),
message: t('select-default-update-channel-header-before-sync-change-message'),
buttons: [
...channels.data.map(channel => ({
text: channel.name,
handler: () => {
buttonRole = channel.id.toString()
},
})),
...(!appRef.value.default_channel_ios || !appRef.value.default_channel_android
? [{
text: t('undefined'),
role: 'danger',
handler: () => {
buttonRole = 'empty'
},
}]
: []),
{
text: t('button-cancel'),
role: 'cancel',
},
],
preventAccidentalClose: true,
buttonVertical: true,
headerStyle: 'w-full text-center',
textStyle: 'min-w-full! text-center',
size: 'max-w-3xl',
buttonCenter: true,
}
displayStore.showDialog = true
const res = await displayStore.onDialogDismiss()
if (res) {
console.log('Dialog dismissed')
defaultChannelSync.value = !value
return
}
if (!buttonRole) {
console.error('No button role')
defaultChannelSync.value = !value
return
}
const channel = buttonRole === 'empty' ? { id: null, name: null } : channels.data.find(chann => chann.id.toString() === buttonRole)
if (!channel) {
console.error('Channel not found')
defaultChannelSync.value = !value
return
}
const { error: appError } = await supabase.from('apps')
.update({ default_channel_ios: channel.id, default_channel_android: channel.id })
.eq('app_id', props.appId)
if (appError) {
toast.error(t('cannot-change-update-channel-sync'))
console.error(appError)
return
}
defaultChannelSync.value = value
if (appRef.value) {
if (channel) {
appRef.value.default_channel_ios = channel as any
appRef.value.default_channel_android = channel as any
}
else {
appRef.value.default_channel_ios = null
appRef.value.default_channel_android = null
}
}
forceBump.value += 1
toast.success(t('updated-default-update-channel-sync'))
}
const { error } = await supabase.from('apps')
.update({ default_channel_sync: value })
.eq('app_id', props.appId)
if (error) {
toast.error(t('cannot-change-update-channel-sync'))
console.error(error)
}
if (appRef.value) {
appRef.value.default_channel_sync = value
forceBump.value += 1
}
}
Copy link

@coderabbitai coderabbitai bot May 18, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Cancelling the sync-change dialog still updates the DB

If the user cancels or closes the confirm dialog, you revert the local checkbox (defaultChannelSync.value = !value) but execution continues and the code below (lines 170-179) still issues

await supabase.from('apps')
  .update({ default_channel_sync: value })

This permanently flips the flag in the database – the exact opposite of what the user asked for.

Early-return on cancellation to avoid the write:

-    if (res) {
-      console.log('Dialog dismissed')
-      defaultChannelSync.value = !value
-      return
-    }
+    if (res) {                     // user clicked “cancel” (or backdrop)
+      defaultChannelSync.value = !value
+      return                       // ← stop here, DO NOT update Supabase
+    }

You’ll also want the same early return in the two other places where you revert defaultChannelSync.value (!buttonRole and channel not found).

🤖 Prompt for AI Agents
In src/components/dashboard/AppSetting.vue between lines 84 and 181, when the
user cancels or closes the sync-change dialog, the function reverts the local
checkbox state but continues to update the database, which is incorrect. To fix
this, add early return statements immediately after reverting
defaultChannelSync.value in the cancellation check, the no buttonRole check, and
the channel not found check to prevent further execution and avoid updating the
database when the user cancels or an error occurs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, no, the return is here already ??

Copy link

Choose a reason for hiding this comment

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

@WcaleNieWolny You're absolutely right! I apologize for the confusion in my review.

Looking at the code structure again, I can see that there are return statements handling the dialog cancellation properly:

if (res) {
  console.log('Dialog dismissed')
  defaultChannelSync.value = !value
  return  // ← This DOES prevent further execution
}

The database update code at the bottom (await supabase.from('apps').update({ default_channel_sync: value })) is intentionally outside the conflict-resolution if block, so it correctly runs for:

  • Enabling sync when there are no conflicts (skips dialog)
  • Disabling sync (skips dialog)
  • Enabling sync after user resolves conflicts via dialog

But it properly skips when the user cancels the dialog due to the return statements you mentioned.

My initial analysis was incorrect - the code flow is actually working as intended. Thank you for the correction!

🐰✨
/| /|
( ✌️ ✌️ )
)_) (_/(
I learned something!


✏️ Learnings added
Learnt from: WcaleNieWolny
PR: Cap-go/capgo#1107
File: src/components/dashboard/AppSetting.vue:85-182
Timestamp: 2025-05-29T18:21:17.686Z
Learning: In setUpdateChannelSync function in src/components/dashboard/AppSetting.vue, the database update code at the end is intentionally outside the conflict-resolution if block and should run for cases where sync is being toggled without conflicts or after conflicts are resolved. The return statements inside the dialog handling correctly prevent database updates when the user cancels.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@riderx riderx moved this to In Progress in Roadmap May 26, 2025
@riderx riderx added this to Roadmap May 26, 2025
WcaleNieWolny and others added 3 commits May 27, 2025 05:34
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…/capgo into move_default_channel_to_app_table
Copy link

@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: 0

♻️ Duplicate comments (2)
src/components/dashboard/AppSetting.vue (2)

133-137: ⚠️ Potential issue

Add early returns to prevent database updates after cancellation.

When the user cancels the dialog or an error occurs, the function reverts the UI state but continues to update the database, causing inconsistency.

Add early returns after reverting the checkbox state:

     if (res) {
       console.log('Dialog dismissed')
       defaultChannelSync.value = !value
+      return
-      return
     }
     if (!buttonRole) {
       console.error('No button role')
       defaultChannelSync.value = !value
+      return
-      return
     }
     if (!channel) {
       console.error('Channel not found')
       defaultChannelSync.value = !value
+      return
-      return
     }

Also applies to: 138-142, 144-148


747-747: ⚠️ Potential issue

Use the current value from v-model instead of stale appRef value.

The @change handler uses the stale previous value from appRef, which causes toggle issues after the first change.

Since you're already using v-model="defaultChannelSync", pass the current value:

-  @change="setUpdateChannelSync(!appRef?.default_channel_sync)"
+  @change="setUpdateChannelSync(defaultChannelSync)"
🧹 Nitpick comments (2)
supabase/functions/_backend/utils/pg.ts (1)

259-269: Fix indentation to match project style.

The ESLint errors indicate incorrect indentation. The conditions should be indented with 10 spaces and the closing parentheses with 8 spaces.

Apply this diff to fix the indentation:

     .where(!defaultChannel
       ? and(
-        sql`(SELECT (apps.default_channel_android = ${channelAlias.id} OR apps.default_channel_ios = ${channelAlias.id}) FROM ${schema.apps} WHERE app_id = ${app_id}) = true`,
-        eq(channelAlias.app_id, app_id),
-        eq(platform === 'android' ? channelAlias.android : channelAlias.ios, true),
-      )
-      : and(
-        eq(channelAlias.app_id, app_id),
-        eq(channelAlias.name, defaultChannel),
-      ),
+          sql`(SELECT (apps.default_channel_android = ${channelAlias.id} OR apps.default_channel_ios = ${channelAlias.id}) FROM ${schema.apps} WHERE app_id = ${app_id}) = true`,
+          eq(channelAlias.app_id, app_id),
+          eq(platform === 'android' ? channelAlias.android : channelAlias.ios, true),
+        )
+        : and(
+          eq(channelAlias.app_id, app_id),
+          eq(channelAlias.name, defaultChannel),
+        ),
     )

And similarly for the V2 function:

     .where(!defaultChannel
       ? and(
-        sql`(SELECT (apps.default_channel_android = ${channelAlias.id} OR apps.default_channel_ios = ${channelAlias.id}) FROM ${schemaV2.apps} WHERE app_id = ${app_id}) = true`,
-        eq(channelAlias.app_id, app_id),
-        eq(platform === 'android' ? channelAlias.android : channelAlias.ios, true),
-      )
-      : and(
-        eq(channelAlias.app_id, app_id),
-        eq(channelAlias.name, defaultChannel),
-      ),
+          sql`(SELECT (apps.default_channel_android = ${channelAlias.id} OR apps.default_channel_ios = ${channelAlias.id}) FROM ${schemaV2.apps} WHERE app_id = ${app_id}) = true`,
+          eq(channelAlias.app_id, app_id),
+          eq(platform === 'android' ? channelAlias.android : channelAlias.ios, true),
+        )
+        : and(
+          eq(channelAlias.app_id, app_id),
+          eq(channelAlias.name, defaultChannel),
+        ),
     )

Also applies to: 385-395

🧰 Tools
🪛 ESLint

[error] 261-261: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 262-262: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 263-263: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 264-264: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 266-266: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 267-267: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 268-268: Expected indentation of 8 spaces but found 6.

(style/indent)

supabase/functions/_backend/public/channel/post.ts (1)

104-104: Fix brace style to follow project conventions.

The closing braces should be on separate lines from the subsequent blocks.

Apply this diff to fix the brace style:

       }
-    } else {
+    }
+    else {

And:

     }
     return c.json(BRES)
-  } catch (e) {
+  }
+  catch (e) {

Also applies to: 130-130

🧰 Tools
🪛 ESLint

[error] 104-104: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9573aa5 and a51bd53.

📒 Files selected for processing (3)
  • src/components/dashboard/AppSetting.vue (7 hunks)
  • supabase/functions/_backend/public/channel/post.ts (1 hunks)
  • supabase/functions/_backend/utils/pg.ts (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
supabase/functions/_backend/public/channel/post.ts (2)
supabase/functions/_backend/utils/supabase.ts (2)
  • updateOrCreateChannel (110-147)
  • supabaseAdmin (59-68)
supabase/functions/_backend/utils/hono.ts (1)
  • BRES (208-208)
🪛 ESLint
supabase/functions/_backend/public/channel/post.ts

[error] 104-104: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)


[error] 130-130: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)

supabase/functions/_backend/utils/pg.ts

[error] 261-261: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 262-262: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 263-263: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 264-264: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 266-266: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 267-267: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 268-268: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 387-387: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 388-388: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 389-389: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 390-390: Expected indentation of 8 spaces but found 6.

(style/indent)


[error] 392-392: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 393-393: Expected indentation of 10 spaces but found 8.

(style/indent)


[error] 394-394: Expected indentation of 8 spaces but found 6.

(style/indent)

🔇 Additional comments (2)
supabase/functions/_backend/public/channel/post.ts (1)

79-129: Well-implemented default channel management logic!

The code correctly handles both setting and unsetting default channels based on the public flag, addressing the previous review comments. The implementation properly checks platform support before updating defaults.

🧰 Tools
🪛 ESLint

[error] 104-104: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)

src/components/dashboard/AppSetting.vue (1)

312-422: Excellent implementation of default channel management UI!

The new functionality provides a clean interface for managing default channels with proper synchronization controls. The conditional rendering based on sync state and the ability to set/unset channels per platform or synchronously is well implemented.

Also applies to: 742-843

@WcaleNieWolny
Copy link
Contributor Author

Cap-go/supabase-d1-sync#2

@@ -2,6 +2,28 @@ import { randomUUID } from 'node:crypto'
import { afterAll, beforeEach, describe, expect, it } from 'vitest'
import { BASE_URL, getBaseData, getSupabaseClient, postUpdate, PRODUCT_ID, resetAndSeedAppDataStats, resetAppData, resetAppDataStats, TEST_EMAIL, USER_ID } from './test-utils.ts'

// Helper function to retry tests with delay
function retryTest(testFn: () => Promise<void>, attempts = 30, delayMs = 3000) {
Copy link
Contributor

Choose a reason for hiding this comment

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

why this was added?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It made the tests work, like, actually retry. It was a brute force method to get the tests to work, I think. I don't fully remember

@WcaleNieWolny WcaleNieWolny moved this from In Progress to Todo in Roadmap May 28, 2025
@WcaleNieWolny WcaleNieWolny moved this from Todo to In review in Roadmap May 28, 2025
@riderx riderx moved this from In review to In Progress in Roadmap May 29, 2025
@riderx riderx removed the status in Roadmap May 29, 2025
@riderx riderx moved this to In Progress in Roadmap May 29, 2025
@riderx riderx assigned WcaleNieWolny and unassigned riderx May 29, 2025
@riderx riderx moved this from In Progress to Todo in Roadmap May 29, 2025
Copy link

@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: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 921a24a and b420b67.

📒 Files selected for processing (6)
  • src/pages/app/p/[package].channel.[channel].vue (6 hunks)
  • src/types/supabase.types.ts (4 hunks)
  • supabase/functions/_backend/utils/pg.ts (2 hunks)
  • supabase/functions/_backend/utils/supabase.ts (1 hunks)
  • supabase/functions/_backend/utils/supabase.types.ts (4 hunks)
  • supabase/seed.sql (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • supabase/functions/_backend/utils/supabase.ts
  • src/types/supabase.types.ts
  • supabase/functions/_backend/utils/pg.ts
  • src/pages/app/p/[package].channel.[channel].vue
  • supabase/functions/_backend/utils/supabase.types.ts
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Run tests
🔇 Additional comments (5)
supabase/seed.sql (5)

99-101: Initialize default channel fields for seeded apps
The INSERT INTO public.apps now includes the new default_channel_ios, default_channel_android, and default_channel_sync columns. Setting both channel IDs to NULL with default_channel_sync = true is sensible for demo apps that start in synced mode without a preselected channel.


119-123: Seed channel entries without the old public flag
The INSERT INTO public.channels block correctly omits the removed is_public column, uses 't' for disable_auto_update_under_native and 'major'::disable_update for disable_auto_update, and sets the ios/android booleans as intended. This aligns with the new schema.


124-124: Set per-app default channels for com.demo.app
The UPDATE public.apps statement assigns channel ID 3 for iOS, channel ID 1 for Android, and toggles default_channel_sync to false as expected for this demo app.


317-317: Align reset function’s channel insert with schema changes
In reset_and_seed_app_data, the INSERT INTO public.channels now matches the updated column list (without public), ensuring the reset flow won’t break after the migration.


334-338: Define channel tuples for seeded app data
The CTE’s VALUES clause correctly enumerates production, beta, development, and no_access channels with the right version_name, ios/android flags, and created_by UUIDs.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@WcaleNieWolny WcaleNieWolny moved this from Todo to In Progress in Roadmap May 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

2 participants