controller.dart 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  1. import 'dart:async';
  2. import 'dart:io';
  3. import 'package:connectivity_plus/connectivity_plus.dart';
  4. import 'package:dsbridge_flutter/dsbridge_flutter.dart';
  5. import 'package:electronic_assistant/base/base_controller.dart';
  6. import 'package:electronic_assistant/data/consts/event_report_id.dart';
  7. import 'package:electronic_assistant/data/repositories/account_repository.dart';
  8. import 'package:electronic_assistant/data/repositories/task_repository.dart';
  9. import 'package:electronic_assistant/handler/event_handler.dart';
  10. import 'package:electronic_assistant/module/chat/view.dart';
  11. import 'package:electronic_assistant/module/login/view.dart';
  12. import 'package:electronic_assistant/module/record/record_handler.dart';
  13. import 'package:electronic_assistant/module/store/view.dart';
  14. import 'package:electronic_assistant/module/talk/mindmap/mind_util.dart';
  15. import 'package:electronic_assistant/module/talk/todo/controller.dart';
  16. import 'package:electronic_assistant/resource/assets.gen.dart';
  17. import 'package:electronic_assistant/resource/colors.gen.dart';
  18. import 'package:electronic_assistant/resource/string.gen.dart';
  19. import 'package:electronic_assistant/router/app_pages.dart';
  20. import 'package:electronic_assistant/utils/audio_picker_utils.dart';
  21. import 'package:electronic_assistant/utils/error_handler.dart';
  22. import 'package:electronic_assistant/utils/expand.dart';
  23. import 'package:electronic_assistant/utils/file_upload_check_helper.dart';
  24. import 'package:electronic_assistant/utils/mmkv_util.dart';
  25. import 'package:flutter/cupertino.dart';
  26. import 'package:flutter/material.dart';
  27. import 'package:flutter/scheduler.dart';
  28. import 'package:flutter/services.dart';
  29. import 'package:flutter_screenutil/flutter_screenutil.dart';
  30. import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
  31. import 'package:get/get.dart';
  32. import 'package:just_audio/just_audio.dart';
  33. import 'package:share_plus/share_plus.dart';
  34. import 'package:wakelock_plus/wakelock_plus.dart';
  35. import '../../data/api/request/agenda_update_bean.dart';
  36. import '../../data/bean/agenda.dart';
  37. import '../../data/bean/agenda_list_all_bean.dart';
  38. import '../../data/bean/talks.dart';
  39. import '../../data/bean/template_bean.dart';
  40. import '../../data/repositories/agenda_repository.dart';
  41. import '../../data/repositories/talk_repository.dart';
  42. import '../../dialog/add_agenda_dialog.dart';
  43. import '../../dialog/alert_dialog.dart';
  44. import '../../dialog/rename_dialog.dart';
  45. import '../../dialog/talk_share_dialog.dart';
  46. import '../../utils/common_utils.dart';
  47. import '../../utils/event_bus.dart';
  48. import '../../utils/system_share_util.dart';
  49. import '../../utils/toast_util.dart';
  50. import 'package:webview_flutter/webview_flutter.dart';
  51. class TalkController extends BaseController {
  52. static const String argumentItem = 'argument_item';
  53. static const String argumentTalkId = 'argument_talk_id';
  54. static const String argumentEventTag = 'argument_event_tag';
  55. final String uploadNoPrompts = "UPLOAD_NO_PROMPTS";
  56. final Rxn<TalkBean> talkBean = Rxn();
  57. StreamSubscription? _talkUploadListener;
  58. final RxDouble uploadProgress = RxDouble(0);
  59. final isShowElectricLow = false.obs;
  60. bool isAudioLoading = false;
  61. final double sliderMax = 1;
  62. bool? audioFileIsExist;
  63. bool? isUploadedFile;
  64. Rxn<bool> isUploading = Rxn();
  65. final isAudioPlaying = false.obs;
  66. final audioProgressValue = 0.0.obs;
  67. final audioDuration = Duration.zero.obs;
  68. final agendaOriginalAllList = <AgendaListAllBean>[];
  69. final agendaAllList = <AgendaListAllBean>[].obs;
  70. final _isEditModel = false.obs;
  71. final defaultIndex = 0;
  72. final Rxn<TalkBarBean> checkTabBean = Rxn();
  73. final List<TalkBarBean> tabBeans = [
  74. TalkBarBean(TalkBarType.summary, StringName.talkTabSummary.tr, true),
  75. TalkBarBean(TalkBarType.mindMap, StringName.talkMindMap.tr, false,
  76. isDisallowScroll: true),
  77. TalkBarBean(TalkBarType.myTask, StringName.talkTabMyTask.tr, true),
  78. TalkBarBean(TalkBarType.original, StringName.talkTabOriginal.tr, false)
  79. ];
  80. bool get isEditModel => _isEditModel.value;
  81. RxBool get isEditModelRx => _isEditModel;
  82. final _audioPlayer = AudioPlayer();
  83. StreamSubscription? _talkBeanListener;
  84. TextEditingController? _agendaContentController;
  85. TextEditingController? _agendaNameController;
  86. TextEditingController get agendaContentController {
  87. _agendaContentController ??= TextEditingController();
  88. return _agendaContentController!;
  89. }
  90. TextEditingController get agendaNameController {
  91. _agendaNameController ??= TextEditingController();
  92. return _agendaNameController!;
  93. }
  94. String? paramId;
  95. String? eventTag;
  96. bool isLocalFileHas = false;
  97. final Rxn<Duration> playingDuration = Rxn();
  98. bool isFirstRequestTask = true;
  99. //模板
  100. Rxn<List<TemplateBean>?> templateList = Rxn();
  101. Rxn<int> templateSelectId = Rxn();
  102. final RxBool isShowMindFullScreen = false.obs;
  103. final mindFullDuration = const Duration(milliseconds: 250);
  104. GlobalKey headGlobalKey = GlobalKey();
  105. GlobalKey bottomGlobalKey = GlobalKey();
  106. double? _bottomViewHeight;
  107. final RxnDouble _headViewHeight = RxnDouble();
  108. double? get headViewHeight => _headViewHeight.value;
  109. final DWebViewController webViewController =
  110. MindUtil.createMindWebViewController();
  111. @override
  112. void onInit() {
  113. super.onInit();
  114. checkTabBean.value = tabBeans[defaultIndex];
  115. }
  116. @override
  117. void onReady() {
  118. super.onReady();
  119. _initAudioPlayer();
  120. _getArguments();
  121. eventReport(EventId.event_101001, params: {EventId.id: eventTag});
  122. WidgetsBinding.instance.addPostFrameCallback((_) {
  123. _bottomViewHeight = bottomGlobalKey.currentContext?.size?.height;
  124. _headViewHeight.value = headGlobalKey.currentContext?.size?.height;
  125. });
  126. }
  127. double getBottomViewHeight() {
  128. if (_bottomViewHeight == null) {
  129. return -250.h;
  130. }
  131. return -_bottomViewHeight!;
  132. }
  133. void eventReport(String eventId, {Map<String, dynamic>? params}) {
  134. if (talkBean.value == null || talkBean.value?.isExample == true) {
  135. return;
  136. }
  137. EventHandler.report(eventId, params: params);
  138. }
  139. void _initAudioPlayer() {
  140. _audioPlayer.playerStateStream.listen((playerState) {
  141. if (playerState.processingState == ProcessingState.loading ||
  142. playerState.processingState == ProcessingState.buffering) {
  143. isAudioLoading = true;
  144. debugPrint('音频load = true');
  145. } else {
  146. debugPrint('音频load = false');
  147. isAudioLoading = false;
  148. if (playerState.processingState == ProcessingState.completed) {
  149. _audioPlayer.stop();
  150. _audioPlayer.seek(Duration.zero);
  151. isAudioPlaying.value = false;
  152. debugPrint('音频 播放结束了');
  153. }
  154. }
  155. isAudioPlaying.value = playerState.playing;
  156. }, onError: (Object e, StackTrace stackTrace) {
  157. debugPrint('音频加载异常 == $e');
  158. });
  159. _audioPlayer.durationStream.listen((duration) {
  160. if (duration != null) {
  161. debugPrint('音频总播放时长 == ${duration.inMilliseconds}');
  162. audioDuration.value = duration;
  163. }
  164. });
  165. _audioPlayer.positionStream.listen((duration) {
  166. debugPrint('音频播放时长 == ${duration.inMilliseconds}');
  167. playingDuration.value = duration;
  168. if (audioDuration.value.inMilliseconds > 0) {
  169. audioProgressValue.value =
  170. (duration.inMilliseconds / audioDuration.value.inMilliseconds)
  171. .clamp(0.0, sliderMax);
  172. }
  173. });
  174. }
  175. void _dealTalk(TalkBean? bean) async {
  176. debugPrint('talkBean == $bean');
  177. String? id = bean?.id;
  178. if (id == null) {
  179. return;
  180. }
  181. _loadAudioFile(bean);
  182. if (bean?.status.value == TalkStatus.notAnalysis) {
  183. setUploadingProgress(id);
  184. }
  185. if (bean?.status.value == TalkStatus.notAnalysis &&
  186. talkRepository.isUploadingTalk(id)) {
  187. isUploading.value = true;
  188. } else {
  189. isUploading.value = false;
  190. }
  191. }
  192. void setUploadingProgress(String id) {
  193. talkRepository.getUploadProgress(id).listen((progress) {
  194. uploadProgress.value = (progress * 20).toFormattedDouble(1);
  195. });
  196. }
  197. Future<void> _loadAudioFile(TalkBean? bean, {bool? loadPlay}) async {
  198. try {
  199. Uri? uri;
  200. if (bean?.isExample == true && bean?.audioUrl != null) {
  201. uri = Uri.parse(bean!.audioUrl!);
  202. } else {
  203. File? file = await getFileByTalk(talkBean.value);
  204. if (file?.existsSync() == true) {
  205. uri = file?.uri;
  206. }
  207. }
  208. if (uri == null) {
  209. throw '音频文件不存在';
  210. }
  211. await _audioPlayer.setAudioSource(AudioSource.uri(uri));
  212. if (loadPlay == true) {
  213. clickPlayAudio();
  214. }
  215. audioFileIsExist = true;
  216. } catch (e) {
  217. audioFileIsExist = false;
  218. debugPrint('音频设置异常 == $e');
  219. }
  220. }
  221. void _getArguments() {
  222. TalkBean? bean = parameters?[argumentItem];
  223. if (bean != null) {
  224. talkBean.value = bean;
  225. debugPrint('talkBean == ${bean.summary}');
  226. _dealTalk(bean);
  227. } else {
  228. paramId = parameters?[argumentTalkId];
  229. if (paramId != null) {
  230. talkRepository.talkInfo(paramId!).then((data) {
  231. talkBean.value = data.talkInfo;
  232. _dealTalk(data.talkInfo);
  233. }).catchError((error) {
  234. ErrorHandler.toastError(error);
  235. });
  236. }
  237. }
  238. eventTag = parameters?[argumentEventTag];
  239. }
  240. void updateProgress(double value) {
  241. final newPosition = Duration(
  242. milliseconds: (value * audioDuration.value.inMilliseconds).toInt());
  243. _audioPlayer.seek(newPosition);
  244. }
  245. void clickPlayAudio() async {
  246. if (audioFileIsExist != true && isLocalFileHas == false) {
  247. ToastUtil.showToast(StringName.talkFileNotFind.tr);
  248. return;
  249. }
  250. if (isLocalFileHas == true &&
  251. audioFileIsExist == false &&
  252. !await AudioPickerUtils.hasPermission()) {
  253. bool has = await AudioPickerUtils.requestPermissionExtend();
  254. if (has == false) {
  255. ToastUtil.showToast(StringName.authorizationFailed.tr);
  256. return;
  257. }
  258. //重新加载
  259. _loadAudioFile(talkBean.value, loadPlay: true);
  260. return;
  261. }
  262. if (isAudioLoading) {
  263. ToastUtil.showToast(StringName.talkAudioLoading.tr);
  264. return;
  265. }
  266. if (_audioPlayer.playing) {
  267. _audioPlayer.pause();
  268. isAudioPlaying.value = false;
  269. } else {
  270. _audioPlayer.play();
  271. isAudioPlaying.value = true;
  272. }
  273. }
  274. void _checkFileSizeAndNet() async {
  275. String? id = talkBean.value?.id;
  276. if (id == null) {
  277. return;
  278. }
  279. File? file = await getFileByTalk(talkBean.value);
  280. if (isLocalFileHas == true &&
  281. audioFileIsExist == false &&
  282. !await AudioPickerUtils.hasPermission()) {
  283. bool has = await AudioPickerUtils.requestPermissionExtend();
  284. if (has == false) {
  285. ToastUtil.showToast(StringName.authorizationFailed.tr);
  286. return;
  287. }
  288. //重新上传
  289. _checkFileSizeAndNet();
  290. return;
  291. }
  292. if (file == null || !file.existsSync()) {
  293. ToastUtil.showToast(StringName.talkUploadFileNotExist.tr);
  294. return;
  295. }
  296. bool isCheckRemind = KVUtil.getBool(uploadNoPrompts, false);
  297. if (isCheckRemind) {
  298. _requestAnalyze(file);
  299. return;
  300. }
  301. //如果文件大小低于250MB 不弹窗提醒
  302. if (file.lengthSync() < 250 * 1024 * 1024) {
  303. _requestAnalyze(file);
  304. return;
  305. }
  306. final List<ConnectivityResult> connectivityResult =
  307. await (Connectivity().checkConnectivity());
  308. if (connectivityResult.contains(ConnectivityResult.wifi)) {
  309. _requestAnalyze(file);
  310. } else {
  311. _showTrafficRemindDialog(file.lengthSync().toReadableSize(),
  312. confirmOnTap: (isCheckRemind) {
  313. if (isCheckRemind) {
  314. KVUtil.putBool(uploadNoPrompts, true);
  315. }
  316. _requestAnalyze(file);
  317. });
  318. }
  319. }
  320. void _showTrafficRemindDialog(String holderTxt,
  321. {void Function(bool isCheckRemind)? confirmOnTap}) {
  322. final remindTrafficConsume = false.obs;
  323. Widget getSelectIcon() {
  324. return Obx(() {
  325. return remindTrafficConsume.value
  326. ? Assets.images.iconSelectTrue.image()
  327. : Assets.images.iconSelectFalse.image();
  328. });
  329. }
  330. Assets.images.iconSelectTrue.image();
  331. EAAlertDialog.show(
  332. contentWidget: Column(
  333. children: [
  334. Text(
  335. StringName.talkTrafficRemindTitle.tr
  336. .replacePlaceholders([holderTxt]),
  337. style:
  338. TextStyle(fontSize: 15.sp, color: ColorName.primaryTextColor),
  339. ),
  340. SizedBox(height: 8.h),
  341. GestureDetector(
  342. onTap: () {
  343. remindTrafficConsume.value = !remindTrafficConsume.value;
  344. },
  345. child: IntrinsicWidth(
  346. child: Row(
  347. children: [
  348. SizedBox(width: 20.w, height: 20.w, child: getSelectIcon()),
  349. SizedBox(width: 5.w),
  350. Text(
  351. StringName.talkTrafficRemindTips.tr,
  352. style: TextStyle(
  353. fontSize: 15.sp, color: ColorName.tertiaryTextColor),
  354. )
  355. ],
  356. ),
  357. ),
  358. )
  359. ],
  360. ),
  361. cancelText: StringName.cancel.tr,
  362. confirmText: StringName.sure.tr,
  363. confirmOnTap: () {
  364. confirmOnTap?.call(remindTrafficConsume.value);
  365. });
  366. }
  367. void checkCanAnalyze() {
  368. String? id = talkBean.value?.id;
  369. double? duration = talkBean.value?.duration;
  370. if (id == null || duration == null) {
  371. return;
  372. }
  373. eventReport(EventId.event_101002);
  374. talkRepository.checkElectric(duration).then((data) {
  375. if (data.enough) {
  376. //检查网络以及文件大小
  377. _checkFileSizeAndNet();
  378. } else {
  379. ToastUtil.showToast(StringName.talkAnalyseLowToast.tr);
  380. isShowElectricLow.value = true;
  381. }
  382. }).catchError((error) {
  383. ErrorHandler.toastError(error);
  384. });
  385. }
  386. void _requestAnalyze(File file) {
  387. String? talkId = talkBean.value?.id;
  388. double? duration = talkBean.value?.duration;
  389. if (talkId == null || duration == null || isUploadedFile == true) {
  390. return;
  391. }
  392. isUploading.value = true;
  393. WakelockPlus.enable();
  394. talkRepository.uploadTalkFile(talkId, duration, file).then((taskId) {
  395. isUploadedFile = true;
  396. isUploading.value = false;
  397. talkBean.value?.progressContent.value =
  398. StringName.talkUploadingFileTip.tr;
  399. talkBean.value?.progress.value = 20;
  400. talkBean.value?.status.value = TalkStatus.analysing;
  401. taskRepository.addTask(taskId);
  402. }).catchError((error) {
  403. isUploading.value = false;
  404. ErrorHandler.toastError(error);
  405. }).whenComplete(() => WakelockPlus.disable());
  406. }
  407. void refreshAgendaAllData({bool isForceRefresh = false}) {
  408. String? id = talkBean.value?.id;
  409. if (id == null || (!isForceRefresh && agendaAllList.isNotEmpty)) {
  410. return;
  411. }
  412. agendaRepository.agendaListAll(id).then((agenda) {
  413. isFirstRequestTask = false;
  414. agendaAllList.clear();
  415. agendaOriginalAllList.clear();
  416. if (agenda.list != null) {
  417. agendaOriginalAllList.addAll(
  418. agenda.list!.map((item) => AgendaListAllBean.from(item)).toList());
  419. agendaAllList.addAll(agenda.list!);
  420. }
  421. });
  422. }
  423. void clickAIAnalysis() async {
  424. if (!await checkLogin()) {
  425. return;
  426. }
  427. if (talkBean.value != null) {
  428. eventReport(EventId.event_101003);
  429. ChatPage.startByTalk(
  430. talkBean.value!.isExample == true
  431. ? ChatFromType.fromTalkExample
  432. : ChatFromType.fromTalkDetail,
  433. talkBean.value!);
  434. }
  435. }
  436. void onGoElectricStore() {
  437. StorePage.start(fromType: StoreFromType.analyse);
  438. Future.delayed(const Duration(milliseconds: 250), () {
  439. isShowElectricLow.value = false;
  440. });
  441. }
  442. void onEditModelClick() async {
  443. if (!await checkLogin()) {
  444. return;
  445. }
  446. _isEditModel.value = true;
  447. if (_audioPlayer.playing) {
  448. _audioPlayer.pause();
  449. isAudioPlaying.value = false;
  450. }
  451. }
  452. void updateTabIndex(int index) {
  453. checkTabBean.value = tabBeans[index];
  454. }
  455. void onEditCancel() {
  456. _isEditModel.value = false;
  457. agendaAllList.assignAll(agendaOriginalAllList
  458. .map((item) => AgendaListAllBean.from(item))
  459. .toList());
  460. }
  461. void onEditDoneClick() {
  462. if (talkBean.value == null) {
  463. return;
  464. }
  465. List<AgendaUpdateBean> list = [];
  466. for (AgendaListAllBean item in agendaAllList) {
  467. if (item.list != null) {
  468. for (Agenda agenda in item.list!) {
  469. list.add(AgendaUpdateBean(agenda.id, agenda.name, agenda.content));
  470. }
  471. }
  472. }
  473. agendaRepository.agendaUpdate(talkBean.value!.id, list).then((data) {
  474. refreshAgendaAllData(isForceRefresh: true);
  475. eventBus.emit(TodoController.refreshTalkMineTask);
  476. isEditModelRx.value = false;
  477. }).catchError((error) {
  478. ErrorHandler.toastError(error);
  479. });
  480. }
  481. void removeTalkAgenda(List<Agenda>? list, Agenda agenda) {
  482. list?.remove(agenda);
  483. agendaAllList.refresh();
  484. }
  485. void showSingleAddAgendaDialog(BuildContext context) {
  486. showAddAgendaDialog(context, agendaContentController, agendaNameController,
  487. list: agendaAllList.map((e) => e.name ?? "").toList(), callback: () {
  488. if (agendaContentController.text.isEmpty) {
  489. ToastUtil.showToast(StringName.talkAddAgendaContentHint.tr);
  490. return;
  491. }
  492. if (agendaNameController.text.isEmpty) {
  493. ToastUtil.showToast(StringName.talkAddAgendaNameHint.tr);
  494. return;
  495. }
  496. Get.back();
  497. _dealAddProcedureList();
  498. });
  499. }
  500. void _dealAddProcedureList() {
  501. String name = agendaNameController.text;
  502. final addItem = Agenda(
  503. id: "",
  504. talkId: "",
  505. name: name,
  506. );
  507. addItem.content = agendaContentController.text;
  508. for (AgendaListAllBean item in agendaAllList) {
  509. if (item.name == name) {
  510. List<Agenda> list = item.list ?? [];
  511. list.add(addItem);
  512. item.list = list;
  513. agendaAllList.refresh();
  514. agendaContentController.clear();
  515. agendaNameController.clear();
  516. return;
  517. }
  518. }
  519. agendaAllList.add(AgendaListAllBean(name: name, list: [addItem]));
  520. agendaContentController.clear();
  521. agendaNameController.clear();
  522. }
  523. Future<File?> getFileByTalk(TalkBean? bean) async {
  524. isLocalFileHas = false;
  525. if (bean == null) {
  526. return null;
  527. }
  528. if (bean.uploadType == TalkUploadType.localUpload) {
  529. String? audioId =
  530. FileUploadCheckHelper.getLocalAudioId(bean.localAudioUrl);
  531. if (audioId != null && audioId.isNotEmpty) {
  532. isLocalFileHas = true;
  533. return await AudioPickerUtils.getAssetFile(audioId);
  534. } else {
  535. return await FileUploadCheckHelper.getChoiceUploadFile(bean.id);
  536. }
  537. } else {
  538. return await RecordHandler.getRecordFile(bean.id);
  539. }
  540. }
  541. Future<bool> checkLogin() async {
  542. if (!accountRepository.isLogin.value) {
  543. bool isLogin = await LoginPage.start(fromType: LoginFromType.talkDetail);
  544. if (isLogin) {
  545. backToSpecificPage(RoutePath.mainTab);
  546. }
  547. return false;
  548. }
  549. return true;
  550. }
  551. void onShareClick() async {
  552. if (!await checkLogin()) {
  553. return;
  554. }
  555. if (talkBean.value?.status.value != TalkStatus.analysisSuccess) {
  556. return;
  557. }
  558. eventReport(EventId.event_101004);
  559. showTalkShareDialog(talkBean.value?.title.value,
  560. (type, shareTo, fileName, tag) {
  561. if (type == ShareTalkType.summary || type == ShareTalkType.original) {
  562. _shareSummaryOrOriginal(
  563. talkBean.value!.id, fileName, type, shareTo, tag);
  564. } else if (type == ShareTalkType.mindMap) {
  565. _shareMindMap(
  566. talkBean.value?.title.value, talkBean.value?.summary.value);
  567. }
  568. });
  569. }
  570. Future<String> loadSvgFromAssets() async {
  571. String svgString = await rootBundle.loadString('assets/images/test2.svg');
  572. return svgString;
  573. }
  574. void _shareMindMap(String? title, String? summary) async {
  575. // String svgString = await loadSvgFromAssets();
  576. // final pngBytes = await MindUtil.svgToPng(svgString, Get.context!);
  577. // MindUtil.convertToFile(pngBytes, 'test.png');
  578. // webViewController.callHandler(MindUtil.functionToJsExport, args: ['test'],
  579. // handler: (value) async {
  580. // if (value == null) {
  581. // throw Exception('Unable to convert SVG to PNG');
  582. // }
  583. // debugPrint('mindMap summary: $value');
  584. //
  585. // ToastUtil.showToast('思维导图svg下载成功');
  586. // });
  587. // await webViewController.loadFlutterAsset(Assets.html.index);
  588. // webViewController.setNavigationDelegate(
  589. // NavigationDelegate(
  590. // onPageFinished: (String url) {
  591. //
  592. // },
  593. // onNavigationRequest: (NavigationRequest request) {
  594. // return NavigationDecision.navigate;
  595. // },
  596. // ),
  597. // );
  598. }
  599. void _shareSummaryOrOriginal(String id, String fileName, ShareTalkType type,
  600. ShareTo shareTo, String tag) async {
  601. talkRepository.talkExport(id, fileName, type).then((file) async {
  602. if (shareTo == ShareTo.ios) {
  603. await Share.shareXFiles([XFile(file.path)], subject: fileName);
  604. } else if (shareTo == ShareTo.wechat) {
  605. await SystemShareUtil.shareWechatFile(file.path);
  606. } else if (shareTo == ShareTo.qq) {
  607. await SystemShareUtil.shareQQFile(file.path);
  608. }
  609. SmartDialog.dismiss(tag: tag);
  610. }).catchError((error) {
  611. if (error is SystemShareException) {
  612. ToastUtil.showToast(error.message);
  613. } else {
  614. ErrorHandler.toastError(error);
  615. }
  616. });
  617. }
  618. void seekTo(int? startMs) {
  619. if (startMs == null) {
  620. return;
  621. }
  622. _audioPlayer.seek(Duration(milliseconds: startMs));
  623. }
  624. Future<TalkBean?> refreshTalkDetail() async {
  625. String? id = talkBean.value?.id;
  626. if (id == null) {
  627. return null;
  628. }
  629. return talkRepository.talkInfo(id).then((data) {
  630. var bean = data.talkInfo;
  631. if (bean != null) {
  632. if (talkBean.value == null) {
  633. talkBean.value = data.talkInfo;
  634. } else {
  635. talkBean.value?.updateBean(bean);
  636. }
  637. templateSelectId.value = bean.templateId;
  638. }
  639. templateList.value = data.templateList;
  640. return bean;
  641. });
  642. }
  643. void onEditTitleClick() {
  644. reNameDialog(StringName.talkRenameTitle.tr, talkBean.value?.title.value,
  645. hintTxt: StringName.talkRenameTitleHint.tr,
  646. maxLength: 15, returnBuilder: (newName) {
  647. talkRepository.talkRename(talkBean.value!.id, newName).then((data) {
  648. talkBean.value?.title.value = newName;
  649. }).catchError((error) {
  650. ErrorHandler.toastError(error);
  651. });
  652. });
  653. }
  654. void onExitMindFullScreen() {
  655. isShowMindFullScreen.value = false;
  656. }
  657. @override
  658. void onClose() {
  659. super.onClose();
  660. _talkUploadListener?.cancel();
  661. _talkBeanListener?.cancel();
  662. _audioPlayer.dispose();
  663. _agendaContentController?.dispose();
  664. _agendaNameController?.dispose();
  665. }
  666. }
  667. enum TalkBarType { summary, mindMap, myTask, original }
  668. class TalkBarBean {
  669. final TalkBarType type;
  670. final String title;
  671. final bool isShowEdit;
  672. final bool? isDisallowScroll;
  673. TalkBarBean(this.type, this.title, this.isShowEdit, {this.isDisallowScroll});
  674. }