interact_controller.dart
6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
import 'dart:async';
import 'package:doublefeel_flutter/app/routes/app_pages.dart';
import 'package:doublefeel_flutter/core/network/api/interaction_api.dart';
import 'package:doublefeel_flutter/core/result/app_result.dart';
import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
import 'package:doublefeel_flutter/data/models/interaction/interaction_models.dart';
import 'package:doublefeel_flutter/data/models/user/user_models.dart';
import 'package:get/get.dart';
enum InteractAction { stick, miss, punch }
extension InteractActionUi on InteractAction {
int get apiValue => switch (this) {
InteractAction.stick => 0,
InteractAction.miss => 1,
InteractAction.punch => 2,
};
String get title => switch (this) {
InteractAction.stick => '戳一戳',
InteractAction.miss => '想Ta',
InteractAction.punch => '打一拳',
};
bool get needsVip => this != InteractAction.stick;
String get iconAsset => switch (this) {
InteractAction.stick => 'assets/images/interact/icon_stick.png',
InteractAction.miss => 'assets/images/interact/icon_miss.png',
InteractAction.punch => 'assets/images/interact/icon_punch.png',
};
String get titleAsset => switch (this) {
InteractAction.stick => 'assets/images/interact/title_stick.png',
InteractAction.miss => 'assets/images/interact/title_miss.png',
InteractAction.punch => 'assets/images/interact/title_punch.png',
};
String get lockedTitleAsset => switch (this) {
InteractAction.stick => 'assets/images/interact/title_stick_locked.png',
InteractAction.miss => 'assets/images/interact/title_miss_locked.png',
InteractAction.punch => 'assets/images/interact/title_punch_locked.png',
};
}
class InteractController extends GetxController {
InteractController(this._interactionApi, this._userPreferencesStorage);
final InteractionApi _interactionApi;
final UserPreferencesStorage _userPreferencesStorage;
final records = <InteractionRecord>[].obs;
final isLoading = false.obs;
final sendingAction = Rxn<InteractAction>();
final activeAction = Rxn<InteractAction>();
UserInfoResponse? get me =>
_userPreferencesStorage.preferences.value.meUserInfo;
UserInfoResponse? get partner =>
_userPreferencesStorage.preferences.value.partnerUserInfo;
bool get isPaired => partner?.id != null;
bool get isVip =>
_userPreferencesStorage.preferences.value.vipInfo?.isVip ?? false;
@override
void onInit() {
super.onInit();
unawaited(loadRecords());
}
Future<void> loadRecords() async {
isLoading.value = true;
final result = await _interactionApi.getInteractionRecordList();
isLoading.value = false;
switch (result) {
case AppSuccess(:final data):
records.assignAll(data.records ?? const <InteractionRecord>[]);
case AppFailure():
// Keep the last successful list visible when a refresh fails.
}
}
Future<void> sendAction(
InteractAction action, {
InteractionDataAction? actionVariables,
}) async {
if (!isPaired) {
_showToast('请先绑定另一半');
Get.toNamed(AppRoutes.bindPartner);
return;
}
if (action.needsVip && !isVip) {
_showToast('${action.title}为会员互动,请先开通会员');
Get.toNamed(Routes.PURCHASE);
return;
}
if (sendingAction.value != null) return;
sendingAction.value = action;
activeAction.value = action;
final result = await _interactionApi.sendInteraction(
InteractionData(
interactionType: 0,
actionType: action.apiValue,
action: actionVariables,
),
);
sendingAction.value = null;
switch (result) {
case AppSuccess():
_showToast('${action.title}成功!');
unawaited(loadRecords());
case AppFailure(:final error):
_showToast('互动发送失败:$error');
}
Future<void>.delayed(const Duration(seconds: 4), () {
if (activeAction.value == action) activeAction.value = null;
});
}
String recordText(InteractionRecord record) {
final text = record.text?.trim();
if (text != null && text.isNotEmpty) return text;
final actor = record.userId == me?.id ? '我' : '对方';
final target = record.action?.objectTarget == 1 ? '自己' : 'Ta';
return switch (record.actionType) {
0 => '$actor 戳了戳$target~',
1 => '$actor 想$target 了一下~',
2 => '$actor 打了$target 一拳~',
_ => '$actor 发起了一次互动~',
};
}
String recordTime(InteractionRecord record) {
final timestamp = record.createTime;
if (timestamp == null || timestamp <= 0) return '';
final date = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
return '${date.month.toString().padLeft(2, '0')}-'
'${date.day.toString().padLeft(2, '0')} '
'${date.hour.toString().padLeft(2, '0')}:'
'${date.minute.toString().padLeft(2, '0')}';
}
String? partnerActionAsset() {
final action = activeAction.value;
if (action == null || !isPaired) return null;
final character = switch (partner?.persona) {
2 => 'Dog',
3 => 'Rabbit',
4 => 'Elephant',
_ => 'Cat',
};
final actionName = switch (action) {
InteractAction.stick => 'Stick',
InteractAction.miss => 'Miss',
InteractAction.punch => 'Punch',
};
return 'assets/images/interact/animations/'
'character${character}Interaction${actionName}Animation.png';
}
String? actionMaskAsset() => switch (activeAction.value) {
InteractAction.stick =>
'assets/images/interact/animations/interactionStickMaskAnimation.png',
InteractAction.miss =>
'assets/images/interact/animations/interactionMissMaskAnimation.png',
InteractAction.punch =>
'assets/images/interact/animations/interactionPunchMaskAnimation.png',
null => null,
};
void _showToast(String message) {
Get.snackbar('互动', message, snackPosition: SnackPosition.BOTTOM);
}
}