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
13 changes: 13 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Build-time secrets — pass via --dart-define when building or running locally.
# Never commit real values to version control.
#
# Example:
# flutter run \
# --dart-define=SENTRY_DSN=https://xxx@yyy.ingest.sentry.io/zzz \
# --dart-define=MIXPANEL_TOKEN=your_token_here
#
# In CI, store these as repository secrets and inject them in the build step.
# If either variable is absent the corresponding service is silently disabled.

SENTRY_DSN=
MIXPANEL_TOKEN=
8 changes: 6 additions & 2 deletions android/fastlane/Fastfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ platform :android do
lane :beta do
Dir.chdir("../..") do
sh("flutter", "pub", "get")
sh("flutter", "build", "appbundle", "--release")
sh("flutter", "build", "appbundle", "--release",
"--dart-define=SENTRY_DSN=#{ENV['SENTRY_DSN']}",
"--dart-define=MIXPANEL_TOKEN=#{ENV['MIXPANEL_TOKEN']}")
end

aab_path = File.expand_path("../../build/app/outputs/bundle/release/app-release.aab", __dir__)
Expand All @@ -36,7 +38,9 @@ platform :android do
lane :release do
Dir.chdir("../..") do
sh("flutter", "pub", "get")
sh("flutter", "build", "appbundle", "--release")
sh("flutter", "build", "appbundle", "--release",
"--dart-define=SENTRY_DSN=#{ENV['SENTRY_DSN']}",
"--dart-define=MIXPANEL_TOKEN=#{ENV['MIXPANEL_TOKEN']}")
end

aab_path = File.expand_path("../../build/app/outputs/bundle/release/app-release.aab", __dir__)
Expand Down
8 changes: 6 additions & 2 deletions ios/fastlane/Fastfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ platform :ios do

Dir.chdir("../..") do
sh("flutter", "pub", "get")
sh("flutter", "build", "ios", "--release", "--no-codesign")
sh("flutter", "build", "ios", "--release", "--no-codesign",
"--dart-define=SENTRY_DSN=#{ENV['SENTRY_DSN']}",
"--dart-define=MIXPANEL_TOKEN=#{ENV['MIXPANEL_TOKEN']}")
end

build_app(
Expand Down Expand Up @@ -69,7 +71,9 @@ platform :ios do

Dir.chdir("../..") do
sh("flutter", "pub", "get")
sh("flutter", "build", "ios", "--release", "--no-codesign")
sh("flutter", "build", "ios", "--release", "--no-codesign",
"--dart-define=SENTRY_DSN=#{ENV['SENTRY_DSN']}",
"--dart-define=MIXPANEL_TOKEN=#{ENV['MIXPANEL_TOKEN']}")
end

build_app(
Expand Down
18 changes: 9 additions & 9 deletions lib/bloc/edit/edit_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class EditCubit extends Cubit<EditState> {
Future<void> enter() async {
analytics.event(name: enterEditModeEvent);
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
mixpanel.track(enterEditModeEvent);
mixpanel?.track(enterEditModeEvent);
}
emit(state.copyWith(
enabled: true,
Expand All @@ -69,7 +69,7 @@ class EditCubit extends Cubit<EditState> {
Future<void> add() async {
analytics.event(name: addEvent);
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
mixpanel.track(addEvent);
mixpanel?.track(addEvent);
}
Defibrillator defibrillator = Defibrillator(
location: LatLng(state.cursor.latitude, state.cursor.longitude), id: 0);
Expand All @@ -87,7 +87,7 @@ class EditCubit extends Cubit<EditState> {
Future<void> edit(Defibrillator defibrillator) async {
analytics.event(name: editEvent);
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
mixpanel.track(editEvent);
mixpanel?.track(editEvent);
}
if (!await pointsRepository.authenticate()) return;
defibrillator = defibrillator.copyWith();
Expand Down Expand Up @@ -184,7 +184,7 @@ class EditCubit extends Cubit<EditState> {
));
analytics.event(name: saveInsertEvent);
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
mixpanel.track(saveInsertEvent,
mixpanel?.track(saveInsertEvent,
properties: saved.getEventProperties());
}
final updatedPendingChanges = await pendingChangesRepository.fetch();
Expand All @@ -205,7 +205,7 @@ class EditCubit extends Cubit<EditState> {
));
analytics.event(name: saveUpdateEvent);
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
mixpanel.track(saveUpdateEvent,
mixpanel?.track(saveUpdateEvent,
properties: saved.getEventProperties());
}
final updatedPendingChanges = await pendingChangesRepository.fetch();
Expand Down Expand Up @@ -240,7 +240,7 @@ class EditCubit extends Cubit<EditState> {
cursor: state.cursor,
pendingChanges: updatedPendingChanges));
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
mixpanel.track(deleteEvent,
mixpanel?.track(deleteEvent,
properties: defibrillator.getEventProperties());
}
} on OsmApiException catch (exception) {
Expand Down Expand Up @@ -301,7 +301,7 @@ class EditCubit extends Cubit<EditState> {
final updatedPendingChanges = await pendingChangesRepository.fetch();
analytics.event(name: photoUploadEvent);
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
mixpanel.track(photoUploadEvent,
mixpanel?.track(photoUploadEvent,
properties: updated.getEventProperties());
}
emit(state.copyWith(
Expand All @@ -321,7 +321,7 @@ class EditCubit extends Cubit<EditState> {
await pointsRepository.reportPhoto(photoId);
analytics.event(name: photoReportEvent);
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
mixpanel.track(photoReportEvent, properties: {
mixpanel?.track(photoReportEvent, properties: {
...defibrillator.getEventProperties(),
'aed_photo_id': photoId,
});
Expand All @@ -348,7 +348,7 @@ class EditCubit extends Cubit<EditState> {
await InAppReview.instance.requestReview();
}
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
mixpanel.track(requestReviewEvent,
mixpanel?.track(requestReviewEvent,
properties: {'available': isAvailable});
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/bloc/feedback/feedback_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class FeedbackCubit extends Cubit<FeedbackState> {
Future<void> send(UserFeedback feedback) async {
analytics.event(name: feedbackEvent);
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
mixpanel.track(feedbackEvent);
mixpanel?.track(feedbackEvent);
}
emit(const FeedbackSending());
await feedbackRepository.sendFeedback(feedback);
Expand Down
2 changes: 1 addition & 1 deletion lib/bloc/points/points_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class PointsCubit extends Cubit<PointsState> {
contentType: 'aed', itemId: defibrillator.id.toString());
HapticFeedback.mediumImpact();
analytics.event(name: selectEvent);
mixpanel.track(selectEvent, properties: defibrillator.getEventProperties());
mixpanel?.track(selectEvent, properties: defibrillator.getEventProperties());
if (state is PointsLoadSuccess) {
emit((state as PointsLoadSuccess)
.copyWith(selected: defibrillator, hash: generateRandomString(32)));
Expand Down
2 changes: 1 addition & 1 deletion lib/bloc/routing/routing_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class RoutingCubit extends Cubit<RoutingState> {
}
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
FirebaseAnalytics.instance.logSearch(searchTerm: defibrillator.id.toString());
mixpanel.track(navigateEvent, properties: defibrillator.getEventProperties());
mixpanel?.track(navigateEvent, properties: defibrillator.getEventProperties());
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const iosUserAgent =
const androidUserAgent =
'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Mobile Safari/537.36';

const mixpanelToken = '7aa0fecdfd096ba41039b52c5a16ec47';
const mixpanelToken = String.fromEnvironment('MIXPANEL_TOKEN');

const enterEditModeEvent = 'enter_edit_mode';
const addEvent = 'add';
Expand Down
36 changes: 21 additions & 15 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import 'generated/i18n/app_localizations.dart';
final analytics = Plausible(plausible, 'aedmapa.app',
userAgent: Platform.isIOS ? iosUserAgent : androidUserAgent);

late final Mixpanel mixpanel;
Mixpanel? mixpanel;

void main() async {
WidgetsFlutterBinding.ensureInitialized();
Expand All @@ -53,21 +53,27 @@ void main() async {
"livechat": false,
});
remoteConfig.fetchAndActivate();
mixpanel = await Mixpanel.init(mixpanelToken, trackAutomaticEvents: true);
mixpanel.setServerURL('https://api-eu.mixpanel.com');
if (mixpanelToken.isNotEmpty) {
mixpanel = await Mixpanel.init(mixpanelToken, trackAutomaticEvents: true);
mixpanel!.setServerURL('https://api-eu.mixpanel.com');
}
await NsfwDetector.initialize(threshold: 0.7);
await SentryFlutter.init(
(options) {
options.dsn =
'https://492fa94bb5e0bdf492c5a8b8a108d84e@o337011.ingest.sentry.io/4506661810274304';
options.tracesSampleRate = 1.0;
options.attachScreenshot = true;
options.replay.sessionSampleRate = 0.0;
options.replay.onErrorSampleRate = 0.0;
},
appRunner: () => runApp(
SentryWidget(child: BetterFeedback(child: Phoenix(child: App())))),
);
const sentryDsn = String.fromEnvironment('SENTRY_DSN');
final Widget app = BetterFeedback(child: Phoenix(child: App()));
if (sentryDsn.isNotEmpty) {
await SentryFlutter.init(
(options) {
options.dsn = sentryDsn;
options.tracesSampleRate = 1.0;
options.attachScreenshot = true;
options.replay.sessionSampleRate = 0.0;
options.replay.onErrorSampleRate = 0.0;
},
appRunner: () => runApp(SentryWidget(child: app)),
);
} else {
runApp(app);
}
}

class App extends StatefulWidget {
Expand Down
22 changes: 11 additions & 11 deletions lib/repositories/points_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ class PointsRepository {
Future<(List<Defibrillator>, int)> loadDefibrillators(
LatLng currentLocation) async {
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
await mixpanel.registerSuperProperties({
await mixpanel?.registerSuperProperties({
"\$latitude": currentLocation.latitude,
"\$longitude": currentLocation.longitude
});
mixpanel.getPeople().set('\$latitude', currentLocation.latitude);
mixpanel.getPeople().set('\$longitude', currentLocation.longitude);
mixpanel?.getPeople().set('\$latitude', currentLocation.latitude);
mixpanel?.getPeople().set('\$longitude', currentLocation.longitude);
}
List<Defibrillator> defibrillators = [];
if (!(await (await cacheFile).exists())) {
Expand Down Expand Up @@ -123,7 +123,7 @@ class PointsRepository {

Future<bool> authenticate() async {
if (token != null || devMode) return true;
mixpanel.track(loginEvent);
mixpanel?.track(loginEvent);
var clientId = 'fMwHrWOkZCboGJR1umv202RX2aBLBFgMt8SLqg1iktA';
var clientSecret = 'zhfFUhRW5KnjsQnGbZR0gnZObfvuxn-F-_HOxLNd72A';
try {
Expand All @@ -140,7 +140,7 @@ class PointsRepository {
headers: {'Content-Type': 'application/x-www-form-urlencoded'});
token = json.decode(response.body)['access_token'];
print('Got OAuth2 token: $token');
mixpanel.track(authenticatedEvent);
mixpanel?.track(authenticatedEvent);
await getUser();
return token != null;
} on Exception catch (_) {
Expand All @@ -162,11 +162,11 @@ class PointsRepository {
user = user.copyWith(avatar: payload['img']['href']);
}
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
mixpanel.identify(user.id.toString());
mixpanel.getPeople().set('\$user_id', user.id);
mixpanel.getPeople().set('\$name', user.name);
mixpanel.getPeople().set('\$avatar', user.avatar);
await mixpanel.flush();
mixpanel?.identify(user.id.toString());
mixpanel?.getPeople().set('\$user_id', user.id);
mixpanel?.getPeople().set('\$name', user.name);
mixpanel?.getPeople().set('\$avatar', user.avatar);
await mixpanel?.flush();
Sentry.configureScope((scope) => scope.setUser(SentryUser(
id: user.id.toString(),
username: user.name,
Expand All @@ -180,7 +180,7 @@ class PointsRepository {

Future<void> logout() async {
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
await mixpanel.reset();
await mixpanel?.reset();
}
token = null;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/screens/map/bottom_panel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ class BottomPanel extends StatelessWidget {
behavior: HitTestBehavior.translucent,
onTap: () {
if (state.selected.phone.purge() != null) {
mixpanel.track(phoneEvent);
mixpanel?.track(phoneEvent);
launchUrl(Uri.parse(
'tel:${state.selected.phone.toString().replaceAll(' ', '')}'));
}
Expand Down
4 changes: 2 additions & 2 deletions lib/screens/map/map_header.dart
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ class MapHeader extends StatelessWidget {
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
mixpanel.track(aboutEvent);
mixpanel?.track(aboutEvent);
var pointsCubit = context.read<PointsCubit>();
var locationCubit = context.read<LocationCubit>();
var feedbackCubit = context.read<FeedbackCubit>();
Expand Down Expand Up @@ -220,7 +220,7 @@ class MapHeader extends StatelessWidget {
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () async {
mixpanel.track(livechatEvent);
mixpanel?.track(livechatEvent);
FirebaseAnalytics.instance
.logEvent(name: livechatEvent);
launchUrl(Uri.parse('https://pomoc.aedmapa.pl/'));
Expand Down
2 changes: 1 addition & 1 deletion lib/screens/pending_changes/pending_changes_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class _PendingChangesPageState extends State<PendingChangesPage> {
@override
void initState() {
super.initState();
mixpanel.track(pendingChangesEvent);
mixpanel?.track(pendingChangesEvent);
}

@override
Expand Down
8 changes: 4 additions & 4 deletions lib/screens/photo/photo_source_bottom_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import '../../generated/i18n/app_localizations.dart';

Future<void> showPhotoSourceSheet(
BuildContext context, Defibrillator defibrillator) async {
mixpanel.track(photoSelectorOpenedEvent,
mixpanel?.track(photoSelectorOpenedEvent,
properties: defibrillator.getEventProperties());
var appLocalizations = AppLocalizations.of(context)!;
await showCupertinoModalPopup(
Expand Down Expand Up @@ -56,7 +56,7 @@ Future<void> pickAndProceed(BuildContext context, Defibrillator defibrillator,
var appLocalizations = AppLocalizations.of(context)!;
var picked = await ImagePicker().pickImage(source: source);
if (picked == null) return;
mixpanel.track(photoSelectedEvent, properties: {
mixpanel?.track(photoSelectedEvent, properties: {
...defibrillator.getEventProperties(),
'source': source == ImageSource.gallery ? 'gallery' : 'camera',
});
Expand All @@ -80,7 +80,7 @@ Future<void> pickAndProceed(BuildContext context, Defibrillator defibrillator,
var unsafe = await editCubit.isPhotoUnsafe(file);

if (unsafe) {
mixpanel.track(photoNsfwBlockedEvent,
mixpanel?.track(photoNsfwBlockedEvent,
properties: defibrillator.getEventProperties());
await showCupertinoDialog(
context: context,
Expand All @@ -98,7 +98,7 @@ Future<void> pickAndProceed(BuildContext context, Defibrillator defibrillator,
return;
}

mixpanel.track(photoConfirmationEvent,
mixpanel?.track(photoConfirmationEvent,
properties: defibrillator.getEventProperties());
var pointsCubit = context.read<PointsCubit>();
await Navigator.of(context).push(
Expand Down
Loading