Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,15 @@
"compat-verdict-compatible-detail": "This bundle can be delivered over-the-air to devices running {bundle}.",
"compat-verdict-incompatible": "Not compatible ({count})",
"compat-verdict-incompatible-detail": "Some packages require an app store update, so this bundle cannot be delivered over-the-air.",
"compat-fix-explanation": "Capgo delivers your web and JavaScript code instantly, but it can't change native code. Devices still running the previous native build ({bundle}) may crash or behave incorrectly on this update until your users install a new native build from the app stores.",
"compat-fix-how-title": "How to fix it",
"compat-fix-manage-channels": "Manage channels",
"compat-fix-rebuild-cta": "Rebuild with Capgo Builder",
"compat-fix-rebuild-detail": "Ship a new native build to the App Store and Play Store so your users' app has the matching native code. Capgo Builder can produce one in minutes — no local Xcode or Android Studio setup needed.",
"compat-fix-rollback-detail": "In the meantime, roll your channel back to {bundle} so users stay on the last compatible bundle until the new native build is live.",
"compat-fix-title": "This bundle needs a new native build",
"compat-fix-why-detail": "Over-the-air updates can only replace the web layer — JavaScript, HTML and CSS. Native plugins are compiled into the app binary, which only the App Store and Play Store can update. When a bundle's native packages differ from the build installed on a device, its JavaScript can call native APIs that aren't there — which is why it may crash.",
"compat-fix-why-title": "Why native changes need an app-store update",
"dependencies": "Dependencies",
"dependencies-added-packages": "Added",
"dependencies-changed-packages": "Changed",
Expand Down
73 changes: 72 additions & 1 deletion src/pages/app/[app].bundle.[bundle].dependencies.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
import IconExternalLink from '~icons/heroicons/arrow-top-right-on-square'
import IconCheckCircle from '~icons/heroicons/check-circle'
import IconChevronRight from '~icons/heroicons/chevron-right'
import IconPuzzle from '~icons/heroicons/puzzle-piece'
import IconAlertCircle from '~icons/lucide/alert-circle'
import { comparePackages, summarizeCompatibility } from '~/services/bundleCompatibility'
import { useSupabase } from '~/services/supabase'
import { pushEvent } from '~/services/posthog'
import { getLocalConfig, useSupabase } from '~/services/supabase'
import { useDisplayStore } from '~/stores/display'

type VersionRow = Pick<Database['public']['Tables']['app_versions']['Row'], 'id' | 'name' | 'created_at' | 'manifest_count' | 'app_id'>
Expand Down Expand Up @@ -40,6 +42,9 @@ const router = useRouter()
const displayStore = useDisplayStore()
const { t } = useI18n()
const supabase = useSupabase()
const config = getLocalConfig()
// Capgo Builder sell deck (5-slide modal), opened from the incompatible-bundle CTA.
const builderOpen = ref(false)
const packageId = ref<string>('')
const id = ref<number>(0)
const loading = ref(true)
Expand Down Expand Up @@ -120,6 +125,13 @@ function openNpmPackage(packageName: string) {
window.open(`https://www.npmjs.com/package/${packageName}`, '_blank', 'noopener,noreferrer')
}

// Open the Capgo Builder sell deck from the incompatible-bundle CTA, and track the
// click so we can measure this surface against the other Builder entry points.
function openBuilder() {
builderOpen.value = true
pushEvent('builder_cta_compatibility_clicked', config.supaHost, { app_id: packageId.value })
}

async function fetchBaselinePackages(versionId: number) {
if (!packageId.value)
return []
Expand Down Expand Up @@ -407,6 +419,64 @@ watch(bundleRouteKey, async (key) => {
</p>
</div>

<!-- Fix guidance + Capgo Builder CTA, shown only when the diff is incompatible -->
<div
v-if="compareVersionId && !tableLoading && comparisons.length > 0 && !compatibilitySummary.compatible"
class="mt-3 rounded-lg border border-slate-200 bg-slate-50 px-4 py-4 dark:border-slate-700 dark:bg-slate-900/60"
>
<div class="flex items-start gap-3">
<IconAlertCircle class="mt-0.5 h-5 w-5 shrink-0 text-amber-500 dark:text-amber-400" />
<div class="min-w-0">
<h4 class="text-sm font-semibold text-slate-900 dark:text-white">
{{ t('compat-fix-title') }}
</h4>
<p class="mt-1 text-xs leading-relaxed text-slate-600 dark:text-slate-300">
{{ t('compat-fix-explanation', { bundle: selectedCompareVersion?.name ?? t('unknown') }) }}
</p>
</div>
</div>

<div class="mt-4">
<div class="text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400">
{{ t('compat-fix-how-title') }}
</div>
<div class="mt-2 flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
<p class="min-w-0 text-xs leading-relaxed text-slate-600 dark:text-slate-300">
{{ t('compat-fix-rebuild-detail') }}
</p>
<button
type="button"
class="inline-flex shrink-0 items-center justify-center gap-1 whitespace-nowrap rounded-md bg-blue-500 px-4 py-2 text-sm font-semibold text-white transition-colors hover:bg-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-300"
@click="openBuilder"
>
{{ t('compat-fix-rebuild-cta') }} →
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
</button>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
</div>
<p class="mt-3 text-xs leading-relaxed text-slate-600 dark:text-slate-300">
{{ t('compat-fix-rollback-detail', { bundle: selectedCompareVersion?.name ?? t('unknown') }) }}
<button
type="button"
class="ml-1 font-medium text-blue-600 hover:underline dark:text-blue-400"
@click="router.push(`/app/${packageId}/channels`)"
>
{{ t('compat-fix-manage-channels') }}
</button>
</p>
</div>

<details class="group mt-4">
<summary class="cursor-pointer list-none text-xs font-medium text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200">
<span class="inline-flex items-center gap-1">
<IconChevronRight class="h-3.5 w-3.5 transition-transform group-open:rotate-90" />
{{ t('compat-fix-why-title') }}
</span>
</summary>
<p class="mt-2 pl-5 text-xs leading-relaxed text-slate-600 dark:text-slate-300">
{{ t('compat-fix-why-detail') }}
</p>
</details>
</div>

<p v-if="compareStatusMessage" class="mt-2 text-xs text-slate-500 dark:text-slate-400">
{{ compareStatusMessage }}
</p>
Expand Down Expand Up @@ -550,6 +620,7 @@ watch(bundleRouteKey, async (key) => {
{{ t('back-to-bundles') }}
</button>
</div>
<BuilderPresentationModal :open="builderOpen" :app-id="packageId" @close="builderOpen = false" />
</div>
</template>

Expand Down
Loading