PostHog last fixes hopefully

This commit is contained in:
Arseni
2025-12-12 16:39:18 +03:00
parent 67b52af150
commit 6ee146b95a
18 changed files with 317 additions and 54 deletions

View File

@@ -11,6 +11,8 @@ class CommonConstants {
static String apiEndpoint = '/api/v1'; static String apiEndpoint = '/api/v1';
static String amplitudeSecret = 'c3d75b3e2520d708440acbb16b923e79'; static String amplitudeSecret = 'c3d75b3e2520d708440acbb16b923e79';
static String amplitudeServerZone = 'EU'; static String amplitudeServerZone = 'EU';
static String posthogApiKey = 'phc_lVhbruaZpxiQxppHBJpL36ARnPlkqbCewv6cauoceTN';
static String posthogHost = 'https://eu.i.posthog.com';
static Locale defaultLocale = const Locale('en'); static Locale defaultLocale = const Locale('en');
static String defaultCurrency = 'EUR'; static String defaultCurrency = 'EUR';
static int defaultDimensionLength = 500; static int defaultDimensionLength = 500;
@@ -36,6 +38,8 @@ class CommonConstants {
apiEndpoint = configJson['apiEndpoint'] ?? apiEndpoint; apiEndpoint = configJson['apiEndpoint'] ?? apiEndpoint;
amplitudeSecret = configJson['amplitudeSecret'] ?? amplitudeSecret; amplitudeSecret = configJson['amplitudeSecret'] ?? amplitudeSecret;
amplitudeServerZone = configJson['amplitudeServerZone'] ?? amplitudeServerZone; amplitudeServerZone = configJson['amplitudeServerZone'] ?? amplitudeServerZone;
posthogApiKey = configJson['posthogApiKey'] ?? posthogApiKey;
posthogHost = configJson['posthogHost'] ?? posthogHost;
defaultLocale = Locale(configJson['defaultLocale'] ?? defaultLocale.languageCode); defaultLocale = Locale(configJson['defaultLocale'] ?? defaultLocale.languageCode);
defaultCurrency = configJson['defaultCurrency'] ?? defaultCurrency; defaultCurrency = configJson['defaultCurrency'] ?? defaultCurrency;
wsProto = configJson['wsProto'] ?? wsProto; wsProto = configJson['wsProto'] ?? wsProto;

View File

@@ -15,6 +15,8 @@ class Constants extends CommonConstants {
static String get currentOrgKey => CommonConstants.currentOrgKey; static String get currentOrgKey => CommonConstants.currentOrgKey;
static String get apiUrl => CommonConstants.apiUrl; static String get apiUrl => CommonConstants.apiUrl;
static String get serviceUrl => CommonConstants.serviceUrl; static String get serviceUrl => CommonConstants.serviceUrl;
static String get posthogApiKey => CommonConstants.posthogApiKey;
static String get posthogHost => CommonConstants.posthogHost;
static int get defaultDimensionLength => CommonConstants.defaultDimensionLength; static int get defaultDimensionLength => CommonConstants.defaultDimensionLength;
static String get deviceIdStorageKey => CommonConstants.deviceIdStorageKey; static String get deviceIdStorageKey => CommonConstants.deviceIdStorageKey;
static String get nilObjectRef => CommonConstants.nilObjectRef; static String get nilObjectRef => CommonConstants.nilObjectRef;

View File

@@ -21,6 +21,8 @@ extension AppConfigExtension on AppConfig {
external String? get apiEndpoint; external String? get apiEndpoint;
external String? get amplitudeSecret; external String? get amplitudeSecret;
external String? get amplitudeServerZone; external String? get amplitudeServerZone;
external String? get posthogApiKey;
external String? get posthogHost;
external String? get defaultLocale; external String? get defaultLocale;
external String? get wsProto; external String? get wsProto;
external String? get wsEndpoint; external String? get wsEndpoint;
@@ -40,6 +42,8 @@ class Constants extends CommonConstants {
static String get currentOrgKey => CommonConstants.currentOrgKey; static String get currentOrgKey => CommonConstants.currentOrgKey;
static String get apiUrl => CommonConstants.apiUrl; static String get apiUrl => CommonConstants.apiUrl;
static String get serviceUrl => CommonConstants.serviceUrl; static String get serviceUrl => CommonConstants.serviceUrl;
static String get posthogApiKey => CommonConstants.posthogApiKey;
static String get posthogHost => CommonConstants.posthogHost;
static int get defaultDimensionLength => CommonConstants.defaultDimensionLength; static int get defaultDimensionLength => CommonConstants.defaultDimensionLength;
static String get deviceIdStorageKey => CommonConstants.deviceIdStorageKey; static String get deviceIdStorageKey => CommonConstants.deviceIdStorageKey;
static String get nilObjectRef => CommonConstants.nilObjectRef; static String get nilObjectRef => CommonConstants.nilObjectRef;
@@ -57,6 +61,8 @@ class Constants extends CommonConstants {
'apiEndpoint': config.apiEndpoint, 'apiEndpoint': config.apiEndpoint,
'amplitudeSecret': config.amplitudeSecret, 'amplitudeSecret': config.amplitudeSecret,
'amplitudeServerZone': config.amplitudeServerZone, 'amplitudeServerZone': config.amplitudeServerZone,
'posthogApiKey': config.posthogApiKey,
'posthogHost': config.posthogHost,
'defaultLocale': config.defaultLocale, 'defaultLocale': config.defaultLocale,
'wsProto': config.wsProto, 'wsProto': config.wsProto,
'wsEndpoint': config.wsEndpoint, 'wsEndpoint': config.wsEndpoint,

View File

@@ -79,12 +79,13 @@ enum ResourceType {
@JsonValue('payments') @JsonValue('payments')
payments, payments,
@JsonValue('payment_methods') /// Represents payment orchestration service
paymentMethods,
@JsonValue('payment_orchestrator') @JsonValue('payment_orchestrator')
paymentOrchestrator, paymentOrchestrator,
@JsonValue('payment_methods')
paymentMethods,
/// Represents permissions service /// Represents permissions service
@JsonValue('permissions') @JsonValue('permissions')
permissions, permissions,

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
@@ -20,6 +22,8 @@ import 'package:pshared/utils/exception.dart';
class AccountProvider extends ChangeNotifier { class AccountProvider extends ChangeNotifier {
AccountProvider();
static String get currentUserRef => Constants.nilObjectRef; static String get currentUserRef => Constants.nilObjectRef;
// The resource now wraps our Account? state along with its loading/error state. // The resource now wraps our Account? state along with its loading/error state.
@@ -27,6 +31,7 @@ class AccountProvider extends ChangeNotifier {
Resource<Account?> get resource => _resource; Resource<Account?> get resource => _resource;
late LocaleProvider _localeProvider; late LocaleProvider _localeProvider;
PendingLogin? _pendingLogin; PendingLogin? _pendingLogin;
Future<void>? _restoreFuture;
Account? get account => _resource.data; Account? get account => _resource.data;
PendingLogin? get pendingLogin => _pendingLogin; PendingLogin? get pendingLogin => _pendingLogin;
@@ -52,9 +57,18 @@ class AccountProvider extends ChangeNotifier {
); );
} }
// Private helper to update the resource and notify listeners. @protected
Future<void> onAccountChanged(Account? previous, Account? current) => Future<void>.value();
void _setResource(Resource<Account?> newResource) { void _setResource(Resource<Account?> newResource) {
final previousAccount = _resource.data;
_resource = newResource; _resource = newResource;
final currentAccount = newResource.data;
if (previousAccount != currentAccount) {
unawaited(onAccountChanged(previousAccount, currentAccount));
}
notifyListeners(); notifyListeners();
} }
@@ -220,4 +234,11 @@ class AccountProvider extends ChangeNotifier {
rethrow; rethrow;
} }
} }
Future<void> restoreIfPossible() {
return _restoreFuture ??= AuthorizationService.isAuthorizationStored().then<void>((hasAuth) async {
if (!hasAuth) return;
await restore();
});
}
} }

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
@@ -21,9 +23,17 @@ class PermissionsProvider extends ChangeNotifier {
Resource<UserAccess> _userAccess = Resource(data: null, isLoading: false, error: null); Resource<UserAccess> _userAccess = Resource(data: null, isLoading: false, error: null);
late OrganizationsProvider _organizations; late OrganizationsProvider _organizations;
bool _isLoaded = false; bool _isLoaded = false;
String? _loadedOrgRef;
//For permissions to auto-load when an organization is set, so the dashboard no longer hangs waiting for permissions to become ready.
void update(OrganizationsProvider venue) { void update(OrganizationsProvider venue) {
_organizations = venue; _organizations = venue;
// Trigger a reload when organization changes or when permissions were never loaded.
if (_organizations.isOrganizationSet &&
_loadedOrgRef != _organizations.current.id &&
!_userAccess.isLoading) {
unawaited(load());
}
} }
// Generic wrapper to perform service calls and reload state // Generic wrapper to perform service calls and reload state
@@ -56,6 +66,7 @@ class PermissionsProvider extends ChangeNotifier {
_userAccess = _userAccess.copyWith(data: allAccess, isLoading: false); _userAccess = _userAccess.copyWith(data: allAccess, isLoading: false);
} }
_isLoaded = true; _isLoaded = true;
_loadedOrgRef = orgRef;
} catch (e) { } catch (e) {
_userAccess = _userAccess.copyWith( _userAccess = _userAccess.copyWith(
error: e is Exception ? e : Exception(e.toString()), error: e is Exception ? e : Exception(e.toString()),
@@ -164,6 +175,7 @@ class PermissionsProvider extends ChangeNotifier {
void reset() { void reset() {
_userAccess = Resource(data: null, isLoading: false, error: null); _userAccess = Resource(data: null, isLoading: false, error: null);
_isLoaded = false; _isLoaded = false;
_loadedOrgRef = null;
notifyListeners(); notifyListeners();
} }

View File

@@ -12,6 +12,8 @@ import 'package:pshared/provider/locale.dart';
import 'package:pshared/provider/permissions.dart'; import 'package:pshared/provider/permissions.dart';
import 'package:pshared/provider/account.dart'; import 'package:pshared/provider/account.dart';
import 'package:pshared/provider/organizations.dart'; import 'package:pshared/provider/organizations.dart';
import 'package:pshared/provider/payment/amount.dart';
import 'package:pshared/provider/payment/quotation.dart';
import 'package:pshared/provider/recipient/provider.dart'; import 'package:pshared/provider/recipient/provider.dart';
import 'package:pshared/provider/recipient/pmethods.dart'; import 'package:pshared/provider/recipient/pmethods.dart';
@@ -26,8 +28,10 @@ import 'package:pweb/providers/wallets.dart';
import 'package:pweb/providers/wallet_transactions.dart'; import 'package:pweb/providers/wallet_transactions.dart';
import 'package:pweb/services/operations.dart'; import 'package:pweb/services/operations.dart';
import 'package:pweb/services/payments/history.dart'; import 'package:pweb/services/payments/history.dart';
import 'package:pweb/services/posthog.dart';
import 'package:pweb/services/wallet_transactions.dart'; import 'package:pweb/services/wallet_transactions.dart';
import 'package:pweb/services/wallets.dart'; import 'package:pweb/services/wallets.dart';
import 'package:pweb/providers/account.dart';
void _setupLogging() { void _setupLogging() {
@@ -39,11 +43,9 @@ void _setupLogging() {
} }
void main() async { void main() async {
await Constants.initialize();
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await Constants.initialize();
// await AmplitudeService.initialize(); await PosthogService.initialize();
_setupLogging(); _setupLogging();
@@ -56,7 +58,7 @@ void main() async {
providers: [ providers: [
ChangeNotifierProvider(create: (_) => LocaleProvider(null)), ChangeNotifierProvider(create: (_) => LocaleProvider(null)),
ChangeNotifierProxyProvider<LocaleProvider, AccountProvider>( ChangeNotifierProxyProvider<LocaleProvider, AccountProvider>(
create: (_) => AccountProvider(), create: (_) => PwebAccountProvider(),
update: (context, localeProvider, provider) => provider!..updateProvider(localeProvider), update: (context, localeProvider, provider) => provider!..updateProvider(localeProvider),
), ),
ChangeNotifierProxyProvider<AccountProvider, TwoFactorProvider>( ChangeNotifierProxyProvider<AccountProvider, TwoFactorProvider>(
@@ -69,6 +71,7 @@ void main() async {
update: (context, orgnization, provider) => provider!..update(orgnization), update: (context, orgnization, provider) => provider!..update(orgnization),
), ),
ChangeNotifierProvider(create: (_) => CarouselIndexProvider()), ChangeNotifierProvider(create: (_) => CarouselIndexProvider()),
ChangeNotifierProvider( ChangeNotifierProvider(
create: (_) => UploadHistoryProvider(service: MockUploadHistoryService())..load(), create: (_) => UploadHistoryProvider(service: MockUploadHistoryService())..load(),
), ),
@@ -90,9 +93,17 @@ void main() async {
ChangeNotifierProvider( ChangeNotifierProvider(
create: (_) => MockPaymentProvider(), create: (_) => MockPaymentProvider(),
), ),
ChangeNotifierProvider( ChangeNotifierProvider(
create: (_) => OperationProvider(OperationService())..loadOperations(), create: (_) => OperationProvider(OperationService())..loadOperations(),
), ),
ChangeNotifierProvider(
create: (_) => PaymentAmountProvider(),
),
ChangeNotifierProxyProvider2<OrganizationsProvider, PaymentAmountProvider, QuotationProvider>(
create: (_) => QuotationProvider(),
update: (context, orgnization, payment, provider) => provider!..update(orgnization, payment),
),
], ],
child: const PayApp(), child: const PayApp(),
), ),

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
@@ -14,6 +16,7 @@ import 'package:pshared/provider/recipient/pmethods.dart';
import 'package:pshared/provider/recipient/provider.dart'; import 'package:pshared/provider/recipient/provider.dart';
import 'package:pweb/pages/address_book/form/view.dart'; import 'package:pweb/pages/address_book/form/view.dart';
import 'package:pweb/services/posthog.dart';
import 'package:pweb/utils/error/snackbar.dart'; import 'package:pweb/utils/error/snackbar.dart';
import 'package:pweb/utils/payment/label.dart'; import 'package:pweb/utils/payment/label.dart';
import 'package:pweb/utils/snackbar.dart'; import 'package:pweb/utils/snackbar.dart';
@@ -106,11 +109,11 @@ class _AdressBookRecipientFormState extends State<AdressBookRecipientForm> {
return; return;
} }
// AmplitudeService.recipientAddCompleted( unawaited(PosthogService.recipientAddCompleted(
// _type, _type,
// _status, _status,
// _methods.keys.toSet(), _methods.keys.toSet(),
// ); ));
final recipient = await executeActionWithNotification( final recipient = await executeActionWithNotification(
context: context, context: context,
action: _doSave, action: _doSave,

View File

@@ -1,5 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/provider/account.dart'; import 'package:pshared/provider/account.dart';
@@ -12,24 +14,56 @@ import 'package:pweb/generated/i18n/app_localizations.dart';
class AccountLoader extends StatelessWidget { class AccountLoader extends StatelessWidget {
final Widget child; final Widget child;
const AccountLoader({super.key, required this.child}); const AccountLoader({super.key, required this.child});
@override static final _logger = Logger('loader.account');
Widget build(BuildContext context) => Consumer<AccountProvider>(builder: (context, provider, _) {
if (provider.isLoading) return const Center(child: CircularProgressIndicator()); void _notifyErrorAndRedirect(BuildContext context, Object error) {
if (provider.error != null) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (!context.mounted) return;
postNotifyUserOfErrorX( postNotifyUserOfErrorX(
context: context, context: context,
errorSituation: AppLocalizations.of(context)!.errorLogin, errorSituation: AppLocalizations.of(context)!.errorLogin,
exception: provider.error!, exception: error,
); );
navigateAndReplace(context, Pages.login); navigateAndReplace(context, Pages.login);
} });
if (provider.account == null) { }
WidgetsBinding.instance.addPostFrameCallback((_) => navigateAndReplace(context, Pages.login));
void _restoreAndHandleResult(BuildContext context, AccountProvider provider) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
try {
await provider.restoreIfPossible();
} catch (error, stack) {
_logger.warning('Account restore failed: $error', error, stack);
}
if (!context.mounted) return;
final latest = Provider.of<AccountProvider>(context, listen: false);
if (latest.account != null) return;
if (latest.error != null) {
_notifyErrorAndRedirect(context, latest.error!);
return;
}
if (!latest.isLoading) {
navigateAndReplace(context, Pages.login);
}
});
}
@override
Widget build(BuildContext context) {
return Consumer<AccountProvider>(builder: (context, provider, _) {
if (provider.account != null) return child;
if (provider.error != null) {
_notifyErrorAndRedirect(context, provider.error!);
return const Center(child: CircularProgressIndicator());
}
if (!provider.isLoading) {
_restoreAndHandleResult(context, provider);
}
if (provider.isLoading) return const Center(child: CircularProgressIndicator());
// In case of error or missing account we show loader while side effects handle navigation.
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} });
return child; }
});
} }

View File

@@ -13,28 +13,32 @@ import 'package:pweb/generated/i18n/app_localizations.dart';
class PermissionsLoader extends StatelessWidget { class PermissionsLoader extends StatelessWidget {
final Widget child; final Widget child;
const PermissionsLoader({super.key, required this.child}); const PermissionsLoader({super.key, required this.child});
@override void _notifyError(BuildContext context, Exception exception) {
Widget build(BuildContext context) => Consumer2<PermissionsProvider, AccountProvider>(builder: (context, provider, accountProvider, _) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (provider.isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (provider.error != null) {
postNotifyUserOfErrorX( postNotifyUserOfErrorX(
context: context, context: context,
errorSituation: AppLocalizations.of(context)!.errorLogin, errorSituation: AppLocalizations.of(context)!.errorLogin,
exception: provider.error!, exception: exception,
); );
navigateAndReplace(context, Pages.login); navigateAndReplace(context, Pages.login);
} });
if (provider.error == null && !provider.isReady && accountProvider.account != null) { }
WidgetsBinding.instance.addPostFrameCallback((_) {
provider.load(); @override
}); Widget build(BuildContext context) {
return const Center(child: CircularProgressIndicator()); return Consumer2<PermissionsProvider, AccountProvider>(
} builder: (context, provider, accountProvider, _) {
return child; if (provider.isLoading) {
}); return const Center(child: CircularProgressIndicator());
}
if (provider.error != null) {
_notifyError(context, provider.error!);
return const Center(child: CircularProgressIndicator());
}
return child;
},
);
}
} }

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -14,6 +16,7 @@ import 'package:pweb/widgets/password/password.dart';
import 'package:pweb/widgets/username.dart'; import 'package:pweb/widgets/username.dart';
import 'package:pweb/widgets/vspacer.dart'; import 'package:pweb/widgets/vspacer.dart';
import 'package:pweb/widgets/error/snackbar.dart'; import 'package:pweb/widgets/error/snackbar.dart';
import 'package:pweb/services/posthog.dart';
import 'package:pweb/generated/i18n/app_localizations.dart'; import 'package:pweb/generated/i18n/app_localizations.dart';
@@ -43,6 +46,7 @@ class _LoginFormState extends State<LoginForm> {
password: _passwordController.text, password: _passwordController.text,
locale: context.read<LocaleProvider>().locale.languageCode, locale: context.read<LocaleProvider>().locale.languageCode,
); );
unawaited(PosthogService.login(pending: outcome.isPending));
if (outcome.isPending) { if (outcome.isPending) {
// TODO: fix context usage // TODO: fix context usage
navigateAndReplace(context, Pages.sfactor); navigateAndReplace(context, Pages.sfactor);

View File

@@ -16,6 +16,7 @@ import 'package:pweb/providers/payment_flow.dart';
import 'package:pweb/pages/payment_methods/payment_page/body.dart'; import 'package:pweb/pages/payment_methods/payment_page/body.dart';
import 'package:pweb/providers/wallets.dart'; import 'package:pweb/providers/wallets.dart';
import 'package:pweb/widgets/sidebar/destinations.dart'; import 'package:pweb/widgets/sidebar/destinations.dart';
import 'package:pweb/services/posthog.dart';
class PaymentPage extends StatefulWidget { class PaymentPage extends StatefulWidget {
@@ -109,7 +110,7 @@ class _PaymentPageState extends State<PaymentPage> {
void _handleSendPayment() { void _handleSendPayment() {
// TODO: Handle Payment logic // TODO: Handle Payment logic
// AmplitudeService.paymentInitiated(); PosthogService.paymentInitiated(method: _flowProvider.selectedType);
} }
@override @override

View File

@@ -1,10 +1,12 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pshared/provider/locale.dart'; import 'package:pshared/provider/locale.dart';
// import 'package:pweb/services/amplitude.dart'; import 'package:pweb/services/posthog.dart';
import 'package:pweb/generated/i18n/app_localizations.dart'; import 'package:pweb/generated/i18n/app_localizations.dart';
@@ -58,7 +60,7 @@ class LocalePicker extends StatelessWidget {
onChanged: (locale) { onChanged: (locale) {
if (locale != null) { if (locale != null) {
localeProvider.setLocale(locale); localeProvider.setLocale(locale);
// AmplitudeService.localeChanged(locale); unawaited(PosthogService.localeChanged(locale));
} }
}, },
decoration: const InputDecoration( decoration: const InputDecoration(

View File

@@ -0,0 +1,17 @@
import 'dart:async';
import 'package:pshared/models/account/account.dart';
import 'package:pshared/provider/account.dart';
import 'package:pweb/services/posthog.dart';
class PwebAccountProvider extends AccountProvider {
@override
Future<void> onAccountChanged(Account? previous, Account? current) {
if (current != null) {
return PosthogService.identify(current);
}
return PosthogService.reset();
}
}

View File

@@ -0,0 +1,136 @@
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:posthog_flutter/posthog_flutter.dart';
import 'package:pshared/config/constants.dart';
import 'package:pshared/models/account/account.dart';
import 'package:pshared/models/payment/type.dart';
import 'package:pshared/models/recipient/status.dart';
import 'package:pshared/models/recipient/type.dart';
import 'package:pweb/widgets/sidebar/destinations.dart';
class PosthogService {
static final _logger = Logger('service.posthog');
static String? _identifiedUserId;
static bool _initialized = false;
static bool get isEnabled => _initialized;
static Future<void> initialize() async {
final apiKey = Constants.posthogApiKey;
if (apiKey.isEmpty) {
_logger.warning('PostHog API key is not configured, analytics disabled.');
return;
}
try {
final config = PostHogConfig(apiKey)
..host = Constants.posthogHost
..captureApplicationLifecycleEvents = true;
await Posthog().setup(config);
await Posthog().register('client_id', Constants.clientId);
_initialized = true;
_logger.info('PostHog initialized with host ${Constants.posthogHost}');
} catch (e, st) {
_initialized = false;
_logger.warning('Failed to initialize PostHog: $e', e, st);
}
}
static Future<void> identify(Account account) async {
if (!_initialized) return;
if (_identifiedUserId == account.id) return;
await Posthog().identify(
userId: account.id,
userProperties: {
'email': account.login,
'name': account.name,
'locale': account.locale,
'created_at': account.createdAt.toIso8601String(),
},
);
_identifiedUserId = account.id;
}
static Future<void> reset() async {
if (!_initialized) return;
_identifiedUserId = null;
await Posthog().reset();
}
static Future<void> login({required bool pending}) async {
if (!_initialized) return;
await _capture(
'login',
properties: {
'result': pending ? 'pending' : 'success',
},
);
}
static Future<void> pageOpened(PayoutDestination page, {String? path, String? uiSource}) async {
if (!_initialized) return;
return _capture(
'pageOpened',
properties: {
'page': page.name,
if (path != null) 'path': path,
if (uiSource != null) 'uiSource': uiSource,
},
);
}
static Future<void> localeChanged(Locale locale) async {
if (!_initialized) return;
return _capture(
'localeChanged',
properties: {'locale': locale.toLanguageTag()},
);
}
static Future<void> recipientAddCompleted(
RecipientType type,
RecipientStatus status,
Set<PaymentType> methods,
) async {
if (!_initialized) return;
return _capture(
'recipientAddCompleted',
properties: {
'methods': methods.map((m) => m.name).toList(),
'type': type.name,
'status': status.name,
},
);
}
static Future<void> paymentInitiated({PaymentType? method}) async {
if (!_initialized) return;
return _capture(
'paymentInitiated',
properties: {
if (method != null) 'method': method.name,
},
);
}
static Future<void> _capture(
String eventName, {
Map<String, Object?>? properties,
}) async {
if (!_initialized) return;
final filtered = <String, Object>{};
if (properties != null) {
for (final entry in properties.entries) {
final value = entry.value;
if (value != null) filtered[entry.key] = value;
}
}
await Posthog().capture(eventName: eventName, properties: filtered.isEmpty ? null : filtered);
}
}

View File

@@ -1,6 +1,8 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
// import 'package:pweb/services/amplitude.dart'; import 'package:pweb/services/posthog.dart';
import 'package:pweb/widgets/sidebar/destinations.dart'; import 'package:pweb/widgets/sidebar/destinations.dart';
@@ -49,7 +51,7 @@ class SideMenuColumn extends StatelessWidget {
child: InkWell( child: InkWell(
onTap: () { onTap: () {
onSelected(item); onSelected(item);
// AmplitudeService.pageOpened(item, uiSource: 'sidebar'); unawaited(PosthogService.pageOpened(item, uiSource: 'sidebar'));
}, },
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
hoverColor: theme.colorScheme.primaryContainer, hoverColor: theme.colorScheme.primaryContainer,

View File

@@ -9,6 +9,7 @@ import amplitude_flutter
import file_selector_macos import file_selector_macos
import flutter_timezone import flutter_timezone
import path_provider_foundation import path_provider_foundation
import posthog_flutter
import share_plus import share_plus
import shared_preferences_foundation import shared_preferences_foundation
import sqflite_darwin import sqflite_darwin
@@ -19,6 +20,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FlutterTimezonePlugin.register(with: registry.registrar(forPlugin: "FlutterTimezonePlugin")) FlutterTimezonePlugin.register(with: registry.registrar(forPlugin: "FlutterTimezonePlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
PosthogFlutterPlugin.register(with: registry.registrar(forPlugin: "PosthogFlutterPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))

View File

@@ -35,6 +35,7 @@ dependencies:
sdk: flutter sdk: flutter
pshared: pshared:
path: ../pshared path: ../pshared
posthog_flutter: ^5.9.0
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.