Skip to content
Merged
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
22 changes: 21 additions & 1 deletion web/src/landing-copy.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ const COPY = {
pi: {
title: 'Machen Sie Ihren Raspberry Pi zur privaten Cloud',
subtitle: 'Image aufspielen, einstecken, fertig. Nextcloud, Fotos und Passwort-Manager installieren Sie danach per Klick.'
},
access: {
title: 'Ihr Server läuft, ist aber von außen nicht erreichbar?',
subtitle: 'DS-Lite, CGNAT oder eine wechselnde IP-Adresse: ohne öffentliche IP hilft auch keine Portfreigabe. Syncloud stellt die Verbindung über einen Relay her.',
points: [
'Erreichbar ohne Portfreigabe, auch hinter DS-Lite und CGNAT',
'Eigene Adresse wie ihrname.syncloud.it, die IP-Wechsel automatisch nachzieht',
'HTTPS-Zertifikat wird ausgestellt und selbstständig erneuert',
'E-Mail-Versand über einen Relay, damit Ihre Apps nicht im Spam landen'
]
}
}
},
Expand All @@ -43,6 +53,16 @@ const COPY = {
pi: {
title: 'Turn your Raspberry Pi into a private cloud',
subtitle: 'Write the image, plug it in, done. Nextcloud, photos and a password manager install with one click afterwards.'
},
access: {
title: 'Your server runs, but you cannot reach it from outside',
subtitle: 'Behind CGNAT, DS-Lite or a changing IP there is no port to forward. Syncloud reaches your server through a relay instead.',
points: [
'Reachable with no port forwarding, even behind CGNAT',
'An address like yourname.syncloud.it that follows your IP when it changes',
'An HTTPS certificate, issued and renewed for you',
'Outgoing mail through a relay, so your apps are not treated as spam'
]
}
}
}
Expand All @@ -59,7 +79,7 @@ export function landingCopy (variant, language = DEFAULT_LANGUAGE) {
cta: copy.cta,
price: copy.price,
shotAlt: copy.shotAlt,
points: copy.points,
points: chosen.points || copy.points,
trust: copy.trust
}
}
12 changes: 12 additions & 0 deletions web/src/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ const routes = [
component: () => import('../views/Landing.vue'),
meta: { variant: 'pi', language: 'de', noindex: true, bare: true }
},
{
path: '/en/remote-access',
name: 'LandingAccessEn',
component: () => import('../views/Landing.vue'),
meta: { variant: 'access', language: 'en', noindex: true, bare: true }
},
{
path: '/de/remote-access',
name: 'LandingAccessDe',
component: () => import('../views/Landing.vue'),
meta: { variant: 'access', language: 'de', noindex: true, bare: true }
},
{ path: '/:catchAll(.*)', redirect: '/' }
]

Expand Down
28 changes: 28 additions & 0 deletions web/tests/landing.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,31 @@ describe('German landing pages', () => {
}
})
})

describe('the remote access variant', () => {
it('leads with not being reachable, not with what the product is', () => {
const wrapper = landing('access', 'de')
expect(wrapper.get('[data-testid="landing-title"]').text())
.toBe(landingCopy('access', 'de').title)
expect(wrapper.get('[data-testid="landing-subtitle"]').text()).toContain('CGNAT')
})

it('answers the access problem rather than listing apps', () => {
for (const language of LANGUAGES) {
const copy = landingCopy('access', language)
const points = copy.points.join(' ').toLowerCase()
expect(points, language).not.toContain('40')
expect(points, language).toMatch(/relay|port|cgnat/)
expect(copy.points, language).not.toEqual(landingCopy('cloud', language).points)
}
})

it('keeps the shared price and call to action', () => {
for (const language of LANGUAGES) {
expect(landingCopy('access', language).price)
.toBe(landingCopy('cloud', language).price)
expect(landingCopy('access', language).cta)
.toBe(landingCopy('cloud', language).cta)
}
})
})