fix(mobile-next): 把加载、失败、断网接到真实 SDK 与目录请求
会话列表、通讯录、好友申请、添加同事页覆盖 CompanyLoadingView/CompanyFailView; 主页断网横幅跟随 IMSdkStatus.connectionFailed。不伪造状态。 Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Cursor
multica-agent
parent
092f513708
commit
2b2c2d28a4
@@ -6,6 +6,7 @@ import 'package:openim_common/openim_common.dart';
|
|||||||
import '../auth/auth_api.dart';
|
import '../auth/auth_api.dart';
|
||||||
import '../brand/app_tokens.dart';
|
import '../brand/app_tokens.dart';
|
||||||
import '../directory/directory_api.dart';
|
import '../directory/directory_api.dart';
|
||||||
|
import 'state_views.dart';
|
||||||
|
|
||||||
class AddColleagueLogic extends GetxController {
|
class AddColleagueLogic extends GetxController {
|
||||||
final staffCtrl = TextEditingController();
|
final staffCtrl = TextEditingController();
|
||||||
@@ -13,9 +14,17 @@ class AddColleagueLogic extends GetxController {
|
|||||||
final sending = false.obs;
|
final sending = false.obs;
|
||||||
final staffError = RxnString();
|
final staffError = RxnString();
|
||||||
final directoryHint = RxnString();
|
final directoryHint = RxnString();
|
||||||
|
final loading = true.obs;
|
||||||
|
final loadFailed = false.obs;
|
||||||
|
|
||||||
final _directory = DirectoryApi();
|
final _directory = DirectoryApi();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onReady() {
|
||||||
|
reload();
|
||||||
|
super.onReady();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
staffCtrl.dispose();
|
staffCtrl.dispose();
|
||||||
@@ -23,6 +32,18 @@ class AddColleagueLogic extends GetxController {
|
|||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> reload() async {
|
||||||
|
loading.value = true;
|
||||||
|
loadFailed.value = false;
|
||||||
|
try {
|
||||||
|
await _directory.search(keyword: '', limit: 1);
|
||||||
|
} catch (e) {
|
||||||
|
loadFailed.value = true;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> lookupDirectory() async {
|
Future<void> lookupDirectory() async {
|
||||||
final id = staffCtrl.text.trim();
|
final id = staffCtrl.text.trim();
|
||||||
if (id.isEmpty) {
|
if (id.isEmpty) {
|
||||||
@@ -35,7 +56,8 @@ class AddColleagueLogic extends GetxController {
|
|||||||
if (hit.isNotEmpty) {
|
if (hit.isNotEmpty) {
|
||||||
directoryHint.value = hit.first.nickname;
|
directoryHint.value = hit.first.nickname;
|
||||||
} else if (items.isNotEmpty) {
|
} else if (items.isNotEmpty) {
|
||||||
directoryHint.value = items.map((e) => '${e.nickname}(${e.userID})').join('、');
|
directoryHint.value =
|
||||||
|
items.map((e) => '${e.nickname}(${e.userID})').join('、');
|
||||||
} else {
|
} else {
|
||||||
directoryHint.value = null;
|
directoryHint.value = null;
|
||||||
}
|
}
|
||||||
@@ -56,7 +78,8 @@ class AddColleagueLogic extends GetxController {
|
|||||||
try {
|
try {
|
||||||
await OpenIM.iMManager.friendshipManager.addFriend(
|
await OpenIM.iMManager.friendshipManager.addFriend(
|
||||||
userID: id,
|
userID: id,
|
||||||
reason: msgCtrl.text.trim().isEmpty ? '你好,我想加你为同事' : msgCtrl.text.trim(),
|
reason:
|
||||||
|
msgCtrl.text.trim().isEmpty ? '你好,我想加你为同事' : msgCtrl.text.trim(),
|
||||||
);
|
);
|
||||||
IMViews.showToast('申请已发送,等对方通过');
|
IMViews.showToast('申请已发送,等对方通过');
|
||||||
Get.back();
|
Get.back();
|
||||||
@@ -85,67 +108,84 @@ class AddColleaguePage extends StatelessWidget {
|
|||||||
onPressed: Get.back,
|
onPressed: Get.back,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: Obx(() {
|
||||||
padding: const EdgeInsets.all(AppGap.x4),
|
if (logic.loading.value) {
|
||||||
child: Column(
|
return const CompanyLoadingView();
|
||||||
children: [
|
}
|
||||||
Obx(() => _box(
|
if (logic.loadFailed.value) {
|
||||||
controller: logic.staffCtrl,
|
return CompanyFailView(onRetry: logic.reload);
|
||||||
hint: '请输入对方工号',
|
}
|
||||||
height: 48,
|
return Padding(
|
||||||
error: logic.staffError.value,
|
padding: const EdgeInsets.all(AppGap.x4),
|
||||||
enabled: !logic.sending.value,
|
child: Column(
|
||||||
onChanged: (_) {
|
children: [
|
||||||
logic.staffError.value = null;
|
_box(
|
||||||
logic.lookupDirectory();
|
controller: logic.staffCtrl,
|
||||||
},
|
hint: '请输入对方工号',
|
||||||
)),
|
|
||||||
Obx(() {
|
|
||||||
final hint = logic.directoryHint.value;
|
|
||||||
if (hint == null) return const SizedBox.shrink();
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(top: AppGap.x2),
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: Text(hint, style: const TextStyle(fontSize: AppFont.sub, color: AppColors.textSecondary)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
const SizedBox(height: AppGap.x3),
|
|
||||||
_box(
|
|
||||||
controller: logic.msgCtrl,
|
|
||||||
hint: '你好,我想加你为同事',
|
|
||||||
height: 96,
|
|
||||||
maxLines: 4,
|
|
||||||
enabled: !logic.sending.value,
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
Obx(() {
|
|
||||||
final busy = logic.sending.value;
|
|
||||||
return SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: 48,
|
height: 48,
|
||||||
child: FilledButton(
|
error: logic.staffError.value,
|
||||||
onPressed: busy ? null : logic.send,
|
enabled: !logic.sending.value,
|
||||||
style: FilledButton.styleFrom(
|
onChanged: (_) {
|
||||||
backgroundColor: AppColors.primary,
|
logic.staffError.value = null;
|
||||||
disabledBackgroundColor: AppColors.primary.withOpacity(0.4),
|
logic.lookupDirectory();
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(AppRadius.control)),
|
},
|
||||||
|
),
|
||||||
|
Builder(builder: (_) {
|
||||||
|
final hint = logic.directoryHint.value;
|
||||||
|
if (hint == null) return const SizedBox.shrink();
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(top: AppGap.x2),
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text(hint,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: AppFont.sub,
|
||||||
|
color: AppColors.textSecondary)),
|
||||||
),
|
),
|
||||||
child: busy
|
);
|
||||||
? const SizedBox(
|
}),
|
||||||
width: 16,
|
const SizedBox(height: AppGap.x3),
|
||||||
height: 16,
|
_box(
|
||||||
child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white),
|
controller: logic.msgCtrl,
|
||||||
)
|
hint: '你好,我想加你为同事',
|
||||||
: const Text('发送申请', style: TextStyle(fontSize: AppFont.body, color: Colors.white)),
|
height: 96,
|
||||||
),
|
maxLines: 4,
|
||||||
);
|
enabled: !logic.sending.value,
|
||||||
}),
|
),
|
||||||
const SizedBox(height: AppGap.x4),
|
const Spacer(),
|
||||||
],
|
Builder(builder: (_) {
|
||||||
),
|
final busy = logic.sending.value;
|
||||||
),
|
return SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 48,
|
||||||
|
child: FilledButton(
|
||||||
|
onPressed: busy ? null : logic.send,
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
backgroundColor: AppColors.primary,
|
||||||
|
disabledBackgroundColor:
|
||||||
|
AppColors.primary.withOpacity(0.4),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(AppRadius.control)),
|
||||||
|
),
|
||||||
|
child: busy
|
||||||
|
? const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2, color: Colors.white),
|
||||||
|
)
|
||||||
|
: const Text('发送申请',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: AppFont.body, color: Colors.white)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
const SizedBox(height: AppGap.x4),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,30 +203,36 @@ class AddColleaguePage extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
height: height,
|
height: height,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: AppGap.x3, vertical: AppGap.x2),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: AppGap.x3, vertical: AppGap.x2),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.searchBg,
|
color: AppColors.searchBg,
|
||||||
borderRadius: BorderRadius.circular(AppRadius.control),
|
borderRadius: BorderRadius.circular(AppRadius.control),
|
||||||
border: Border.all(color: error != null ? AppColors.danger : Colors.transparent),
|
border: Border.all(
|
||||||
|
color: error != null ? AppColors.danger : Colors.transparent),
|
||||||
),
|
),
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
maxLines: maxLines,
|
maxLines: maxLines,
|
||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
style: const TextStyle(fontSize: AppFont.body, color: AppColors.textPrimary),
|
style: const TextStyle(
|
||||||
|
fontSize: AppFont.body, color: AppColors.textPrimary),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
isDense: true,
|
isDense: true,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
hintText: hint,
|
hintText: hint,
|
||||||
hintStyle: const TextStyle(fontSize: AppFont.body, color: AppColors.textSecondary),
|
hintStyle: const TextStyle(
|
||||||
|
fontSize: AppFont.body, color: AppColors.textSecondary),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (error != null)
|
if (error != null)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: AppGap.x1),
|
padding: const EdgeInsets.only(top: AppGap.x1),
|
||||||
child: Text(error, style: const TextStyle(fontSize: AppFont.small, color: AppColors.danger)),
|
child: Text(error,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: AppFont.small, color: AppColors.danger)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,10 +14,14 @@ class ContactsLogic extends GetxController
|
|||||||
final homeLogic = Get.find<HomeLogic>();
|
final homeLogic = Get.find<HomeLogic>();
|
||||||
|
|
||||||
final friendApplicationList = <UserInfo>[];
|
final friendApplicationList = <UserInfo>[];
|
||||||
|
final loading = true.obs;
|
||||||
|
final loadFailed = false.obs;
|
||||||
|
|
||||||
int get friendApplicationCount => homeLogic.unhandledFriendApplicationCount.value;
|
int get friendApplicationCount =>
|
||||||
|
homeLogic.unhandledFriendApplicationCount.value;
|
||||||
|
|
||||||
int get groupApplicationCount => homeLogic.unhandledGroupApplicationCount.value;
|
int get groupApplicationCount =>
|
||||||
|
homeLogic.unhandledGroupApplicationCount.value;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@@ -28,6 +32,36 @@ class ContactsLogic extends GetxController
|
|||||||
super.onInit();
|
super.onInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onReady() {
|
||||||
|
reload();
|
||||||
|
super.onReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> reload() async {
|
||||||
|
loading.value = true;
|
||||||
|
loadFailed.value = false;
|
||||||
|
try {
|
||||||
|
await Future.wait([
|
||||||
|
OpenIM.iMManager.friendshipManager
|
||||||
|
.getFriendApplicationListAsRecipient(),
|
||||||
|
OpenIM.iMManager.groupManager.getGroupApplicationListAsRecipient(),
|
||||||
|
OpenIM.iMManager.friendshipManager.getFriendListPage(
|
||||||
|
offset: 0,
|
||||||
|
count: 1,
|
||||||
|
filterBlack: true,
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
homeLogic.getUnhandledFriendApplicationCount();
|
||||||
|
homeLogic.getUnhandledGroupApplicationCount();
|
||||||
|
} catch (e, s) {
|
||||||
|
Logger.print('contacts reload e: $e $s');
|
||||||
|
loadFailed.value = true;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
PackageBridge.selectContactsBridge = null;
|
PackageBridge.selectContactsBridge = null;
|
||||||
@@ -69,7 +103,8 @@ class ContactsLogic extends GetxController
|
|||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
viewUserProfile(String userID, String? nickname, String? faceURL, [String? groupID]) =>
|
viewUserProfile(String userID, String? nickname, String? faceURL,
|
||||||
|
[String? groupID]) =>
|
||||||
AppNavigator.startUserProfilePane(
|
AppNavigator.startUserProfilePane(
|
||||||
userID: userID,
|
userID: userID,
|
||||||
nickname: nickname,
|
nickname: nickname,
|
||||||
@@ -85,5 +120,6 @@ class ContactsLogic extends GetxController
|
|||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
scanOutUserID(String userID) => AppNavigator.startUserProfilePane(userID: userID, offAndToNamed: true);
|
scanOutUserID(String userID) =>
|
||||||
|
AppNavigator.startUserProfilePane(userID: userID, offAndToNamed: true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:openim_common/openim_common.dart';
|
import 'package:openim_common/openim_common.dart';
|
||||||
|
|
||||||
import '../../company/brand/app_tokens.dart';
|
import '../../company/brand/app_tokens.dart';
|
||||||
|
import '../../company/ui/state_views.dart';
|
||||||
import 'contacts_logic.dart';
|
import 'contacts_logic.dart';
|
||||||
|
|
||||||
class ContactsPage extends StatelessWidget {
|
class ContactsPage extends StatelessWidget {
|
||||||
@@ -19,12 +20,19 @@ class ContactsPage extends StatelessWidget {
|
|||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: logic.addContacts,
|
onPressed: logic.addContacts,
|
||||||
icon: const Icon(Icons.person_add_alt, size: AppIconSize.md, color: AppColors.textPrimary),
|
icon: const Icon(Icons.person_add_alt,
|
||||||
|
size: AppIconSize.md, color: AppColors.textPrimary),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: Obx(
|
body: Obx(() {
|
||||||
() => Column(
|
if (logic.loading.value) {
|
||||||
|
return const CompanyLoadingView();
|
||||||
|
}
|
||||||
|
if (logic.loadFailed.value) {
|
||||||
|
return CompanyFailView(onRetry: logic.reload);
|
||||||
|
}
|
||||||
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
_entry(
|
_entry(
|
||||||
icon: Icons.person_add_alt,
|
icon: Icons.person_add_alt,
|
||||||
@@ -44,8 +52,8 @@ class ContactsPage extends StatelessWidget {
|
|||||||
onTap: logic.myFriend,
|
onTap: logic.myFriend,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
);
|
||||||
),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,10 +79,13 @@ class ContactsPage extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(AppRadius.control),
|
borderRadius: BorderRadius.circular(AppRadius.control),
|
||||||
),
|
),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Icon(icon, size: AppIconSize.sm, color: AppColors.primary),
|
child:
|
||||||
|
Icon(icon, size: AppIconSize.sm, color: AppColors.primary),
|
||||||
),
|
),
|
||||||
const SizedBox(width: AppGap.x3),
|
const SizedBox(width: AppGap.x3),
|
||||||
Text(label, style: const TextStyle(fontSize: AppFont.body, color: AppColors.textPrimary)),
|
Text(label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: AppFont.body, color: AppColors.textPrimary)),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
if (count > 0) UnreadCountView(count: count),
|
if (count > 0) UnreadCountView(count: count),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ class FriendRequestsLogic extends GetxController {
|
|||||||
final imLogic = Get.find<IMController>();
|
final imLogic = Get.find<IMController>();
|
||||||
final homeLogic = Get.find<HomeLogic>();
|
final homeLogic = Get.find<HomeLogic>();
|
||||||
final applicationList = <FriendApplicationInfo>[].obs;
|
final applicationList = <FriendApplicationInfo>[].obs;
|
||||||
|
final loading = true.obs;
|
||||||
|
final loadFailed = false.obs;
|
||||||
late StreamSubscription faSub;
|
late StreamSubscription faSub;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -27,6 +29,8 @@ class FriendRequestsLogic extends GetxController {
|
|||||||
super.onReady();
|
super.onReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void retry() => _getFriendRequestsList();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
faSub.cancel();
|
faSub.cancel();
|
||||||
@@ -35,43 +39,56 @@ class FriendRequestsLogic extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _getFriendRequestsList() async {
|
void _getFriendRequestsList() async {
|
||||||
final list = await Future.wait([
|
loading.value = true;
|
||||||
OpenIM.iMManager.friendshipManager.getFriendApplicationListAsRecipient(),
|
loadFailed.value = false;
|
||||||
OpenIM.iMManager.friendshipManager.getFriendApplicationListAsApplicant(),
|
try {
|
||||||
]);
|
final list = await Future.wait([
|
||||||
|
OpenIM.iMManager.friendshipManager
|
||||||
|
.getFriendApplicationListAsRecipient(),
|
||||||
|
OpenIM.iMManager.friendshipManager
|
||||||
|
.getFriendApplicationListAsApplicant(),
|
||||||
|
]);
|
||||||
|
|
||||||
final allList = <FriendApplicationInfo>[];
|
final allList = <FriendApplicationInfo>[];
|
||||||
allList
|
allList
|
||||||
..addAll(list[0])
|
..addAll(list[0])
|
||||||
..addAll(list[1]);
|
..addAll(list[1]);
|
||||||
|
|
||||||
allList.sort((a, b) {
|
allList.sort((a, b) {
|
||||||
if (a.createTime! > b.createTime!) {
|
if (a.createTime! > b.createTime!) {
|
||||||
return -1;
|
return -1;
|
||||||
} else if (a.createTime! < b.createTime!) {
|
} else if (a.createTime! < b.createTime!) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
var haveReadList = DataSp.getHaveReadUnHandleFriendApplication();
|
var haveReadList = DataSp.getHaveReadUnHandleFriendApplication();
|
||||||
haveReadList ??= <String>[];
|
haveReadList ??= <String>[];
|
||||||
for (var e in list[0]) {
|
for (var e in list[0]) {
|
||||||
var id = IMUtils.buildFriendApplicationID(e);
|
var id = IMUtils.buildFriendApplicationID(e);
|
||||||
if (!haveReadList.contains(id)) {
|
if (!haveReadList.contains(id)) {
|
||||||
haveReadList.add(id);
|
haveReadList.add(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
DataSp.putHaveReadUnHandleFriendApplication(haveReadList);
|
||||||
|
applicationList.assignAll(allList);
|
||||||
|
} catch (e, s) {
|
||||||
|
Logger.print('friend requests e: $e $s');
|
||||||
|
loadFailed.value = true;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
}
|
}
|
||||||
DataSp.putHaveReadUnHandleFriendApplication(haveReadList);
|
|
||||||
applicationList.assignAll(allList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isISendRequest(FriendApplicationInfo info) => info.fromUserID == OpenIM.iMManager.userID;
|
bool isISendRequest(FriendApplicationInfo info) =>
|
||||||
|
info.fromUserID == OpenIM.iMManager.userID;
|
||||||
|
|
||||||
void acceptFriendApplication(FriendApplicationInfo info) async {
|
void acceptFriendApplication(FriendApplicationInfo info) async {
|
||||||
try {
|
try {
|
||||||
await LoadingView.singleton.wrap(
|
await LoadingView.singleton.wrap(
|
||||||
asyncFunction: () => OpenIM.iMManager.friendshipManager.acceptFriendApplication(userID: info.fromUserID!),
|
asyncFunction: () => OpenIM.iMManager.friendshipManager
|
||||||
|
.acceptFriendApplication(userID: info.fromUserID!),
|
||||||
);
|
);
|
||||||
IMViews.showToast(StrRes.addSuccessfully);
|
IMViews.showToast(StrRes.addSuccessfully);
|
||||||
_getFriendRequestsList();
|
_getFriendRequestsList();
|
||||||
@@ -83,7 +100,8 @@ class FriendRequestsLogic extends GetxController {
|
|||||||
void refuseFriendApplication(FriendApplicationInfo info) async {
|
void refuseFriendApplication(FriendApplicationInfo info) async {
|
||||||
try {
|
try {
|
||||||
await LoadingView.singleton.wrap(
|
await LoadingView.singleton.wrap(
|
||||||
asyncFunction: () => OpenIM.iMManager.friendshipManager.refuseFriendApplication(userID: info.fromUserID!),
|
asyncFunction: () => OpenIM.iMManager.friendshipManager
|
||||||
|
.refuseFriendApplication(userID: info.fromUserID!),
|
||||||
);
|
);
|
||||||
IMViews.showToast(StrRes.rejectSuccessfully);
|
IMViews.showToast(StrRes.rejectSuccessfully);
|
||||||
_getFriendRequestsList();
|
_getFriendRequestsList();
|
||||||
|
|||||||
@@ -18,6 +18,12 @@ class FriendRequestsPage extends StatelessWidget {
|
|||||||
appBar: AppBar(title: const Text('新的同事')),
|
appBar: AppBar(title: const Text('新的同事')),
|
||||||
backgroundColor: AppColors.pageBg,
|
backgroundColor: AppColors.pageBg,
|
||||||
body: Obx(() {
|
body: Obx(() {
|
||||||
|
if (logic.loading.value) {
|
||||||
|
return const CompanyLoadingView();
|
||||||
|
}
|
||||||
|
if (logic.loadFailed.value) {
|
||||||
|
return CompanyFailView(onRetry: logic.retry);
|
||||||
|
}
|
||||||
if (logic.applicationList.isEmpty) {
|
if (logic.applicationList.isEmpty) {
|
||||||
return const CompanyEmptyView(
|
return const CompanyEmptyView(
|
||||||
icon: Icons.person_outline,
|
icon: Icons.person_outline,
|
||||||
@@ -27,7 +33,8 @@ class FriendRequestsPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
itemCount: logic.applicationList.length,
|
itemCount: logic.applicationList.length,
|
||||||
itemBuilder: (_, index) => _buildItemView(logic.applicationList[index]),
|
itemBuilder: (_, index) =>
|
||||||
|
_buildItemView(logic.applicationList[index]),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -43,10 +50,12 @@ class FriendRequestsPage extends StatelessWidget {
|
|||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
constraints: const BoxConstraints(minHeight: 72),
|
constraints: const BoxConstraints(minHeight: 72),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: AppGap.x4, vertical: AppGap.x3),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: AppGap.x4, vertical: AppGap.x3),
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: AppColors.pageBg,
|
color: AppColors.pageBg,
|
||||||
border: Border(bottom: BorderSide(color: AppColors.divider, width: 0.5)),
|
border:
|
||||||
|
Border(bottom: BorderSide(color: AppColors.divider, width: 0.5)),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -61,21 +70,25 @@ class FriendRequestsPage extends StatelessWidget {
|
|||||||
name ?? '',
|
name ?? '',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(fontSize: AppFont.body, color: AppColors.textPrimary),
|
style: const TextStyle(
|
||||||
|
fontSize: AppFont.body, color: AppColors.textPrimary),
|
||||||
),
|
),
|
||||||
if (IMUtils.isNotNullEmptyStr(info.reqMsg))
|
if (IMUtils.isNotNullEmptyStr(info.reqMsg))
|
||||||
Text(
|
Text(
|
||||||
info.reqMsg ?? '',
|
info.reqMsg ?? '',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(fontSize: AppFont.sub, color: AppColors.textSecondary),
|
style: const TextStyle(
|
||||||
|
fontSize: AppFont.sub, color: AppColors.textSecondary),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: AppGap.x2),
|
const SizedBox(width: AppGap.x2),
|
||||||
if (waiting && isISendRequest)
|
if (waiting && isISendRequest)
|
||||||
const Text('等待对方通过', style: TextStyle(fontSize: AppFont.sub, color: AppColors.textSecondary)),
|
const Text('等待对方通过',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: AppFont.sub, color: AppColors.textSecondary)),
|
||||||
if (waiting && !isISendRequest)
|
if (waiting && !isISendRequest)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 132,
|
width: 132,
|
||||||
@@ -87,11 +100,15 @@ class FriendRequestsPage extends StatelessWidget {
|
|||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
minimumSize: const Size(0, 32),
|
minimumSize: const Size(0, 32),
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
side: const BorderSide(color: AppColors.textSecondary, width: 0.5),
|
side: const BorderSide(
|
||||||
|
color: AppColors.textSecondary, width: 0.5),
|
||||||
foregroundColor: AppColors.textPrimary,
|
foregroundColor: AppColors.textPrimary,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(AppRadius.control)),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(AppRadius.control)),
|
||||||
),
|
),
|
||||||
child: const Text('拒绝', style: TextStyle(fontSize: AppFont.sub)),
|
child: const Text('拒绝',
|
||||||
|
style: TextStyle(fontSize: AppFont.sub)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: AppGap.x2),
|
const SizedBox(width: AppGap.x2),
|
||||||
@@ -102,16 +119,26 @@ class FriendRequestsPage extends StatelessWidget {
|
|||||||
minimumSize: const Size(0, 32),
|
minimumSize: const Size(0, 32),
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
backgroundColor: AppColors.primary,
|
backgroundColor: AppColors.primary,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(AppRadius.control)),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(AppRadius.control)),
|
||||||
),
|
),
|
||||||
child: const Text('接受', style: TextStyle(fontSize: AppFont.sub, color: Colors.white)),
|
child: const Text('接受',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: AppFont.sub, color: Colors.white)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (agreed) const Text('已添加', style: TextStyle(fontSize: AppFont.sub, color: AppColors.textSecondary)),
|
if (agreed)
|
||||||
if (rejected) const Text('已拒绝', style: TextStyle(fontSize: AppFont.sub, color: AppColors.textSecondary)),
|
const Text('已添加',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: AppFont.sub, color: AppColors.textSecondary)),
|
||||||
|
if (rejected)
|
||||||
|
const Text('已拒绝',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: AppFont.sub, color: AppColors.textSecondary)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -25,10 +25,13 @@ class ConversationLogic extends GetxController {
|
|||||||
final pageSize = 400;
|
final pageSize = 400;
|
||||||
|
|
||||||
final imStatus = IMSdkStatus.connectionSucceeded.obs;
|
final imStatus = IMSdkStatus.connectionSucceeded.obs;
|
||||||
|
final loading = true.obs;
|
||||||
|
final loadFailed = false.obs;
|
||||||
bool reInstall = false;
|
bool reInstall = false;
|
||||||
|
|
||||||
final ItemScrollController itemScrollController = ItemScrollController();
|
final ItemScrollController itemScrollController = ItemScrollController();
|
||||||
final ItemPositionsListener itemPositionsListener = ItemPositionsListener.create();
|
final ItemPositionsListener itemPositionsListener =
|
||||||
|
ItemPositionsListener.create();
|
||||||
|
|
||||||
int scrollIndex = -1;
|
int scrollIndex = -1;
|
||||||
final onChangeConversations = <ConversationInfo>[];
|
final onChangeConversations = <ConversationInfo>[];
|
||||||
@@ -52,13 +55,16 @@ class ConversationLogic extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.print('IM SDK Status: $status, reinstall: $reInstall, progress: $progress');
|
Logger.print(
|
||||||
|
'IM SDK Status: $status, reinstall: $reInstall, progress: $progress');
|
||||||
|
|
||||||
if (status == IMSdkStatus.syncProgress && reInstall) {
|
if (status == IMSdkStatus.syncProgress && reInstall) {
|
||||||
final p = (progress!).toDouble() / 100.0;
|
final p = (progress!).toDouble() / 100.0;
|
||||||
|
|
||||||
EasyLoading.showProgress(p, status: '${StrRes.synchronizing}(${(p * 100.0).truncate()}%)');
|
EasyLoading.showProgress(p,
|
||||||
} else if (status == IMSdkStatus.syncEnded || status == IMSdkStatus.syncFailed) {
|
status: '${StrRes.synchronizing}(${(p * 100.0).truncate()}%)');
|
||||||
|
} else if (status == IMSdkStatus.syncEnded ||
|
||||||
|
status == IMSdkStatus.syncFailed) {
|
||||||
EasyLoading.dismiss();
|
EasyLoading.dismiss();
|
||||||
if (reInstall) {
|
if (reInstall) {
|
||||||
onRefresh();
|
onRefresh();
|
||||||
@@ -81,7 +87,8 @@ class ConversationLogic extends GetxController {
|
|||||||
onChangeConversations.addAll(newList);
|
onChangeConversations.addAll(newList);
|
||||||
}
|
}
|
||||||
for (var newValue in newList) {
|
for (var newValue in newList) {
|
||||||
Logger.print('======== conversation changed: ${newValue.toJson()} ========');
|
Logger.print(
|
||||||
|
'======== conversation changed: ${newValue.toJson()} ========');
|
||||||
list.removeWhere((e) => e.conversationID == newValue.conversationID);
|
list.removeWhere((e) => e.conversationID == newValue.conversationID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +139,8 @@ class ConversationLogic extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void deleteConversation(ConversationInfo info) async {
|
void deleteConversation(ConversationInfo info) async {
|
||||||
await OpenIM.iMManager.conversationManager.deleteConversationAndDeleteAllMsg(
|
await OpenIM.iMManager.conversationManager
|
||||||
|
.deleteConversationAndDeleteAllMsg(
|
||||||
conversationID: info.conversationID,
|
conversationID: info.conversationID,
|
||||||
);
|
);
|
||||||
list.remove(info);
|
list.remove(info);
|
||||||
@@ -171,7 +179,8 @@ class ConversationLogic extends GetxController {
|
|||||||
|
|
||||||
final text = IMUtils.parseNtf(info.latestMsg!, isConversation: true);
|
final text = IMUtils.parseNtf(info.latestMsg!, isConversation: true);
|
||||||
if (text != null) return text;
|
if (text != null) return text;
|
||||||
if (info.isSingleChat || info.latestMsg!.sendID == OpenIM.iMManager.userID)
|
if (info.isSingleChat ||
|
||||||
|
info.latestMsg!.sendID == OpenIM.iMManager.userID)
|
||||||
return IMUtils.parseMsg(info.latestMsg!, isConversation: true);
|
return IMUtils.parseMsg(info.latestMsg!, isConversation: true);
|
||||||
|
|
||||||
return "${info.latestMsg!.senderNickname}: ${IMUtils.parseMsg(info.latestMsg!, isConversation: true)} ";
|
return "${info.latestMsg!.senderNickname}: ${IMUtils.parseMsg(info.latestMsg!, isConversation: true)} ";
|
||||||
@@ -264,16 +273,31 @@ class ConversationLogic extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool get isFailedSdkStatus =>
|
bool get isFailedSdkStatus =>
|
||||||
imStatus.value == IMSdkStatus.connectionFailed || imStatus.value == IMSdkStatus.syncFailed;
|
imStatus.value == IMSdkStatus.connectionFailed ||
|
||||||
|
imStatus.value == IMSdkStatus.syncFailed;
|
||||||
|
|
||||||
void _sortConversationList() => OpenIM.iMManager.conversationManager.simpleSort(list);
|
bool get showLoading {
|
||||||
|
if (loading.value) return true;
|
||||||
|
if (list.isNotEmpty) return false;
|
||||||
|
switch (imStatus.value) {
|
||||||
|
case IMSdkStatus.connecting:
|
||||||
|
case IMSdkStatus.syncStart:
|
||||||
|
case IMSdkStatus.synchronizing:
|
||||||
|
case IMSdkStatus.syncProgress:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get showFail => list.isEmpty && (loadFailed.value || isFailedSdkStatus);
|
||||||
|
|
||||||
|
void _sortConversationList() =>
|
||||||
|
OpenIM.iMManager.conversationManager.simpleSort(list);
|
||||||
|
|
||||||
void onRefresh() async {
|
void onRefresh() async {
|
||||||
late List<ConversationInfo> list;
|
|
||||||
try {
|
try {
|
||||||
list = await _request();
|
await reloadConversations();
|
||||||
this.list.assignAll(list);
|
|
||||||
|
|
||||||
if (list.isEmpty || list.length < pageSize) {
|
if (list.isEmpty || list.length < pageSize) {
|
||||||
refreshController.loadNoData();
|
refreshController.loadNoData();
|
||||||
} else {
|
} else {
|
||||||
@@ -285,16 +309,46 @@ class ConversationLogic extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Future<List<ConversationInfo>> getConversationFirstPage() async {
|
static Future<List<ConversationInfo>> getConversationFirstPage() async {
|
||||||
final result = await OpenIM.iMManager.conversationManager.getConversationListSplit(offset: 0, count: 400);
|
final result = await OpenIM.iMManager.conversationManager
|
||||||
|
.getConversationListSplit(offset: 0, count: 400);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void getFirstPage() async {
|
void getFirstPage() async {
|
||||||
final result = homeLogic.conversationsAtFirstPage;
|
loading.value = true;
|
||||||
|
loadFailed.value = false;
|
||||||
|
try {
|
||||||
|
final cached = homeLogic.conversationsAtFirstPage;
|
||||||
|
if (cached.isNotEmpty) {
|
||||||
|
list.assignAll(cached);
|
||||||
|
_sortConversationList();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final result = await _request();
|
||||||
|
list.assignAll(result);
|
||||||
|
_sortConversationList();
|
||||||
|
} catch (e, s) {
|
||||||
|
Logger.print('conversation first page e: $e $s');
|
||||||
|
loadFailed.value = true;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
list.assignAll(result);
|
Future<void> reloadConversations() async {
|
||||||
_sortConversationList();
|
loading.value = true;
|
||||||
|
loadFailed.value = false;
|
||||||
|
try {
|
||||||
|
final result = await _request();
|
||||||
|
list.assignAll(result);
|
||||||
|
_sortConversationList();
|
||||||
|
} catch (e, s) {
|
||||||
|
Logger.print('conversation reload e: $e $s');
|
||||||
|
loadFailed.value = true;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearConversations() {
|
void clearConversations() {
|
||||||
@@ -305,20 +359,23 @@ class ConversationLogic extends GetxController {
|
|||||||
final temp = <ConversationInfo>[];
|
final temp = <ConversationInfo>[];
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
var result = await OpenIM.iMManager.conversationManager.getConversationListSplit(
|
var result =
|
||||||
|
await OpenIM.iMManager.conversationManager.getConversationListSplit(
|
||||||
offset: temp.length,
|
offset: temp.length,
|
||||||
count: pageSize,
|
count: pageSize,
|
||||||
);
|
);
|
||||||
if (onChangeConversations.isNotEmpty) {
|
if (onChangeConversations.isNotEmpty) {
|
||||||
final bSet = Set.from(onChangeConversations);
|
final bSet = Set.from(onChangeConversations);
|
||||||
|
|
||||||
Logger.print('replace conversation: [${onChangeConversations.length}], $bSet');
|
Logger.print(
|
||||||
|
'replace conversation: [${onChangeConversations.length}], $bSet');
|
||||||
|
|
||||||
for (int i = 0; i < result.length; i++) {
|
for (int i = 0; i < result.length; i++) {
|
||||||
final info = result[i];
|
final info = result[i];
|
||||||
|
|
||||||
if (bSet.contains(info)) {
|
if (bSet.contains(info)) {
|
||||||
result[i] = onChangeConversations[onChangeConversations.indexOf(info)];
|
result[i] =
|
||||||
|
onChangeConversations[onChangeConversations.indexOf(info)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -367,7 +424,8 @@ class ConversationLogic extends GetxController {
|
|||||||
if (scrollIndex == 0) {
|
if (scrollIndex == 0) {
|
||||||
itemScrollController.jumpTo(index: 0);
|
itemScrollController.jumpTo(index: 0);
|
||||||
} else {
|
} else {
|
||||||
itemScrollController.scrollTo(index: scrollIndex, duration: const Duration(milliseconds: 300));
|
itemScrollController.scrollTo(
|
||||||
|
index: scrollIndex, duration: const Duration(milliseconds: 300));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,7 +434,8 @@ class ConversationLogic extends GetxController {
|
|||||||
required int sessionType,
|
required int sessionType,
|
||||||
}) =>
|
}) =>
|
||||||
LoadingView.singleton.wrap(
|
LoadingView.singleton.wrap(
|
||||||
asyncFunction: () => OpenIM.iMManager.conversationManager.getOneConversation(
|
asyncFunction: () =>
|
||||||
|
OpenIM.iMManager.conversationManager.getOneConversation(
|
||||||
sourceID: sourceID,
|
sourceID: sourceID,
|
||||||
sessionType: sessionType,
|
sessionType: sessionType,
|
||||||
));
|
));
|
||||||
@@ -438,9 +497,11 @@ class ConversationLogic extends GetxController {
|
|||||||
|
|
||||||
addFriend() => AppNavigator.startAddColleague();
|
addFriend() => AppNavigator.startAddColleague();
|
||||||
|
|
||||||
createGroup() => AppNavigator.startCreateGroup(defaultCheckedList: [OpenIM.iMManager.userInfo]);
|
createGroup() => AppNavigator.startCreateGroup(
|
||||||
|
defaultCheckedList: [OpenIM.iMManager.userInfo]);
|
||||||
|
|
||||||
addGroup() => AppNavigator.startAddContactsBySearch(searchType: SearchType.group);
|
addGroup() =>
|
||||||
|
AppNavigator.startAddContactsBySearch(searchType: SearchType.group);
|
||||||
|
|
||||||
void globalSearch() => AppNavigator.startGlobalSearch();
|
void globalSearch() => AppNavigator.startGlobalSearch();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,8 @@ class ConversationPage extends StatelessWidget {
|
|||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(AppGap.x4, 0, AppGap.x4, AppGap.x2),
|
padding: const EdgeInsets.fromLTRB(
|
||||||
|
AppGap.x4, 0, AppGap.x4, AppGap.x2),
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 36,
|
height: 36,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@@ -57,47 +58,64 @@ class ConversationPage extends StatelessWidget {
|
|||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: const Row(
|
child: const Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.search, size: AppIconSize.sm, color: AppColors.textSecondary),
|
Icon(Icons.search,
|
||||||
|
size: AppIconSize.sm, color: AppColors.textSecondary),
|
||||||
SizedBox(width: AppGap.x2),
|
SizedBox(width: AppGap.x2),
|
||||||
Text('搜索', style: TextStyle(fontSize: AppFont.sub, color: AppColors.textSecondary)),
|
Text('搜索',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: AppFont.sub,
|
||||||
|
color: AppColors.textSecondary)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(child: _conversationBody()),
|
||||||
child: logic.list.isEmpty
|
|
||||||
? const CompanyEmptyView(
|
|
||||||
icon: Icons.chat_bubble_outline,
|
|
||||||
title: '暂时没有会话',
|
|
||||||
subtitle: '去通讯录找个同事聊聊吧',
|
|
||||||
)
|
|
||||||
: SlidableAutoCloseBehavior(
|
|
||||||
child: ScrollablePositionedList.builder(
|
|
||||||
itemScrollController: logic.itemScrollController,
|
|
||||||
itemBuilder: (_, index) => _buildConversationItemView(
|
|
||||||
logic.list.elementAt(index),
|
|
||||||
),
|
|
||||||
itemCount: logic.list.length,
|
|
||||||
itemPositionsListener: logic.itemPositionsListener,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _conversationBody() {
|
||||||
|
if (logic.showLoading) {
|
||||||
|
return const CompanyLoadingView();
|
||||||
|
}
|
||||||
|
if (logic.showFail) {
|
||||||
|
return CompanyFailView(onRetry: logic.reloadConversations);
|
||||||
|
}
|
||||||
|
if (logic.list.isEmpty) {
|
||||||
|
return const CompanyEmptyView(
|
||||||
|
icon: Icons.chat_bubble_outline,
|
||||||
|
title: '暂时没有会话',
|
||||||
|
subtitle: '去通讯录找个同事聊聊吧',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return SlidableAutoCloseBehavior(
|
||||||
|
child: ScrollablePositionedList.builder(
|
||||||
|
itemScrollController: logic.itemScrollController,
|
||||||
|
itemBuilder: (_, index) => _buildConversationItemView(
|
||||||
|
logic.list.elementAt(index),
|
||||||
|
),
|
||||||
|
itemCount: logic.list.length,
|
||||||
|
itemPositionsListener: logic.itemPositionsListener,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildConversationItemView(ConversationInfo info) => Slidable(
|
Widget _buildConversationItemView(ConversationInfo info) => Slidable(
|
||||||
endActionPane: ActionPane(
|
endActionPane: ActionPane(
|
||||||
motion: const ScrollMotion(),
|
motion: const ScrollMotion(),
|
||||||
extentRatio: logic.existUnreadMsg(info) ? 0.7 : (logic.isPinned(info) ? 0.5 : 0.4),
|
extentRatio: logic.existUnreadMsg(info)
|
||||||
|
? 0.7
|
||||||
|
: (logic.isPinned(info) ? 0.5 : 0.4),
|
||||||
children: [
|
children: [
|
||||||
CustomSlidableAction(
|
CustomSlidableAction(
|
||||||
onPressed: (_) => logic.pinConversation(info),
|
onPressed: (_) => logic.pinConversation(info),
|
||||||
flex: logic.isPinned(info) ? 3 : 2,
|
flex: logic.isPinned(info) ? 3 : 2,
|
||||||
backgroundColor: Styles.c_0089FF,
|
backgroundColor: Styles.c_0089FF,
|
||||||
padding: const EdgeInsets.all(1),
|
padding: const EdgeInsets.all(1),
|
||||||
child: (logic.isPinned(info) ? StrRes.cancelTop : StrRes.top).toText..style = Styles.ts_FFFFFF_14sp,
|
child:
|
||||||
|
(logic.isPinned(info) ? StrRes.cancelTop : StrRes.top).toText
|
||||||
|
..style = Styles.ts_FFFFFF_14sp,
|
||||||
),
|
),
|
||||||
if (logic.existUnreadMsg(info))
|
if (logic.existUnreadMsg(info))
|
||||||
CustomSlidableAction(
|
CustomSlidableAction(
|
||||||
@@ -163,7 +181,8 @@ class ConversationPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
logic.getTime(info).toText..style = Styles.ts_8E9AB0_12sp,
|
logic.getTime(info).toText
|
||||||
|
..style = Styles.ts_8E9AB0_12sp,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
3.verticalSpace,
|
3.verticalSpace,
|
||||||
@@ -175,9 +194,12 @@ class ConversationPage extends StatelessWidget {
|
|||||||
prefixSpan: TextSpan(
|
prefixSpan: TextSpan(
|
||||||
text: '',
|
text: '',
|
||||||
children: [
|
children: [
|
||||||
if (logic.isNotDisturb(info) && logic.getUnreadCount(info) > 0)
|
if (logic.isNotDisturb(info) &&
|
||||||
|
logic.getUnreadCount(info) > 0)
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: '[${sprintf(StrRes.nPieces, [logic.getUnreadCount(info)])}] ',
|
text: '[${sprintf(StrRes.nPieces, [
|
||||||
|
logic.getUnreadCount(info)
|
||||||
|
])}] ',
|
||||||
style: Styles.ts_8E9AB0_14sp,
|
style: Styles.ts_8E9AB0_14sp,
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
@@ -195,7 +217,8 @@ class ConversationPage extends StatelessWidget {
|
|||||||
..width = 13.63.w
|
..width = 13.63.w
|
||||||
..height = 14.07.h
|
..height = 14.07.h
|
||||||
else
|
else
|
||||||
UnreadCountView(count: logic.getUnreadCount(info)),
|
UnreadCountView(
|
||||||
|
count: logic.getUnreadCount(info)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class HomeLogic extends SuperController {
|
|||||||
final unhandledFriendApplicationCount = 0.obs;
|
final unhandledFriendApplicationCount = 0.obs;
|
||||||
final unhandledGroupApplicationCount = 0.obs;
|
final unhandledGroupApplicationCount = 0.obs;
|
||||||
final unhandledCount = 0.obs;
|
final unhandledCount = 0.obs;
|
||||||
|
final isOffline = false.obs;
|
||||||
String? _lockScreenPwd;
|
String? _lockScreenPwd;
|
||||||
bool _isShowScreenLock = false;
|
bool _isShowScreenLock = false;
|
||||||
bool? _isAutoLogin;
|
bool? _isAutoLogin;
|
||||||
@@ -50,7 +51,8 @@ class HomeLogic extends SuperController {
|
|||||||
|
|
||||||
void getUnhandledFriendApplicationCount() async {
|
void getUnhandledFriendApplicationCount() async {
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var list = await OpenIM.iMManager.friendshipManager.getFriendApplicationListAsRecipient();
|
var list = await OpenIM.iMManager.friendshipManager
|
||||||
|
.getFriendApplicationListAsRecipient();
|
||||||
var haveReadList = DataSp.getHaveReadUnHandleFriendApplication();
|
var haveReadList = DataSp.getHaveReadUnHandleFriendApplication();
|
||||||
haveReadList ??= <String>[];
|
haveReadList ??= <String>[];
|
||||||
for (var info in list) {
|
for (var info in list) {
|
||||||
@@ -65,7 +67,8 @@ class HomeLogic extends SuperController {
|
|||||||
|
|
||||||
void getUnhandledGroupApplicationCount() async {
|
void getUnhandledGroupApplicationCount() async {
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var list = await OpenIM.iMManager.groupManager.getGroupApplicationListAsRecipient();
|
var list = await OpenIM.iMManager.groupManager
|
||||||
|
.getGroupApplicationListAsRecipient();
|
||||||
var haveReadList = DataSp.getHaveReadUnHandleGroupApplication();
|
var haveReadList = DataSp.getHaveReadUnHandleGroupApplication();
|
||||||
haveReadList ??= <String>[];
|
haveReadList ??= <String>[];
|
||||||
for (var info in list) {
|
for (var info in list) {
|
||||||
@@ -101,7 +104,12 @@ class HomeLogic extends SuperController {
|
|||||||
if (value.status == IMSdkStatus.syncStart) {
|
if (value.status == IMSdkStatus.syncStart) {
|
||||||
_getRTCInvitationStart();
|
_getRTCInvitationStart();
|
||||||
}
|
}
|
||||||
|
isOffline.value = value.status == IMSdkStatus.connectionFailed;
|
||||||
});
|
});
|
||||||
|
final lastStatus = imLogic.imSdkStatusSubject.values.lastOrNull;
|
||||||
|
if (lastStatus != null) {
|
||||||
|
isOffline.value = lastStatus.status == IMSdkStatus.connectionFailed;
|
||||||
|
}
|
||||||
|
|
||||||
Apis.kickoffController.stream.listen((event) {
|
Apis.kickoffController.stream.listen((event) {
|
||||||
DataSp.removeLoginCertificate();
|
DataSp.removeLoginCertificate();
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import '../mine/mine_view.dart';
|
|||||||
import 'home_logic.dart';
|
import 'home_logic.dart';
|
||||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||||
import '../../company/feature_flags.dart';
|
import '../../company/feature_flags.dart';
|
||||||
|
import '../../company/ui/state_views.dart';
|
||||||
|
|
||||||
class HomePage extends StatelessWidget {
|
class HomePage extends StatelessWidget {
|
||||||
final logic = Get.find<HomeLogic>();
|
final logic = Get.find<HomeLogic>();
|
||||||
@@ -22,9 +23,11 @@ class HomePage extends StatelessWidget {
|
|||||||
onDoubleTap: () {
|
onDoubleTap: () {
|
||||||
logic.scrollToUnreadMessage();
|
logic.scrollToUnreadMessage();
|
||||||
},
|
},
|
||||||
child: _setupIcon(ImageRes.homeTab1Sel.toImage, logic.unreadMsgCount.value),
|
child: _setupIcon(
|
||||||
|
ImageRes.homeTab1Sel.toImage, logic.unreadMsgCount.value),
|
||||||
),
|
),
|
||||||
inactiveIcon: _setupIcon(ImageRes.homeTab1Nor.toImage, logic.unreadMsgCount.value),
|
inactiveIcon: _setupIcon(
|
||||||
|
ImageRes.homeTab1Nor.toImage, logic.unreadMsgCount.value),
|
||||||
title: StrRes.home,
|
title: StrRes.home,
|
||||||
textStyle: Styles.ts_0089FF_10sp_semibold,
|
textStyle: Styles.ts_0089FF_10sp_semibold,
|
||||||
),
|
),
|
||||||
@@ -32,8 +35,10 @@ class HomePage extends StatelessWidget {
|
|||||||
PersistentTabConfig(
|
PersistentTabConfig(
|
||||||
screen: ContactsPage(),
|
screen: ContactsPage(),
|
||||||
item: ItemConfig(
|
item: ItemConfig(
|
||||||
icon: _setupIcon(ImageRes.homeTab2Sel.toImage, logic.unhandledCount.value),
|
icon: _setupIcon(
|
||||||
inactiveIcon: _setupIcon(ImageRes.homeTab2Nor.toImage, logic.unhandledCount.value),
|
ImageRes.homeTab2Sel.toImage, logic.unhandledCount.value),
|
||||||
|
inactiveIcon: _setupIcon(
|
||||||
|
ImageRes.homeTab2Nor.toImage, logic.unhandledCount.value),
|
||||||
title: StrRes.contacts,
|
title: StrRes.contacts,
|
||||||
textStyle: Styles.ts_0089FF_10sp_semibold,
|
textStyle: Styles.ts_0089FF_10sp_semibold,
|
||||||
),
|
),
|
||||||
@@ -81,19 +86,30 @@ class HomePage extends StatelessWidget {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Styles.c_FFFFFF,
|
backgroundColor: Styles.c_FFFFFF,
|
||||||
body: Obx(
|
body: Obx(
|
||||||
() => PersistentTabView(
|
() => Column(
|
||||||
tabs: _tabs(),
|
children: [
|
||||||
navBarBuilder: (navBarConfig) => Style1BottomNavBar(
|
if (logic.isOffline.value) const CompanyOfflineBanner(),
|
||||||
navBarConfig: navBarConfig,
|
Expanded(
|
||||||
navBarDecoration: const NavBarDecoration(
|
child: PersistentTabView(
|
||||||
color: Colors.white,
|
tabs: _tabs(),
|
||||||
boxShadow: [
|
navBarBuilder: (navBarConfig) => Style1BottomNavBar(
|
||||||
BoxShadow(color: Colors.black12, blurRadius: 0.5, spreadRadius: 0.5),
|
navBarConfig: navBarConfig,
|
||||||
],
|
navBarDecoration: const NavBarDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black12,
|
||||||
|
blurRadius: 0.5,
|
||||||
|
spreadRadius: 0.5),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
navBarOverlap: const NavBarOverlap.none(),
|
||||||
|
screenTransitionAnimation:
|
||||||
|
const ScreenTransitionAnimation.none(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
navBarOverlap: const NavBarOverlap.none(),
|
|
||||||
screenTransitionAnimation: const ScreenTransitionAnimation.none(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user