Commit 2837b6c8e43bb69b84d72b9deef93c11b698fbd3

Authored by 常守达
1 parent 9a6eb289

feat(track):埋点相关

... ... @@ -98,6 +98,19 @@ class PlatformHostApiImpl(private val application: android.app.Application) : Pl
return BuildConfig.DEBUG
}
override fun shareText(
text: String,
callback: (Result<Boolean>) -> Unit
) {
}
override fun shareFileData(
databytes: ByteArray,
callback: (Result<Boolean>) -> Unit
) {
}
override fun requestAppReview(callback: (Result<Boolean>) -> Unit) {
}
... ...
... ... @@ -8,6 +8,7 @@ import 'package:doublefeel_flutter/core/error/app_error.dart';
import 'package:doublefeel_flutter/core/error/http_error_handling_policy.dart';
import 'package:doublefeel_flutter/core/network/api/friend_api.dart';
import 'package:doublefeel_flutter/core/result/app_result.dart';
import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
import 'package:doublefeel_flutter/core/services/user_state_service.dart';
import 'package:doublefeel_flutter/core/util/app_toast.dart';
import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
... ... @@ -34,12 +35,22 @@ class BindPartnerController extends GetxController {
late bool isShowAppBarBack;
late bool isShowSkipButton;
var channel = '';
@override
void onInit() {
super.onInit();
final from = Get.arguments?['from'] ?? '';
if (from == 'onboarding') {
channel = '新手引导';
} else if (from == 'FriendsTab') {
channel = '好友页';
} else if (from == 'today') {
channel = '首页';
} else {
channel = '其他';
}
if (from == 'onboarding') {
isShowAppBarBack = false;
isShowSkipButton = true;
} else {
... ... @@ -57,6 +68,13 @@ class BindPartnerController extends GetxController {
}
@override
void onReady() {
super.onReady();
ta.track('enter_doublefeel_add_friend_page',
properties: {'channel_type': channel});
}
@override
void onClose() {
partnerIdController.dispose();
super.onClose();
... ... @@ -66,6 +84,8 @@ class BindPartnerController extends GetxController {
void onShareMyCode() async {
try {
ta.track('enter_doublefeel_add_friend_page',
properties: {'channel_type': channel, 'click_content': '分享我的码'});
final success = await PlatformHostApi()
.shareText(l10n.sharePartnerCodeTemplate(myInviteCode.value));
} catch (e) {
... ... @@ -74,6 +94,8 @@ class BindPartnerController extends GetxController {
}
Future<void> onSubmitPartnerId() async {
ta.track('enter_doublefeel_add_friend_page',
properties: {'channel_type': channel, 'click_content': '继续'});
if (!canSubmit || isSubmitting.value) return;
isSubmitting.value = true;
try {
... ... @@ -120,6 +142,7 @@ class BindPartnerController extends GetxController {
} else {
Get.to(_BindSuccessPage(friendItem));
}
ta.track('success_add_doubelfeel_friend');
break;
}
} catch (e) {
... ... @@ -129,6 +152,8 @@ class BindPartnerController extends GetxController {
}
Future<void> onSkip() async {
ta.track('enter_doublefeel_add_friend_page',
properties: {'channel_type': channel, 'click_content': '下次再说'});
final userStateService = Get.find<UserStateService>();
if (!userStateService.isVip) {
// 使用 offNamed 移除 BIND_PARTNER,并将 onboarding 标识传入 PURCHASE
... ...
import 'package:doublefeel_flutter/app/widget/dash_divider.dart';
import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
import 'package:doublefeel_flutter/core/theme/app_theme.dart';
import 'package:doublefeel_flutter/core/util/app_toast.dart';
import 'package:flutter/material.dart';
... ... @@ -198,6 +199,10 @@ class BindPartnerView extends GetView<BindPartnerController> {
ClipboardData(text: controller.myInviteCode.value),
);
AppToast.show('复制成功');
ta.track('enter_doublefeel_add_friend_page', properties: {
'channel_type': controller.channel,
'click_content': '复制共享码'
});
},
child: Container(
width: 20,
... ...
... ... @@ -2,6 +2,7 @@ import 'package:doublefeel_flutter/app/modules/friends/controllers/friend_trend_
import 'package:doublefeel_flutter/app/modules/friends/controllers/friends_controller.dart';
import 'package:doublefeel_flutter/core/network/api/friend_api.dart';
import 'package:doublefeel_flutter/core/result/app_result.dart';
import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
import 'package:doublefeel_flutter/core/services/user_state_service.dart';
import 'package:doublefeel_flutter/data/local/local_storage.dart';
import 'package:doublefeel_flutter/pigeon/platform_api.g.dart';
... ... @@ -119,6 +120,14 @@ class HomeController extends GetxController {
} else if (previousIndex == friendsTabIndex && index != friendsTabIndex) {
Get.find<FriendsController>().markPageHidden();
}
switch (index) {
case 0:
// ta.track('enter_doublefeel_my_page');
break;
case 3:
ta.track('enter_doublefeel_my_page');
break;
}
}
void openTrend(TrendType type, {DateTime? date, ReportPeriod? period}) {
... ...
import 'dart:async';
import 'dart:ui';
import 'package:doublefeel_flutter/app/modules/friends/controllers/friend_trend_controller.dart';
import 'package:doublefeel_flutter/app/modules/home/controllers/home_controller.dart';
... ... @@ -14,6 +15,7 @@ import 'package:doublefeel_flutter/core/network/api/pay_api.dart';
import 'package:doublefeel_flutter/core/network/api/vip_api.dart';
import 'package:doublefeel_flutter/core/result/app_result.dart';
import 'package:doublefeel_flutter/core/services/loading_service.dart';
import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
import 'package:doublefeel_flutter/core/services/user_state_service.dart';
import 'package:doublefeel_flutter/core/util/app_toast.dart';
import 'package:doublefeel_flutter/data/local/user_account_storage.dart';
... ... @@ -29,6 +31,7 @@ import 'package:doublefeel_flutter/pigeon/wear_engine_api.g.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:permission_handler/permission_handler.dart';
/// HRV 趋势数据点
class HrvDataPoint {
... ... @@ -135,6 +138,15 @@ class TodayController extends GetMaterialController {
final v2ActivityTarget = Rxn<V2ActivityTarget>();
@override
void onReady() {
super.onReady();
ta.track(
'enter_doublefeel_today_status',
properties: {'user_role': isFriend ? '好友的' : '我的'},
);
}
@override
void onInit() {
super.onInit();
targetFriendInfo.value = _initialFriendInfo;
... ... @@ -155,6 +167,7 @@ class TodayController extends GetMaterialController {
checkAddFriendVisible();
checkHrvAdBannerVisible();
checkHealthDataAuthCardVisible();
checkNotificationCardVisible();
if (!Get.find<UserStateService>().isVip) {
getProductList();
}
... ... @@ -287,6 +300,7 @@ class TodayController extends GetMaterialController {
}
Future<void> onAuthorizeTap() async {
ta.track('click_doublefeel_today_status', properties: {'ita': '授权访问健康数据'});
if (showHealthDataAuthCardStatus.value == 1) {
return;
} else if (showHealthDataAuthCardStatus.value == 0) {
... ... @@ -322,7 +336,7 @@ class TodayController extends GetMaterialController {
[HealthAuthorization? preResult]) async {
try {
final result = preResult ?? await _hostApi.checkHealthAppAuthorization();
ta.userSet({'health_data_permission': result.status.clamp(0, 1)});
if (result.status == 1) {
await _performDataUpload();
}
... ... @@ -335,6 +349,26 @@ class TodayController extends GetMaterialController {
} catch (e) {}
}
Future<void> checkNotificationCardVisible() async {
try {
Permission.notification.status.then((status) {
ta.userSet({'notification_permission': status.isGranted ? 1 : 0});
// if (status.isGranted) {
// return;
// }
// if (status.isPermanentlyDenied) {
// // await openAppSettings();
// return;
// }
// final result = await Permission.notification.request();
});
} catch (error) {
AppLogger.e('Notification authorization failed: $error');
}
}
Future<void> _refreshHealthDataForDate(DateTime date) async {
_clearRealTimeData();
final intDate = int.parse(DateFormat('yyyyMMdd').format(date));
... ...
... ... @@ -4,6 +4,7 @@ import 'package:doublefeel_flutter/app/modules/home/widgets/today/hrv_measuremen
import 'package:doublefeel_flutter/app/modules/report_common/models/report_period.dart';
import 'package:doublefeel_flutter/app/modules/report_common/widgets/report_bottom_slogan.dart';
import 'package:doublefeel_flutter/app/modules/report_common/widgets/report_date_picker_sheet.dart';
import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
import 'package:doublefeel_flutter/core/services/user_state_service.dart';
import 'package:doublefeel_flutter/core/theme/app_theme.dart';
... ... @@ -99,6 +100,8 @@ class TodayTabBody extends StatelessWidget {
),
onBackToToday: _backToToday,
onTapPremiumCard: () {
ta.track('click_doublefeel_today_status',
properties: {'ita': '右上角VIP优惠入口'});
controller.toPremiumDiscoutPage();
},
),
... ... @@ -227,7 +230,11 @@ class TodayTabBody extends StatelessWidget {
return Get.find<UserStateService>().isVip
? const SizedBox.shrink()
: GestureDetector(
onTap: () => controller.toPremiumDiscoutPage(),
onTap: () {
ta.track('click_doublefeel_today_status',
properties: {'ita': '会员banner'});
controller.toPremiumDiscoutPage();
},
child: PremiumCard(
controller,
));
... ... @@ -674,6 +681,8 @@ class _TopFriendBar extends StatelessWidget {
highlightColor: Colors.transparent,
padding: EdgeInsets.zero,
onPressed: () {
ta.track('click_doublefeel_today_status',
properties: {'ita': '切换好友'});
controller.showFriendListBottomSheet();
},
icon: Image.asset(
... ...
import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
import 'package:doublefeel_flutter/core/theme/app_theme.dart';
import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void showHrvMeasurementBottomSheet() {
ta.track('click_doublefeel_today_status', properties: {'ita': '立刻测量HRV'});
Get.bottomSheet(
const FractionallySizedBox(
heightFactor: 0.9,
... ...
import 'package:doublefeel_flutter/app/routes/app_pages.dart';
import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
import 'package:doublefeel_flutter/core/theme/app_theme.dart';
import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
import 'package:flutter/material.dart';
... ... @@ -18,6 +19,8 @@ class TodayHrvAdBanner extends StatelessWidget {
return GestureDetector(
onTap: () async {
ta.track('click_doublefeel_today_status',
properties: {'ita': '点击添加HRV表盘'});
await Get.toNamed(Routes.WATCH_THEME);
controller.checkAddFriendVisible();
},
... ... @@ -61,7 +64,9 @@ class TodayPartnerAdBanner extends StatelessWidget {
return GestureDetector(
onTap: () async {
await Get.toNamed(AppRoutes.bindPartner);
ta.track('click_doublefeel_today_status', properties: {'ita': '加好友'});
await Get.toNamed(AppRoutes.bindPartner,
arguments: {'from': 'today'});
controller.checkAddFriendVisible();
},
child: _TodayGuideBanner(
... ...
... ... @@ -34,8 +34,7 @@ class TodayHrvNumberCard extends StatelessWidget {
label: controller.isFriend
? context.l10n.reportOtherPossessiveTitle(otherHrv)
: otherHrv,
value:
controller.v2HealthData.value?.hrvAvg?.toString() ?? '--',
value: controller.v2HealthData.value?.hrvAvg?.toString() ?? '-',
unit: 'ms',
),
),
... ... @@ -48,7 +47,7 @@ class TodayHrvNumberCard extends StatelessWidget {
: context.l10n.restingHeartRate,
value: controller.v2HealthData.value?.lastRestingHrValue
?.toString() ??
'--',
'-',
unit: 'bpm',
),
),
... ...
... ... @@ -13,6 +13,7 @@ class TodaySleepCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
return GestureDetector(
onTap: controller.toTrendSleepPage,
child: Obx(() {
... ... @@ -25,10 +26,10 @@ class TodaySleepCard extends StatelessWidget {
? context.colors.textPrimary
: healthData?.sleepStateColor(context);
String sleepStateStr =
sleepDuration == 0 ? '--' : healthData?.sleepStateStr() ?? '--';
sleepDuration == 0 ? '-' : healthData?.sleepStateStr() ?? '-';
String sleepAverageHeartRate =
(healthData?.hrAvg == null || healthData?.hrAvg == 0)
? '--'
? '-'
: '${healthData?.hrAvg}';
double sleepDurationProgress = sleepDuration == 0
? -1
... ... @@ -39,36 +40,103 @@ class TodaySleepCard extends StatelessWidget {
return _TodaySummaryCard(
iconAsset: 'assets/images/common/ic_sleep_stroke.png',
iconColor: context.colors.primary,
title: context.l10n.sleep,
actionText: context.l10n.viewSleepReport,
title: l10n.sleep,
actionText: l10n.viewSleepReport,
metrics: [
_MetricData(
label: context.l10n.duration,
labelColor: context.colors.primary,
valueSpans: [
_MetricValueSpan(
sleepHours > 0 ? '$sleepHours' : '--', l10n.reportUnitHour),
_MetricValueSpan(sleepMinutes > 0 ? '$sleepMinutes' : '--',
l10n.reportUnitMinute),
],
),
_MetricData(
label: context.l10n.quality,
_MetricBlock(
label: l10n.duration,
labelColor: context.colors.primary,
value: (sleepDuration > 0)
? Text.rich(
TextSpan(
style: TextStyle(
color: context.colors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.w600,
height: 19 / 16,
),
children: [
if (sleepHours > 0) ...[
TextSpan(text: '$sleepHours'),
TextSpan(
text: ' ${l10n.reportUnitHour} ',
style: TextStyle(
color: context.colors.textSecondary,
fontSize: 12,
fontWeight: FontWeight.w400,
),
)
],
if (sleepMinutes > 0) ...[
TextSpan(text: '$sleepMinutes'),
TextSpan(
text: ' ${l10n.reportUnitMinute}',
style: TextStyle(
color: context.colors.textSecondary,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
]
],
),
)
: Text(
'-',
style: TextStyle(
color: context.colors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.w600,
height: 19 / 16,
),
)),
_MetricBlock(
label: l10n.quality,
labelColor: const Color(0xFF7B9BFB),
valueSpans: [
_MetricValueSpan(
sleepStateStr,
'',
valueColor: sleepStateColor,
value: Text(
sleepStateStr,
style: TextStyle(
color: sleepStateColor,
fontSize: 16,
fontWeight: FontWeight.w600,
height: 19 / 16,
),
],
),
),
_MetricData(
label: context.l10n.averageHeartRate,
_MetricBlock(
label: l10n.averageHeartRate,
labelColor: context.colors.textSecondary,
valueSpans: [
_MetricValueSpan(sleepAverageHeartRate, 'bpm'),
],
value: sleepDuration > 0 && ((healthData?.hrAvg ?? 0) >= 0)
? Text.rich(
TextSpan(
style: TextStyle(
color: context.colors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.w600,
height: 19 / 16,
),
children: [
TextSpan(text: sleepAverageHeartRate),
TextSpan(
text: ' bpm',
style: TextStyle(
color: context.colors.textSecondary,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
],
),
)
: Text(
'-',
style: TextStyle(
color: context.colors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.w600,
height: 19 / 16,
),
),
),
],
rightWidget: Container(
... ... @@ -104,6 +172,7 @@ class TodayActivityCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
return GestureDetector(
onTap: controller.toTrendActivityPage,
child: Obx(() {
... ... @@ -115,30 +184,100 @@ class TodayActivityCard extends StatelessWidget {
var activityStr = (activity > 0 && (activityTarget?.move ?? 0) > 0)
? '$activity'
: '--';
: '-';
// exercise 单位为秒,转换为小时+分钟
List<_MetricValueSpan> exerciseSpans;
Widget exerciseWidget;
if (exercise > 0 && (activityTarget?.exercise ?? 0) > 0) {
final exHours = exercise ~/ 3600;
final exMinutes = (exercise % 3600) ~/ 60;
exerciseSpans = [
if (exHours > 0) _MetricValueSpan('$exHours', l10n.reportUnitHour),
if (exMinutes > 0 || exHours == 0)
_MetricValueSpan(
exMinutes > 0 ? '$exMinutes' : '0', l10n.reportUnitMinute),
];
exerciseWidget = Text.rich(
TextSpan(
style: TextStyle(
color: context.colors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.w600,
height: 19 / 16,
),
children: [
if (exHours > 0) ...[
TextSpan(text: '$exHours'),
TextSpan(
text: ' ${l10n.reportUnitHour} ',
style: TextStyle(
color: context.colors.textSecondary,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
],
if (exMinutes > 0 || exHours == 0) ...[
TextSpan(text: exMinutes > 0 ? '$exMinutes' : '0'),
TextSpan(
text: ' ${l10n.reportUnitMinute}',
style: TextStyle(
color: context.colors.textSecondary,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
],
],
),
);
} else {
exerciseSpans = [_MetricValueSpan('--', l10n.reportUnitMinute)];
exerciseWidget = Text.rich(
TextSpan(
style: TextStyle(
color: context.colors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.w600,
height: 19 / 16,
),
children: [
const TextSpan(text: '-'),
],
),
);
}
// stand 单位为秒,转换为小时+分钟
List<_MetricValueSpan> standSpans;
Widget standWidget;
if (stand > 0 && (activityTarget?.stand ?? 0) > 0) {
standSpans = [
_MetricValueSpan('$stand', l10n.reportUnitHour),
];
standWidget = Text.rich(
TextSpan(
style: TextStyle(
color: context.colors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.w600,
height: 19 / 16,
),
children: [
TextSpan(text: '$stand'),
TextSpan(
text: ' ${l10n.reportUnitHour}',
style: TextStyle(
color: context.colors.textSecondary,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
],
),
);
} else {
standSpans = [_MetricValueSpan('--', l10n.reportUnitHour)];
standWidget = Text.rich(
TextSpan(
style: TextStyle(
color: context.colors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.w600,
height: 19 / 16,
),
children: [
const TextSpan(text: '-'),
],
),
);
}
double activityProgress =
(activity > 0 && (activityTarget?.move ?? 0) > 0)
... ... @@ -155,25 +294,43 @@ class TodayActivityCard extends StatelessWidget {
return _TodaySummaryCard(
iconAsset: 'assets/images/common/ic_exercise.png',
iconColor: context.colors.warning,
title: context.l10n.fitness,
actionText: context.l10n.viewFitnessReport,
title: l10n.fitness,
actionText: l10n.viewFitnessReport,
metrics: [
_MetricData(
label: context.l10n.event,
_MetricBlock(
label: l10n.event,
labelColor: context.colors.warning,
valueSpans: [
_MetricValueSpan(activityStr, l10n.reportUnitKcal),
],
value: Text.rich(
TextSpan(
style: TextStyle(
color: context.colors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.w600,
height: 19 / 16,
),
children: [
TextSpan(text: activityStr),
TextSpan(
text: ' ${l10n.reportUnitKcal}',
style: TextStyle(
color: context.colors.textSecondary,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
],
),
),
),
_MetricData(
label: context.l10n.exercise,
_MetricBlock(
label: l10n.exercise,
labelColor: context.colors.chartGreen,
valueSpans: exerciseSpans,
value: exerciseWidget,
),
_MetricData(
label: context.l10n.standing,
_MetricBlock(
label: l10n.standing,
labelColor: const Color(0xFF7B9BFB),
valueSpans: standSpans,
value: standWidget,
),
],
rightWidget: Container(
... ... @@ -238,7 +395,7 @@ class _TodaySummaryCard extends StatelessWidget {
final Color iconColor;
final String title;
final String actionText;
final List<_MetricData> metrics;
final List<Widget> metrics;
final Widget rightWidget;
... ... @@ -299,8 +456,7 @@ class _TodaySummaryCard extends StatelessWidget {
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (final metric in metrics)
Expanded(child: _MetricBlock(metric: metric)),
for (final metric in metrics) Expanded(child: metric),
],
),
),
... ... @@ -314,9 +470,15 @@ class _TodaySummaryCard extends StatelessWidget {
}
class _MetricBlock extends StatelessWidget {
const _MetricBlock({required this.metric});
const _MetricBlock({
required this.label,
required this.labelColor,
required this.value,
});
final _MetricData metric;
final String label;
final Color labelColor;
final Widget value;
@override
Widget build(BuildContext context) {
... ... @@ -326,11 +488,11 @@ class _MetricBlock extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
metric.label,
label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: metric.labelColor,
color: labelColor,
fontSize: 12,
fontWeight: FontWeight.w500,
height: 17 / 12,
... ... @@ -340,78 +502,10 @@ class _MetricBlock extends StatelessWidget {
FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.centerLeft,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
for (final span in metric.valueSpans) _MetricValue(value: span),
],
),
child: value,
),
],
),
);
}
}
class _MetricValue extends StatelessWidget {
const _MetricValue({required this.value});
final _MetricValueSpan value;
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
value.value,
style: TextStyle(
color: value.valueColor ?? context.colors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.w600,
height: 19 / 16,
),
),
if (value.unit.isNotEmpty)
Padding(
padding: const EdgeInsets.only(left: 1, bottom: 1),
child: Text(
value.unit,
style: TextStyle(
color: context.colors.textSecondary,
fontSize: 12,
fontWeight: FontWeight.w400,
height: 17 / 12,
),
),
),
if (value.unit.isNotEmpty) const SizedBox(width: 4),
],
);
}
}
class _MetricData {
const _MetricData({
required this.label,
required this.labelColor,
required this.valueSpans,
});
final String label;
final Color labelColor;
final List<_MetricValueSpan> valueSpans;
}
class _MetricValueSpan {
const _MetricValueSpan(
this.value,
this.unit, {
this.valueColor,
});
final String value;
final String unit;
final Color? valueColor;
}
... ...
... ... @@ -57,6 +57,7 @@ class LoginController extends GetxController {
AppToast.show(AppLocalizations.of(Get.context!)!.loginAgreeToTermsToast);
return;
}
ta.track('enter_doublefeel_telephone_login_page');
Get.toNamed(AppRoutes.phoneLogin);
}
... ... @@ -227,6 +228,7 @@ class LoginController extends GetxController {
String accessToken = loginData.accessToken;
if (isRegister) {
ta.track('success_regist');
final regRes = await _userApi.register(isApple
? {
'login_type': 'apple',
... ...
... ... @@ -172,8 +172,8 @@ class PhoneLoginView extends GetView<LoginController> {
return TextField(
controller: controller.codeController,
focusNode: controller.codeFocusNode,
keyboardType: TextInputType.text,
maxLength: 8,
keyboardType: TextInputType.phone,
maxLength: 6,
style: TextStyle(
color: context.colors.textPrimary,
fontSize: 16,
... ...
... ... @@ -5,6 +5,7 @@ import 'package:doublefeel_flutter/core/logging/app_logger.dart';
import 'package:doublefeel_flutter/core/network/api/pay_api.dart';
import 'package:doublefeel_flutter/core/network/api/vip_api.dart';
import 'package:doublefeel_flutter/core/result/app_result.dart';
import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
import 'package:doublefeel_flutter/core/util/app_toast.dart';
import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
import 'package:doublefeel_flutter/data/models/local/user_preferences.dart';
... ... @@ -214,4 +215,10 @@ class MembershipOfferController extends GetxController {
Get.back();
}
}
@override
void onReady() {
super.onReady();
ta.track('enter_doublefeel_vip_discount_page');
}
}
... ...
... ... @@ -45,7 +45,7 @@ class MembershipOfferView extends GetView<MembershipOfferController> {
0,
MediaQuery.paddingOf(context).top + 4,
0,
140,
0,
),
child: Stack(
children: [
... ...
import 'package:doublefeel_flutter/app/apple_health_upload/apple_health_upload_tool.dart';
import 'package:doublefeel_flutter/core/logging/app_logger.dart';
import 'package:doublefeel_flutter/core/network/api/user_api.dart';
import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
import 'package:doublefeel_flutter/core/services/user_state_service.dart';
import 'package:doublefeel_flutter/data/local/user_account_storage.dart';
import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
... ... @@ -119,6 +120,11 @@ class UserOnboardingController extends GetxController {
report();
} on Exception catch (e) {}
currentPageIndex.value++;
if (currentPageIndex.value == 7) {
ta.track('enter_doublefeel_health_grant_page');
} else if (currentPageIndex.value == 8) {
ta.track('enter_doublefeel_notification_grant_page');
}
}
void report() {
... ...
... ... @@ -26,11 +26,13 @@ class AppErrorHandler {
}
if (businessCode == null ||
!policy.excludeErrorCodeList.contains(businessCode)) {
_showMessage(businessMessage ?? error.displayMessage);
if (businessMessage != null) {
_showMessage(businessMessage);
}
}
case AppNetworkError():
case AppUnknownError():
_showMessage(error.displayMessage);
// _showMessage(error.displayMessage);
case AppCancelledError():
break;
}
... ...
... ... @@ -18,6 +18,13 @@ class ThinkingDataService {
);
TDAnalytics.init(
_environment.thinkingDataAppId, AppConst.thinkingDataServerUrl);
TDAnalytics.calibrateTimeWithNtp('time.apple.com');
TDAnalytics.enableAutoTrack(TDAutoTrackEventType.APP_START |
TDAutoTrackEventType.APP_END |
TDAutoTrackEventType.APP_INSTALL);
if (_environment.isDebug) {
TDAnalytics.enableLog(true);
}
}
void login(String userId) {
... ... @@ -32,7 +39,7 @@ class ThinkingDataService {
void userSet(Map<String, Object?> properties) {
AppLogger.i('ThinkingData userSet $properties');
TDAnalytics.setSuperProperties(properties);
TDAnalytics.userSet(properties);
}
void timeEvent(String s) {
... ...
... ... @@ -61,10 +61,16 @@ class UserStateService {
final partner = _userPrefs.preferences.value.partnerUserInfo;
final props = <String, Object?>{};
if ((me?.id ?? 0) > 0) {
props['uid'] = me!.id;
props['user_id'] = me!.id;
}
if ((partner?.id ?? 0) > 0) {
props['lid'] = partner!.id;
if (me?.telephone != null) {
props['user_phone'] = me!.telephone;
}
if (me?.thirdAccountId != null) {
props['apple_id'] = me!.thirdAccountId;
}
if ((me?.createTime ?? 0) > 0) {
props['account_create_time'] = me!.createTime;
}
if (props.isNotEmpty) {
_thinkingDataService.userSet(props);
... ...