mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-13 15:51:00 +00:00
feat(native): a plan picker for TREK Cloud, with live prices
The Get TREK Cloud path now leads to a plan picker that reads the live plans from the site (/api/plans), so the prices and quotas always match the website. Monthly and yearly in a segmented toggle, the year marked with its saving, each plan a card with its price and what it includes. Subscribe is wired to finish on the web order for now; it becomes an Apple/Google in-app purchase once the store products exist. New strings across all locales (en and de translated).
This commit is contained in:
+127
-2
@@ -246,8 +246,11 @@ function feature(path: string, text: string): HTMLElement {
|
||||
}
|
||||
|
||||
function showCloud(opts: { back?: boolean } = {}): void {
|
||||
const cta = el('a', { className: 'btn', href: CLOUD_URL, target: '_blank', rel: 'noopener noreferrer' }, t('native.cloud.cta'), icon(I.chevron, 20, 'chev'))
|
||||
cta.addEventListener('click', () => void tap())
|
||||
const cta = el('button', { type: 'button', className: 'btn' }, t('native.cloud.cta'), icon(I.chevron, 20, 'chev'))
|
||||
cta.addEventListener('click', () => {
|
||||
void tap()
|
||||
showPlans()
|
||||
})
|
||||
|
||||
const have = el('button', { type: 'button', className: 'linkbtn' }, t('native.cloud.haveServer'))
|
||||
have.addEventListener('click', () => {
|
||||
@@ -271,6 +274,128 @@ function showCloud(opts: { back?: boolean } = {}): void {
|
||||
)
|
||||
}
|
||||
|
||||
// ── Choose a plan (TREK Cloud) ──────────────────────────────────────────────
|
||||
interface CloudPlan {
|
||||
id: string
|
||||
name: string
|
||||
monthlyCents: number
|
||||
yearlyCents: number
|
||||
aiDocuments: number
|
||||
storageGb: number
|
||||
}
|
||||
|
||||
function money(cents: number): string {
|
||||
return new Intl.NumberFormat(locale, { style: 'currency', currency: 'EUR' }).format(cents / 100)
|
||||
}
|
||||
|
||||
async function fetchPlans(): Promise<CloudPlan[] | null> {
|
||||
try {
|
||||
const res = await get(`${CLOUD_URL}/api/plans`)
|
||||
const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data
|
||||
const plans = (data as { plans?: CloudPlan[] })?.plans
|
||||
return Array.isArray(plans) && plans.length ? plans : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function subscribe(plan: CloudPlan): void {
|
||||
void tap(ImpactStyle.Medium)
|
||||
// Phase 2: the subscription is an Apple/Google in-app purchase. Until the
|
||||
// store products exist, the order is finished on the web for this plan.
|
||||
window.open(`${CLOUD_URL}/order?plan=${encodeURIComponent(plan.id)}`, '_blank', 'noopener')
|
||||
}
|
||||
|
||||
function planCard(plan: CloudPlan, interval: 'month' | 'year', featured: boolean): HTMLElement {
|
||||
const price = interval === 'year' ? plan.yearlyCents : plan.monthlyCents
|
||||
const per = interval === 'year' ? t('native.plans.perYear') : t('native.plans.perMonth')
|
||||
const feat = (text: string) => el('span', {}, icon(I.check, 16), text)
|
||||
const sub = el('button', { type: 'button', className: 'btn' }, t('native.plans.subscribe'))
|
||||
sub.addEventListener('click', () => subscribe(plan))
|
||||
return el(
|
||||
'div',
|
||||
{ className: featured ? 'plan featured' : 'plan' },
|
||||
el(
|
||||
'div',
|
||||
{ className: 'plan-top' },
|
||||
el('span', { className: 'plan-name' }, plan.name),
|
||||
el('span', { className: 'plan-price' }, money(price), el('small', {}, per)),
|
||||
),
|
||||
el(
|
||||
'div',
|
||||
{ className: 'plan-feats' },
|
||||
feat(t('native.plans.unlimited')),
|
||||
feat(t('native.plans.storage', { n: String(plan.storageGb) })),
|
||||
feat(plan.aiDocuments > 0 ? t('native.plans.aiDocs', { n: String(plan.aiDocuments) }) : t('native.plans.noAi')),
|
||||
),
|
||||
sub,
|
||||
)
|
||||
}
|
||||
|
||||
function showPlans(opts: { back?: boolean } = {}): void {
|
||||
let interval: 'month' | 'year' = 'month'
|
||||
let plans: CloudPlan[] | null = null
|
||||
|
||||
const content = el('div', { className: 'loading' }, el('span', { className: 'spinner' }))
|
||||
|
||||
const monthBtn = el('button', { type: 'button' }, t('native.plans.monthly'))
|
||||
const yearBtn = el('button', { type: 'button' }, t('native.plans.yearly'), el('span', { className: 'save' }, t('native.plans.save')))
|
||||
const seg = el('div', { className: 'segmented' }, monthBtn, yearBtn)
|
||||
|
||||
const paintSeg = () => {
|
||||
monthBtn.setAttribute('aria-pressed', String(interval === 'month'))
|
||||
yearBtn.setAttribute('aria-pressed', String(interval === 'year'))
|
||||
}
|
||||
const renderList = () => {
|
||||
const list = plans
|
||||
if (!list) return
|
||||
content.className = 'plans'
|
||||
content.replaceChildren(...list.map((p, i) => planCard(p, interval, i === list.length - 1)))
|
||||
}
|
||||
const choose = (next: 'month' | 'year') => {
|
||||
if (interval === next) return
|
||||
void tap()
|
||||
interval = next
|
||||
paintSeg()
|
||||
renderList()
|
||||
}
|
||||
monthBtn.addEventListener('click', () => choose('month'))
|
||||
yearBtn.addEventListener('click', () => choose('year'))
|
||||
paintSeg()
|
||||
|
||||
const load = async () => {
|
||||
plans = await fetchPlans()
|
||||
if (plans) {
|
||||
renderList()
|
||||
return
|
||||
}
|
||||
content.className = ''
|
||||
const retry = el('button', { type: 'button', className: 'btn btn-ghost' }, t('native.plans.retry'))
|
||||
retry.addEventListener('click', () => {
|
||||
void tap()
|
||||
content.className = 'loading'
|
||||
content.replaceChildren(el('span', { className: 'spinner' }))
|
||||
void load()
|
||||
})
|
||||
content.replaceChildren(el('p', { className: 'error' }, t('native.plans.loadError')), retry)
|
||||
}
|
||||
|
||||
show(
|
||||
el(
|
||||
'div',
|
||||
{},
|
||||
topbar(() => showCloud({ back: true })),
|
||||
el('h1', { className: 'title-sm' }, t('native.plans.title')),
|
||||
el('p', { className: 'sub' }, t('native.plans.sub')),
|
||||
seg,
|
||||
content,
|
||||
el('p', { className: 'note' }, t('native.plans.soon')),
|
||||
),
|
||||
opts,
|
||||
)
|
||||
void load()
|
||||
}
|
||||
|
||||
// ── Shared actions ──────────────────────────────────────────────────────────
|
||||
async function runDemo(button: HTMLButtonElement, error?: HTMLElement): Promise<void> {
|
||||
void tap()
|
||||
|
||||
@@ -517,3 +517,138 @@ a.linkbtn {
|
||||
transition-duration: 0.001ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Plan picker (cloud subscription) ────────────────────────────────────── */
|
||||
.segmented {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
padding: 4px;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--edge);
|
||||
border-radius: 13px;
|
||||
align-self: stretch;
|
||||
}
|
||||
.segmented button {
|
||||
flex: 1;
|
||||
font: inherit;
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
padding: 9px;
|
||||
border: 0;
|
||||
border-radius: 9px;
|
||||
background: none;
|
||||
color: var(--muted);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background 0.2s,
|
||||
color 0.2s;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 7px;
|
||||
}
|
||||
.segmented button[aria-pressed='true'] {
|
||||
background: var(--accent);
|
||||
color: var(--accent-ink);
|
||||
}
|
||||
.segmented .save {
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
padding: 1px 6px;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in srgb, var(--ok) 22%, transparent);
|
||||
color: var(--ok);
|
||||
}
|
||||
.segmented button[aria-pressed='true'] .save {
|
||||
background: color-mix(in srgb, var(--accent-ink) 16%, transparent);
|
||||
color: var(--accent-ink);
|
||||
}
|
||||
|
||||
.plans {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.plan {
|
||||
border: 1px solid var(--edge);
|
||||
border-radius: var(--radius);
|
||||
padding: 16px;
|
||||
background: var(--surface);
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
transform 0.14s var(--ease);
|
||||
}
|
||||
.plan:active {
|
||||
transform: scale(0.99);
|
||||
}
|
||||
.plan.featured {
|
||||
border-color: var(--edge-strong);
|
||||
background: var(--surface-2);
|
||||
}
|
||||
.plan-top {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
.plan-name {
|
||||
font-weight: 700;
|
||||
font-size: 1.12rem;
|
||||
}
|
||||
.plan-price {
|
||||
font-weight: 700;
|
||||
font-size: 1.3rem;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
.plan-price small {
|
||||
font-weight: 500;
|
||||
font-size: 0.8rem;
|
||||
color: var(--muted);
|
||||
margin-left: 3px;
|
||||
}
|
||||
.plan-feats {
|
||||
margin: 10px 0 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
color: var(--muted);
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
.plan-feats span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.plan-feats svg {
|
||||
flex: none;
|
||||
color: var(--ok);
|
||||
}
|
||||
.plan .btn {
|
||||
width: 100%;
|
||||
min-height: 48px;
|
||||
padding: 13px;
|
||||
}
|
||||
.plan:not(.featured) .btn {
|
||||
background: var(--surface-2);
|
||||
color: var(--ink);
|
||||
border-color: var(--edge);
|
||||
}
|
||||
|
||||
.loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 40px 0;
|
||||
}
|
||||
.loading .spinner {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-color: var(--edge-strong);
|
||||
border-top-color: var(--ink);
|
||||
}
|
||||
.note {
|
||||
text-align: center;
|
||||
color: var(--faint);
|
||||
font-size: 0.84rem;
|
||||
margin: 2px 0 0;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -40,6 +40,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'Ich habe schon ein TREK',
|
||||
'native.connecting': 'Verbinde mit {host}',
|
||||
'native.back': 'Zurück',
|
||||
'native.plans.title': 'Tarif wählen',
|
||||
'native.plans.sub': 'Gehostetes TREK auf deiner eigenen Adresse. Jederzeit änder- oder kündbar.',
|
||||
'native.plans.monthly': 'Monatlich',
|
||||
'native.plans.yearly': 'Jährlich',
|
||||
'native.plans.save': '2 Monate gratis',
|
||||
'native.plans.perMonth': 'pro Monat',
|
||||
'native.plans.perYear': 'pro Jahr',
|
||||
'native.plans.aiDocs': '{n} KI-Dokumente im Monat',
|
||||
'native.plans.noAi': 'Keine KI-Dokumente',
|
||||
'native.plans.storage': '{n} GB Speicher',
|
||||
'native.plans.unlimited': 'Unbegrenzt Orte, Reisen und Personen',
|
||||
'native.plans.subscribe': 'Abonnieren',
|
||||
'native.plans.loadError': 'Die Tarife konnten nicht geladen werden. Prüfe deine Verbindung und versuch es erneut.',
|
||||
'native.plans.retry': 'Erneut versuchen',
|
||||
'native.plans.soon': 'In-App-Abos kommen bald. Vorerst auf der Website abschließen.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -40,6 +40,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -40,6 +40,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -41,6 +41,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -40,6 +40,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -40,6 +40,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -40,6 +40,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -40,6 +40,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
@@ -39,6 +39,21 @@ const native: TranslationStrings = {
|
||||
'native.cloud.haveServer': 'I already have a TREK',
|
||||
'native.connecting': 'Connecting to {host}',
|
||||
'native.back': 'Back',
|
||||
'native.plans.title': 'Choose your plan',
|
||||
'native.plans.sub': 'Hosted TREK on your own address. Change or cancel anytime.',
|
||||
'native.plans.monthly': 'Monthly',
|
||||
'native.plans.yearly': 'Yearly',
|
||||
'native.plans.save': '2 months free',
|
||||
'native.plans.perMonth': 'per month',
|
||||
'native.plans.perYear': 'per year',
|
||||
'native.plans.aiDocs': '{n} AI documents a month',
|
||||
'native.plans.noAi': 'No AI documents',
|
||||
'native.plans.storage': '{n} GB storage',
|
||||
'native.plans.unlimited': 'Unlimited places, trips and people',
|
||||
'native.plans.subscribe': 'Subscribe',
|
||||
'native.plans.loadError': 'Could not load the plans. Check your connection and try again.',
|
||||
'native.plans.retry': 'Try again',
|
||||
'native.plans.soon': 'In-app subscriptions are coming. For now, finish on the website.',
|
||||
};
|
||||
|
||||
export default native;
|
||||
|
||||
Reference in New Issue
Block a user