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
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Fully implemented end-to-end.

## Tests

- `test/widget_test.dart` uses `firebase_auth_mocks` + `fake_cloud_firestore` and `setupFirebaseCoreMocks()` — tests run without real Firebase. **707 Dart tests passing.**
- `test/widget_test.dart` uses `firebase_auth_mocks` + `fake_cloud_firestore` and `setupFirebaseCoreMocks()` — tests run without real Firebase. **709 Dart tests passing.**
- `functions/src/test/test.index.ts` uses `firebase-functions-test`. **606 TypeScript tests passing** (pure unit tests + auth/validation integration tests that gracefully skip when no emulator is running). Includes tests for `updateFcmTokens`, `diffFriends`, `shouldNotifyFirstClaim`, `shouldNotifyOvertake`, `buildOsmChange`, `parsePhotos`, `nextQuotaState`, `pointsForMonarch`, `maxDailyFromClaims`, `repointClaimsForPostbox` (mock Firestore), `submitReport`/`reviewReport` auth & validation, and the `plan_route` CLI helpers.
- `test/cross_language_sync_test.dart` is the drift guard for facts duplicated across languages/files. Beyond the constants listed under "Added features", it now also parses source to assert: every `startScoring` call site sends an `attemptId` (Dart sheet, Wear, **and the Kotlin car**); every claim surface consults `MaintenanceGuard` and every entry point initialises Remote Config; and every collection the Cloud Functions touch has a `match` block in `firestore.rules`. Each is verified to FAIL when the thing it guards is removed. `countySlug` is checked against all 218 features of the heatmap geojson (TS side, `test.index.ts`).

Expand Down
24 changes: 18 additions & 6 deletions lib/friends_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart' show setEquals;
import 'package:flutter/foundation.dart'
show TargetPlatform, defaultTargetPlatform, setEquals;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:postbox_game/analytics_service.dart';
Expand All @@ -21,13 +22,24 @@ import 'package:share_plus/share_plus.dart';
const String _playStoreUrl =
'https://play.google.com/store/apps/details?id=com.code418.postbox_game';

/// App Store listing, from the app's Apple ID in App Store Connect.
const String _appStoreUrl = 'https://apps.apple.com/app/id6819213888';

/// Builds the invite message shared via the OS share sheet. Includes the
/// user's UID so the recipient can add them back as a friend.
String buildInviteMessage(String uid) =>
"Join me on Postbox Game! Hunt down Britain's historic postboxes, "
'claim them for points and climb the leaderboards.\n\n'
'Add me as a friend with my UID: $uid\n\n'
'$_playStoreUrl';
///
/// The recipient's phone is unknown, so both store links are included,
/// labelled. The sender's own store comes first ([platform], defaulting to
/// this device), since friends tend to share a platform.
String buildInviteMessage(String uid, {TargetPlatform? platform}) {
final ios = (platform ?? defaultTargetPlatform) == TargetPlatform.iOS;
const iphone = 'iPhone: $_appStoreUrl';
const android = 'Android: $_playStoreUrl';
return "Join me on Postbox Game! Hunt down Britain's historic postboxes, "
'claim them for points and climb the leaderboards.\n\n'
'Add me as a friend with my UID: $uid\n\n'
'${ios ? '$iphone\n$android' : '$android\n$iphone'}';
}

/// Friends list and add-friend by UID.
/// Display names learned from one batched `users` lookup of [batch], given
Expand Down
17 changes: 17 additions & 0 deletions test/widget_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,23 @@ void main() {
test('uses no em-dash, matching the app writing voice', () {
expect(buildInviteMessage('abc123'), isNot(contains('—')));
});

test('includes the App Store link too, whatever the sender uses', () {
for (final platform in [TargetPlatform.android, TargetPlatform.iOS]) {
expect(buildInviteMessage('abc123', platform: platform),
contains('https://apps.apple.com/app/id6819213888'),
reason: '$platform');
}
});

test("lists the sender's own store first", () {
int at(String msg, String s) => msg.indexOf(s);
final fromIos = buildInviteMessage('abc123', platform: TargetPlatform.iOS);
expect(at(fromIos, 'iPhone:'), lessThan(at(fromIos, 'Android:')));
final fromAndroid =
buildInviteMessage('abc123', platform: TargetPlatform.android);
expect(at(fromAndroid, 'Android:'), lessThan(at(fromAndroid, 'iPhone:')));
});
});

// ---------------------------------------------------------------------------
Expand Down
Loading