Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
139 changes: 139 additions & 0 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,145 @@
"builder-promo-banner-title": "There's more in your plan than you're using.",
"builder-promo-banner-subtitle": "Native iOS & Android builds are included in your Capgo plan — explore Capgo Builder.",
"builder-promo-banner-cta": "Learn more",
"priority-support-promo-title": "Priority help for Capgo plugins",
"priority-support-promo-paying-subtitle": "Report bugs, request features, or ask for help. Paying Capgo users receive priority support.",
"priority-support-promo-trial-subtitle": "Report bugs, request features, or ask for help. Trial users receive priority support.",
"priority-support-promo-cta": "Link your GitHub account",
"priority-support-dialog-label": "Priority support for Capgo plugins",
"priority-support-progress-label": "Priority support presentation progress",
"priority-support-close": "Close",
"priority-support-back": "Back",
"priority-support-next": "Next",
"priority-support-s1-title": "Priority support, from issue to resolution.",
"priority-support-s1-lead": "Your GitHub issue is recognized automatically, and the Capgo plugin team is notified.",
"priority-support-s1-item1": "Your priority status is confirmed",
"priority-support-s1-item2": "The right maintainers see your issue",
"priority-support-s1-item3": "Replies and fixes remain visible on GitHub",
"priority-support-s1-chip": "Support from the people building the plugin",
"priority-support-s2-title": "Your feedback matters.",
"priority-support-s2-paying-lead": "Bugs, feature requests, and questions are all welcome. Your Capgo plan gives them a direct path to the plugin team.",
"priority-support-s2-trial-lead": "Bugs, feature requests, and questions are all welcome. Your Capgo trial gives them a direct path to the plugin team.",
"priority-support-s2-item1": "Report behavior that doesn’t look right",
"priority-support-s2-item2": "Request features that would help your app",
"priority-support-s2-item3": "Ask for guidance from the maintainers",
"priority-support-s2-paying-chip": "Priority support is included in your plan",
"priority-support-s2-trial-chip": "Priority support is included during your trial",
"priority-support-s3-title": "Link GitHub to activate priority support.",
"priority-support-s3-lead": "Connect the GitHub account you use to open plugin issues. Capgo will recognize your requests automatically.",
"priority-support-s3-item1": "Your GitHub identity is matched to your Capgo account",
"priority-support-s3-item2": "Priority support is recognized automatically",
"priority-support-s3-caption": "Recognized when it matters",
"priority-support-demo-issue-primary": "Google sign-in is cancelled on Android 15",
"priority-support-demo-issue-secondary": "Improve Android callback handling",
"priority-support-demo-issue-third": "Document provider configuration",
"priority-support-demo-issue-fourth": "Expose refresh token errors",
"priority-support-demo-issue-fifth": "Clarify redirect URI setup",
"priority-support-demo-opened-now": "opened just now",
"priority-support-demo-opened-two-days": "opened 2 days ago",
"priority-support-demo-opened-week": "opened last week",
"priority-support-demo-opened-month": "opened last month",
"priority-support-story-github-handle": "your-handle",
"priority-support-story-your-mac": "Your Mac",
"priority-support-story-support-mac": "Capgo Support Team Mac",
"priority-support-story-search": "Type / to search",
"priority-support-story-public": "Public",
"priority-support-story-code": "Code",
"priority-support-story-issues": "Issues",
"priority-support-story-pull-requests": "Pull requests",
"priority-support-story-actions": "Actions",
"priority-support-story-new-issue": "New issue",
"priority-support-story-feature-request": "feature request",
"priority-support-story-add-title": "Add a title",
"priority-support-story-write": "Write",
"priority-support-story-preview": "Preview",
"priority-support-story-assignees": "Assignees",
"priority-support-story-assignee": "Assignee",
"priority-support-story-no-one": "No one",
"priority-support-story-labels": "Labels",
"priority-support-story-label": "Label",
"priority-support-story-projects": "Projects",
"priority-support-story-none-yet": "None yet",
"priority-support-story-microsoft-title": "Feature request: support Sign in with Microsoft on Android",
"priority-support-story-microsoft-body": "Could the plugin support brokered MSAL sign-in through Microsoft Authenticator on Android?",
"priority-support-story-submit": "Submit new issue",
"priority-support-story-opened-issue-now": "{'@'}acme-mobile opened this issue just now",
"priority-support-story-commented-now": "commented just now",
"priority-support-story-author": "Author",
"priority-support-story-bot": "bot",
"priority-support-story-bot-reply": "Priority support confirmed for {'@'}acme-mobile. The Capgo plugin team has been notified and will follow up here.",
"priority-support-story-priority-assigned-event": "capgo-bot added priority support and assigned Capgo Support",
"priority-support-story-capgo-support": "Capgo Support",
"priority-support-story-priority-support": "Priority support",
"priority-support-story-development": "Development",
"priority-support-story-no-branches-yet": "No branches yet",
"priority-support-story-finder": "Finder",
"priority-support-story-mail": "Mail",
"priority-support-story-now": "now",
"priority-support-story-notification-title": "Priority issue received",
"priority-support-story-notification-body": "capacitor-social-login #418 · {'@'}acme-mobile",
"priority-support-story-favorites": "Favorites",
"priority-support-story-inbox": "Inbox",
"priority-support-story-priority": "Priority",
"priority-support-story-sent": "Sent",
"priority-support-story-on-my-mac": "On My Mac",
"priority-support-story-archive": "Archive",
"priority-support-story-email-subject": "[Priority] capacitor-social-login #418",
"priority-support-story-email-snippet": "Feature request: support Sign in with Microsoft…",
"priority-support-story-release-published": "New release published",
"priority-support-story-yesterday": "Yesterday",
"priority-support-story-support-digest": "Weekly support digest",
"priority-support-story-issues-resolved": "3 plugin issues resolved",
"priority-support-story-email-from": "From: Capgo Bot <support{'@'}capgo.app>",
"priority-support-story-email-paying-body": "Linked paying Capgo account · {'@'}acme-mobile",
"priority-support-story-email-trial-body": "Linked Capgo trial account · {'@'}acme-mobile",
"priority-support-story-open-github": "Open on GitHub",
"priority-support-story-explorer": "EXPLORER",
"priority-support-story-thinking": "Thinking about the solution",
"priority-support-story-hacking": "Hacking the matrix",
"priority-support-story-opening-pr": "Opening a pull request",
"priority-support-story-problems": "PROBLEMS",
"priority-support-story-output": "OUTPUT",
"priority-support-story-terminal": "TERMINAL",
"priority-support-story-ports": "PORTS",
"priority-support-story-tests-passed": "Tests passed · Android build successful",
"priority-support-story-editor-status": "Ln 18, Col 7 UTF-8 Kotlin",
"priority-support-story-pr-title": "Add brokered Microsoft sign-in on Android",
"priority-support-story-open": "Open",
"priority-support-story-wants-to-merge": "capgo-support wants to merge 3 commits into main",
"priority-support-story-merged-commits": "capgo-support merged 3 commits into main just now",
"priority-support-story-conversation": "Conversation",
"priority-support-story-commits": "Commits",
"priority-support-story-checks-tab": "Checks",
"priority-support-story-files-changed": "Files changed",
"priority-support-story-pr-summary": "Adds brokered Microsoft sign-in for managed Android devices.",
"priority-support-story-refs": "Refs",
"priority-support-story-checks": "All checks have passed",
"priority-support-story-ready": "This branch is ready to merge",
"priority-support-story-no-conflicts": "No conflicts with the base branch",
"priority-support-story-merge": "Merge pull request",
"priority-support-story-confirm-merge": "Confirm merge",
"priority-support-story-create-merge-commit": "Create a merge commit for 3 commits into main.",
"priority-support-story-merged": "Merged",
"priority-support-story-merge-success": "Pull request successfully merged and closed",
"priority-support-story-safe-delete": "You’re all set — the branch can be safely deleted.",
"priority-support-story-closed": "Closed",
"priority-support-story-milestones": "Milestones",
"priority-support-story-opened-now-by-acme": "opened just now by acme-mobile",
"priority-support-story-google-issue-title": "Google sign-in is cancelled on Android 15",
"priority-support-story-opened-yesterday-by-jules": "opened yesterday by jules-dev",
"priority-support-story-bug": "bug",
"priority-support-story-redirect-issue-title": "Clarify redirect URI setup",
"priority-support-story-opened-last-week-by-native-labs": "opened last week by native-labs",
"priority-support-story-documentation": "documentation",
"priority-support-story-linked-merged-pr": "capgo-support linked merged pull request #482",
"priority-support-story-attach-files": "Attach files by dragging & dropping",
"priority-support-story-maintainer-reply": "Implemented and merged in #482. This will ship in the next capacitor-social-login release.",
"priority-support-story-member": "Member",
"priority-support-story-closed-completed": "capgo-support closed this as completed just now",
"priority-support-story-thanks": "Thank you for the swift fix 🥰",
"priority-support-story-comment-close": "Close with comment",
"priority-support-story-example-flow": "Example priority issue flow",
"priority-support-story-replay": "Replay priority issue flow",
"builder-promo-close": "Close",
"builder-promo-back": "Back",
"builder-promo-next": "Next",
Expand Down
76 changes: 76 additions & 0 deletions playwright/e2e/priority-support.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { getDailyPromoVariant, getUtcPromoDay } from '../../src/services/prioritySupportPromo'
import { expect, test } from '../support/commands'

function getSupportPromoTime() {
const start = new Date('2026-01-01T12:00:00.000Z')
for (let day = 0; day < 366; day += 1) {
const candidate = new Date(start)
candidate.setUTCDate(start.getUTCDate() + day)
if (getDailyPromoVariant(getUtcPromoDay(candidate)) === 'support')
return candidate
}
throw new Error('Daily promo selection did not produce a support day')
}

test.describe('Priority plugin support', () => {
test.beforeEach(async ({ page }) => {
await page.login('test@capgo.app', 'testtest')
})

test('links GitHub from the priority-support presentation', async ({ page }) => {
const supportTime = getSupportPromoTime()
await page.clock.setFixedTime(supportTime)
await page.emulateMedia({ reducedMotion: 'no-preference' })
await page.goto('/app/com.demo.app')

const banner = page.getByRole('button', { name: /Priority help for Capgo plugins/ })
await expect(banner).toBeVisible()
const overflowBefore = await page.locator('body').evaluate(body => body.style.overflow)
await banner.click()

const presentation = page.getByRole('dialog')
const progress = presentation.getByRole('progressbar')
const slides = presentation.locator('.ps-slide')
const shownIndexes = () => slides.evaluateAll(elements => elements
.flatMap((element, index) => element.classList.contains('show') ? [index] : []))
const visibleIndexes = () => slides.evaluateAll(elements => elements
.map((element, index) => ({ index, style: getComputedStyle(element) }))
.filter(({ style }) => style.visibility !== 'hidden' && Number(style.opacity) > 0.99)
.map(({ index }) => index))
await expect(progress).toHaveAttribute('aria-valuenow', '1')

const nextButton = presentation.getByRole('button', { name: /^Next/ })
await expect(nextButton).toBeVisible()
await page.clock.pauseAt(supportTime)
await nextButton.evaluate(button => (button as HTMLButtonElement).click())
expect(await shownIndexes()).toEqual([0, 1])
expect(await visibleIndexes()).toEqual([0, 1])
expect(await progress.getAttribute('aria-valuenow')).toBe('2')

await page.emulateMedia({ reducedMotion: 'reduce' })
await expect.poll(shownIndexes).toEqual([1])
await expect.poll(visibleIndexes).toEqual([1])
await expect(slides.nth(0)).toHaveAttribute('aria-hidden', 'true')
await expect(slides.nth(1)).toHaveAttribute('aria-hidden', 'false')
await expect(slides.nth(2)).toHaveAttribute('aria-hidden', 'true')
await page.clock.resume()

await presentation.locator('.ps-x').click()
await expect(presentation).toBeHidden()
expect(await page.locator('body').evaluate(body => body.style.overflow)).toBe(overflowBefore)

await banner.click()
await expect(progress).toHaveAttribute('aria-valuenow', '1')
await presentation.getByRole('button', { name: /^Next/ }).click()
await presentation.getByRole('button', { name: /^Next/ }).click()
await expect(progress).toHaveAttribute('aria-valuenow', '3')
await presentation.getByRole('button', { name: 'Link your GitHub account', exact: true }).click()

await expect(page).toHaveURL(/\/settings\/account$/)
await expect(page.getByRole('heading', { name: 'GitHub username', exact: true })).toBeVisible()
await expect(page.locator('#github-username-input')).toBeVisible()

await page.getByRole('button', { name: 'Cancel', exact: true }).click()
await expect(page.getByRole('heading', { name: 'GitHub username', exact: true })).not.toBeVisible()
})
})
2 changes: 1 addition & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<script setup lang="ts">
import { defineAsyncComponent, watch } from 'vue'
import DialogV2 from '~/components/DialogV2.vue'

const Toast = defineAsyncComponent(() => import('~/components/Toast.vue'))
const DialogV2 = defineAsyncComponent(() => import('~/components/DialogV2.vue'))

const route = useRoute()
const display = useDisplayStore()
Expand Down
3 changes: 3 additions & 0 deletions src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ declare module 'vue' {
PaymentRequiredModal: typeof import('./components/PaymentRequiredModal.vue')['default']
PeriodDaySelector: typeof import('./components/dashboard/PeriodDaySelector.vue')['default']
PreviewDownloadPrompt: typeof import('./components/PreviewDownloadPrompt.vue')['default']
PrioritySupportPresentationModal: typeof import('./components/dashboard/PrioritySupportPresentationModal.vue')['default']
PrioritySupportPromoBanner: typeof import('./components/dashboard/PrioritySupportPromoBanner.vue')['default']
PrioritySupportStory: typeof import('./components/dashboard/PrioritySupportStory.vue')['default']
RbacPermissionOnlyModal: typeof import('./components/RbacPermissionOnlyModal.vue')['default']
ReleaseBanner: typeof import('./components/dashboard/ReleaseBanner.vue')['default']
ReleaseStatusCard: typeof import('./components/dashboard/ReleaseStatusCard.vue')['default']
Expand Down
48 changes: 28 additions & 20 deletions src/components/dashboard/BuilderPromoBanner.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import { pushEvent } from '~/services/posthog'
import { getLocalConfig, useSupabase } from '~/services/supabase'
import { useOrganizationStore } from '~/stores/organization'

const props = defineProps<{ appId: string }>()
const props = defineProps<{ appId: string, active: boolean }>()
const emit = defineEmits<{ eligibility: [eligible: boolean], ready: [ready: boolean] }>()

const { t } = useI18n()
const config = getLocalConfig()
Expand All @@ -42,9 +43,13 @@ function openModal() {
async function checkEligibility() {
const token = ++reqToken
eligible.value = false
emit('eligibility', false)
emit('ready', false)
const appId = props.appId
if (!appId)
if (!appId) {
emit('ready', true)
return
}
try {
// currentOrganization is unreliable on app-based URLs (the app may belong
// to an org other than the selected one), so resolve the app's owning org
Expand All @@ -64,14 +69,16 @@ async function checkEligibility() {
if (error || (count ?? 0) > 0)
return
eligible.value = true
if (!shownTracked) {
shownTracked = true
track('builder_promo_banner_shown')
}
}
catch (e) {
console.error('[BuilderPromoBanner] eligibility check failed', e)
}
finally {
if (token === reqToken) {
emit('eligibility', eligible.value)
emit('ready', true)
}
}
}

watch(
Expand All @@ -83,12 +90,26 @@ watch(
},
{ immediate: true },
)

watch(
() => [props.active, eligible.value] as const,
([active, isEligible]) => {
if (active && isEligible && !shownTracked) {
shownTracked = true
track('builder_promo_banner_shown')
}
else if (!active) {
shownTracked = false
}
},
{ immediate: true },
)
</script>

<template>
<div>
<div
v-if="eligible"
v-if="active && eligible"
class="animate-fade-in mb-4 flex cursor-pointer flex-col gap-4 rounded-lg border border-blue-200/80 bg-blue-100/40 px-5 py-3 shadow-sm transition-shadow hover:shadow-md dark:border-blue-700/70 dark:bg-[#121b3a] sm:flex-row sm:items-center sm:justify-between"
role="button"
tabindex="0"
Expand Down Expand Up @@ -157,17 +178,4 @@ watch(
background: #7dd3fc;
box-shadow: none;
}
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(-8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in {
animation: fade-in 0.3s ease-out;
}
</style>
19 changes: 1 addition & 18 deletions src/components/dashboard/DeploymentBanner.vue
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ defineExpose({
-->
<div
v-if="showBanner"
class="mb-4 flex flex-col gap-4 rounded-lg border border-blue-200/80 bg-blue-100/40 px-5 py-3 shadow-sm animate-fade-in dark:border-blue-700/70 dark:bg-[#121b3a] sm:flex-row sm:items-center sm:justify-between"
class="mb-4 flex flex-col gap-4 rounded-lg border border-blue-200/80 bg-blue-100/40 px-5 py-3 shadow-sm animate-fade-in animate-fade-in-from-10 dark:border-blue-700/70 dark:bg-[#121b3a] sm:flex-row sm:items-center sm:justify-between"
>
<!-- Left side: Info icon and message -->
<div class="flex items-center gap-3">
Expand Down Expand Up @@ -527,20 +527,3 @@ defineExpose({
</div>
</Teleport>
</template>

<style scoped>
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

.animate-fade-in {
animation: fade-in 0.3s ease-out;
}
</style>
Loading
Loading