Bump Android app version to 1.31.1

This commit is contained in:
MartinBraquet
2026-07-24 04:38:10 +02:00
parent c391de5a70
commit cfa39ec88c
11 changed files with 89 additions and 38 deletions

View File

@@ -11,7 +11,7 @@ android {
applicationId "com.compassconnections.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 130
versionCode 131
versionName "1.31.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {

View File

@@ -12,6 +12,7 @@ dependencies {
implementation project(':capacitor-app')
implementation project(':capacitor-keyboard')
implementation project(':capacitor-push-notifications')
implementation project(':capacitor-share')
implementation project(':capacitor-status-bar')
implementation project(':capawesome-capacitor-live-update')
implementation project(':capgo-capacitor-social-login')

View File

@@ -11,6 +11,9 @@ project(':capacitor-keyboard').projectDir = new File('../node_modules/@capacitor
include ':capacitor-push-notifications'
project(':capacitor-push-notifications').projectDir = new File('../node_modules/@capacitor/push-notifications/android')
include ':capacitor-share'
project(':capacitor-share').projectDir = new File('../node_modules/@capacitor/share/android')
include ':capacitor-status-bar'
project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android')

View File

@@ -20,6 +20,7 @@ const config: CapacitorConfig = {
'@capacitor/core',
'@capacitor/keyboard',
'@capacitor/push-notifications',
'@capacitor/share',
'@capacitor/status-bar',
'@capawesome/capacitor-live-update',
'@capgo/capacitor-social-login',

View File

@@ -60,9 +60,9 @@
"about.share.copied": "Link kopiert!",
"about.share.headline": "Compass wird für Sie mit jeder Person besser, die Sie mitbringen.",
"about.share.kicker": "Ein Teilen, eine Person — so entsteht eine Gemeinschaft wie diese.",
"about.share.reframe": "Selbst wenn ein Freund nicht Ihr Match ist, bringt er seine Welt mit — seine Freunde, seine Kreise, die nachdenklichen Menschen, denen Sie sonst nie begegnet wären. Teilen ist kein Gefallen an ihn. Es ist eine Investition in Ihre eigenen künftigen Verbindungen.",
"about.share.text": "Durchdachte Eins-zu-eins-Verbindungen, offen entwickelt.",
"about.share.title": "Compass",
"about.share.reframe": "Selbst wenn ein Freund nicht der ist, den Sie suchen, bringt er seine Welt mit — seine Freunde, seine Kreise, die nachdenklichen Menschen, denen Sie sonst nie begegnet wären. Teilen ist kein Gefallen an ihn. Es ist eine Investition in Ihre eigenen künftigen Verbindungen.",
"about.share.text": "Hallo! Ich melde mich wegen einer Sache, die mir am Herzen liegt: Compass, ein kostenloses Verzeichnis, um Ihre Leute zu finden — vollständig durchsuchbar nach Werten, Interessen und demografischen Angaben. Kein Swipen, keine Werbung, kein Algorithmus.\n\nEs wird mit jeder Person besser, die dazukommt. Selbst wenn ein Freund nicht der ist, den Sie suchen, bringt er seine Welt mit — seine Kreise, die nachdenklichen Menschen, denen Sie sonst nie begegnet wären. Ob Sie sich also anmelden oder es einfach weitergeben: Sie erweitern den Kreis für uns beide.",
"about.share.title": "Etwas, das Ihnen gefallen dürfte",
"about.subtitle": "Finden Sie Ihre Leute ganz einfach — basierend auf wer sie sind, nicht wie sie aussehen.",
"about.suggestions.button": "Hier vorschlagen →",
"about.suggestions.text": "Machen Sie Vorschläge oder lassen Sie uns wissen, dass Sie durch dieses Formular helfen möchten. Jede Idee zählt.",

View File

@@ -60,9 +60,9 @@
"about.share.copied": "Lien copié !",
"about.share.headline": "Compass s'améliore pour toi à chaque personne que tu invites.",
"about.share.kicker": "Un partage, une personne — c'est ainsi qu'une communauté comme celle-ci se construit.",
"about.share.reframe": "Même si un ami n'est pas ton match, il amène son monde avec lui — ses amis, ses cercles, les personnes réfléchies que tu n'aurais jamais rencontrées autrement. Partager n'est pas un service que tu lui rends. C'est un investissement dans tes propres connexions futures.",
"about.share.text": "Des connexions authentiques en tête-à-tête, construites en toute transparence.",
"about.share.title": "Compass",
"about.share.reframe": "Même si un ami n'est pas la personne que tu cherches, il amène son monde avec lui — ses amis, ses cercles, les personnes réfléchies que tu n'aurais jamais rencontrées autrement. Partager n'est pas un service que tu lui rends. C'est un investissement dans tes propres connexions futures.",
"about.share.text": "Bonjour ! Je te parle de quelque chose qui me tient à cœur : Compass, un annuaire gratuit pour trouver les personnes qui te ressemblent — entièrement consultable par valeurs, centres d'intérêt et données démographiques. Sans swipe, sans pub, sans algorithme.\n\nIl s'améliore à chaque nouvelle personne qui le rejoint. Même si un ami n'est pas la personne que tu cherches, il amène son monde avec lui — ses cercles, les personnes réfléchies que tu n'aurais jamais rencontrées autrement. Alors que tu t'inscrives ou que tu le partages simplement, tu élargis le cercle pour nous deux.",
"about.share.title": "Quelque chose qui devrait te plaire",
"about.subtitle": "Trouve tes personnes en toute simplicité — selon qui ils sont, pas selon leur apparence.",
"about.suggestions.button": "Suggérer ici →",
"about.suggestions.text": "Donne des suggestions ou fais-nous savoir que tu souhaites aider via ce formulaire. Chaque idée compte.",

View File

@@ -8,6 +8,7 @@ import {Button, ColorType, IconButton, SizeType} from 'web/components/buttons/bu
import {useT} from 'web/lib/locale'
import {track} from 'web/lib/service/analytics'
import {copyToClipboard} from 'web/lib/util/copy'
import {nativeShare} from 'web/lib/util/share'
import {Tooltip} from '../widgets/tooltip'
@@ -167,19 +168,11 @@ export function SimpleCopyTextButton(props: {
)
}
export async function mobileShare(url: string) {
try {
await navigator.share({
title: 'My Compass profile',
text: 'Thoughtful connections > swiping.',
url: url,
})
return true
} catch (e) {
if (!(e instanceof Error && e.message.includes('navigator.share is not a function'))) {
console.error('Failed to share', e)
}
return false
}
return nativeShare({
title: 'My Compass profile',
text: 'Thoughtful connections > swiping.',
url: url,
})
}
export const share = async (url: string) => {

40
web/lib/util/share.ts Normal file
View File

@@ -0,0 +1,40 @@
import {Share} from '@capacitor/share'
import {isAndroidApp} from 'web/lib/util/webview'
/**
* Open the OS share sheet, returning whether it actually opened.
*
* Two paths, because the Web Share API is not one API everywhere: Android's System WebView — which is
* what the Capacitor app runs in — does not implement `navigator.share` at all, and even a WebView that
* did would hide it in dev, where the app loads the cleartext `http://10.0.2.2:3000` (the Web Share API
* is secure-context only). So native goes through the Capacitor bridge and the browser keeps using
* `navigator.share`.
*
* `false` means "no sheet appeared" — the caller should fall back (copy the link, open a new tab, ...).
* A user dismissing the sheet rejects the underlying promise too, so it also reports `false`; re-copying
* the link after a deliberate cancel is harmless, and there is no way to tell the two apart.
*/
export async function nativeShare(data: {title?: string; text?: string; url: string}) {
const {title, text, url} = data
if (isAndroidApp()) {
console.log('Native share', {title, text, url})
try {
await Share.share({title, text, url})
return true
} catch (e) {
console.log('Native share unavailable or dismissed', e)
return false
}
}
if (typeof navigator === 'undefined' || typeof navigator.share !== 'function') return false
try {
await navigator.share({title, text, url})
return true
} catch (e) {
console.log('Web share failed or dismissed', e)
return false
}
}

View File

@@ -25,6 +25,7 @@
"@capacitor/core": "7.4.4",
"@capacitor/keyboard": "7.0.3",
"@capacitor/push-notifications": "7.0.3",
"@capacitor/share": "7.0.4",
"@capacitor/status-bar": "7.0.3",
"@capawesome/capacitor-live-update": "7.2.2",
"@capgo/capacitor-social-login": "7.14.9",

View File

@@ -35,6 +35,7 @@ import {eyebrow, Section, surface, surfaceHover} from 'web/components/widgets/su
import {useUser} from 'web/hooks/use-user'
import {useT} from 'web/lib/locale'
import {copyToClipboard} from 'web/lib/util/copy'
import {nativeShare} from 'web/lib/util/share'
// ─── Types ────────────────────────────────────────────────────────────────────
@@ -359,9 +360,12 @@ function ShareBenefit({icon: Icon, title, text}: {icon: IconType; title: string;
* Universal, not mobile-only: the block's whole argument is that sharing is easy and in the reader's
* interest, so a desktop with no button would undercut it. Phones get the native share sheet (they can
* pick WhatsApp, Messages, ...); everywhere else — and any browser without the Web Share API — it copies
* the link and confirms. The API is probed at click time, so there is no SSR/first-paint branch to get
* the link and confirms. The sheet is probed at click time, so there is no SSR/first-paint branch to get
* wrong and the button always renders.
*
* `nativeShare` is what makes the Android app work: its WebView has no `navigator.share`, so a bare Web
* Share API check would silently degrade the app to copy-the-link.
*
* When the sharer is signed in the link carries their `?referrer=` tag, the same attribution the
* /referrals page and users.ts already speak — so the shares this block is arguing for actually get
* credited to them, which is the whole self-interest case. Logged-out visitors (the page is public)
@@ -377,18 +381,21 @@ function ShareCTA() {
: DEPLOYED_WEB_URL
const onClick = async () => {
if (typeof navigator !== 'undefined' && typeof navigator.share === 'function') {
try {
await navigator.share({
title: t('about.share.title', 'Compass'),
text: t('about.share.text', 'Thoughtful 1-on-1 connections, built in the open.'),
url: shareUrl,
})
} catch {
// The user dismissing the share sheet rejects the promise; not an error worth surfacing.
}
return
}
// The user dismissing the share sheet also reports false; re-copying the link is a harmless outcome.
const shared = await nativeShare({
title: t('about.share.title', 'Compass — Find your people'),
// Two paragraphs, and long for a share sheet, deliberately: this is the referral message a person
// sends their friends, so it carries the same three beats as the ShareStrip below — what Compass
// is, how it works, and why bringing someone is in the sharer's own interest, not a favour. The
// closing line is mutual on purpose: the receiver reads it, but the sender has to feel it too.
text: t(
'about.share.text',
"Hi! Reaching out about something I care about: Compass, a free directory for finding your people — fully searchable by values, interests, and demographics. No ads, no swiping, no dubious algorithm.\n\nIt gets better with every person who joins. Even if a friend isn't who you're looking for, they bring their world with them — their circles, the thoughtful people you'd never have met otherwise. So whether you join or simply pass it along, you're widening the circle for both of us.",
),
url: shareUrl,
})
if (shared) return
copyToClipboard(shareUrl)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
@@ -426,8 +433,8 @@ function ShareCTA() {
*
* The ask is framed as self-interest rather than charity (it used to close with "thank you for
* supporting our mission", which asks for a favour). Same argument as the share-compass email: growth is
* a network effect the sharer benefits from, and even a friend who is not your match brings their world
* with them. The headline states the payoff, the paragraph reframes the obvious objection, the benefits
* a network effect the sharer benefits from, and even a friend who is not who you're looking for brings
* their world with them. The headline states the payoff, the paragraph reframes the obvious objection, the benefits
* make the payoff concrete, and the full-width bar at the base carries the one action.
*/
function ShareStrip() {
@@ -492,12 +499,12 @@ function ShareStrip() {
'Compass gets better for you with every person you bring.',
)}
</h3>
{/* The reframe, condensed from the share-compass email: the friend you tell need not be your
match — they bring their world, and that is the reader's own upside. */}
{/* The reframe, condensed from the share-compass email: the friend you tell need not be who
you're looking for — they bring their world, and that is the reader's own upside. */}
<p className="text-white/70 text-base leading-relaxed max-w-xl">
{t(
'about.share.reframe',
"Even if a friend isn't your match, they bring their world with them — their friends, their circles, the thoughtful people you'd never have met otherwise. Sharing isn't just a favor to them. It's an investment in your own future connections.",
"Even if a friend isn't who you're looking for, they bring their world with them — their friends, their circles, the thoughtful people you'd never have met otherwise. Sharing isn't just a favor to them. It's an investment in your own future connections.",
)}
</p>
</div>

View File

@@ -1424,6 +1424,11 @@
resolved "https://registry.yarnpkg.com/@capacitor/push-notifications/-/push-notifications-7.0.3.tgz#b053f5c8db8cc8ceddda8dd9a1c744a72113e202"
integrity sha512-4qt5dRVBkHzq202NEoj3JC+S+zQCrZ1FJh7sjkICy/i1iEos+VaoB3bie8eWDQ2LTARktB4+k2xkdpu8pcVo/g==
"@capacitor/share@7.0.4":
version "7.0.4"
resolved "https://registry.yarnpkg.com/@capacitor/share/-/share-7.0.4.tgz#8906308aad5d74be0fc3853e4f5262a74726d7d4"
integrity sha512-wNXxmXUcChrtbZ5jQv8scFIbIGKo3rk6B7qpWySxfUhYJP5NANgAqQ9Z69m0k/zYsucdh66SBU7XW1ykd9aUug==
"@capacitor/status-bar@7.0.3":
version "7.0.3"
resolved "https://registry.yarnpkg.com/@capacitor/status-bar/-/status-bar-7.0.3.tgz#4e8c1ac49cf928576cfac1c78cc3f9886088ebfe"