Flutter TUIKit V2.1.0
54
CHANGELOG.md
|
|
@ -1,3 +1,55 @@
|
||||||
|
## 2.1.0
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
* Migrated to Flutter 3.10.0 and Dart 3.0.0, no longer supporting projects with Flutter < 3.10.0 and Dart < 3.0.0.
|
||||||
|
* Updated the minimum requirement for Android AGP to 7.0, projects with AGP < 7.0 are no longer supported.
|
||||||
|
|
||||||
|
We highly recommend updating to these new versions for a better experience.
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
|
||||||
|
* Added several methods to `TIMUIKitChatController`, including `hideAllBottomPanelOnMobile`, `mentionOtherMemberInGroup`, `setInputTextField`, and `getGroupMemberList`. Please refer to the corresponding annotations for usage.
|
||||||
|
* Added more parameter fields to the `TIMUIKitChatController`'s `sendMessage` method. For details, please refer to the corresponding annotations.
|
||||||
|
* Added `onSecondaryTapAvatar` to `TIMUIKitChat`, serving as callback trigger for secondary avatar clicks in the message list.
|
||||||
|
* Introduced `isUseMessageHoverBarOnDesktop` and `desktopMessageInputFieldLines` to `TIMUIKitChatConfig`. For usage details, please refer to the corresponding annotations.
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* Enhanced performance and user experience when switching conversations on Desktop, including features like text field auto-focus and draft text.
|
||||||
|
* Enabled displaying correct new lines in markdown mode.
|
||||||
|
* Changed the order of members in the mentioned member selection panel: Group Owner => Group Administrator => Member, sorted based on the code units' first differing position in the member show names.
|
||||||
|
* Implemented auto-focus after clicking a member in the mentioned member selection panel.
|
||||||
|
* Added text field auto-focus when replying to a message.
|
||||||
|
* Updated other members' display names in at-tag messages to use `namecard`, followed by `nickname` and `userId`.
|
||||||
|
* Widened Desktop message input area's control bar.
|
||||||
|
* Replaced the default icon in Desktop's message input area from `png` to `svg` for better performance and clarity. `DesktopControlBarConfig` now supports defining `svgPath` for each item as well.
|
||||||
|
* Improved Web platform detection.
|
||||||
|
* Mentioning "all" or "at all" can now only be used by group owners and administrators.
|
||||||
|
* Supported returning null for each message item builder in `MessageItemBuilder` to use the default message widget.
|
||||||
|
* Enhanced group members filtering in the group member mentioned selection panel with case-insensitive fuzzy matching, leading to increased filtering accuracy.
|
||||||
|
* For security purposes, downloading files by `fetch` and `blob` in the Web now replaces previewing files in a new browser tab, whereas previewing images and videos is displayed in a new tab on the Web.
|
||||||
|
* Changed the default order in the message tooltip menu.
|
||||||
|
* Previewing images and videos is set to open in a new tab on the Web.
|
||||||
|
* Improved the ratio for sending video messages.
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Fixed issues when enabling the section function in markdown mode with `inEnableTextSelection` set to `true`.
|
||||||
|
* Addressed an issue where the replied message was removed when selecting all text in the message and clicking backspace.
|
||||||
|
* Fixed an issue where Chinese characters could not be entered while replying to a message.
|
||||||
|
* Resolved some console errors during debugging.
|
||||||
|
* Fixed an issue with links not opening in markdown mode.
|
||||||
|
* Fixed an issue that caused two `Scrollbar`s to appear in the message input field on Desktop.
|
||||||
|
* Solved an issue that might cause incorrect layout when the app is launched.
|
||||||
|
* Addressed an issue where messages were directly sent when the Enter key was pressed while entering Chinese text.
|
||||||
|
* Fixed related issues with the mentioned member selection panel on Desktop.
|
||||||
|
* Resolved an issue where images couldn't be pasted directly into the message input area for sending on the Web.
|
||||||
|
* Fixed an issue where files couldn't be sent on the Web.
|
||||||
|
* Remedied an issue where media and files couldn't be opened when local downloaded resources were deleted; now, resources will automatically re-download.
|
||||||
|
* Fixed an issue that caused the `iconImageAsset` of the `MessageToolTipItem` config to head internally to this chat UIKit.
|
||||||
|
* Improved the downloading process of media and files by avoiding frequent calls to `setState`, thus preventing the entire project from re-rendering.
|
||||||
|
|
||||||
## 2.0.0
|
## 2.0.0
|
||||||
|
|
||||||
If you are upgrading from version 1.7.0, please refer to the changelog of all 2.0.0-preview versions, ranging from preview.1 to preview.7.
|
If you are upgrading from version 1.7.0, please refer to the changelog of all 2.0.0-preview versions, ranging from preview.1 to preview.7.
|
||||||
|
|
@ -377,4 +429,4 @@ The first released of TUIKit for Flutter of Tencent Cloud IM, the component of t
|
||||||
* TIMUIKitGroup: Joined group list.
|
* TIMUIKitGroup: Joined group list.
|
||||||
* TIMUIKitBlackList: Blocklist.
|
* TIMUIKitBlackList: Blocklist.
|
||||||
* TIMUIKitContact: Contacts list.
|
* TIMUIKitContact: Contacts list.
|
||||||
* TIMUIKitNewContact: New contact application list.
|
* TIMUIKitNewContact: New contact application list.
|
||||||
|
|
|
||||||
15
README.md
|
|
@ -74,19 +74,6 @@ Official Documentation</button></a>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## Preview Version Release Notes
|
|
||||||
|
|
||||||
This version is a major update with version number 2.0.0-preview series, which is not backward
|
|
||||||
compatible. Tencent Cloud Chat UIKit has been extended from mobile-only (iOS/Android/mobile web) to
|
|
||||||
support all platforms, including iOS/Android/Web/Windows/macOS, resulting in significant changes to
|
|
||||||
the codebase.
|
|
||||||
|
|
||||||
Therefore, users should evaluate the compatibility complexity of their business logic before
|
|
||||||
upgrading, while new users can use this version without any impact.
|
|
||||||
|
|
||||||
The documentation for the new version is still being improved, and users can refer to the sample app
|
|
||||||
source code at at https://github.com/TencentCloud/chat-demo-flutter.
|
|
||||||
|
|
||||||
## Check Out Our Sample Apps
|
## Check Out Our Sample Apps
|
||||||
|
|
||||||
Experience our Chat and Voice/Video Call modules by trying out our sample apps.
|
Experience our Chat and Voice/Video Call modules by trying out our sample apps.
|
||||||
|
|
@ -512,4 +499,4 @@ tend to learn more about the use cases.
|
||||||
- WhatsApp Group: <https://chat.whatsapp.com/Gfbxk7rQBqc8Rz4pzzP27A>
|
- WhatsApp Group: <https://chat.whatsapp.com/Gfbxk7rQBqc8Rz4pzzP27A>
|
||||||
- QQ Group: 788910197, chat in Chinese
|
- QQ Group: 788910197, chat in Chinese
|
||||||
|
|
||||||
Our Website: <https://www.tencentcloud.com/products/im?from=pub>
|
Our Website: <https://www.tencentcloud.com/products/im?from=pub>
|
||||||
|
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
// ignore_for_file: file_names
|
|
||||||
|
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:crypto/crypto.dart';
|
|
||||||
import 'package:archive/archive.dart';
|
|
||||||
import 'package:archive/archive_io.dart';
|
|
||||||
|
|
||||||
/// 生成腾讯云即时通信测试用userSig
|
|
||||||
/// Generate userSig for Tencent Cloud instant messaging test
|
|
||||||
///
|
|
||||||
class GenerateTestUserSig {
|
|
||||||
GenerateTestUserSig({required this.sdkappid, required this.key});
|
|
||||||
int sdkappid;
|
|
||||||
String key;
|
|
||||||
|
|
||||||
/// 生成UserSig
|
|
||||||
/// Generate UserSig
|
|
||||||
///
|
|
||||||
String genSig({
|
|
||||||
required String identifier,
|
|
||||||
required int expire,
|
|
||||||
}) {
|
|
||||||
int currTime = _getCurrentTime();
|
|
||||||
String sig = '';
|
|
||||||
Map<String, dynamic> sigDoc = <String, dynamic>{};
|
|
||||||
sigDoc.addAll({
|
|
||||||
"TLS.ver": "2.0",
|
|
||||||
"TLS.identifier": identifier,
|
|
||||||
// ignore: unnecessary_this
|
|
||||||
"TLS.sdkappid": this.sdkappid,
|
|
||||||
"TLS.expire": expire,
|
|
||||||
"TLS.time": currTime,
|
|
||||||
});
|
|
||||||
|
|
||||||
sig = _hmacsha256(
|
|
||||||
identifier: identifier,
|
|
||||||
currTime: currTime,
|
|
||||||
expire: expire,
|
|
||||||
);
|
|
||||||
sigDoc['TLS.sig'] = sig;
|
|
||||||
String jsonStr = json.encode(sigDoc);
|
|
||||||
List<int>? compress = const ZLibEncoder().encode(utf8.encode(jsonStr));
|
|
||||||
return _escape(content: base64.encode(compress));
|
|
||||||
}
|
|
||||||
|
|
||||||
int _getCurrentTime() {
|
|
||||||
return (DateTime.now().millisecondsSinceEpoch / 1000).floor();
|
|
||||||
}
|
|
||||||
|
|
||||||
String _hmacsha256({
|
|
||||||
required String identifier,
|
|
||||||
required int currTime,
|
|
||||||
int expire = 30 * 24 * 60 * 60,
|
|
||||||
}) {
|
|
||||||
int sdkappid = this.sdkappid;
|
|
||||||
String contentToBeSigned =
|
|
||||||
"TLS.identifier:$identifier\nTLS.sdkappid:$sdkappid\nTLS.time:$currTime\nTLS.expire:$expire\n";
|
|
||||||
Hmac hmacSha256 = Hmac(sha256, utf8.encode(key));
|
|
||||||
Digest hmacSha256Digest =
|
|
||||||
hmacSha256.convert(utf8.encode(contentToBeSigned));
|
|
||||||
return base64.encode(hmacSha256Digest.bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _escape({
|
|
||||||
required String content,
|
|
||||||
}) {
|
|
||||||
return content
|
|
||||||
.replaceAll('+', '*')
|
|
||||||
.replaceAll('/', '-')
|
|
||||||
.replaceAll('=', '_');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
// ignore_for_file: avoid_print
|
// ignore_for_file: avoid_print
|
||||||
|
|
||||||
import 'package:example/GenerateUserSig.dart';
|
|
||||||
import 'package:example/TIMUIKitChatExample.dart';
|
|
||||||
import 'package:example/TIMUIKitConversationExample.dart';
|
|
||||||
import 'package:example/TIMUIKitProfileExample.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||||
import 'TIMUIKitAddFriendExample.dart';
|
import 'TIMUIKitAddFriendExample.dart';
|
||||||
|
|
@ -74,12 +70,15 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||||
return const String.fromEnvironment('SECRET', defaultValue: "");
|
return const String.fromEnvironment('SECRET', defaultValue: "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getUsersig() {
|
||||||
|
return const String.fromEnvironment('USERSIG', defaultValue: "");
|
||||||
|
}
|
||||||
|
|
||||||
initTIMUIKIT() async {
|
initTIMUIKIT() async {
|
||||||
int sdkappid = getSDKAPPID();
|
int sdkappid = getSDKAPPID();
|
||||||
String userid = getUserID();
|
String userid = getUserID();
|
||||||
String secret = getSecret();
|
String secret = getSecret();
|
||||||
String usersig = GenerateTestUserSig(sdkappid: sdkappid, key: secret)
|
String usersig = getUsersig();
|
||||||
.genSig(identifier: userid, expire: 24 * 7 * 60 * 60 * 1000);
|
|
||||||
if (sdkappid == 0 || userid == '' || secret == '' || usersig == '') {
|
if (sdkappid == 0 || userid == '' || secret == '' || usersig == '') {
|
||||||
print("The running parameters are abnormal, please check");
|
print("The running parameters are abnormal, please check");
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,15 @@ import Foundation
|
||||||
|
|
||||||
import audioplayers_darwin
|
import audioplayers_darwin
|
||||||
import desktop_drop
|
import desktop_drop
|
||||||
import device_info_plus_macos
|
import device_info_plus
|
||||||
import fc_native_video_thumbnail_for_us
|
import fc_native_video_thumbnail_for_us
|
||||||
import package_info_plus_macos
|
import package_info_plus
|
||||||
import pasteboard
|
import pasteboard
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import photo_manager
|
import photo_manager
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
import sqflite
|
import sqflite
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
import wakelock_macos
|
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
||||||
|
|
@ -30,5 +29,4 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin"))
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,10 +45,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0
|
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.10.0"
|
version: "2.11.0"
|
||||||
audioplayers:
|
audioplayers:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -197,10 +197,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: characters
|
name: characters
|
||||||
sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c
|
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.3.0"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -209,14 +209,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.1"
|
||||||
chewie:
|
chewie_for_us:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: chewie
|
name: chewie_for_us
|
||||||
sha256: e9da4898ee4859825404f507969f57113c04ca0060e152b95c9afd73934126ad
|
sha256: "0307723e811508d361fffa6f8bbd9040b1bfea5536544e4d655e10c27de002ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.5.0"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -229,10 +229,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
|
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.17.0"
|
version: "1.17.1"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -293,50 +293,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: device_info_plus
|
name: device_info_plus
|
||||||
sha256: b809c4ed5f7fcdb325ccc70b80ad934677dc4e2aa414bf46859a42bfdfafcbb6
|
sha256: "2c35b6d1682b028e42d07b3aee4b98fa62996c10bc12cb651ec856a80d6a761b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.3"
|
version: "9.0.2"
|
||||||
device_info_plus_linux:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: device_info_plus_linux
|
|
||||||
sha256: "77a8b3c4af06bc46507f89304d9f49dfc64b4ae004b994532ed23b34adeae4b3"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.0"
|
|
||||||
device_info_plus_macos:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: device_info_plus_macos
|
|
||||||
sha256: "37961762fbd46d3620c7b69ca606671014db55fc1b7a11e696fd90ed2e8fe03d"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.0"
|
|
||||||
device_info_plus_platform_interface:
|
device_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: device_info_plus_platform_interface
|
name: device_info_plus_platform_interface
|
||||||
sha256: "83fdba24fcf6846d3b10f10dfdc8b6c6d7ada5f8ed21d62ea2909c2dfa043773"
|
sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "7.0.0"
|
||||||
device_info_plus_web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: device_info_plus_web
|
|
||||||
sha256: "5890f6094df108181c7a29720bc23d0fd6159f17d82787fac093d1fefcaf6325"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.0"
|
|
||||||
device_info_plus_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: device_info_plus_windows
|
|
||||||
sha256: "23a2874af0e23ee6e3a2a0ebcecec3a9da13241f2cb93a93a44c8764df123dd7"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.1.0"
|
|
||||||
diff_match_patch:
|
diff_match_patch:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -365,10 +333,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: extended_image
|
name: extended_image
|
||||||
sha256: "5854d0d05ee0c687d1852af9db05f15cfe058520fa56f417075705c5bce965d4"
|
sha256: e77d18f956649ba6e5ecebd0cb68542120886336a75ee673788145bd4c3f0767
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.4.0"
|
version: "8.0.2"
|
||||||
extended_image_library:
|
extended_image_library:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -377,6 +345,30 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.1"
|
version: "3.4.1"
|
||||||
|
extended_text:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: extended_text
|
||||||
|
sha256: "75ddf28ce7d5be33a050ff2179b6567b4b98e6225ad3e61e4c3748f7448c25f7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "11.0.0"
|
||||||
|
extended_text_field:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: extended_text_field
|
||||||
|
sha256: "6cf8c090de4dc1e309cf3b24cb9448d7463c6c17926b628cf0954631bf4e56db"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "12.0.0"
|
||||||
|
extended_text_library:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: extended_text_library
|
||||||
|
sha256: "308b50cfcc8e3accf46a09cb692715fbd1097333817c15b0f7527de1766bc1ff"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "11.0.1"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -421,10 +413,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file_picker
|
name: file_picker
|
||||||
sha256: b85eb92b175767fdaa0c543bf3b0d1f610fe966412ea72845fe5ba7801e763ff
|
sha256: "9d6e95ec73abbd31ec54d0e0df8a961017e165aba1395e462e5b31ea0c165daf"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.2.10"
|
version: "5.3.1"
|
||||||
file_utils:
|
file_utils:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -490,10 +482,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_plugin_android_lifecycle
|
name: flutter_plugin_android_lifecycle
|
||||||
sha256: "60fc7b78455b94e6de2333d2f95196d32cf5c22f4b0b0520a628804cb463503b"
|
sha256: "950e77c2bbe1692bc0874fc7fb491b96a4dc340457f4ea1641443d0a6c1ea360"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.7"
|
version: "2.0.15"
|
||||||
flutter_plugin_record_plus:
|
flutter_plugin_record_plus:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -636,18 +628,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: intl
|
name: intl
|
||||||
sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91"
|
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.17.0"
|
version: "0.18.1"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: js
|
name: js
|
||||||
sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7"
|
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.5"
|
version: "0.6.7"
|
||||||
json_annotation:
|
json_annotation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -708,10 +700,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72"
|
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.13"
|
version: "0.12.15"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -724,10 +716,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42"
|
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0"
|
version: "1.9.1"
|
||||||
mime_type:
|
mime_type:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -764,50 +756,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_info_plus
|
name: package_info_plus
|
||||||
sha256: f62d7253edc197fe3c88d7c2ddab82d68f555e778d55390ccc3537eca8e8d637
|
sha256: ceb027f6bc6a60674a233b4a90a7658af1aebdea833da0b5b53c1e9821a78c7b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.3+1"
|
version: "4.0.2"
|
||||||
package_info_plus_linux:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: package_info_plus_linux
|
|
||||||
sha256: "04b575f44233d30edbb80a94e57cad9107aada334fc02aabb42b6becd13c43fc"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.5"
|
|
||||||
package_info_plus_macos:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: package_info_plus_macos
|
|
||||||
sha256: a2ad8b4acf4cd479d4a0afa5a74ea3f5b1c7563b77e52cc32b3ee6956d5482a6
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.3.0"
|
|
||||||
package_info_plus_platform_interface:
|
package_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_info_plus_platform_interface
|
name: package_info_plus_platform_interface
|
||||||
sha256: f7a0c8f1e7e981bc65f8b64137a53fd3c195b18d429fba960babc59a5a1c7ae8
|
sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "2.0.1"
|
||||||
package_info_plus_web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: package_info_plus_web
|
|
||||||
sha256: f0829327eb534789e0a16ccac8936a80beed4e2401c4d3a74f3f39094a822d3b
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.6"
|
|
||||||
package_info_plus_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: package_info_plus_windows
|
|
||||||
sha256: "79524f11c42dd9078b96d797b3cf79c0a2883a50c4920dc43da8562c115089bc"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.0"
|
|
||||||
pasteboard:
|
pasteboard:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -820,10 +780,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b
|
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
version: "1.8.3"
|
||||||
path_drawing:
|
path_drawing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -948,10 +908,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: photo_manager
|
name: photo_manager
|
||||||
sha256: "55d50ad1b8f984c57fa7c4bd4980f4760e80d3d9355263cf72624a6ff1bf2b5b"
|
sha256: bdc4ab1fa9fb064d8ccfea6ab44119f55b220293d7ce2e19eb5a5f998db86c88
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.2"
|
version: "2.6.0"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -964,10 +924,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: plugin_platform_interface
|
name: plugin_platform_interface
|
||||||
sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a
|
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.4"
|
||||||
pointycastle:
|
pointycastle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1177,73 +1137,49 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: tencent_cloud_chat_sdk
|
name: tencent_cloud_chat_sdk
|
||||||
sha256: "765a93262a41080e155ce5b8a6ca20147a81c7d306f7f87444077c5eaae87e08"
|
sha256: f98bdb55164051e2b196cac6e2e79e60248ed8351dc5a91d25568712ccb15839
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.1.5"
|
version: "5.1.7"
|
||||||
tencent_cloud_chat_uikit:
|
tencent_cloud_chat_uikit:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: ".."
|
path: ".."
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "2.0.0+1"
|
version: "2.1.0"
|
||||||
tencent_cloud_uikit_core:
|
tencent_cloud_uikit_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: tencent_cloud_uikit_core
|
name: tencent_cloud_uikit_core
|
||||||
sha256: "829dfde0c4fbdae019ba233f7f2c299e7cbd18c3ae20ecfe3ab4a43084a33064"
|
sha256: "517d760b0d497ea291d70fe6a021508e5b66f0754c72679f19ee16c0bb354e91"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.3"
|
||||||
tencent_extended_text:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: tencent_extended_text
|
|
||||||
sha256: "27a2f7ee58ada480e295102471f1733a7402178a239d0c80a7aa33a134c641ef"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.2"
|
|
||||||
tencent_extended_text_field:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: tencent_extended_text_field
|
|
||||||
sha256: d311c240983dbf78e31b58f91e425920a40d6564942813e692a3419bf5c9deb0
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.1"
|
|
||||||
tencent_extended_text_library:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: tencent_extended_text_library
|
|
||||||
sha256: d6dad4e4e426e6319db809267f160082c44a334716e9f8593fac56d65ae75545
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.0"
|
|
||||||
tencent_im_base:
|
tencent_im_base:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: tencent_im_base
|
name: tencent_im_base
|
||||||
sha256: "516356a80f43b94a6c0719b54e4c641cb1f164830b2b3e887d175ae862ebab3f"
|
sha256: "9b8e712bf27ffae9b686ec532ee8417b8263eba8bab04f105e28a95de1807322"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.51"
|
version: "1.0.57"
|
||||||
tencent_im_sdk_plugin_desktop:
|
tencent_im_sdk_plugin_desktop:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: tencent_im_sdk_plugin_desktop
|
name: tencent_im_sdk_plugin_desktop
|
||||||
sha256: "5fe5ab0765183185fe4a1f94ce1fdc6ab0a450b8522011806549678edb52130d"
|
sha256: "8d986f2f6aedeac8d771286e31b7bbb9bbee12192461fc879c857be903a41a7f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.13"
|
version: "0.1.19"
|
||||||
tencent_im_sdk_plugin_platform_interface:
|
tencent_im_sdk_plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: tencent_im_sdk_plugin_platform_interface
|
name: tencent_im_sdk_plugin_platform_interface
|
||||||
sha256: "04043582f1af698b4abe12d53cd0f043466228fae712677688988d8ff7bfc1f1"
|
sha256: "53263e4acd7179871aad2a67ec4964bc8fae861f54384fba2c60bd2c16d2867c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.19"
|
version: "0.3.20"
|
||||||
tencent_im_sdk_plugin_web:
|
tencent_im_sdk_plugin_web:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -1276,14 +1212,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
tencent_wechat_camera_picker:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: tencent_wechat_camera_picker
|
|
||||||
sha256: "8f95b435c7a12a9221f00fe4354fb9c0f9313d79cc09ddb5902b7b418185092d"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.6.5+1"
|
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1296,10 +1224,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206
|
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.16"
|
version: "0.5.1"
|
||||||
tim_ui_kit_sticker_plugin:
|
tim_ui_kit_sticker_plugin:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1432,10 +1360,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: video_player
|
name: video_player
|
||||||
sha256: "59f7f31c919c59cbedd37c617317045f5f650dc0eeb568b0b0de9a36472bdb28"
|
sha256: "868a139229acb5018d22aded3eb9cb4767ff43a8216573c086b6c535a4957481"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.1"
|
version: "2.6.0"
|
||||||
video_player_android:
|
video_player_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1468,22 +1396,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.13"
|
version: "2.0.13"
|
||||||
wakelock:
|
wakelock_for_us:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: wakelock
|
name: wakelock_for_us
|
||||||
sha256: "769ecf42eb2d07128407b50cb93d7c10bd2ee48f0276ef0119db1d25cc2f87db"
|
sha256: b73bfa90e5e764f41155063ae92fbd1e3a04ee6372e65ff7d288d2c3057f9498
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.2"
|
version: "0.6.3"
|
||||||
wakelock_macos:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: wakelock_macos
|
|
||||||
sha256: "047c6be2f88cb6b76d02553bca5a3a3b95323b15d30867eca53a19a0a319d4cd"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.4.0"
|
|
||||||
wakelock_platform_interface:
|
wakelock_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1492,22 +1412,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.0"
|
version: "0.3.0"
|
||||||
wakelock_web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: wakelock_web
|
|
||||||
sha256: "1b256b811ee3f0834888efddfe03da8d18d0819317f20f6193e2922b41a501b5"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.4.0"
|
|
||||||
wakelock_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: wakelock_windows
|
|
||||||
sha256: "857f77b3fe6ae82dd045455baa626bc4b93cb9bb6c86bf3f27c182167c3a5567"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.1"
|
|
||||||
watcher:
|
watcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1520,18 +1424,34 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: wechat_assets_picker
|
name: wechat_assets_picker
|
||||||
sha256: "49184fbc83f855bade59961566a6323a2015634ece1f889de5af6fa133a10706"
|
sha256: "5aeac81c6a28e1142a2c9ba9ee802b909c2dad9186d9a58dbe4eb74493af4743"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.3.4"
|
version: "8.5.0"
|
||||||
|
wechat_camera_picker:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: wechat_camera_picker
|
||||||
|
sha256: d8108ea33b1ed25933770199d08d64c210a2854bc6a326fd058a2f34dca8bf46
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.8.0"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4
|
sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.4"
|
version: "4.1.4"
|
||||||
|
win32_registry:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32_registry
|
||||||
|
sha256: "1c52f994bdccb77103a6231ad4ea331a244dbcef5d1f37d8462f713143b0bfae"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1557,5 +1477,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.19.0 <3.0.0"
|
dart: ">=3.0.0 <4.0.0"
|
||||||
flutter: ">=3.7.0"
|
flutter: ">=3.10.0"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
<svg t="1684915863093" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13769" width="200" height="200"><path d="M872.802928 755.99406 872.864326 755.99406 872.864326 755.624646Z" fill="#232832" p-id="13770"></path><path d="M641.612475 478.465233 378.562407 478.465233c-18.447141 0-33.352619 13.225209-33.352619 29.515221 0 16.305361 14.905478 29.544897 33.352619 29.544897l263.050068 0c18.417465 0 33.383318-13.239536 33.383318-29.544897C674.995793 491.690442 660.088268 478.465233 641.612475 478.465233" fill="#232832" p-id="13771"></path><path d="M512.006651 94.661963c-228.163513 0-413.794327 158.926616-413.794327 354.272699 0 99.213629 49.568952 192.906521 136.968511 260.892939l101.667517 78.787414c-3.004427 16.778128-15.917528 56.111972-30.527271 92.115977-4.582365 11.159154-1.845021 23.979134 6.78452 32.340569 5.623068 5.445012 13.06148 8.30106 20.588921 8.30106 4.046153 0 8.123005-0.831948 12.019755-2.527566 33.6811-14.997576 115.680673-55.042617 173.285648-115.920126 224.933959-3.243881 406.785703-160.637584 406.785703-353.989243C925.785629 253.588579 740.185514 94.661963 512.006651 94.661963M510.431783 744.875838c-1.847068 0-1.847068 0.029676-3.571339 0.326435-12.734022 1.457188-28.682249 18.655895-28.682249 18.655895s-52.634778 52.692083-96.758718 77.745688c16.096607-53.705156 13.269211-73.488735 8.123005-81.138972-1.279133-1.992377-2.707668-3.508917-4.432962-4.670369l0.118704-0.058328c0 0-0.416486-0.208754-1.130753-0.595564-0.38681-0.237407-0.773619-0.416486-1.190105-0.655939-13.449313-7.198959-83.338057-46.055942-140.16839-100.506065-55.101969-57.871035-85.421508-135.467321-85.421508-205.04498 0-162.780387 159.121044-295.226908 354.689185-295.226908 195.567117 0 354.688161 132.447545 354.688161 295.226908C866.694813 611.728352 705.998901 744.875838 510.431783 744.875838" fill="#232832" p-id="13772"></path><path d="M645.450896 360.374674l-266.88849 0c-18.447141 0-33.383318 13.224186-33.383318 29.515221 0 16.304338 14.936177 29.529547 33.383318 29.529547l266.88849 0c18.47784 0 33.352619-13.225209 33.352619-29.529547C678.803515 373.59886 663.928736 360.374674 645.450896 360.374674" fill="#232832" p-id="13773"></path></svg>
|
||||||
|
After Width: | Height: | Size: 2.2 KiB |
|
|
@ -0,0 +1,7 @@
|
||||||
|
<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="编组 9备份 3" opacity="0.795169">
|
||||||
|
<rect id="矩形" x="0.5" y="0.5" width="15" height="13" rx="1.75" stroke="#232832"/>
|
||||||
|
<rect id="矩形_2" x="6.25" y="10.25" width="3.5" height="0.5" rx="0.25" stroke="#232832" stroke-width="0.5"/>
|
||||||
|
<path id="路径 8" d="M10.5 3.5L3.59271 10.5222" stroke="#232832" stroke-linecap="round"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 593 B |
|
|
@ -0,0 +1,5 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="8" cy="8" r="7.5" stroke="#232832"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 5C5.55228 5 6 5.44772 6 6C6 6.55228 5.55228 7 5 7C4.44772 7 4 6.55228 4 6C4 5.44772 4.44772 5 5 5Z" fill="#232832"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 5C11.5523 5 12 5.44772 12 6C12 6.55228 11.5523 7 11 7C10.4477 7 10 6.55228 10 6C10 5.44772 10.4477 5 11 5Z" fill="#232832"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 499 B |
|
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="编组 9备份 2" opacity="0.795169">
|
||||||
|
<path id="矩形" d="M0.5 2.25C0.5 1.2835 1.2835 0.5 2.25 0.5H6.54211C6.62301 0.5 6.70271 0.519631 6.77435 0.557208L8.63621 1.53374C8.85115 1.64647 9.09023 1.70536 9.33293 1.70536H13.75C14.7165 1.70536 15.5 2.48886 15.5 3.45536V11.75C15.5 12.7165 14.7165 13.5 13.75 13.5H2.25C1.2835 13.5 0.5 12.7165 0.5 11.75V2.25Z" stroke="#232832"/>
|
||||||
|
<rect id="矩形_2" x="0.5" y="3.5" width="15" height="10" rx="1.75" fill="white" stroke="#232832"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 710 B |
|
|
@ -0,0 +1,7 @@
|
||||||
|
<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="编组 9" opacity="0.795169">
|
||||||
|
<circle id="椭圆形" cx="5.5" cy="4.5" r="1" stroke="#232832"/>
|
||||||
|
<rect id="矩形" x="0.5" y="0.5" width="15" height="13" rx="1.75" stroke="#232832"/>
|
||||||
|
<path id="路径 2" d="M3 11L5.42403 8.9433C5.50934 8.87092 5.63487 8.87221 5.71867 8.94633L7.88124 10.8592C7.97058 10.9382 8.10606 10.9338 8.19006 10.8491L13 6" stroke="#232832" stroke-linecap="round"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 636 B |
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200">]
|
||||||
|
<path d="M779.7 588.6c-23.2 0-45.3 4.4-65.7 12.5L282.9 108.3c-10.2-11.6-27.9-12.8-39.5-2.6-11.6 10.2-12.8 27.9-2.6 39.5l424.4 485.1c-39.3 32.8-64.3 82.2-64.3 137.2 0 98.6 80.2 178.8 178.8 178.8 98.6 0 178.8-80.2 178.8-178.8s-80.2-178.9-178.8-178.9z m0 301.7c-67.7 0-122.8-55.1-122.8-122.8 0-67.7 55.1-122.8 122.8-122.8 67.7 0 122.8 55.1 122.8 122.8 0 67.7-55.1 122.8-122.8 122.8z" fill="#232832" p-id="6499"></path>
|
||||||
|
<path d="M779.7 105.6c-11.6-10.2-29.3-9-39.5 2.6L309 601.1c-20.3-8.1-42.5-12.5-65.7-12.5-98.6 0-178.8 80.2-178.8 178.8s80.2 178.8 178.8 178.8c98.6 0 178.8-80.2 178.8-178.8 0-55.1-25-104.4-64.3-137.2l424.4-485.1c10.3-11.6 9.1-29.3-2.5-39.5zM243.4 890.3c-67.7 0-122.8-55.1-122.8-122.8 0-67.7 55.1-122.8 122.8-122.8 67.7 0 122.8 55.1 122.8 122.8 0 67.7-55.1 122.8-122.8 122.8z" fill="#232832" p-id="6500"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 950 B |
|
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="编组 9备份" opacity="0.795169">
|
||||||
|
<rect id="矩形" x="0.5" y="0.5" width="15" height="13" rx="1.75" stroke="#232832"/>
|
||||||
|
<path id="三角形" d="M10.0282 7L6.5 9.1169L6.5 4.8831L10.0282 7Z" stroke="#232832"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 460 B |
|
|
@ -7,6 +7,7 @@ import 'package:flutter/cupertino.dart';
|
||||||
|
|
||||||
// ignore: unnecessary_import
|
// ignore: unnecessary_import
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
@ -30,6 +31,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
||||||
final GroupServices _groupServices = serviceLocator<GroupServices>();
|
final GroupServices _groupServices = serviceLocator<GroupServices>();
|
||||||
final TUIChatGlobalModel globalModel = serviceLocator<TUIChatGlobalModel>();
|
final TUIChatGlobalModel globalModel = serviceLocator<TUIChatGlobalModel>();
|
||||||
final TUIChatModelTools tools = serviceLocator<TUIChatModelTools>();
|
final TUIChatModelTools tools = serviceLocator<TUIChatModelTools>();
|
||||||
|
final TUISelfInfoViewModel selfModel = serviceLocator<TUISelfInfoViewModel>();
|
||||||
final _uuid = const Uuid();
|
final _uuid = const Uuid();
|
||||||
|
|
||||||
ChatLifeCycle? lifeCycle;
|
ChatLifeCycle? lifeCycle;
|
||||||
|
|
@ -57,6 +59,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
||||||
V2TimGroupInfo? _groupInfo;
|
V2TimGroupInfo? _groupInfo;
|
||||||
String groupMemberListSeq = "0";
|
String groupMemberListSeq = "0";
|
||||||
List<V2TimGroupMemberFullInfo?>? groupMemberList = [];
|
List<V2TimGroupMemberFullInfo?>? groupMemberList = [];
|
||||||
|
V2TimGroupMemberFullInfo? selfMemberInfo;
|
||||||
double atPositionX = 0.0;
|
double atPositionX = 0.0;
|
||||||
double atPositionY = 0.0;
|
double atPositionY = 0.0;
|
||||||
int _activeAtIndex = -1;
|
int _activeAtIndex = -1;
|
||||||
|
|
@ -191,6 +194,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
||||||
isGroupExist = true;
|
isGroupExist = true;
|
||||||
_groupInfo = null;
|
_groupInfo = null;
|
||||||
groupMemberList?.clear();
|
groupMemberList?.clear();
|
||||||
|
selfMemberInfo = null;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
if (conversationType == ConvType.c2c) {
|
if (conversationType == ConvType.c2c) {
|
||||||
|
|
@ -216,7 +220,6 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
markMessageAsRead();
|
|
||||||
globalModel.lifeCycle = lifeCycle;
|
globalModel.lifeCycle = lifeCycle;
|
||||||
globalModel.setCurrentConversation(
|
globalModel.setCurrentConversation(
|
||||||
CurrentConversation(conversationID, conversationType ?? ConvType.c2c));
|
CurrentConversation(conversationID, conversationType ?? ConvType.c2c));
|
||||||
|
|
@ -225,6 +228,9 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
||||||
globalModel.setChatConfig(chatConfig);
|
globalModel.setChatConfig(chatConfig);
|
||||||
globalModel.clearRecivedNewMessageCount();
|
globalModel.clearRecivedNewMessageCount();
|
||||||
_isInit = true;
|
_isInit = true;
|
||||||
|
Future.delayed(const Duration(milliseconds: 300), (){
|
||||||
|
markMessageAsRead();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> loadListForSpecificMessage({
|
Future<bool> loadListForSpecificMessage({
|
||||||
|
|
@ -504,6 +510,8 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
||||||
return await loadGroupMemberList(
|
return await loadGroupMemberList(
|
||||||
groupID: groupID, count: count, seq: nextSeq);
|
groupID: groupID, count: count, seq: nextSeq);
|
||||||
} else {
|
} else {
|
||||||
|
selfMemberInfo = groupMemberList
|
||||||
|
?.firstWhere((e) => e?.userID == selfModel.loginInfo?.userID);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -575,6 +583,11 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
||||||
V2TimMessage? messageInfo,
|
V2TimMessage? messageInfo,
|
||||||
OfflinePushInfo? offlinePushInfo,
|
OfflinePushInfo? offlinePushInfo,
|
||||||
bool? onlineUserOnly = false,
|
bool? onlineUserOnly = false,
|
||||||
|
MessagePriorityEnum priority = MessagePriorityEnum.V2TIM_PRIORITY_NORMAL,
|
||||||
|
bool? isExcludedFromUnreadCount,
|
||||||
|
bool? needReadReceipt,
|
||||||
|
String? cloudCustomData,
|
||||||
|
String? localCustomData,
|
||||||
bool? isEditStatusMessage = false,
|
bool? isEditStatusMessage = false,
|
||||||
}) async {
|
}) async {
|
||||||
String receiver = convType == ConvType.c2c ? convID : '';
|
String receiver = convType == ConvType.c2c ? convID : '';
|
||||||
|
|
@ -586,27 +599,32 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
||||||
setLoadingMessageMap(convID, messageInfo);
|
setLoadingMessageMap(convID, messageInfo);
|
||||||
}
|
}
|
||||||
final sendMsgRes = await _messageService.sendMessage(
|
final sendMsgRes = await _messageService.sendMessage(
|
||||||
|
priority: priority,
|
||||||
|
localCustomData: localCustomData,
|
||||||
|
isExcludedFromUnreadCount: isExcludedFromUnreadCount ?? false,
|
||||||
id: id,
|
id: id,
|
||||||
receiver: receiver,
|
receiver: receiver,
|
||||||
needReadReceipt: chatConfig.isShowGroupReadingStatus &&
|
needReadReceipt: needReadReceipt ??
|
||||||
convType == ConvType.group &&
|
chatConfig.isShowGroupReadingStatus &&
|
||||||
((chatConfig.groupReadReceiptPermissionList != null &&
|
convType == ConvType.group &&
|
||||||
chatConfig.groupReadReceiptPermissionList!
|
((chatConfig.groupReadReceiptPermissionList != null &&
|
||||||
.contains(_groupType)) ||
|
chatConfig.groupReadReceiptPermissionList!
|
||||||
(chatConfig.groupReadReceiptPermisionList != null &&
|
.contains(_groupType)) ||
|
||||||
chatConfig.groupReadReceiptPermisionList!
|
(chatConfig.groupReadReceiptPermisionList != null &&
|
||||||
.contains(oldGroupType))),
|
chatConfig.groupReadReceiptPermisionList!
|
||||||
|
.contains(oldGroupType))),
|
||||||
groupID: groupID,
|
groupID: groupID,
|
||||||
offlinePushInfo: offlinePushInfo,
|
offlinePushInfo: offlinePushInfo,
|
||||||
onlineUserOnly: onlineUserOnly ?? false,
|
onlineUserOnly: onlineUserOnly ?? false,
|
||||||
cloudCustomData: showC2cMessageEditStatus == true
|
cloudCustomData: cloudCustomData ??
|
||||||
? json.encode({
|
(showC2cMessageEditStatus == true
|
||||||
"messageFeature": {
|
? json.encode({
|
||||||
"needTyping": 1,
|
"messageFeature": {
|
||||||
"version": 1,
|
"needTyping": 1,
|
||||||
}
|
"version": 1,
|
||||||
})
|
}
|
||||||
: "",
|
})
|
||||||
|
: ""),
|
||||||
);
|
);
|
||||||
if (isEditStatusMessage == false &&
|
if (isEditStatusMessage == false &&
|
||||||
globalModel.getMessageListPosition(conversationID) !=
|
globalModel.getMessageListPosition(conversationID) !=
|
||||||
|
|
@ -916,6 +934,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
||||||
|
|
||||||
Future<V2TimValueCallback<V2TimMessage>?> sendImageMessage(
|
Future<V2TimValueCallback<V2TimMessage>?> sendImageMessage(
|
||||||
{String? imagePath,
|
{String? imagePath,
|
||||||
|
String? imageName,
|
||||||
required String convID,
|
required String convID,
|
||||||
dynamic inputElement,
|
dynamic inputElement,
|
||||||
required ConvType convType}) async {
|
required ConvType convType}) async {
|
||||||
|
|
@ -939,7 +958,9 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
final imageMessageInfo = await _messageService.createImageMessage(
|
final imageMessageInfo = await _messageService.createImageMessage(
|
||||||
imagePath: image ?? imagePath, inputElement: inputElement);
|
imageName: imageName,
|
||||||
|
imagePath: image ?? imagePath,
|
||||||
|
inputElement: inputElement);
|
||||||
List<V2TimMessage> currentHistoryMsgList = getOriginMessageList();
|
List<V2TimMessage> currentHistoryMsgList = getOriginMessageList();
|
||||||
final messageInfo = imageMessageInfo!.messageInfo;
|
final messageInfo = imageMessageInfo!.messageInfo;
|
||||||
if (messageInfo != null) {
|
if (messageInfo != null) {
|
||||||
|
|
@ -1334,6 +1355,12 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
||||||
|
|
||||||
/// Offline push info
|
/// Offline push info
|
||||||
OfflinePushInfo? offlinePushInfo,
|
OfflinePushInfo? offlinePushInfo,
|
||||||
|
MessagePriorityEnum priority = MessagePriorityEnum.V2TIM_PRIORITY_NORMAL,
|
||||||
|
bool? onlineUserOnly,
|
||||||
|
bool? isExcludedFromUnreadCount,
|
||||||
|
bool? needReadReceipt,
|
||||||
|
String? cloudCustomData,
|
||||||
|
String? localCustomData,
|
||||||
}) {
|
}) {
|
||||||
List<V2TimMessage> currentHistoryMsgList = getOriginMessageList();
|
List<V2TimMessage> currentHistoryMsgList = getOriginMessageList();
|
||||||
if (messageInfo != null) {
|
if (messageInfo != null) {
|
||||||
|
|
@ -1351,6 +1378,12 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
return _sendMessage(
|
return _sendMessage(
|
||||||
|
priority: priority,
|
||||||
|
onlineUserOnly: onlineUserOnly,
|
||||||
|
isExcludedFromUnreadCount: isExcludedFromUnreadCount,
|
||||||
|
needReadReceipt: needReadReceipt,
|
||||||
|
cloudCustomData: cloudCustomData,
|
||||||
|
localCustomData: localCustomData,
|
||||||
convID: conversationID,
|
convID: conversationID,
|
||||||
id: messageInfo.id as String,
|
id: messageInfo.id as String,
|
||||||
convType: conversationType ?? ConvType.c2c,
|
convType: conversationType ?? ConvType.c2c,
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ class CurrentConversation {
|
||||||
CurrentConversation(this.conversationID, this.conversationType);
|
CurrentConversation(this.conversationID, this.conversationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
||||||
final MessageService _messageService = serviceLocator<MessageService>();
|
final MessageService _messageService = serviceLocator<MessageService>();
|
||||||
final GroupServices _groupServices = serviceLocator<GroupServices>();
|
final GroupServices _groupServices = serviceLocator<GroupServices>();
|
||||||
final Map<String, List<V2TimMessage>?> _messageListMap = {};
|
final Map<String, List<V2TimMessage>?> _messageListMap = {};
|
||||||
|
|
@ -56,8 +56,8 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
late V2TimAdvancedMsgListener advancedMsgListener;
|
late V2TimAdvancedMsgListener advancedMsgListener;
|
||||||
int _unreadCountForConversation = 0;
|
int _unreadCountForConversation = 0;
|
||||||
|
|
||||||
// use for generate a new sliver list to show recived messag list
|
// use for generate a new sliver list to show received message list
|
||||||
int _recivedNewMessageCount = 0;
|
int _receivedNewMessageCount = 0;
|
||||||
TIMUIKitChatConfig chatConfig = const TIMUIKitChatConfig();
|
TIMUIKitChatConfig chatConfig = const TIMUIKitChatConfig();
|
||||||
List<V2TimGroupApplication>? _groupApplicationList;
|
List<V2TimGroupApplication>? _groupApplicationList;
|
||||||
String Function(V2TimMessage message)? _abstractMessageBuilder;
|
String Function(V2TimMessage message)? _abstractMessageBuilder;
|
||||||
|
|
@ -65,7 +65,7 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
Map.from({}); // 0 normal 1 sending
|
Map.from({}); // 0 normal 1 sending
|
||||||
final Map<String, bool> _c2cMessageFromUserActiveMap = Map.from({});
|
final Map<String, bool> _c2cMessageFromUserActiveMap = Map.from({});
|
||||||
final Map<String, Timer> _c2cMessageActiveTimer = Map.from({});
|
final Map<String, Timer> _c2cMessageActiveTimer = Map.from({});
|
||||||
bool _showC2cMessageEditStaus = true;
|
bool _showC2cMessageEditStatus = true;
|
||||||
final Map<String, Timer> _c2cMessageStatusShowTimer = Map.from({});
|
final Map<String, Timer> _c2cMessageStatusShowTimer = Map.from({});
|
||||||
Map<String, List> loadingMessage = {};
|
Map<String, List> loadingMessage = {};
|
||||||
|
|
||||||
|
|
@ -144,7 +144,7 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
print("start another download");
|
print("start another download");
|
||||||
}
|
}
|
||||||
|
|
||||||
int getRecevied(msgID) {
|
int getReceived(msgID) {
|
||||||
return messageListProgressMap[msgID] ?? 0;
|
return messageListProgressMap[msgID] ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,11 +173,11 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
int get receivedMessageListCount {
|
int get receivedMessageListCount {
|
||||||
return _recivedNewMessageCount;
|
return _receivedNewMessageCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
set receivedNewMessageCount(int value) {
|
set receivedNewMessageCount(int value) {
|
||||||
_recivedNewMessageCount = value;
|
_receivedNewMessageCount = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get unreadCountForConversation => _unreadCountForConversation;
|
int get unreadCountForConversation => _unreadCountForConversation;
|
||||||
|
|
@ -207,6 +207,10 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCurrentConversation() {
|
clearCurrentConversation() {
|
||||||
|
// Only keep the last 20 messages when existing a chat.
|
||||||
|
_messageListMap[currentSelectedConv] =
|
||||||
|
(_messageListMap[currentSelectedConv] ?? []).sublist(
|
||||||
|
max(0, ((_messageListMap[currentSelectedConv] ?? []).length - 20)));
|
||||||
_currentConversationList.removeLast();
|
_currentConversationList.removeLast();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
@ -216,7 +220,7 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
setShowC2cEditStatus(bool show) {
|
setShowC2cEditStatus(bool show) {
|
||||||
_showC2cMessageEditStaus = show;
|
_showC2cMessageEditStatus = show;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// set edit status from chats
|
/// set edit status from chats
|
||||||
|
|
@ -373,14 +377,14 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
_totalUnreadCount = 0;
|
_totalUnreadCount = 0;
|
||||||
_groupApplicationList?.clear();
|
_groupApplicationList?.clear();
|
||||||
_totalUnreadCount = 0;
|
_totalUnreadCount = 0;
|
||||||
_recivedNewMessageCount = 0;
|
_receivedNewMessageCount = 0;
|
||||||
_messageReadReceiptMap.clear();
|
_messageReadReceiptMap.clear();
|
||||||
_messageListProgressMap.clear();
|
_messageListProgressMap.clear();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
clearRecivedNewMessageCount() {
|
clearRecivedNewMessageCount() {
|
||||||
_recivedNewMessageCount = 0;
|
_receivedNewMessageCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_preLoadImage(List<V2TimMessage> msgList) {
|
_preLoadImage(List<V2TimMessage> msgList) {
|
||||||
|
|
@ -441,7 +445,6 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setFileMessageLocation(String msgID, String location) {
|
setFileMessageLocation(String msgID, String location) {
|
||||||
|
|
@ -526,7 +529,7 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
sendEditStatusMessage(bool isEditing, String toUser) async {
|
sendEditStatusMessage(bool isEditing, String toUser) async {
|
||||||
if (!_showC2cMessageEditStaus) {
|
if (!_showC2cMessageEditStatus) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(_c2cMessageFromUserActiveMap[toUser] ?? false)) {
|
if (!(_c2cMessageFromUserActiveMap[toUser] ?? false)) {
|
||||||
|
|
@ -612,7 +615,7 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_recivedNewMessageCount = 0;
|
_receivedNewMessageCount = 0;
|
||||||
final currentMsg = _messageListMap[convID] ?? [];
|
final currentMsg = _messageListMap[convID] ?? [];
|
||||||
_messageListMap[convID] = [newMsg, ...currentMsg];
|
_messageListMap[convID] = [newMsg, ...currentMsg];
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
@ -631,7 +634,7 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
} else {
|
} else {
|
||||||
if (convID == currentSelectedConv) {
|
if (convID == currentSelectedConv) {
|
||||||
unreadCountForConversation++;
|
unreadCountForConversation++;
|
||||||
_recivedNewMessageCount++;
|
_receivedNewMessageCount++;
|
||||||
final currentMsg = _messageListMap[convID] ?? [];
|
final currentMsg = _messageListMap[convID] ?? [];
|
||||||
_messageListMap[convID] = [newMsg, ...currentMsg];
|
_messageListMap[convID] = [newMsg, ...currentMsg];
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
@ -746,23 +749,12 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (messageProgress.isError) {
|
|
||||||
TIMUIKitClass.onTIMCallback(
|
|
||||||
TIMCallback(
|
|
||||||
type: TIMCallbackType.INFO,
|
|
||||||
infoRecommendText:
|
|
||||||
TIM_t("视频保存失败") + ": ${messageProgress.errorCode.toString()}",
|
|
||||||
infoCode: 6660403),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messageProgress.totalSize != -1) {
|
if (messageProgress.totalSize != -1) {
|
||||||
int progrss =
|
int progress =
|
||||||
(messageProgress.currentSize / messageProgress.totalSize * 100)
|
(messageProgress.currentSize / messageProgress.totalSize * 100)
|
||||||
.ceil();
|
.ceil();
|
||||||
if (progrss > 1) {
|
if (progress > 1) {
|
||||||
setMessageProgress(messageProgress.msgID, progrss);
|
setMessageProgress(messageProgress.msgID, progress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -791,6 +783,12 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
required String convID,
|
required String convID,
|
||||||
ValueChanged<String>? setInputField,
|
ValueChanged<String>? setInputField,
|
||||||
OfflinePushInfo? offlinePushInfo,
|
OfflinePushInfo? offlinePushInfo,
|
||||||
|
MessagePriorityEnum priority = MessagePriorityEnum.V2TIM_PRIORITY_NORMAL,
|
||||||
|
bool? onlineUserOnly,
|
||||||
|
bool? isExcludedFromUnreadCount,
|
||||||
|
bool? needReadReceipt,
|
||||||
|
String? cloudCustomData,
|
||||||
|
String? localCustomData,
|
||||||
}) {
|
}) {
|
||||||
final TUIChatModelTools tools = serviceLocator<TUIChatModelTools>();
|
final TUIChatModelTools tools = serviceLocator<TUIChatModelTools>();
|
||||||
List<V2TimMessage> currentHistoryMsgList = _messageListMap[convID] ?? [];
|
List<V2TimMessage> currentHistoryMsgList = _messageListMap[convID] ?? [];
|
||||||
|
|
@ -807,6 +805,12 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
loadingMessage[convID] = <V2TimMessage>[messageInfoWithSender];
|
loadingMessage[convID] = <V2TimMessage>[messageInfoWithSender];
|
||||||
}
|
}
|
||||||
return _sendMessage(
|
return _sendMessage(
|
||||||
|
priority: priority,
|
||||||
|
onlineUserOnly: onlineUserOnly,
|
||||||
|
isExcludedFromUnreadCount: isExcludedFromUnreadCount,
|
||||||
|
needReadReceipt: needReadReceipt,
|
||||||
|
cloudCustomData: cloudCustomData,
|
||||||
|
localCustomData: localCustomData,
|
||||||
convID: convID,
|
convID: convID,
|
||||||
setInputField: setInputField,
|
setInputField: setInputField,
|
||||||
id: messageInfo.id as String,
|
id: messageInfo.id as String,
|
||||||
|
|
@ -868,6 +872,11 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
bool? isEditStatusMessage = false,
|
bool? isEditStatusMessage = false,
|
||||||
GroupReceiptAllowType? groupType,
|
GroupReceiptAllowType? groupType,
|
||||||
ValueChanged<String>? setInputField,
|
ValueChanged<String>? setInputField,
|
||||||
|
MessagePriorityEnum priority = MessagePriorityEnum.V2TIM_PRIORITY_NORMAL,
|
||||||
|
bool? isExcludedFromUnreadCount,
|
||||||
|
bool? needReadReceipt,
|
||||||
|
String? cloudCustomData,
|
||||||
|
String? localCustomData,
|
||||||
}) async {
|
}) async {
|
||||||
String receiver = convType == ConvType.c2c ? convID : '';
|
String receiver = convType == ConvType.c2c ? convID : '';
|
||||||
String groupID = convType == ConvType.group ? convID : '';
|
String groupID = convType == ConvType.group ? convID : '';
|
||||||
|
|
@ -876,23 +885,28 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
final sendMsgRes = await _messageService.sendMessage(
|
final sendMsgRes = await _messageService.sendMessage(
|
||||||
id: id,
|
id: id,
|
||||||
receiver: receiver,
|
receiver: receiver,
|
||||||
needReadReceipt: chatConfig.isShowGroupReadingStatus &&
|
needReadReceipt: needReadReceipt ??
|
||||||
convType == ConvType.group &&
|
chatConfig.isShowGroupReadingStatus &&
|
||||||
((chatConfig.groupReadReceiptPermissionList != null &&
|
convType == ConvType.group &&
|
||||||
chatConfig.groupReadReceiptPermissionList!
|
((chatConfig.groupReadReceiptPermissionList != null &&
|
||||||
.contains(groupType)) ||
|
chatConfig.groupReadReceiptPermissionList!
|
||||||
(chatConfig.groupReadReceiptPermisionList != null &&
|
.contains(groupType)) ||
|
||||||
chatConfig.groupReadReceiptPermisionList!
|
(chatConfig.groupReadReceiptPermisionList != null &&
|
||||||
.contains(oldGroupType))),
|
chatConfig.groupReadReceiptPermisionList!
|
||||||
|
.contains(oldGroupType))),
|
||||||
groupID: groupID,
|
groupID: groupID,
|
||||||
|
priority: priority,
|
||||||
|
localCustomData: localCustomData,
|
||||||
|
isExcludedFromUnreadCount: isExcludedFromUnreadCount ?? false,
|
||||||
offlinePushInfo: offlinePushInfo,
|
offlinePushInfo: offlinePushInfo,
|
||||||
onlineUserOnly: onlineUserOnly ?? false,
|
onlineUserOnly: onlineUserOnly ?? false,
|
||||||
cloudCustomData: json.encode({
|
cloudCustomData: cloudCustomData ??
|
||||||
"messageFeature": {
|
json.encode({
|
||||||
"needTyping": 1,
|
"messageFeature": {
|
||||||
"version": 1,
|
"needTyping": 1,
|
||||||
}
|
"version": 1,
|
||||||
}));
|
}
|
||||||
|
}));
|
||||||
if (isEditStatusMessage == false) {
|
if (isEditStatusMessage == false) {
|
||||||
updateMessage(sendMsgRes, convID, id, convType, groupType, setInputField);
|
updateMessage(sendMsgRes, convID, id, convType, groupType, setInputField);
|
||||||
}
|
}
|
||||||
|
|
@ -904,7 +918,7 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
{bool needResetNewMessageCount = true}) {
|
{bool needResetNewMessageCount = true}) {
|
||||||
_messageListMap[conversationID] = messageList;
|
_messageListMap[conversationID] = messageList;
|
||||||
if (needResetNewMessageCount) {
|
if (needResetNewMessageCount) {
|
||||||
_recivedNewMessageCount = 0;
|
_receivedNewMessageCount = 0;
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
@ -961,18 +975,21 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
|
||||||
String convID,
|
String convID,
|
||||||
) {
|
) {
|
||||||
message.id = DateTime.now().millisecondsSinceEpoch.toString();
|
message.id = DateTime.now().millisecondsSinceEpoch.toString();
|
||||||
|
|
||||||
final activeMessageList = _messageListMap[convID];
|
final activeMessageList = _messageListMap[convID];
|
||||||
if (activeMessageList == null || activeMessageList.isEmpty) {
|
if (activeMessageList == null || activeMessageList.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final msgID = message.msgID;
|
final msgID = message.msgID;
|
||||||
_messageListMap[currentSelectedConv] = activeMessageList.map((item) {
|
_messageListMap[convID] = activeMessageList.map((item) {
|
||||||
if (item.msgID == msgID) {
|
if (item.msgID == msgID) {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
}).toList();
|
}).toList();
|
||||||
notifyListeners();
|
if (convID == currentSelectedConv) {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<V2TimMessage>? getMessageList(String conversationID) {
|
List<V2TimMessage>? getMessageList(String conversationID) {
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ class LoginInfo {
|
||||||
{this.sdkAppID = 0, this.userSig = "", this.userID = "", this.loginUser});
|
{this.sdkAppID = 0, this.userSig = "", this.userID = "", this.loginUser});
|
||||||
}
|
}
|
||||||
|
|
||||||
class CoreServicesImpl with CoreServices {
|
class CoreServicesImpl implements CoreServices {
|
||||||
V2TimUserFullInfo? _loginInfo;
|
V2TimUserFullInfo? _loginInfo;
|
||||||
late int _sdkAppID;
|
late int _sdkAppID;
|
||||||
late String _userID;
|
late String _userID;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_ser
|
||||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||||
import 'package:tencent_im_base/tencent_im_base.dart';
|
import 'package:tencent_im_base/tencent_im_base.dart';
|
||||||
|
|
||||||
class FriendshipServicesImpl with FriendshipServices {
|
class FriendshipServicesImpl implements FriendshipServices {
|
||||||
final CoreServicesImpl _coreService = serviceLocator<CoreServicesImpl>();
|
final CoreServicesImpl _coreService = serviceLocator<CoreServicesImpl>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -36,26 +36,26 @@ class MessageServiceImpl extends MessageService {
|
||||||
lastMsgID: lastMsgID,
|
lastMsgID: lastMsgID,
|
||||||
lastMsgSeq: lastMsgSeq,
|
lastMsgSeq: lastMsgSeq,
|
||||||
messageTypeList: messageTypeList);
|
messageTypeList: messageTypeList);
|
||||||
final List<V2TimMessage> reponseMessageList = res.data ?? [];
|
final List<V2TimMessage> responsedMessageList = res.data ?? [];
|
||||||
final conversationID = userID ?? groupID;
|
final conversationID = userID ?? groupID;
|
||||||
final cachedMessageList = messgaeListMap[conversationID];
|
final cachedMessageList = messgaeListMap[conversationID];
|
||||||
List<V2TimMessage> commbinedMessageList = [];
|
List<V2TimMessage> combinedMessageList = [];
|
||||||
// 加载更多
|
// 加载更多
|
||||||
if (lastMsgID != null && cachedMessageList != null) {
|
if (lastMsgID != null && cachedMessageList != null) {
|
||||||
commbinedMessageList = [...cachedMessageList, ...reponseMessageList];
|
combinedMessageList = [...cachedMessageList, ...responsedMessageList];
|
||||||
// 首次加载
|
// 首次加载
|
||||||
} else {
|
} else {
|
||||||
final bool existSendingMessage = sendingMessage[conversationID] != null &&
|
final bool existSendingMessage = sendingMessage[conversationID] != null &&
|
||||||
sendingMessage[conversationID]!.isNotEmpty;
|
sendingMessage[conversationID]!.isNotEmpty;
|
||||||
// 存在未发送完成的消息
|
// 存在未发送完成的消息
|
||||||
if (existSendingMessage) {
|
if (existSendingMessage) {
|
||||||
commbinedMessageList = [
|
combinedMessageList = [
|
||||||
...sendingMessage[conversationID]!,
|
...sendingMessage[conversationID]!,
|
||||||
...reponseMessageList
|
...responsedMessageList
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
sendingMessage.remove(conversationID);
|
sendingMessage.remove(conversationID);
|
||||||
commbinedMessageList = reponseMessageList;
|
combinedMessageList = responsedMessageList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (res.code != 0) {
|
if (res.code != 0) {
|
||||||
|
|
@ -64,15 +64,16 @@ class MessageServiceImpl extends MessageService {
|
||||||
errorMsg: res.desc,
|
errorMsg: res.desc,
|
||||||
errorCode: res.code));
|
errorCode: res.code));
|
||||||
}
|
}
|
||||||
if (reponseMessageList.isEmpty ||
|
if (responsedMessageList.isEmpty ||
|
||||||
(!PlatformUtils().isWeb && reponseMessageList.length < count) ||
|
(!PlatformUtils().isWeb && responsedMessageList.length < count) ||
|
||||||
(PlatformUtils().isWeb && reponseMessageList.length < min(count, 20))) {
|
(PlatformUtils().isWeb &&
|
||||||
|
responsedMessageList.length < min(count, 20))) {
|
||||||
haveMoreData = false;
|
haveMoreData = false;
|
||||||
} else {
|
} else {
|
||||||
haveMoreData = true;
|
haveMoreData = true;
|
||||||
}
|
}
|
||||||
return MessageListResponse(
|
return MessageListResponse(
|
||||||
haveMoreData: haveMoreData, data: commbinedMessageList);
|
haveMoreData: haveMoreData, data: combinedMessageList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -302,11 +303,13 @@ class MessageServiceImpl extends MessageService {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<V2TimMsgCreateInfoResult?> createImageMessage(
|
Future<V2TimMsgCreateInfoResult?> createImageMessage(
|
||||||
{String? imagePath, dynamic inputElement}) async {
|
{String? imageName, String? imagePath, dynamic inputElement}) async {
|
||||||
final res = await TencentImSDKPlugin.v2TIMManager
|
final res = await TencentImSDKPlugin.v2TIMManager
|
||||||
.getMessageManager()
|
.getMessageManager()
|
||||||
.createImageMessage(
|
.createImageMessage(
|
||||||
imagePath: imagePath ?? "", inputElement: inputElement);
|
imageName: imageName,
|
||||||
|
imagePath: imagePath ?? "",
|
||||||
|
inputElement: inputElement);
|
||||||
if (res.code == 0) {
|
if (res.code == 0) {
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
|
|
@ -345,7 +348,7 @@ class MessageServiceImpl extends MessageService {
|
||||||
bool isExcludedFromUnreadCount = false,
|
bool isExcludedFromUnreadCount = false,
|
||||||
bool needReadReceipt = false,
|
bool needReadReceipt = false,
|
||||||
OfflinePushInfo? offlinePushInfo,
|
OfflinePushInfo? offlinePushInfo,
|
||||||
String? cloudCustomData, // 云自定义消息字段,只能在消息发送前添加
|
String? cloudCustomData,
|
||||||
String? localCustomData,
|
String? localCustomData,
|
||||||
}) async {
|
}) async {
|
||||||
final result =
|
final result =
|
||||||
|
|
@ -812,5 +815,4 @@ class MessageServiceImpl extends MessageService {
|
||||||
}
|
}
|
||||||
return result.data?[text] ?? "";
|
return result.data?[text] ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import 'package:tencent_im_base/tencent_im_base.dart';
|
||||||
class MessageListResponse {
|
class MessageListResponse {
|
||||||
final bool haveMoreData;
|
final bool haveMoreData;
|
||||||
final List<V2TimMessage> data;
|
final List<V2TimMessage> data;
|
||||||
|
|
||||||
MessageListResponse({required this.haveMoreData, required this.data});
|
MessageListResponse({required this.haveMoreData, required this.data});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,6 +52,7 @@ abstract class MessageService {
|
||||||
Future<void> removeSimpleMsgListener({V2TimSimpleMsgListener? listener});
|
Future<void> removeSimpleMsgListener({V2TimSimpleMsgListener? listener});
|
||||||
|
|
||||||
Future<V2TimMsgCreateInfoResult?> createTextMessage({required String text});
|
Future<V2TimMsgCreateInfoResult?> createTextMessage({required String text});
|
||||||
|
|
||||||
Future<V2TimMsgCreateInfoResult?> createFaceMessage(
|
Future<V2TimMsgCreateInfoResult?> createFaceMessage(
|
||||||
{required int index, required String data});
|
{required int index, required String data});
|
||||||
|
|
||||||
|
|
@ -88,7 +90,7 @@ abstract class MessageService {
|
||||||
{required V2TimMessage message});
|
{required V2TimMessage message});
|
||||||
|
|
||||||
Future<V2TimMsgCreateInfoResult?> createImageMessage(
|
Future<V2TimMsgCreateInfoResult?> createImageMessage(
|
||||||
{String? imagePath, dynamic inputElement});
|
{String? imageName, String? imagePath, dynamic inputElement});
|
||||||
|
|
||||||
Future<V2TimMsgCreateInfoResult?> createVideoMessage(
|
Future<V2TimMsgCreateInfoResult?> createVideoMessage(
|
||||||
{String? videoPath = "",
|
{String? videoPath = "",
|
||||||
|
|
|
||||||
|
|
@ -688,5 +688,6 @@
|
||||||
"k_1698c42": "在访达中打开",
|
"k_1698c42": "在访达中打开",
|
||||||
"k_066fxsz": "查看文件夹",
|
"k_066fxsz": "查看文件夹",
|
||||||
"k_0k432y2": "无法发送,包含文件夹",
|
"k_0k432y2": "无法发送,包含文件夹",
|
||||||
"k_002wb4y": "会话"
|
"k_002wb4y": "会话",
|
||||||
|
"k_0od4qyh": "视频文件异常"
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// ignore_for_file: avoid_print
|
// ignore_for_file: avoid_print
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:scroll_to_index/scroll_to_index.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
|
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||||
|
|
@ -8,6 +9,8 @@ import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||||
|
|
||||||
class TIMUIKitChatController {
|
class TIMUIKitChatController {
|
||||||
late TUIChatSeparateViewModel? model;
|
late TUIChatSeparateViewModel? model;
|
||||||
|
late TIMUIKitInputTextFieldController? textFieldController;
|
||||||
|
late AutoScrollController? scrollController;
|
||||||
final TUIChatGlobalModel globalChatModel =
|
final TUIChatGlobalModel globalChatModel =
|
||||||
serviceLocator<TUIChatGlobalModel>();
|
serviceLocator<TUIChatGlobalModel>();
|
||||||
|
|
||||||
|
|
@ -35,13 +38,11 @@ class TIMUIKitChatController {
|
||||||
false;
|
false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// clear the current conversation;
|
/// Clear the current conversation;
|
||||||
/// 销毁
|
|
||||||
@Deprecated("No need to dispose after tencent_cloud_chat_uikit 0.1.4")
|
@Deprecated("No need to dispose after tencent_cloud_chat_uikit 0.1.4")
|
||||||
dispose() {}
|
dispose() {}
|
||||||
|
|
||||||
/// clear the history of current conversation;
|
/// Clear the history of current conversation;
|
||||||
/// 清除历史记录
|
|
||||||
/// Please provide `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
|
/// Please provide `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
|
||||||
clearHistory([String? convID]) {
|
clearHistory([String? convID]) {
|
||||||
if (convID != null) {
|
if (convID != null) {
|
||||||
|
|
@ -51,7 +52,6 @@ class TIMUIKitChatController {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// refresh the history message list manually;
|
/// refresh the history message list manually;
|
||||||
/// 手动刷新会话历史消息列表
|
|
||||||
/// Please provide `convType` and `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
|
/// Please provide `convType` and `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
|
||||||
Future<bool> refreshCurrentHistoryList(
|
Future<bool> refreshCurrentHistoryList(
|
||||||
[String? convID, ConvType? convType]) async {
|
[String? convID, ConvType? convType]) async {
|
||||||
|
|
@ -64,8 +64,7 @@ class TIMUIKitChatController {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// update single message at UI model
|
/// Update single message at UI model
|
||||||
/// 更新单条消息
|
|
||||||
/// Please provide `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
|
/// Please provide `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
|
||||||
Future<void> updateMessage(
|
Future<void> updateMessage(
|
||||||
{
|
{
|
||||||
|
|
@ -87,39 +86,72 @@ class TIMUIKitChatController {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a message to the specified conversation, or to the current conversation specified on `TIMUIKitChat`.
|
/// Sends a message to the specified conversation, or to the current conversation specified on `TIMUIKitChat`.
|
||||||
/// 发送消息到指定的对话,或者发送到 `TIMUIKitChat` 中指定的当前对话。
|
|
||||||
/// You must provide `convType` and either `userID` or `groupID`, only if you use `TIMUIKitChat` without specifying a `TIMUIKitChatController`, you must provide these parameters.
|
/// You must provide `convType` and either `userID` or `groupID`, only if you use `TIMUIKitChat` without specifying a `TIMUIKitChatController`, you must provide these parameters.
|
||||||
/// 您需要提供 `convType` 和 `userID` 或 `groupID`, 只有在如果您使用 `TIMUIKitChat` 而没有指定 `TIMUIKitChatController`,则必须提供这些参数。
|
|
||||||
Future<V2TimValueCallback<V2TimMessage>?>? sendMessage({
|
Future<V2TimValueCallback<V2TimMessage>?>? sendMessage({
|
||||||
required V2TimMessage? messageInfo,
|
required V2TimMessage? messageInfo,
|
||||||
|
|
||||||
/// The type of the target conversation: either ConvType.group or ConvType.c2c. Required if using `TIMUIKitChat` without specifying a `TIMUIKitChatController`.
|
/// The type of the target conversation: either ConvType.group or ConvType.c2c. Required if using `TIMUIKitChat` without specifying a `TIMUIKitChatController`.
|
||||||
/// 目标对话的类型:ConvType.group 或 ConvType.c2c。只有在如果您使用 `TIMUIKitChat` 而没有指定 `TIMUIKitChatController`,则必须提供此参数。
|
|
||||||
ConvType? convType,
|
ConvType? convType,
|
||||||
|
|
||||||
/// The user ID of the target one-to-one conversation. Required if convType is ConvType.c2c.
|
/// The user ID of the target one-to-one conversation. Required if convType is ConvType.c2c.
|
||||||
/// 目标一对一对话的用户 ID。如果 convType 是 ConvType.c2c,则必填。
|
|
||||||
String? userID,
|
String? userID,
|
||||||
|
|
||||||
/// The target group ID. Required if convType is ConvType.group.
|
/// The target group ID. Required if convType is ConvType.group.
|
||||||
/// 目标群组的 ID。如果 convType 是 ConvType.group,则必填。
|
|
||||||
String? groupID,
|
String? groupID,
|
||||||
|
|
||||||
/// A callback function to update the input field when message sending fails.
|
/// A callback function to update the input field when message sending fails.
|
||||||
/// 当消息发送失败时,用于更新输入框的回调函数。
|
|
||||||
ValueChanged<String>? setInputField,
|
ValueChanged<String>? setInputField,
|
||||||
|
|
||||||
/// Offline push information.
|
/// Offline push information.
|
||||||
/// 离线推送信息。
|
|
||||||
OfflinePushInfo? offlinePushInfo,
|
OfflinePushInfo? offlinePushInfo,
|
||||||
|
|
||||||
|
/// Whether automatically scrolling to the bottom of the message list after sending a message.
|
||||||
|
/// This field solely works when `TIMUIKitChatController` is specified for use within a `TIMUIKitChat`.
|
||||||
|
bool isNavigateToMessageListBottom = true,
|
||||||
|
|
||||||
|
/// Message priorities. This field is valid only for group chat messages.
|
||||||
|
/// You are advised to set higher priorities for important messages (such as red packet and gift messages)
|
||||||
|
/// and set lower priorities for frequent but unimportant messages (such as like messages).
|
||||||
|
MessagePriorityEnum priority = MessagePriorityEnum.V2TIM_PRIORITY_NORMAL,
|
||||||
|
|
||||||
|
/// Whether the message can be received only by online users.
|
||||||
|
/// If this field is set to true, the message cannot be pulled in recipient historical message pulling.
|
||||||
|
/// This field is often used to implement weak notification features such as "The other party is typing" or unimportant notifications in the group. This field is not supported by audio-video groups (AVChatRoom).
|
||||||
|
bool? onlineUserOnly,
|
||||||
|
|
||||||
|
/// Whether the message is excluded from the conversation unread message count.
|
||||||
|
bool? isExcludedFromUnreadCount,
|
||||||
|
|
||||||
|
/// Whether a read receipt is required.
|
||||||
|
bool? needReadReceipt,
|
||||||
|
|
||||||
|
/// Cloud custom data (saved in the cloud, will be sent to the peer end,
|
||||||
|
/// and can still be pulled after the app is uninstalled and reinstalled)
|
||||||
|
String? cloudCustomData,
|
||||||
|
|
||||||
|
/// Local custom message data (saved locally, will not be sent to the peer end,
|
||||||
|
/// and will become invalid after the app is uninstalled and reinstalled).
|
||||||
|
String? localCustomData,
|
||||||
}) {
|
}) {
|
||||||
if (convType != null) {
|
if (convType != null) {
|
||||||
/// Sends a message to the specified conversation. 发送消息到指定的对话。
|
/// Sends a message to the specified conversation.
|
||||||
assert((groupID == null) != (userID == null));
|
assert((groupID == null) != (userID == null));
|
||||||
assert(groupID != null || convType != ConvType.group);
|
assert(groupID != null || convType != ConvType.group);
|
||||||
assert(userID != null || convType != ConvType.c2c);
|
assert(userID != null || convType != ConvType.c2c);
|
||||||
|
if (isNavigateToMessageListBottom) {
|
||||||
|
scrollController?.animateTo(
|
||||||
|
scrollController!.position.minScrollExtent,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
curve: Curves.ease,
|
||||||
|
);
|
||||||
|
}
|
||||||
return globalChatModel.sendMessageFromController(
|
return globalChatModel.sendMessageFromController(
|
||||||
|
priority: priority,
|
||||||
|
onlineUserOnly: onlineUserOnly,
|
||||||
|
isExcludedFromUnreadCount: isExcludedFromUnreadCount,
|
||||||
|
needReadReceipt: needReadReceipt,
|
||||||
|
cloudCustomData: cloudCustomData,
|
||||||
|
localCustomData: localCustomData,
|
||||||
messageInfo: messageInfo,
|
messageInfo: messageInfo,
|
||||||
convType: convType,
|
convType: convType,
|
||||||
convID: (convType == ConvType.group ? groupID : userID) ?? "",
|
convID: (convType == ConvType.group ? groupID : userID) ?? "",
|
||||||
|
|
@ -127,15 +159,28 @@ class TIMUIKitChatController {
|
||||||
offlinePushInfo: offlinePushInfo);
|
offlinePushInfo: offlinePushInfo);
|
||||||
} else if (model != null) {
|
} else if (model != null) {
|
||||||
/// Sends a message to the current conversation specified on `TIMUIKitChat`. 发送到 `TIMUIKitChat` 中指定的当前对话。
|
/// Sends a message to the current conversation specified on `TIMUIKitChat`. 发送到 `TIMUIKitChat` 中指定的当前对话。
|
||||||
|
if (isNavigateToMessageListBottom) {
|
||||||
|
scrollController?.animateTo(
|
||||||
|
scrollController!.position.minScrollExtent,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
curve: Curves.ease,
|
||||||
|
);
|
||||||
|
}
|
||||||
return model!.sendMessageFromController(
|
return model!.sendMessageFromController(
|
||||||
messageInfo: messageInfo, offlinePushInfo: offlinePushInfo);
|
priority: priority,
|
||||||
|
onlineUserOnly: onlineUserOnly,
|
||||||
|
isExcludedFromUnreadCount: isExcludedFromUnreadCount,
|
||||||
|
needReadReceipt: needReadReceipt,
|
||||||
|
cloudCustomData: cloudCustomData,
|
||||||
|
localCustomData: localCustomData,
|
||||||
|
messageInfo: messageInfo,
|
||||||
|
offlinePushInfo: offlinePushInfo);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send forward message;
|
/// Send forward message;
|
||||||
/// 逐条转发
|
/// This function solely works when `TIMUIKitChatController` is specified for use within a `TIMUIKitChat`.
|
||||||
/// This method needs use with TIMUIKitChat directly or model been initialized.
|
|
||||||
sendForwardMessage({
|
sendForwardMessage({
|
||||||
required List<V2TimConversation> conversationList,
|
required List<V2TimConversation> conversationList,
|
||||||
}) async {
|
}) async {
|
||||||
|
|
@ -143,7 +188,6 @@ class TIMUIKitChatController {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send merger message;
|
/// Send merger message;
|
||||||
/// 合并转发
|
|
||||||
/// This method needs use with TIMUIKitChat directly or model been initialized.
|
/// This method needs use with TIMUIKitChat directly or model been initialized.
|
||||||
Future<V2TimValueCallback<V2TimMessage>?> sendMergerMessage({
|
Future<V2TimValueCallback<V2TimMessage>?> sendMergerMessage({
|
||||||
required List<V2TimConversation> conversationList,
|
required List<V2TimConversation> conversationList,
|
||||||
|
|
@ -158,8 +202,7 @@ class TIMUIKitChatController {
|
||||||
context: context);
|
context: context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set local custom data; returns the bool shows if succeed
|
/// Set local custom data; returns the bool shows if succeed.
|
||||||
/// 为本地消息配置额外String字段
|
|
||||||
/// Please provide `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
|
/// Please provide `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
|
||||||
Future<bool> setLocalCustomData(String msgID, String localCustomData,
|
Future<bool> setLocalCustomData(String msgID, String localCustomData,
|
||||||
[String? convID]) async {
|
[String? convID]) async {
|
||||||
|
|
@ -171,8 +214,7 @@ class TIMUIKitChatController {
|
||||||
msgID, localCustomData, conversationID);
|
msgID, localCustomData, conversationID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set local custom int; returns the bool shows if succeed
|
/// Set local custom int; returns the bool shows if succeed.
|
||||||
/// 为本地消息配置额外int字段
|
|
||||||
/// Please provide `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
|
/// Please provide `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
|
||||||
Future<bool> setLocalCustomInt(String msgID, int localCustomInt,
|
Future<bool> setLocalCustomInt(String msgID, int localCustomInt,
|
||||||
[String? convID]) async {
|
[String? convID]) async {
|
||||||
|
|
@ -185,8 +227,52 @@ class TIMUIKitChatController {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get current conversation, returns UserID or GroupID if in the chat page, returns "" if not.
|
/// Get current conversation, returns UserID or GroupID if in the chat page, returns "" if not.
|
||||||
/// 获取当前会话ID,如果在Chat页面,返回UserID or GroupID, 反之返回""
|
|
||||||
String getCurrentConversation() {
|
String getCurrentConversation() {
|
||||||
return globalChatModel.currentSelectedConv;
|
return globalChatModel.currentSelectedConv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Hide all bottom panels, including the sticker panel and the additional functions panel, on mobile devices.
|
||||||
|
/// This function solely works when `TIMUIKitChatController` is specified for use within a `TIMUIKitChat`.
|
||||||
|
void hideAllBottomPanelOnMobile() {
|
||||||
|
textFieldController?.hideAllPanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mention or @ other members in a group manually.
|
||||||
|
/// This function solely works when `TIMUIKitChatController` is specified for use within a `TIMUIKitChat`.
|
||||||
|
void mentionOtherMemberInGroup(
|
||||||
|
{required String showNameInMessage, required String userID}) {
|
||||||
|
textFieldController?.longPressToAt(showNameInMessage, userID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the content within the message input text field.
|
||||||
|
/// This function solely works when `TIMUIKitChatController` is specified for use within a `TIMUIKitChat`.
|
||||||
|
void setInputTextField(String text) {
|
||||||
|
textFieldController?.setTextField(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the list of group members of current group chat based on the provided keyword.
|
||||||
|
///
|
||||||
|
/// This method filters the group members based on the given keyword. If the keyword is not provided,
|
||||||
|
/// it returns the entire list of group members. The filtering is performed by checking if the keyword
|
||||||
|
/// is contained within the userID, nickName, or friendRemark properties of each group member.
|
||||||
|
///
|
||||||
|
/// [keyword] (optional) - The keyword to filter the group members. If not provided, the entire list of group members is returned.
|
||||||
|
/// This function solely works when `TIMUIKitChatController` is specified for use within a `TIMUIKitChat`.
|
||||||
|
List<V2TimGroupMemberFullInfo> getGroupMemberList({String? keyword}) {
|
||||||
|
final List<V2TimGroupMemberFullInfo> memberList =
|
||||||
|
(model?.groupMemberList ?? [])
|
||||||
|
.whereType<V2TimGroupMemberFullInfo>()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return TencentUtils.checkString(keyword) == null
|
||||||
|
? memberList
|
||||||
|
: memberList.where((e) {
|
||||||
|
final userID = e.userID;
|
||||||
|
final nickName = e.nickName ?? "";
|
||||||
|
final friendRemark = e.friendRemark ?? "";
|
||||||
|
return userID.contains(keyword!) ||
|
||||||
|
nickName.contains(keyword) ||
|
||||||
|
friendRemark.contains(keyword);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -278,7 +278,7 @@ class Permissions {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
_entry = entry;
|
_entry = entry;
|
||||||
Overlay.of(context)?.insert(entry);
|
Overlay.of(context).insert(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<bool?> showPermissionConfirmDialog(BuildContext context, value,
|
static Future<bool?> showPermissionConfirmDialog(BuildContext context, value,
|
||||||
|
|
|
||||||
|
|
@ -101,4 +101,5 @@ class ScreenshotHelper {
|
||||||
}));
|
}));
|
||||||
return completer.future;
|
return completer.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
// ignore_for_file: constant_identifier_names
|
// ignore_for_file: constant_identifier_names
|
||||||
|
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
||||||
|
|
||||||
enum DeviceType { Desktop, Mobile }
|
enum DeviceType { Desktop, Mobile }
|
||||||
|
|
||||||
|
|
@ -12,29 +15,48 @@ class FormFactor {
|
||||||
class TUIKitScreenUtils {
|
class TUIKitScreenUtils {
|
||||||
static DeviceType? deviceType;
|
static DeviceType? deviceType;
|
||||||
|
|
||||||
|
/// Although specifying the `BuildContext` is optional, providing it can prevent layout issues when this widget renders immediately after the app launch.
|
||||||
|
/// If this widget needs to be used at the moment the app launches, it's recommended to provide the `BuildContext` here.
|
||||||
static DeviceType getFormFactor([BuildContext? context]) {
|
static DeviceType getFormFactor([BuildContext? context]) {
|
||||||
if (deviceType != null) return deviceType!;
|
if (deviceType != null) return deviceType!;
|
||||||
|
|
||||||
if(context != null){
|
if (PlatformUtils().isWeb) {
|
||||||
double deviceWidth = MediaQuery.of(context).size.width;
|
final win = WidgetsBinding.instance.platformDispatcher.views.first;
|
||||||
double deviceHeight = MediaQuery.of(context).size.height;
|
final size = win.physicalSize;
|
||||||
|
final screenWidth = size.width / win.devicePixelRatio;
|
||||||
|
final screenHeight = size.height / win.devicePixelRatio;
|
||||||
|
|
||||||
if (deviceWidth > FormFactor.desktop || deviceWidth > deviceHeight * 1.1) {
|
final diagonalInInches =
|
||||||
deviceType = DeviceType.Desktop;
|
sqrt(pow(screenWidth, 2) + pow(screenHeight, 2)) / 96.0;
|
||||||
} else if (deviceWidth > FormFactor.handset) {
|
|
||||||
deviceType = DeviceType.Mobile;
|
deviceType = diagonalInInches < 8.0 ? DeviceType.Mobile : DeviceType.Desktop;
|
||||||
}
|
|
||||||
return deviceType ?? DeviceType.Mobile;
|
return deviceType ?? DeviceType.Mobile;
|
||||||
}else{
|
}else{
|
||||||
return DeviceType.Mobile;
|
if(context != null){
|
||||||
|
double deviceWidth = MediaQuery.of(context).size.width;
|
||||||
|
double deviceHeight = MediaQuery.of(context).size.height;
|
||||||
|
|
||||||
|
if (deviceWidth > FormFactor.desktop || deviceWidth > deviceHeight * 1.1) {
|
||||||
|
deviceType = DeviceType.Desktop;
|
||||||
|
} else if (deviceWidth > FormFactor.handset) {
|
||||||
|
deviceType = DeviceType.Mobile;
|
||||||
|
}
|
||||||
|
return deviceType ?? DeviceType.Mobile;
|
||||||
|
}else{
|
||||||
|
return DeviceType.Mobile;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Widget getDeviceWidget({
|
static Widget getDeviceWidget({
|
||||||
|
/// Although specifying the `BuildContext` is optional, providing it can prevent layout issues when this widget renders immediately after the app launch.
|
||||||
|
/// If this widget needs to be used at the moment the app launches, it's recommended to provide the `BuildContext` here.
|
||||||
|
BuildContext? context,
|
||||||
required Widget defaultWidget,
|
required Widget defaultWidget,
|
||||||
Widget? desktopWidget,
|
Widget? desktopWidget,
|
||||||
Widget? mobileWidget,
|
Widget? mobileWidget,
|
||||||
}) {
|
}) {
|
||||||
|
deviceType ??= getFormFactor(context);
|
||||||
if (deviceType == DeviceType.Desktop) return desktopWidget ?? defaultWidget;
|
if (deviceType == DeviceType.Desktop) return desktopWidget ?? defaultWidget;
|
||||||
return mobileWidget ?? defaultWidget;
|
return mobileWidget ?? defaultWidget;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ class TIMUIKitAddFriend extends StatefulWidget {
|
||||||
/// The life cycle hooks for adding friends and contact business logic
|
/// The life cycle hooks for adding friends and contact business logic
|
||||||
final AddFriendLifeCycle? lifeCycle;
|
final AddFriendLifeCycle? lifeCycle;
|
||||||
|
|
||||||
|
/// The callback function to close the widget upon completion by the parent component.
|
||||||
final VoidCallback? closeFunc;
|
final VoidCallback? closeFunc;
|
||||||
|
|
||||||
const TIMUIKitAddFriend(
|
const TIMUIKitAddFriend(
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ class _SendApplicationState extends TIMUIKitState<SendApplication> {
|
||||||
"";
|
"";
|
||||||
final option2 = widget.friendInfo.selfSignature ?? "";
|
final option2 = widget.friendInfo.selfSignature ?? "";
|
||||||
|
|
||||||
Widget sendApplicationBody(){
|
Widget sendApplicationBody() {
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
@ -79,7 +79,7 @@ class _SendApplicationState extends TIMUIKitState<SendApplication> {
|
||||||
Text(
|
Text(
|
||||||
showName,
|
showName,
|
||||||
style:
|
style:
|
||||||
TextStyle(color: theme.darkTextColor, fontSize: 18),
|
TextStyle(color: theme.darkTextColor, fontSize: 18),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 4,
|
height: 4,
|
||||||
|
|
@ -87,17 +87,18 @@ class _SendApplicationState extends TIMUIKitState<SendApplication> {
|
||||||
Text(
|
Text(
|
||||||
"ID: $userID",
|
"ID: $userID",
|
||||||
style:
|
style:
|
||||||
TextStyle(fontSize: 13, color: theme.weakTextColor),
|
TextStyle(fontSize: 13, color: theme.weakTextColor),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 4,
|
height: 4,
|
||||||
),
|
),
|
||||||
if(TencentUtils.checkString(option2) != null)Text(
|
if (TencentUtils.checkString(option2) != null)
|
||||||
TIM_t_para("个性签名: {{option2}}", "个性签名: $option2")(
|
Text(
|
||||||
option2: option2),
|
TIM_t_para("个性签名: {{option2}}", "个性签名: $option2")(
|
||||||
style:
|
option2: option2),
|
||||||
TextStyle(fontSize: 13, color: theme.weakTextColor),
|
style: TextStyle(
|
||||||
),
|
fontSize: 13, color: theme.weakTextColor),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
@ -168,19 +169,19 @@ class _SendApplicationState extends TIMUIKitState<SendApplication> {
|
||||||
Container(
|
Container(
|
||||||
color: theme.white,
|
color: theme.white,
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
TIM_t("分组"),
|
TIM_t("分组"),
|
||||||
style:
|
style:
|
||||||
TextStyle(color: theme.darkTextColor, fontSize: 16),
|
TextStyle(color: theme.darkTextColor, fontSize: 16),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
TIM_t("我的好友"),
|
TIM_t("我的好友"),
|
||||||
style:
|
style:
|
||||||
TextStyle(color: theme.darkTextColor, fontSize: 16),
|
TextStyle(color: theme.darkTextColor, fontSize: 16),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -197,7 +198,7 @@ class _SendApplicationState extends TIMUIKitState<SendApplication> {
|
||||||
|
|
||||||
if (widget.lifeCycle?.shouldAddFriend != null &&
|
if (widget.lifeCycle?.shouldAddFriend != null &&
|
||||||
await widget.lifeCycle!.shouldAddFriend(userID, remark,
|
await widget.lifeCycle!.shouldAddFriend(userID, remark,
|
||||||
friendGroup, addWording, context) ==
|
friendGroup, addWording, context) ==
|
||||||
false) {
|
false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -234,25 +235,25 @@ class _SendApplicationState extends TIMUIKitState<SendApplication> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
desktopWidget: Container(
|
context: context,
|
||||||
padding: const EdgeInsets.only(top: 10),
|
desktopWidget: Container(
|
||||||
color: theme.weakBackgroundColor,
|
padding: const EdgeInsets.only(top: 10),
|
||||||
child: sendApplicationBody(),
|
color: theme.weakBackgroundColor,
|
||||||
),
|
child: sendApplicationBody(),
|
||||||
|
),
|
||||||
defaultWidget: Scaffold(
|
defaultWidget: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
TIM_t("添加好友"),
|
TIM_t("添加好友"),
|
||||||
style: TextStyle(color: theme.appbarTextColor, fontSize: 17),
|
style: TextStyle(color: theme.appbarTextColor, fontSize: 17),
|
||||||
),
|
),
|
||||||
shadowColor: theme.white,
|
shadowColor: theme.white,
|
||||||
backgroundColor: theme.appbarBgColor ??
|
backgroundColor: theme.appbarBgColor ?? theme.primaryColor,
|
||||||
theme.primaryColor,
|
iconTheme: IconThemeData(
|
||||||
iconTheme: IconThemeData(
|
color: theme.appbarTextColor,
|
||||||
color: theme.appbarTextColor,
|
),
|
||||||
),
|
),
|
||||||
),
|
body: sendApplicationBody(),
|
||||||
body: sendApplicationBody(),
|
));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
||||||
class SendJoinGroupApplication extends StatefulWidget {
|
class SendJoinGroupApplication extends StatefulWidget {
|
||||||
final V2TimGroupInfo groupInfo;
|
final V2TimGroupInfo groupInfo;
|
||||||
final AddGroupLifeCycle? lifeCycle;
|
final AddGroupLifeCycle? lifeCycle;
|
||||||
|
|
||||||
const SendJoinGroupApplication(
|
const SendJoinGroupApplication(
|
||||||
{Key? key, required this.groupInfo, this.lifeCycle})
|
{Key? key, required this.groupInfo, this.lifeCycle})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
@ -76,7 +77,7 @@ class _SendJoinGroupApplicationState
|
||||||
final showName = widget.groupInfo.groupName ?? groupID;
|
final showName = widget.groupInfo.groupName ?? groupID;
|
||||||
final option1 = _getGroupType(widget.groupInfo.groupType);
|
final option1 = _getGroupType(widget.groupInfo.groupType);
|
||||||
|
|
||||||
Widget sendGroupApplicationBody(){
|
Widget sendGroupApplicationBody() {
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
@ -100,7 +101,7 @@ class _SendJoinGroupApplicationState
|
||||||
Text(
|
Text(
|
||||||
showName,
|
showName,
|
||||||
style:
|
style:
|
||||||
TextStyle(color: theme.darkTextColor, fontSize: 18),
|
TextStyle(color: theme.darkTextColor, fontSize: 18),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 4,
|
height: 4,
|
||||||
|
|
@ -108,7 +109,7 @@ class _SendJoinGroupApplicationState
|
||||||
Text(
|
Text(
|
||||||
"ID: $groupID",
|
"ID: $groupID",
|
||||||
style:
|
style:
|
||||||
TextStyle(fontSize: 13, color: theme.weakTextColor),
|
TextStyle(fontSize: 13, color: theme.weakTextColor),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 4,
|
height: 4,
|
||||||
|
|
@ -117,7 +118,7 @@ class _SendJoinGroupApplicationState
|
||||||
TIM_t_para("群类型: {{option1}}", "群类型: $option1")(
|
TIM_t_para("群类型: {{option1}}", "群类型: $option1")(
|
||||||
option1: option1),
|
option1: option1),
|
||||||
style:
|
style:
|
||||||
TextStyle(fontSize: 12, color: theme.weakTextColor),
|
TextStyle(fontSize: 12, color: theme.weakTextColor),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
@ -168,23 +169,22 @@ class _SendJoinGroupApplicationState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
desktopWidget: sendGroupApplicationBody(),
|
context: context,
|
||||||
|
desktopWidget: sendGroupApplicationBody(),
|
||||||
defaultWidget: Scaffold(
|
defaultWidget: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
TIM_t("进群申请"),
|
TIM_t("进群申请"),
|
||||||
style: TextStyle(color: theme.appbarTextColor, fontSize: 17),
|
style: TextStyle(color: theme.appbarTextColor, fontSize: 17),
|
||||||
),
|
),
|
||||||
shadowColor: theme.white,
|
shadowColor: theme.white,
|
||||||
backgroundColor: theme.appbarBgColor ??
|
backgroundColor: theme.appbarBgColor ?? theme.primaryColor,
|
||||||
theme.primaryColor,
|
iconTheme: IconThemeData(
|
||||||
iconTheme: IconThemeData(
|
color: theme.appbarTextColor,
|
||||||
color: theme.appbarTextColor,
|
),
|
||||||
),
|
),
|
||||||
),
|
body: sendGroupApplicationBody(),
|
||||||
body: sendGroupApplicationBody(),
|
));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@ class _TIMUIKitBlackListState extends TIMUIKitState<TIMUIKitBlackList> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
desktopWidget: itemWidget(),
|
desktopWidget: itemWidget(),
|
||||||
defaultWidget: Slidable(
|
defaultWidget: Slidable(
|
||||||
endActionPane: ActionPane(motion: const DrawerMotion(), children: [
|
endActionPane: ActionPane(motion: const DrawerMotion(), children: [
|
||||||
|
|
|
||||||
|
|
@ -76,38 +76,38 @@ class MessageHoverControlItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessageItemBuilder {
|
class MessageItemBuilder {
|
||||||
/// text message builder
|
/// text message builder, returns null means using default widget.
|
||||||
final MessageItemContent? textMessageItemBuilder;
|
final MessageItemContent? textMessageItemBuilder;
|
||||||
|
|
||||||
/// text message builder for reply message
|
/// text message builder for reply message, returns null means using default widget.
|
||||||
final MessageItemContent? textReplyMessageItemBuilder;
|
final MessageItemContent? textReplyMessageItemBuilder;
|
||||||
|
|
||||||
/// custom message builder
|
/// custom message builder, returns null means using default widget.
|
||||||
final MessageItemContent? customMessageItemBuilder;
|
final MessageItemContent? customMessageItemBuilder;
|
||||||
|
|
||||||
/// image message builder
|
/// image message builder, returns null means using default widget.
|
||||||
final MessageItemContent? imageMessageItemBuilder;
|
final MessageItemContent? imageMessageItemBuilder;
|
||||||
|
|
||||||
/// sound message builder
|
/// sound message builder, returns null means using default widget.
|
||||||
final MessageItemContent? soundMessageItemBuilder;
|
final MessageItemContent? soundMessageItemBuilder;
|
||||||
|
|
||||||
/// video message builder
|
/// video message builder, returns null means using default widget.
|
||||||
final MessageItemContent? videoMessageItemBuilder;
|
final MessageItemContent? videoMessageItemBuilder;
|
||||||
|
|
||||||
/// file message builder
|
/// file message builder, returns null means using default widget.
|
||||||
final MessageItemContent? fileMessageItemBuilder;
|
final MessageItemContent? fileMessageItemBuilder;
|
||||||
|
|
||||||
/// location message (LBS) item builder;
|
/// location message (LBS) item builder;
|
||||||
/// recommend to use our LBS plug-in: https://pub.dev/packages/tim_ui_kit_lbs_plugin
|
/// recommend to use our LBS plug-in: https://pub.dev/packages/tim_ui_kit_lbs_plugin
|
||||||
final MessageItemContent? locationMessageItemBuilder;
|
final MessageItemContent? locationMessageItemBuilder;
|
||||||
|
|
||||||
/// face message, like emoji, message builder
|
/// face message, like emoji, message builder, returns null means using default widget.
|
||||||
final MessageItemContent? faceMessageItemBuilder;
|
final MessageItemContent? faceMessageItemBuilder;
|
||||||
|
|
||||||
/// group tips message builder
|
/// group tips message builder, returns null means using default widget.
|
||||||
final MessageItemContent? groupTipsMessageItemBuilder;
|
final MessageItemContent? groupTipsMessageItemBuilder;
|
||||||
|
|
||||||
/// merger message builder
|
/// merger message builder, returns null means using default widget.
|
||||||
final MessageItemContent? mergerMessageItemBuilder;
|
final MessageItemContent? mergerMessageItemBuilder;
|
||||||
|
|
||||||
/// The builder for the whole message line, expect for those message type without avatar and nickname.
|
/// The builder for the whole message line, expect for those message type without avatar and nickname.
|
||||||
|
|
@ -148,15 +148,32 @@ class MessageToolTipItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ToolTipsConfig {
|
class ToolTipsConfig {
|
||||||
|
/// Whether to show the reply to a message option.
|
||||||
final bool showReplyMessage;
|
final bool showReplyMessage;
|
||||||
|
|
||||||
|
/// Whether to show the multiple-choice option for messages.
|
||||||
final bool showMultipleChoiceMessage;
|
final bool showMultipleChoiceMessage;
|
||||||
|
|
||||||
|
/// Whether to show the option to delete a message.
|
||||||
final bool showDeleteMessage;
|
final bool showDeleteMessage;
|
||||||
|
|
||||||
|
/// Whether to show the option to recall a message.
|
||||||
final bool showRecallMessage;
|
final bool showRecallMessage;
|
||||||
|
|
||||||
|
/// Whether to show the option to copy a message.
|
||||||
final bool showCopyMessage;
|
final bool showCopyMessage;
|
||||||
|
|
||||||
|
/// Whether to show the option to forward a message.
|
||||||
final bool showForwardMessage;
|
final bool showForwardMessage;
|
||||||
|
|
||||||
|
/// Whether to show the option to translate a text message. This module is not available by default. Please contact your Tencent Cloud sales representative or customer service team to enable this feature.
|
||||||
final bool showTranslation;
|
final bool showTranslation;
|
||||||
|
|
||||||
|
/// A builder for additional custom items. We recommend using `additionalMessageToolTips` instead of this field since version 2.0, as you only need to provide the data rather than the whole widget. This makes usage easier and you don't need to worry about the UI display.
|
||||||
final Widget? Function(V2TimMessage message, Function() closeTooltip,
|
final Widget? Function(V2TimMessage message, Function() closeTooltip,
|
||||||
[Key? key, BuildContext? context])? additionalItemBuilder;
|
[Key? key, BuildContext? context])? additionalItemBuilder;
|
||||||
|
|
||||||
|
/// A list of additional message tooltip menu items, provided with the data only. We recommend using this field instead of the previous `additionalItemBuilder`.
|
||||||
List<MessageToolTipItem> Function(
|
List<MessageToolTipItem> Function(
|
||||||
V2TimMessage message, Function() closeTooltip)? additionalMessageToolTips;
|
V2TimMessage message, Function() closeTooltip)? additionalMessageToolTips;
|
||||||
|
|
||||||
|
|
@ -169,8 +186,9 @@ class ToolTipsConfig {
|
||||||
this.showCopyMessage = true,
|
this.showCopyMessage = true,
|
||||||
this.showForwardMessage = true,
|
this.showForwardMessage = true,
|
||||||
this.additionalMessageToolTips,
|
this.additionalMessageToolTips,
|
||||||
@Deprecated("Please use `additionalMessageToolTips` instead. You are now only expected to specify the data, rather than providing a whole widget. This makes usage easier, as you no longer need to worry about the UI display.")
|
@Deprecated(
|
||||||
this.additionalItemBuilder});
|
"Please use `additionalMessageToolTips` instead. You are now only expected to specify the data, rather than providing a whole widget. This makes usage easier, as you no longer need to worry about the UI display.")
|
||||||
|
this.additionalItemBuilder});
|
||||||
}
|
}
|
||||||
|
|
||||||
class TIMUIKitHistoryMessageListItem extends StatefulWidget {
|
class TIMUIKitHistoryMessageListItem extends StatefulWidget {
|
||||||
|
|
@ -181,6 +199,10 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
|
||||||
final void Function(String userID, TapDownDetails tapDetails)?
|
final void Function(String userID, TapDownDetails tapDetails)?
|
||||||
onTapForOthersPortrait;
|
onTapForOthersPortrait;
|
||||||
|
|
||||||
|
/// secondary tap remote user avatar callback function
|
||||||
|
final void Function(String userID, TapDownDetails tapDetails)?
|
||||||
|
onSecondaryTapForOthersPortrait;
|
||||||
|
|
||||||
/// the function use for reply message, when click replied message can scroll to it.
|
/// the function use for reply message, when click replied message can scroll to it.
|
||||||
final Function? onScrollToIndex;
|
final Function? onScrollToIndex;
|
||||||
|
|
||||||
|
|
@ -262,8 +284,9 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
|
||||||
const TIMUIKitHistoryMessageListItem(
|
const TIMUIKitHistoryMessageListItem(
|
||||||
{Key? key,
|
{Key? key,
|
||||||
required this.message,
|
required this.message,
|
||||||
@Deprecated("Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
|
@Deprecated(
|
||||||
this.showNickName = false,
|
"Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
|
||||||
|
this.showNickName = false,
|
||||||
this.onScrollToIndex,
|
this.onScrollToIndex,
|
||||||
this.onScrollToIndexBegin,
|
this.onScrollToIndexBegin,
|
||||||
this.onTapForOthersPortrait,
|
this.onTapForOthersPortrait,
|
||||||
|
|
@ -287,7 +310,8 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
|
||||||
this.bottomRowBuilder,
|
this.bottomRowBuilder,
|
||||||
this.isUseDefaultEmoji = false,
|
this.isUseDefaultEmoji = false,
|
||||||
this.customEmojiStickerList = const [],
|
this.customEmojiStickerList = const [],
|
||||||
this.textFieldController})
|
this.textFieldController,
|
||||||
|
this.onSecondaryTapForOthersPortrait})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -386,192 +410,206 @@ class _TIMUIKItHistoryMessageListItemState
|
||||||
|
|
||||||
switch (msgType) {
|
switch (msgType) {
|
||||||
case MessageElemType.V2TIM_ELEM_TYPE_CUSTOM:
|
case MessageElemType.V2TIM_ELEM_TYPE_CUSTOM:
|
||||||
if (messageItemBuilder?.customMessageItemBuilder != null) {
|
final customWidget =
|
||||||
return messageItemBuilder!.customMessageItemBuilder!(
|
messageItemBuilder?.customMessageItemBuilder != null
|
||||||
messageItem,
|
? messageItemBuilder!.customMessageItemBuilder!(
|
||||||
isShowJump,
|
messageItem,
|
||||||
() => model.jumpMsgID = "",
|
isShowJump,
|
||||||
)!;
|
() => model.jumpMsgID = "",
|
||||||
}
|
)
|
||||||
return TIMUIKitCustomElem(
|
: null;
|
||||||
message: messageItem,
|
return customWidget ??
|
||||||
customElem: messageItem.customElem,
|
TIMUIKitCustomElem(
|
||||||
isFromSelf: isFromSelf,
|
message: messageItem,
|
||||||
messageBackgroundColor: widget.themeData?.messageBackgroundColor,
|
customElem: messageItem.customElem,
|
||||||
messageBorderRadius: widget.themeData?.messageBorderRadius,
|
isFromSelf: isFromSelf,
|
||||||
messageFontStyle: widget.themeData?.messageTextStyle,
|
messageBackgroundColor: widget.themeData?.messageBackgroundColor,
|
||||||
textPadding: widget.textPadding,
|
messageBorderRadius: widget.themeData?.messageBorderRadius,
|
||||||
isShowMessageReaction: widget.isUseMessageReaction,
|
messageFontStyle: widget.themeData?.messageTextStyle,
|
||||||
);
|
textPadding: widget.textPadding,
|
||||||
|
isShowMessageReaction: widget.isUseMessageReaction,
|
||||||
|
);
|
||||||
case MessageElemType.V2TIM_ELEM_TYPE_SOUND:
|
case MessageElemType.V2TIM_ELEM_TYPE_SOUND:
|
||||||
if (messageItemBuilder?.soundMessageItemBuilder != null) {
|
final customWidget = messageItemBuilder?.soundMessageItemBuilder != null
|
||||||
return messageItemBuilder!.soundMessageItemBuilder!(
|
? messageItemBuilder!.soundMessageItemBuilder!(
|
||||||
messageItem,
|
messageItem,
|
||||||
isShowJump,
|
isShowJump,
|
||||||
clearJump,
|
() => model.jumpMsgID = "",
|
||||||
)!;
|
)
|
||||||
}
|
: null;
|
||||||
return TIMUIKitSoundElem(
|
return customWidget ??
|
||||||
chatModel: model,
|
TIMUIKitSoundElem(
|
||||||
message: messageItem,
|
chatModel: model,
|
||||||
soundElem: messageItem.soundElem!,
|
message: messageItem,
|
||||||
msgID: messageItem.msgID ?? "",
|
soundElem: messageItem.soundElem!,
|
||||||
isFromSelf: messageItem.isSelf ?? true,
|
msgID: messageItem.msgID ?? "",
|
||||||
clearJump: clearJump,
|
isFromSelf: messageItem.isSelf ?? true,
|
||||||
isShowJump: isShowJump,
|
clearJump: clearJump,
|
||||||
localCustomInt: messageItem.localCustomInt,
|
isShowJump: isShowJump,
|
||||||
borderRadius: widget.themeData?.messageBorderRadius,
|
localCustomInt: messageItem.localCustomInt,
|
||||||
fontStyle: widget.themeData?.messageTextStyle,
|
borderRadius: widget.themeData?.messageBorderRadius,
|
||||||
backgroundColor: widget.themeData?.messageBackgroundColor,
|
fontStyle: widget.themeData?.messageTextStyle,
|
||||||
textPadding: widget.textPadding,
|
backgroundColor: widget.themeData?.messageBackgroundColor,
|
||||||
isShowMessageReaction: widget.isUseMessageReaction,
|
textPadding: widget.textPadding,
|
||||||
);
|
isShowMessageReaction: widget.isUseMessageReaction,
|
||||||
|
);
|
||||||
case MessageElemType.V2TIM_ELEM_TYPE_TEXT:
|
case MessageElemType.V2TIM_ELEM_TYPE_TEXT:
|
||||||
if (isReplyMessage(messageItem)) {
|
if (isReplyMessage(messageItem)) {
|
||||||
if (messageItemBuilder?.textReplyMessageItemBuilder != null) {
|
final customWidget =
|
||||||
return messageItemBuilder!.textReplyMessageItemBuilder!(
|
messageItemBuilder?.textReplyMessageItemBuilder != null
|
||||||
messageItem,
|
? messageItemBuilder!.textReplyMessageItemBuilder!(
|
||||||
isShowJump,
|
messageItem,
|
||||||
clearJump,
|
isShowJump,
|
||||||
)!;
|
() => model.jumpMsgID = "",
|
||||||
}
|
)
|
||||||
return TIMUIKitReplyElem(
|
: null;
|
||||||
message: messageItem,
|
return customWidget ??
|
||||||
clearJump: clearJump,
|
TIMUIKitReplyElem(
|
||||||
isShowJump: isShowJump,
|
message: messageItem,
|
||||||
scrollToIndex: widget.onScrollToIndex ?? () {},
|
clearJump: clearJump,
|
||||||
borderRadius: widget.themeData?.messageBorderRadius,
|
isShowJump: isShowJump,
|
||||||
fontStyle: widget.themeData?.messageTextStyle,
|
scrollToIndex: widget.onScrollToIndex ?? () {},
|
||||||
backgroundColor: widget.themeData?.messageBackgroundColor,
|
borderRadius: widget.themeData?.messageBorderRadius,
|
||||||
textPadding: widget.textPadding,
|
fontStyle: widget.themeData?.messageTextStyle,
|
||||||
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
backgroundColor: widget.themeData?.messageBackgroundColor,
|
||||||
customEmojiStickerList: widget.customEmojiStickerList,
|
textPadding: widget.textPadding,
|
||||||
chatModel: model,
|
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
||||||
isShowMessageReaction: widget.isUseMessageReaction,
|
customEmojiStickerList: widget.customEmojiStickerList,
|
||||||
);
|
chatModel: model,
|
||||||
|
isShowMessageReaction: widget.isUseMessageReaction,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (messageItemBuilder?.textMessageItemBuilder != null) {
|
final customWidget = messageItemBuilder?.textMessageItemBuilder != null
|
||||||
return messageItemBuilder!.textMessageItemBuilder!(
|
? messageItemBuilder!.textMessageItemBuilder!(
|
||||||
messageItem,
|
messageItem,
|
||||||
isShowJump,
|
isShowJump,
|
||||||
clearJump,
|
() => model.jumpMsgID = "",
|
||||||
)!;
|
)
|
||||||
}
|
: null;
|
||||||
return TIMUIKitTextElem(
|
return customWidget ??
|
||||||
chatModel: model,
|
TIMUIKitTextElem(
|
||||||
message: messageItem,
|
chatModel: model,
|
||||||
isFromSelf: messageItem.isSelf ?? true,
|
message: messageItem,
|
||||||
clearJump: clearJump,
|
isFromSelf: messageItem.isSelf ?? true,
|
||||||
isShowJump: isShowJump,
|
clearJump: clearJump,
|
||||||
borderRadius: widget.themeData?.messageBorderRadius,
|
isShowJump: isShowJump,
|
||||||
fontStyle: widget.themeData?.messageTextStyle,
|
borderRadius: widget.themeData?.messageBorderRadius,
|
||||||
backgroundColor: widget.themeData?.messageBackgroundColor,
|
fontStyle: widget.themeData?.messageTextStyle,
|
||||||
textPadding: widget.textPadding,
|
backgroundColor: widget.themeData?.messageBackgroundColor,
|
||||||
isShowMessageReaction: widget.isUseMessageReaction,
|
textPadding: widget.textPadding,
|
||||||
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
isShowMessageReaction: widget.isUseMessageReaction,
|
||||||
customEmojiStickerList: widget.customEmojiStickerList,
|
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
||||||
);
|
customEmojiStickerList: widget.customEmojiStickerList,
|
||||||
|
);
|
||||||
case MessageElemType.V2TIM_ELEM_TYPE_FACE:
|
case MessageElemType.V2TIM_ELEM_TYPE_FACE:
|
||||||
if (messageItemBuilder?.faceMessageItemBuilder != null) {
|
final customWidget = messageItemBuilder?.faceMessageItemBuilder != null
|
||||||
return messageItemBuilder!.faceMessageItemBuilder!(
|
? messageItemBuilder!.faceMessageItemBuilder!(
|
||||||
messageItem,
|
messageItem,
|
||||||
isShowJump,
|
isShowJump,
|
||||||
clearJump,
|
() => model.jumpMsgID = "",
|
||||||
)!;
|
)
|
||||||
}
|
: null;
|
||||||
return TIMUIKitFaceElem(
|
return customWidget ??
|
||||||
model: model,
|
TIMUIKitFaceElem(
|
||||||
path: messageItem.faceElem!.data ?? "",
|
model: model,
|
||||||
clearJump: clearJump,
|
path: messageItem.faceElem!.data ?? "",
|
||||||
isShowJump: isShowJump,
|
clearJump: clearJump,
|
||||||
message: messageItem,
|
isShowJump: isShowJump,
|
||||||
isShowMessageReaction: widget.isUseMessageReaction,
|
message: messageItem,
|
||||||
);
|
isShowMessageReaction: widget.isUseMessageReaction,
|
||||||
|
);
|
||||||
case MessageElemType.V2TIM_ELEM_TYPE_FILE:
|
case MessageElemType.V2TIM_ELEM_TYPE_FILE:
|
||||||
if (messageItemBuilder?.fileMessageItemBuilder != null) {
|
final customWidget = messageItemBuilder?.fileMessageItemBuilder != null
|
||||||
return messageItemBuilder!.fileMessageItemBuilder!(
|
? messageItemBuilder!.fileMessageItemBuilder!(
|
||||||
messageItem,
|
messageItem,
|
||||||
isShowJump,
|
isShowJump,
|
||||||
clearJump,
|
() => model.jumpMsgID = "",
|
||||||
)!;
|
)
|
||||||
}
|
: null;
|
||||||
return TIMUIKitFileElem(
|
return customWidget ??
|
||||||
chatModel: model,
|
TIMUIKitFileElem(
|
||||||
message: messageItem,
|
chatModel: model,
|
||||||
messageID: messageItem.msgID,
|
message: messageItem,
|
||||||
fileElem: messageItem.fileElem,
|
messageID: messageItem.msgID,
|
||||||
isSelf: messageItem.isSelf ?? true,
|
fileElem: messageItem.fileElem,
|
||||||
clearJump: clearJump,
|
isSelf: messageItem.isSelf ?? true,
|
||||||
isShowJump: isShowJump,
|
clearJump: clearJump,
|
||||||
isShowMessageReaction: widget.isUseMessageReaction,
|
isShowJump: isShowJump,
|
||||||
);
|
isShowMessageReaction: widget.isUseMessageReaction,
|
||||||
|
);
|
||||||
case MessageElemType.V2TIM_ELEM_TYPE_GROUP_TIPS:
|
case MessageElemType.V2TIM_ELEM_TYPE_GROUP_TIPS:
|
||||||
if (messageItemBuilder?.groupTipsMessageItemBuilder != null) {
|
final customWidget =
|
||||||
return messageItemBuilder!.groupTipsMessageItemBuilder!(
|
messageItemBuilder?.groupTipsMessageItemBuilder != null
|
||||||
messageItem,
|
? messageItemBuilder!.groupTipsMessageItemBuilder!(
|
||||||
isShowJump,
|
messageItem,
|
||||||
clearJump,
|
isShowJump,
|
||||||
)!;
|
() => model.jumpMsgID = "",
|
||||||
}
|
)
|
||||||
return Text(TIM_t("[群系统消息]"));
|
: null;
|
||||||
|
return customWidget ?? Text(TIM_t("[群系统消息]"));
|
||||||
case MessageElemType.V2TIM_ELEM_TYPE_IMAGE:
|
case MessageElemType.V2TIM_ELEM_TYPE_IMAGE:
|
||||||
if (messageItemBuilder?.imageMessageItemBuilder != null) {
|
final customWidget = messageItemBuilder?.imageMessageItemBuilder != null
|
||||||
return messageItemBuilder!.imageMessageItemBuilder!(
|
? messageItemBuilder!.imageMessageItemBuilder!(
|
||||||
messageItem,
|
messageItem,
|
||||||
isShowJump,
|
isShowJump,
|
||||||
clearJump,
|
() => model.jumpMsgID = "",
|
||||||
)!;
|
)
|
||||||
}
|
: null;
|
||||||
return TIMUIKitImageElem(
|
return customWidget ??
|
||||||
clearJump: clearJump,
|
TIMUIKitImageElem(
|
||||||
isShowJump: isShowJump,
|
clearJump: clearJump,
|
||||||
chatModel: model,
|
isShowJump: isShowJump,
|
||||||
message: messageItem,
|
chatModel: model,
|
||||||
isShowMessageReaction: widget.isUseMessageReaction,
|
message: messageItem,
|
||||||
key: Key("${messageItem.seq}_${messageItem.timestamp}"),
|
isShowMessageReaction: widget.isUseMessageReaction,
|
||||||
);
|
key: Key("${messageItem.seq}_${messageItem.timestamp}"),
|
||||||
|
);
|
||||||
case MessageElemType.V2TIM_ELEM_TYPE_VIDEO:
|
case MessageElemType.V2TIM_ELEM_TYPE_VIDEO:
|
||||||
if (messageItemBuilder?.videoMessageItemBuilder != null) {
|
final customWidget = messageItemBuilder?.videoMessageItemBuilder != null
|
||||||
return messageItemBuilder!.videoMessageItemBuilder!(
|
? messageItemBuilder!.videoMessageItemBuilder!(
|
||||||
messageItem,
|
messageItem,
|
||||||
isShowJump,
|
isShowJump,
|
||||||
clearJump,
|
() => model.jumpMsgID = "",
|
||||||
)!;
|
)
|
||||||
}
|
: null;
|
||||||
return TIMUIKitVideoElem(
|
return customWidget ??
|
||||||
messageItem,
|
TIMUIKitVideoElem(
|
||||||
isShowJump: isShowJump,
|
messageItem,
|
||||||
chatModel: model,
|
isShowJump: isShowJump,
|
||||||
clearJump: clearJump,
|
chatModel: model,
|
||||||
isShowMessageReaction: widget.isUseMessageReaction,
|
clearJump: clearJump,
|
||||||
);
|
isShowMessageReaction: widget.isUseMessageReaction,
|
||||||
|
);
|
||||||
case MessageElemType.V2TIM_ELEM_TYPE_LOCATION:
|
case MessageElemType.V2TIM_ELEM_TYPE_LOCATION:
|
||||||
if (messageItemBuilder?.locationMessageItemBuilder != null) {
|
final customWidget =
|
||||||
return messageItemBuilder!.locationMessageItemBuilder!(
|
messageItemBuilder?.locationMessageItemBuilder != null
|
||||||
messageItem,
|
? messageItemBuilder!.locationMessageItemBuilder!(
|
||||||
isShowJump,
|
messageItem,
|
||||||
clearJump,
|
isShowJump,
|
||||||
)!;
|
() => model.jumpMsgID = "",
|
||||||
}
|
)
|
||||||
return Text(TIM_t("[位置]"));
|
: null;
|
||||||
|
return customWidget ?? Text(TIM_t("[位置]"));
|
||||||
case MessageElemType.V2TIM_ELEM_TYPE_MERGER:
|
case MessageElemType.V2TIM_ELEM_TYPE_MERGER:
|
||||||
if (messageItemBuilder?.mergerMessageItemBuilder != null) {
|
final customWidget =
|
||||||
return messageItemBuilder!.mergerMessageItemBuilder!(
|
messageItemBuilder?.mergerMessageItemBuilder != null
|
||||||
messageItem,
|
? messageItemBuilder!.mergerMessageItemBuilder!(
|
||||||
isShowJump,
|
messageItem,
|
||||||
clearJump,
|
isShowJump,
|
||||||
)!;
|
() => model.jumpMsgID = "",
|
||||||
}
|
)
|
||||||
return TIMUIKitMergerElem(
|
: null;
|
||||||
messageItemBuilder: messageItemBuilder,
|
return customWidget ??
|
||||||
model: model,
|
TIMUIKitMergerElem(
|
||||||
isShowJump: isShowJump,
|
messageItemBuilder: messageItemBuilder,
|
||||||
clearJump: clearJump,
|
model: model,
|
||||||
message: messageItem,
|
isShowJump: isShowJump,
|
||||||
isShowMessageReaction: widget.isUseMessageReaction,
|
clearJump: clearJump,
|
||||||
mergerElem: messageItem.mergerElem!,
|
message: messageItem,
|
||||||
messageID: messageItem.msgID ?? "",
|
isShowMessageReaction: widget.isUseMessageReaction,
|
||||||
isSelf: messageItem.isSelf ?? true);
|
mergerElem: messageItem.mergerElem!,
|
||||||
|
messageID: messageItem.msgID ?? "",
|
||||||
|
isSelf: messageItem.isSelf ?? true);
|
||||||
default:
|
default:
|
||||||
return Text(TIM_t("[未知消息]"));
|
return Text(TIM_t("[未知消息]"));
|
||||||
}
|
}
|
||||||
|
|
@ -941,12 +979,18 @@ class _TIMUIKItHistoryMessageListItemState
|
||||||
),
|
),
|
||||||
onClick: (_) {
|
onClick: (_) {
|
||||||
model.repliedMessage = widget.message;
|
model.repliedMessage = widget.message;
|
||||||
if (widget.allowAtUserWhenReply &&
|
final isSelf = widget.message.isSelf ?? true;
|
||||||
widget.onLongPressForOthersHeadPortrait != null &&
|
final isGroup =
|
||||||
!(widget.message.isSelf ?? true)) {
|
TencentUtils.checkString(widget.message.groupID) != null;
|
||||||
widget.onLongPressForOthersHeadPortrait!(
|
final isAtWhenReply = !isSelf &&
|
||||||
widget.message.sender, widget.message.nickName);
|
isGroup &&
|
||||||
}
|
widget.allowAtUserWhenReply &&
|
||||||
|
widget.onLongPressForOthersHeadPortrait != null;
|
||||||
|
|
||||||
|
/// If replying to a self message, do not add a at tag, only requestFocus.
|
||||||
|
widget.onLongPressForOthersHeadPortrait!(
|
||||||
|
!isAtWhenReply ? null : widget.message.sender,
|
||||||
|
!isAtWhenReply ? null : widget.message.nickName);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if ((widget.toolTipsConfig?.showForwardMessage ?? true) &&
|
if ((widget.toolTipsConfig?.showForwardMessage ?? true) &&
|
||||||
|
|
@ -1009,18 +1053,28 @@ class _TIMUIKItHistoryMessageListItemState
|
||||||
context);
|
context);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget renderHoverTipAndReadStatus(TUIChatSeparateViewModel model,
|
Widget renderHoverTipAndReadStatus(
|
||||||
bool isSelf, V2TimMessage message, bool isPeerRead, TUITheme theme) {
|
TUIChatSeparateViewModel model,
|
||||||
|
bool isSelf,
|
||||||
|
V2TimMessage message,
|
||||||
|
bool isPeerRead,
|
||||||
|
TUITheme theme,
|
||||||
|
bool isDownloadWaiting) {
|
||||||
final isDesktopScreen =
|
final isDesktopScreen =
|
||||||
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
||||||
final wideHoverTipList = getMessageHoverControlBar(model, theme);
|
final wideHoverTipList = model.chatConfig.isUseMessageHoverBarOnDesktop
|
||||||
final lastItemName = wideHoverTipList.last.name;
|
? getMessageHoverControlBar(model, theme)
|
||||||
|
: [];
|
||||||
|
final lastItemName =
|
||||||
|
wideHoverTipList.isNotEmpty ? wideHoverTipList.last.name : "";
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
if (isDesktopScreen && isShowWideToolTip)
|
if (isDesktopScreen &&
|
||||||
|
isShowWideToolTip &&
|
||||||
|
!((widget.message.elemType == 6 && isDownloadWaiting)))
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
|
@ -1188,7 +1242,8 @@ class _TIMUIKItHistoryMessageListItemState
|
||||||
children: [
|
children: [
|
||||||
if (model.isMultiSelect)
|
if (model.isMultiSelect)
|
||||||
Container(
|
Container(
|
||||||
margin: EdgeInsets.only(right: 12, top: 10, left: isSelf ? 16 : 0),
|
margin:
|
||||||
|
EdgeInsets.only(right: 12, top: 10, left: isSelf ? 16 : 0),
|
||||||
child: CheckBoxButton(
|
child: CheckBoxButton(
|
||||||
isChecked: model.multiSelectedMessageList.contains(message),
|
isChecked: model.multiSelectedMessageList.contains(message),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
|
|
@ -1264,6 +1319,26 @@ class _TIMUIKItHistoryMessageListItemState
|
||||||
message.sender ?? "", TapDownDetails());
|
message.sender ?? "", TapDownDetails());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onSecondaryTap: isDesktopScreen
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
if (widget.onSecondaryTapForOthersPortrait !=
|
||||||
|
null &&
|
||||||
|
widget.allowAvatarTap) {
|
||||||
|
widget.onSecondaryTapForOthersPortrait!(
|
||||||
|
message.sender ?? "", TapDownDetails());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSecondaryTapDown: isDesktopScreen
|
||||||
|
? (details) {
|
||||||
|
if (widget.onSecondaryTapForOthersPortrait !=
|
||||||
|
null &&
|
||||||
|
widget.allowAvatarTap) {
|
||||||
|
widget.onSecondaryTapForOthersPortrait!(
|
||||||
|
message.sender ?? "", details);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: null,
|
||||||
child: widget.userAvatarBuilder != null
|
child: widget.userAvatarBuilder != null
|
||||||
? widget.userAvatarBuilder!(context, message)
|
? widget.userAvatarBuilder!(context, message)
|
||||||
: Container(
|
: Container(
|
||||||
|
|
@ -1282,6 +1357,16 @@ class _TIMUIKItHistoryMessageListItemState
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (isSelf &&
|
||||||
|
widget.message.elemType == 6 &&
|
||||||
|
isDownloadWaiting)
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.only(top: 2),
|
||||||
|
child: LoadingAnimationWidget.threeArchedCircle(
|
||||||
|
color: theme.weakTextColor ?? Colors.grey,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
Container(
|
Container(
|
||||||
margin: widget.showAvatar
|
margin: widget.showAvatar
|
||||||
? (isSelf
|
? (isSelf
|
||||||
|
|
@ -1319,8 +1404,13 @@ class _TIMUIKItHistoryMessageListItemState
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
if (isSelf)
|
if (isSelf)
|
||||||
renderHoverTipAndReadStatus(model, isSelf,
|
renderHoverTipAndReadStatus(
|
||||||
message, isPeerRead, theme),
|
model,
|
||||||
|
isSelf,
|
||||||
|
message,
|
||||||
|
isPeerRead,
|
||||||
|
theme,
|
||||||
|
isDownloadWaiting),
|
||||||
Container(
|
Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxWidth: constraints.maxWidth * 0.77,
|
maxWidth: constraints.maxWidth * 0.77,
|
||||||
|
|
@ -1406,8 +1496,13 @@ class _TIMUIKItHistoryMessageListItemState
|
||||||
child: Icon(Icons.circle,
|
child: Icon(Icons.circle,
|
||||||
color: theme.cautionColor, size: 10)),
|
color: theme.cautionColor, size: 10)),
|
||||||
if (!isSelf)
|
if (!isSelf)
|
||||||
renderHoverTipAndReadStatus(model, isSelf,
|
renderHoverTipAndReadStatus(
|
||||||
message, isPeerRead, theme),
|
model,
|
||||||
|
isSelf,
|
||||||
|
message,
|
||||||
|
isPeerRead,
|
||||||
|
theme,
|
||||||
|
isDownloadWaiting),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (widget.bottomRowBuilder != null)
|
if (widget.bottomRowBuilder != null)
|
||||||
|
|
@ -1415,7 +1510,9 @@ class _TIMUIKItHistoryMessageListItemState
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (widget.message.elemType == 6 && isDownloadWaiting)
|
if (!isSelf &&
|
||||||
|
widget.message.elemType == 6 &&
|
||||||
|
isDownloadWaiting)
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.only(top: 24, left: 6),
|
margin: const EdgeInsets.only(top: 24, left: 6),
|
||||||
child: LoadingAnimationWidget.threeArchedCircle(
|
child: LoadingAnimationWidget.threeArchedCircle(
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,10 @@ class TIMUIKitMessageTooltipState
|
||||||
}
|
}
|
||||||
|
|
||||||
hasFile() {
|
hasFile() {
|
||||||
if (PlatformUtils().isMobile || widget.message.fileElem == null) {
|
if (PlatformUtils().isMobile ||
|
||||||
|
(widget.message.fileElem == null &&
|
||||||
|
widget.message.imageElem == null &&
|
||||||
|
widget.message.videoElem == null)) {
|
||||||
isShowOpenFile = false;
|
isShowOpenFile = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -94,26 +97,44 @@ class TIMUIKitMessageTooltipState
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (PlatformUtils().isDesktop) {
|
if (PlatformUtils().isDesktop) {
|
||||||
if (globalModal.getMessageProgress(widget.message.msgID) == 100) {
|
if (widget.message.fileElem != null) {
|
||||||
String savePath =
|
if (globalModal.getMessageProgress(widget.message.msgID) == 100) {
|
||||||
TencentUtils.checkString(widget.message.fileElem!.localUrl) ??
|
String savePath =
|
||||||
globalModal.getFileMessageLocation(widget.message.msgID);
|
TencentUtils.checkString(widget.message.fileElem!.localUrl) ??
|
||||||
|
globalModal.getFileMessageLocation(widget.message.msgID);
|
||||||
|
File f = File(savePath);
|
||||||
|
if (f.existsSync() && widget.message.msgID != null) {
|
||||||
|
filePath = savePath;
|
||||||
|
isShowOpenFile = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isShowOpenFile = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String savePath = widget.message.fileElem!.localUrl ?? '';
|
||||||
File f = File(savePath);
|
File f = File(savePath);
|
||||||
if (f.existsSync() && widget.message.msgID != null) {
|
if (f.existsSync() && widget.message.msgID != null) {
|
||||||
filePath = savePath;
|
filePath = savePath;
|
||||||
|
globalModal.setMessageProgress(widget.message.msgID!, 100);
|
||||||
|
isShowOpenFile = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (widget.message.imageElem != null) {
|
||||||
|
if (TencentUtils.checkString(
|
||||||
|
widget.message.imageElem!.imageList![0]!.localUrl) !=
|
||||||
|
null &&
|
||||||
|
File(widget.message.imageElem!.imageList![0]!.localUrl!)
|
||||||
|
.existsSync()) {
|
||||||
|
isShowOpenFile = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (widget.message.videoElem != null) {
|
||||||
|
if (TencentUtils.checkString(widget.message.videoElem!.localVideoUrl) !=
|
||||||
|
null &&
|
||||||
|
File(widget.message.videoElem!.localVideoUrl!).existsSync()) {
|
||||||
isShowOpenFile = true;
|
isShowOpenFile = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isShowOpenFile = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String savePath = widget.message.fileElem!.localUrl ?? '';
|
|
||||||
File f = File(savePath);
|
|
||||||
if (f.existsSync() && widget.message.msgID != null) {
|
|
||||||
filePath = savePath;
|
|
||||||
globalModal.setMessageProgress(widget.message.msgID!, 100);
|
|
||||||
isShowOpenFile = true;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isShowOpenFile = false;
|
isShowOpenFile = false;
|
||||||
|
|
@ -198,11 +219,6 @@ class TIMUIKitMessageTooltipState
|
||||||
id: "forwardMessage",
|
id: "forwardMessage",
|
||||||
iconImageAsset: "images/forward_message.png",
|
iconImageAsset: "images/forward_message.png",
|
||||||
onClick: () => _onTap("forwardMessage", model)),
|
onClick: () => _onTap("forwardMessage", model)),
|
||||||
MessageToolTipItem(
|
|
||||||
label: TIM_t("多选"),
|
|
||||||
id: "multiSelect",
|
|
||||||
iconImageAsset: "images/multi_message.png",
|
|
||||||
onClick: () => _onTap("multiSelect", model)),
|
|
||||||
if (shouldShowReplyAction)
|
if (shouldShowReplyAction)
|
||||||
MessageToolTipItem(
|
MessageToolTipItem(
|
||||||
label: TIM_t(model.chatConfig.isAtWhenReply ? "回复" : "引用"),
|
label: TIM_t(model.chatConfig.isAtWhenReply ? "回复" : "引用"),
|
||||||
|
|
@ -210,15 +226,20 @@ class TIMUIKitMessageTooltipState
|
||||||
iconImageAsset: "images/reply_message.png",
|
iconImageAsset: "images/reply_message.png",
|
||||||
onClick: () => _onTap("replyMessage", model)),
|
onClick: () => _onTap("replyMessage", model)),
|
||||||
MessageToolTipItem(
|
MessageToolTipItem(
|
||||||
label: TIM_t("删除"),
|
label: TIM_t("多选"),
|
||||||
id: "delete",
|
id: "multiSelect",
|
||||||
iconImageAsset: "images/delete_message.png",
|
iconImageAsset: "images/multi_message.png",
|
||||||
onClick: () => _onTap("delete", model)),
|
onClick: () => _onTap("multiSelect", model)),
|
||||||
MessageToolTipItem(
|
MessageToolTipItem(
|
||||||
label: TIM_t("翻译"),
|
label: TIM_t("翻译"),
|
||||||
id: "translate",
|
id: "translate",
|
||||||
iconImageAsset: "images/translate.png",
|
iconImageAsset: "images/translate.png",
|
||||||
onClick: () => _onTap("translate", model)),
|
onClick: () => _onTap("translate", model)),
|
||||||
|
MessageToolTipItem(
|
||||||
|
label: TIM_t("删除"),
|
||||||
|
id: "delete",
|
||||||
|
iconImageAsset: "images/delete_message.png",
|
||||||
|
onClick: () => _onTap("delete", model)),
|
||||||
if (shouldShowRevokeAction)
|
if (shouldShowRevokeAction)
|
||||||
MessageToolTipItem(
|
MessageToolTipItem(
|
||||||
label: TIM_t("撤回"),
|
label: TIM_t("撤回"),
|
||||||
|
|
@ -226,6 +247,7 @@ class TIMUIKitMessageTooltipState
|
||||||
iconImageAsset: "images/revoke_message.png",
|
iconImageAsset: "images/revoke_message.png",
|
||||||
onClick: () => _onTap("revoke", model)),
|
onClick: () => _onTap("revoke", model)),
|
||||||
];
|
];
|
||||||
|
final defaultTipsIds = defaultTipsList.map((e) => e.id);
|
||||||
List<MessageToolTipItem> defaultFormattedTipsList = defaultTipsList;
|
List<MessageToolTipItem> defaultFormattedTipsList = defaultTipsList;
|
||||||
if (tooltipsConfig != null) {
|
if (tooltipsConfig != null) {
|
||||||
defaultFormattedTipsList = defaultTipsList.where((element) {
|
defaultFormattedTipsList = defaultTipsList.where((element) {
|
||||||
|
|
@ -235,10 +257,14 @@ class TIMUIKitMessageTooltipState
|
||||||
widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_TEXT;
|
widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_TEXT;
|
||||||
}
|
}
|
||||||
if (type == "forwardMessage") {
|
if (type == "forwardMessage") {
|
||||||
return tooltipsConfig.showForwardMessage && !isDesktopScreen;
|
return tooltipsConfig.showForwardMessage &&
|
||||||
|
!(isDesktopScreen &&
|
||||||
|
widget.model.chatConfig.isUseMessageHoverBarOnDesktop);
|
||||||
}
|
}
|
||||||
if (type == "replyMessage") {
|
if (type == "replyMessage") {
|
||||||
return tooltipsConfig.showReplyMessage && !isDesktopScreen;
|
return tooltipsConfig.showReplyMessage &&
|
||||||
|
!(isDesktopScreen &&
|
||||||
|
widget.model.chatConfig.isUseMessageHoverBarOnDesktop);
|
||||||
}
|
}
|
||||||
if (type == "delete") {
|
if (type == "delete") {
|
||||||
return (!PlatformUtils().isWeb) && tooltipsConfig.showDeleteMessage;
|
return (!PlatformUtils().isWeb) && tooltipsConfig.showDeleteMessage;
|
||||||
|
|
@ -286,7 +312,9 @@ class TIMUIKitMessageTooltipState
|
||||||
children: [
|
children: [
|
||||||
Image.asset(
|
Image.asset(
|
||||||
item.iconImageAsset,
|
item.iconImageAsset,
|
||||||
package: 'tencent_cloud_chat_uikit',
|
package: defaultTipsIds.contains(item.id)
|
||||||
|
? 'tencent_cloud_chat_uikit'
|
||||||
|
: null,
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
|
|
@ -353,37 +381,53 @@ class TIMUIKitMessageTooltipState
|
||||||
return widgetList;
|
return widgetList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onOpenDesktop(String path) {
|
||||||
|
if (PlatformUtils().isDesktop) {
|
||||||
|
OpenFile.open(path);
|
||||||
|
} else {
|
||||||
|
launchUrl(
|
||||||
|
Uri.parse(path),
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_onTap(String operation, TUIChatSeparateViewModel model) async {
|
_onTap(String operation, TUIChatSeparateViewModel model) async {
|
||||||
final messageItem = widget.message;
|
final messageItem = widget.message;
|
||||||
final msgID = messageItem.msgID as String;
|
final msgID = messageItem.msgID as String;
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case "open":
|
case "open":
|
||||||
if (PlatformUtils().isDesktop) {
|
if (widget.message.fileElem != null) {
|
||||||
final String savePath =
|
_onOpenDesktop(widget.message.fileElem!.localUrl ??
|
||||||
TencentUtils.checkString(widget.message.fileElem!.localUrl) ??
|
widget.message.fileElem?.path ??
|
||||||
globalModal.getFileMessageLocation(widget.message.msgID);
|
"");
|
||||||
launchUrl(Uri.file(savePath));
|
} else if (widget.message.imageElem != null) {
|
||||||
} else {
|
_onOpenDesktop(widget.message.imageElem!.imageList?[0]?.localUrl ??
|
||||||
if (PlatformUtils().isWindows) {
|
widget.message.imageElem?.path ??
|
||||||
OpenFile.open(widget.message.fileElem?.path ?? "");
|
"");
|
||||||
} else {
|
} else if (widget.message.videoElem != null) {
|
||||||
launchUrl(
|
_onOpenDesktop(widget.message.videoElem!.localVideoUrl ??
|
||||||
Uri.parse(widget.message.fileElem?.path ?? ""),
|
widget.message.videoElem?.videoPath ??
|
||||||
mode: LaunchMode.externalApplication,
|
"");
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "finder":
|
case "finder":
|
||||||
final String savePath =
|
String savePath = "";
|
||||||
TencentUtils.checkString(widget.message.fileElem!.localUrl) ??
|
if (widget.message.fileElem != null) {
|
||||||
globalModal.getFileMessageLocation(widget.message.msgID);
|
savePath = (widget.message.fileElem!.localUrl ??
|
||||||
final String fileDir = path.dirname(savePath);
|
widget.message.fileElem?.path ??
|
||||||
if (PlatformUtils().isWindows) {
|
"");
|
||||||
OpenFile.open(fileDir);
|
} else if (widget.message.imageElem != null) {
|
||||||
} else {
|
savePath = (widget.message.imageElem!.imageList?[0]?.localUrl ??
|
||||||
launchUrl(Uri.file(fileDir));
|
widget.message.imageElem?.path ??
|
||||||
|
"");
|
||||||
|
} else if (widget.message.videoElem != null) {
|
||||||
|
savePath = (widget.message.videoElem!.localVideoUrl ??
|
||||||
|
widget.message.videoElem?.videoPath ??
|
||||||
|
"");
|
||||||
}
|
}
|
||||||
|
final String fileDir = path.dirname(savePath);
|
||||||
|
_onOpenDesktop(fileDir);
|
||||||
break;
|
break;
|
||||||
case "delete":
|
case "delete":
|
||||||
model.deleteMsg(msgID, webMessageInstance: messageItem.messageFromWeb);
|
model.deleteMsg(msgID, webMessageInstance: messageItem.messageFromWeb);
|
||||||
|
|
@ -423,12 +467,18 @@ class TIMUIKitMessageTooltipState
|
||||||
break;
|
break;
|
||||||
case "replyMessage":
|
case "replyMessage":
|
||||||
model.repliedMessage = widget.message;
|
model.repliedMessage = widget.message;
|
||||||
if (widget.allowAtUserWhenReply &&
|
final isSelf = widget.message.isSelf ?? true;
|
||||||
widget.onLongPressForOthersHeadPortrait != null &&
|
final isGroup =
|
||||||
!(widget.message.isSelf ?? true)) {
|
TencentUtils.checkString(widget.message.groupID) != null;
|
||||||
widget.onLongPressForOthersHeadPortrait!(
|
final isAtWhenReply = !isSelf &&
|
||||||
widget.message.sender, widget.message.nickName);
|
isGroup &&
|
||||||
}
|
widget.allowAtUserWhenReply &&
|
||||||
|
widget.onLongPressForOthersHeadPortrait != null;
|
||||||
|
|
||||||
|
/// If replying to a self message, do not add a at tag, only requestFocus.
|
||||||
|
widget.onLongPressForOthersHeadPortrait!(
|
||||||
|
!isAtWhenReply ? null : widget.message.sender,
|
||||||
|
!isAtWhenReply ? null : widget.message.nickName);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
onTIMCallback(TIMCallback(
|
onTIMCallback(TIMCallback(
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,13 @@ class TIMUIKitHistoryMessageListContainer extends StatefulWidget {
|
||||||
/// conversation type
|
/// conversation type
|
||||||
final ConvType conversationType;
|
final ConvType conversationType;
|
||||||
|
|
||||||
|
/// Avatar and name in message reaction tap callback.
|
||||||
final void Function(String userID, TapDownDetails tapDetails)? onTapAvatar;
|
final void Function(String userID, TapDownDetails tapDetails)? onTapAvatar;
|
||||||
|
|
||||||
|
/// Avatar and name in message reaction secondary tap callback.
|
||||||
|
final void Function(String userID, TapDownDetails tapDetails)?
|
||||||
|
onSecondaryTapAvatar;
|
||||||
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
"Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
|
"Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
|
||||||
final bool showNickName;
|
final bool showNickName;
|
||||||
|
|
@ -85,8 +90,9 @@ class TIMUIKitHistoryMessageListContainer extends StatefulWidget {
|
||||||
this.extraTipsActionItemBuilder,
|
this.extraTipsActionItemBuilder,
|
||||||
this.isAllowScroll = true,
|
this.isAllowScroll = true,
|
||||||
this.onTapAvatar,
|
this.onTapAvatar,
|
||||||
@Deprecated("Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
|
@Deprecated(
|
||||||
this.showNickName = true,
|
"Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
|
||||||
|
this.showNickName = true,
|
||||||
this.initFindingMsg,
|
this.initFindingMsg,
|
||||||
this.mainHistoryListConfig,
|
this.mainHistoryListConfig,
|
||||||
this.toolTipsConfig,
|
this.toolTipsConfig,
|
||||||
|
|
@ -94,6 +100,7 @@ class TIMUIKitHistoryMessageListContainer extends StatefulWidget {
|
||||||
this.customEmojiStickerList = const [],
|
this.customEmojiStickerList = const [],
|
||||||
this.textFieldController,
|
this.textFieldController,
|
||||||
required this.conversation,
|
required this.conversation,
|
||||||
|
this.onSecondaryTapAvatar,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -169,6 +176,7 @@ class _TIMUIKitHistoryMessageListContainerState
|
||||||
widget.extraTipsActionItemBuilder),
|
widget.extraTipsActionItemBuilder),
|
||||||
message: message!,
|
message: message!,
|
||||||
showAvatar: chatConfig.isShowAvatar,
|
showAvatar: chatConfig.isShowAvatar,
|
||||||
|
onSecondaryTapForOthersPortrait: widget.onSecondaryTapAvatar,
|
||||||
onTapForOthersPortrait: widget.onTapAvatar,
|
onTapForOthersPortrait: widget.onTapAvatar,
|
||||||
messageItemBuilder: widget.messageItemBuilder,
|
messageItemBuilder: widget.messageItemBuilder,
|
||||||
onLongPressForOthersHeadPortrait:
|
onLongPressForOthersHeadPortrait:
|
||||||
|
|
|
||||||
|
|
@ -80,9 +80,8 @@ class _TIMUIKitAppBarState extends TIMUIKitState<TIMUIKitAppBar> {
|
||||||
changedInfo.userProfile?.userID) ??
|
changedInfo.userProfile?.userID) ??
|
||||||
"";
|
"";
|
||||||
}
|
}
|
||||||
// ignore: empty_catches
|
// ignore: empty_catches
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (_friendshipListener != null) {
|
if (_friendshipListener != null) {
|
||||||
|
|
@ -103,9 +102,8 @@ class _TIMUIKitAppBarState extends TIMUIKitState<TIMUIKitAppBar> {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ignore: empty_catches
|
// ignore: empty_catches
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (_groupListener != null) {
|
if (_groupListener != null) {
|
||||||
|
|
@ -161,9 +159,8 @@ class _TIMUIKitAppBarState extends TIMUIKitState<TIMUIKitAppBar> {
|
||||||
"";
|
"";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// ignore: empty_catches
|
// ignore: empty_catches
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -193,7 +190,8 @@ class _TIMUIKitAppBarState extends TIMUIKitState<TIMUIKitAppBar> {
|
||||||
titleTextStyle: setAppbar?.titleTextStyle,
|
titleTextStyle: setAppbar?.titleTextStyle,
|
||||||
toolbarOpacity: setAppbar?.toolbarOpacity ?? 1.0,
|
toolbarOpacity: setAppbar?.toolbarOpacity ?? 1.0,
|
||||||
toolbarTextStyle: setAppbar?.toolbarTextStyle,
|
toolbarTextStyle: setAppbar?.toolbarTextStyle,
|
||||||
textTheme: setAppbar?.textTheme,
|
|
||||||
|
// textTheme: setAppbar?.textTheme,
|
||||||
iconTheme: setAppbar?.iconTheme ??
|
iconTheme: setAppbar?.iconTheme ??
|
||||||
const IconThemeData(
|
const IconThemeData(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
|
|
@ -201,10 +199,8 @@ class _TIMUIKitAppBarState extends TIMUIKitState<TIMUIKitAppBar> {
|
||||||
title: TIMUIKitAppBarTitle(
|
title: TIMUIKitAppBarTitle(
|
||||||
title: setAppbar?.title,
|
title: setAppbar?.title,
|
||||||
onClick: widget.onClickTitle,
|
onClick: widget.onClickTitle,
|
||||||
textStyle: setAppbar?.textTheme?.titleMedium ??
|
textStyle: TextStyle(
|
||||||
TextStyle(
|
color: theme.appbarTextColor ?? hexToColor("010000"), fontSize: 16),
|
||||||
color: theme.appbarTextColor ?? hexToColor("010000"),
|
|
||||||
fontSize: 16),
|
|
||||||
conversationShowName: _conversationShowName,
|
conversationShowName: _conversationShowName,
|
||||||
showC2cMessageEditStatus: widget.showC2cMessageEditStatus,
|
showC2cMessageEditStatus: widget.showC2cMessageEditStatus,
|
||||||
fromUser: widget.conversationID,
|
fromUser: widget.conversationID,
|
||||||
|
|
@ -222,12 +218,10 @@ class _TIMUIKitAppBarState extends TIMUIKitState<TIMUIKitAppBar> {
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
TIM_t('取消'),
|
TIM_t('取消'),
|
||||||
style: setAppbar?.textTheme?.titleMedium ??
|
style: TextStyle(
|
||||||
TextStyle(
|
color: theme.appbarTextColor ?? hexToColor("010000"),
|
||||||
color:
|
fontSize: 16,
|
||||||
theme.appbarTextColor ?? hexToColor("010000"),
|
),
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: setAppbar?.leading ??
|
: setAppbar?.leading ??
|
||||||
|
|
@ -240,10 +234,7 @@ class _TIMUIKitAppBarState extends TIMUIKitState<TIMUIKitAppBar> {
|
||||||
constraints: const BoxConstraints(),
|
constraints: const BoxConstraints(),
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.arrow_back_ios,
|
Icons.arrow_back_ios,
|
||||||
color: setAppbar
|
color: hexToColor("010000"),
|
||||||
?.textTheme?.titleMedium?.color ??
|
|
||||||
theme.appbarTextColor ??
|
|
||||||
hexToColor("010000"),
|
|
||||||
size: 17,
|
size: 17,
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,18 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/utils/permission.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/utils/permission.dart';
|
||||||
import 'package:tencent_open_file/tencent_open_file.dart';
|
import 'package:tencent_open_file/tencent_open_file.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
|
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||||
|
import 'package:universal_html/html.dart' as html;
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_wrapper.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_wrapper.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_file_icon.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_file_icon.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/textSize.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/widgets/textSize.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
class TIMUIKitFileElem extends StatefulWidget {
|
class TIMUIKitFileElem extends StatefulWidget {
|
||||||
final String? messageID;
|
final String? messageID;
|
||||||
|
|
@ -51,6 +51,16 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
|
||||||
String filePath = "";
|
String filePath = "";
|
||||||
bool isDownloading = false;
|
bool isDownloading = false;
|
||||||
final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>();
|
final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>();
|
||||||
|
int downloadProgress = 0;
|
||||||
|
late V2TimAdvancedMsgListener advancedMsgListener;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
TencentImSDKPlugin.v2TIMManager
|
||||||
|
.getMessageManager()
|
||||||
|
.removeAdvancedMsgListener(listener: advancedMsgListener);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -60,6 +70,32 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
|
||||||
hasFile();
|
hasFile();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
advancedMsgListener = V2TimAdvancedMsgListener(
|
||||||
|
onMessageDownloadProgressCallback:
|
||||||
|
(V2TimMessageDownloadProgress messageProgress) async {
|
||||||
|
if (messageProgress.msgID == widget.message.msgID) {
|
||||||
|
if (messageProgress.isFinish) {
|
||||||
|
if(mounted){
|
||||||
|
setState(() {
|
||||||
|
downloadProgress = 100;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(mounted){
|
||||||
|
setState(() {
|
||||||
|
downloadProgress = (messageProgress.currentSize /
|
||||||
|
messageProgress.totalSize *
|
||||||
|
100)
|
||||||
|
.ceil();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
TencentImSDKPlugin.v2TIMManager
|
||||||
|
.getMessageManager()
|
||||||
|
.addAdvancedMsgListener(listener: advancedMsgListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getSavePath() async {
|
Future<String> getSavePath() async {
|
||||||
|
|
@ -75,12 +111,17 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.getMessageProgress(widget.messageID) == 100) {
|
if (model.getMessageProgress(widget.messageID) == 100 ||
|
||||||
String savePath = TencentUtils.checkString(widget.message.fileElem!.localUrl) ??
|
downloadProgress == 100) {
|
||||||
model.getFileMessageLocation(widget.messageID);
|
String savePath =
|
||||||
|
TencentUtils.checkString(widget.message.fileElem!.localUrl) ??
|
||||||
|
model.getFileMessageLocation(widget.messageID);
|
||||||
File f = File(savePath);
|
File f = File(savePath);
|
||||||
if (f.existsSync() && widget.messageID != null) {
|
if (f.existsSync() && widget.messageID != null) {
|
||||||
filePath = savePath;
|
filePath = savePath;
|
||||||
|
setState(() {
|
||||||
|
downloadProgress = 100;
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -89,6 +130,9 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
|
||||||
File f = File(savePath);
|
File f = File(savePath);
|
||||||
if (f.existsSync() && widget.messageID != null) {
|
if (f.existsSync() && widget.messageID != null) {
|
||||||
filePath = savePath;
|
filePath = savePath;
|
||||||
|
setState(() {
|
||||||
|
downloadProgress = 100;
|
||||||
|
});
|
||||||
model.setMessageProgress(widget.messageID!, 100);
|
model.setMessageProgress(widget.messageID!, 100);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -108,7 +152,7 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
|
||||||
}
|
}
|
||||||
|
|
||||||
addUrlToWaitingPath() async {
|
addUrlToWaitingPath() async {
|
||||||
if(widget.messageID !=null ){
|
if (widget.messageID != null) {
|
||||||
model.addWaitingList(widget.messageID!);
|
model.addWaitingList(widget.messageID!);
|
||||||
print("add path success");
|
print("add path success");
|
||||||
}
|
}
|
||||||
|
|
@ -127,7 +171,7 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadFile(TUITheme theme) async {
|
downloadFile(TUITheme theme) async {
|
||||||
if(PlatformUtils().isMobile){
|
if (PlatformUtils().isMobile) {
|
||||||
if (PlatformUtils().isIOS) {
|
if (PlatformUtils().isIOS) {
|
||||||
if (!await Permissions.checkPermission(
|
if (!await Permissions.checkPermission(
|
||||||
context, Permission.photosAddOnly.value, theme, false)) {
|
context, Permission.photosAddOnly.value, theme, false)) {
|
||||||
|
|
@ -136,12 +180,13 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
|
||||||
} else {
|
} else {
|
||||||
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||||
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||||
if ((androidInfo.version.sdkInt ?? 0) >= 33) {
|
if ((androidInfo.version.sdkInt) >= 33) {
|
||||||
} else {
|
} else {
|
||||||
var storage = await Permissions.checkPermission(
|
var storage = await Permissions.checkPermission(
|
||||||
context, Permission.storage.value,
|
context,
|
||||||
|
Permission.storage.value,
|
||||||
);
|
);
|
||||||
if(!storage){
|
if (!storage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -151,7 +196,7 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
|
||||||
}
|
}
|
||||||
|
|
||||||
tryOpenFile(context, theme) async {
|
tryOpenFile(context, theme) async {
|
||||||
if(PlatformUtils().isMobile){
|
if (PlatformUtils().isMobile) {
|
||||||
if (PlatformUtils().isIOS) {
|
if (PlatformUtils().isIOS) {
|
||||||
if (!await Permissions.checkPermission(
|
if (!await Permissions.checkPermission(
|
||||||
context, Permission.photosAddOnly.value, theme!, false)) {
|
context, Permission.photosAddOnly.value, theme!, false)) {
|
||||||
|
|
@ -160,12 +205,13 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
|
||||||
} else {
|
} else {
|
||||||
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||||
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||||
if ((androidInfo.version.sdkInt ?? 0) >= 33) {
|
if ((androidInfo.version.sdkInt) >= 33) {
|
||||||
} else {
|
} else {
|
||||||
var storage = await Permissions.checkPermission(
|
var storage = await Permissions.checkPermission(
|
||||||
context, Permission.storage.value,
|
context,
|
||||||
|
Permission.storage.value,
|
||||||
);
|
);
|
||||||
if(!storage){
|
if (!storage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -173,19 +219,67 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(PlatformUtils().isDesktop && !PlatformUtils().isWindows){
|
if (PlatformUtils().isDesktop && !PlatformUtils().isWindows) {
|
||||||
launchUrl(Uri.file(filePath));
|
launchUrl(Uri.file(filePath));
|
||||||
}else{
|
} else {
|
||||||
OpenFile.open(filePath);
|
OpenFile.open(filePath);
|
||||||
}
|
}
|
||||||
// ignore: empty_catches
|
// ignore: empty_catches
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
void downloadWebFile(String fileUrl) async {
|
||||||
|
String fileName = Uri.parse(fileUrl).pathSegments.last;
|
||||||
|
try {
|
||||||
|
http.Response response = await http.get(
|
||||||
|
Uri.parse(fileUrl),
|
||||||
|
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||||
|
);
|
||||||
|
|
||||||
|
final html.AnchorElement downloadAnchor =
|
||||||
|
html.document.createElement('a') as html.AnchorElement;
|
||||||
|
|
||||||
|
final html.Blob blob = html.Blob([response.bodyBytes]);
|
||||||
|
|
||||||
|
downloadAnchor.href = html.Url.createObjectUrlFromBlob(blob);
|
||||||
|
downloadAnchor.download = widget.message.fileElem?.fileName ?? fileName;
|
||||||
|
|
||||||
|
downloadAnchor.click();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
html.AnchorElement(
|
||||||
|
href: widget.fileElem?.path ?? "",
|
||||||
|
)
|
||||||
|
..setAttribute(
|
||||||
|
"download", widget.message.fileElem?.fileName ?? fileName)
|
||||||
|
..setAttribute("target", '_blank')
|
||||||
|
..style.display = "none"
|
||||||
|
..click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||||
final theme = value.theme;
|
final theme = value.theme;
|
||||||
|
final received = downloadProgress;
|
||||||
|
final fileName = widget.fileElem!.fileName ?? "";
|
||||||
|
final fileSize = widget.fileElem!.fileSize;
|
||||||
|
final borderRadius = widget.isSelf
|
||||||
|
? const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10),
|
||||||
|
topRight: Radius.circular(2),
|
||||||
|
bottomLeft: Radius.circular(10),
|
||||||
|
bottomRight: Radius.circular(10))
|
||||||
|
: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(2),
|
||||||
|
topRight: Radius.circular(10),
|
||||||
|
bottomLeft: Radius.circular(10),
|
||||||
|
bottomRight: Radius.circular(10));
|
||||||
|
String? fileFormat;
|
||||||
|
if (widget.fileElem?.fileName != null &&
|
||||||
|
widget.fileElem!.fileName!.isNotEmpty) {
|
||||||
|
final String fileName = widget.fileElem!.fileName!;
|
||||||
|
fileFormat = fileName.split(".")[max(fileName.split(".").length - 1, 0)];
|
||||||
|
}
|
||||||
return TIMUIKitMessageReactionWrapper(
|
return TIMUIKitMessageReactionWrapper(
|
||||||
chatModel: widget.chatModel,
|
chatModel: widget.chatModel,
|
||||||
isShowJump: widget.isShowJump,
|
isShowJump: widget.isShowJump,
|
||||||
|
|
@ -193,135 +287,104 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
|
||||||
isFromSelf: widget.message.isSelf ?? true,
|
isFromSelf: widget.message.isSelf ?? true,
|
||||||
isShowMessageReaction: widget.isShowMessageReaction ?? true,
|
isShowMessageReaction: widget.isShowMessageReaction ?? true,
|
||||||
message: widget.message,
|
message: widget.message,
|
||||||
child: ChangeNotifierProvider.value(
|
child: GestureDetector(
|
||||||
value: model,
|
onTap: () async {
|
||||||
child:
|
if (PlatformUtils().isWeb) {
|
||||||
Consumer<TUIChatGlobalModel>(builder: (context, value, child) {
|
downloadWebFile(widget.fileElem?.path ?? "");
|
||||||
final received = value.getMessageProgress(widget.messageID);
|
return;
|
||||||
final fileName = widget.fileElem!.fileName ?? "";
|
}
|
||||||
final fileSize = widget.fileElem!.fileSize;
|
if (await hasFile()) {
|
||||||
final borderRadius = widget.isSelf
|
if (received == 100) {
|
||||||
? const BorderRadius.only(
|
tryOpenFile(context, theme);
|
||||||
topLeft: Radius.circular(10),
|
} else {
|
||||||
topRight: Radius.circular(2),
|
// 正在下载中,文件可能不完整
|
||||||
bottomLeft: Radius.circular(10),
|
onTIMCallback(
|
||||||
bottomRight: Radius.circular(10))
|
TIMCallback(
|
||||||
: const BorderRadius.only(
|
type: TIMCallbackType.INFO,
|
||||||
topLeft: Radius.circular(2),
|
infoRecommendText: TIM_t("正在下载中"),
|
||||||
topRight: Radius.circular(10),
|
infoCode: 6660411,
|
||||||
bottomLeft: Radius.circular(10),
|
),
|
||||||
bottomRight: Radius.circular(10));
|
);
|
||||||
String? fileFormat;
|
}
|
||||||
if (widget.fileElem?.fileName != null &&
|
return;
|
||||||
widget.fileElem!.fileName!.isNotEmpty) {
|
|
||||||
final String fileName = widget.fileElem!.fileName!;
|
|
||||||
fileFormat =
|
|
||||||
fileName.split(".")[max(fileName.split(".").length - 1, 0)];
|
|
||||||
}
|
}
|
||||||
return InkWell(
|
|
||||||
onTap: () async {
|
|
||||||
if (PlatformUtils().isWeb) {
|
|
||||||
launchUrl(
|
|
||||||
Uri.parse(widget.fileElem?.path ?? ""),
|
|
||||||
mode: LaunchMode.externalApplication,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (await hasFile()) {
|
|
||||||
if (received == 100) {
|
|
||||||
tryOpenFile(context, theme);
|
|
||||||
} else {
|
|
||||||
// 正在下载中,文件可能不完整
|
|
||||||
onTIMCallback(
|
|
||||||
TIMCallback(
|
|
||||||
type: TIMCallbackType.INFO,
|
|
||||||
infoRecommendText: TIM_t("正在下载中"),
|
|
||||||
infoCode: 6660411,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkIsWaiting()) {
|
if (checkIsWaiting()) {
|
||||||
onTIMCallback(
|
onTIMCallback(
|
||||||
TIMCallback(
|
TIMCallback(
|
||||||
type: TIMCallbackType.INFO,
|
type: TIMCallbackType.INFO,
|
||||||
infoRecommendText: TIM_t("已加入待下载队列,其他文件下载中"),
|
infoRecommendText: TIM_t("已加入待下载队列,其他文件下载中"),
|
||||||
infoCode: 6660413),
|
infoCode: 6660413),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
await addUrlToWaitingPath();
|
await addUrlToWaitingPath();
|
||||||
}
|
}
|
||||||
await downloadFile(theme);
|
await downloadFile(theme);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 237,
|
width: 237,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: theme.weakDividerColor ??
|
color:
|
||||||
CommonColor.weakDividerColor,
|
theme.weakDividerColor ?? CommonColor.weakDividerColor,
|
||||||
),
|
),
|
||||||
borderRadius: borderRadius),
|
borderRadius: borderRadius),
|
||||||
child: Stack(children: [
|
child: Stack(children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
//剪裁为圆角矩形
|
//剪裁为圆角矩形
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
minHeight: 66,
|
minHeight: 66,
|
||||||
value: (received == 100 ? 0 : received) / 100,
|
value: (received == 100 ? 0 : received) / 100,
|
||||||
backgroundColor: received == 100
|
backgroundColor: received == 100
|
||||||
? theme.weakBackgroundColor
|
? theme.weakBackgroundColor
|
||||||
: Colors.white,
|
: Colors.white,
|
||||||
valueColor: AlwaysStoppedAnimation(
|
valueColor: AlwaysStoppedAnimation(
|
||||||
theme.lightPrimaryMaterialColor.shade50)),
|
theme.lightPrimaryMaterialColor.shade50)),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding:
|
||||||
vertical: 8, horizontal: 12),
|
const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: widget.isSelf
|
mainAxisAlignment: widget.isSelf
|
||||||
? MainAxisAlignment.end
|
? MainAxisAlignment.end
|
||||||
: MainAxisAlignment.start,
|
: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
constraints:
|
constraints:
|
||||||
const BoxConstraints(maxWidth: 160),
|
const BoxConstraints(maxWidth: 160),
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder:
|
builder: (buildContext, boxConstraints) {
|
||||||
(buildContext, boxConstraints) {
|
return CustomText(
|
||||||
return CustomText(
|
fileName,
|
||||||
fileName,
|
width: boxConstraints.maxWidth,
|
||||||
width: boxConstraints.maxWidth,
|
style: TextStyle(
|
||||||
style: TextStyle(
|
color: theme.darkTextColor,
|
||||||
color: theme.darkTextColor,
|
fontSize: 16,
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
if (fileSize != null)
|
},
|
||||||
Text(
|
|
||||||
showFileSize(fileSize),
|
|
||||||
// "${received > 0 ? (received / 1024).ceil() : (received / 1024).ceil()} KB",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: theme.weakTextColor),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
TIMUIKitFileIcon(
|
|
||||||
fileFormat: fileFormat,
|
|
||||||
),
|
),
|
||||||
])),
|
),
|
||||||
]),
|
if (fileSize != null)
|
||||||
));
|
Text(
|
||||||
})));
|
showFileSize(fileSize),
|
||||||
|
// "${received > 0 ? (received / 1024).ceil() : (received / 1024).ceil()} KB",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14, color: theme.weakTextColor),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
TIMUIKitFileIcon(
|
||||||
|
fileFormat: fileFormat,
|
||||||
|
),
|
||||||
|
])),
|
||||||
|
]),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ class TIMUIKitImageElem extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
|
class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
|
||||||
|
final TUIChatGlobalModel globalModel = serviceLocator<TUIChatGlobalModel>();
|
||||||
double? networkImagePositionRadio; // 加这个字段用于异步获取被安全打击后的兜底图的比例
|
double? networkImagePositionRadio; // 加这个字段用于异步获取被安全打击后的兜底图的比例
|
||||||
final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>();
|
final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>();
|
||||||
final MessageService _messageService = serviceLocator<MessageService>();
|
final MessageService _messageService = serviceLocator<MessageService>();
|
||||||
|
|
@ -140,7 +141,7 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
|
||||||
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||||
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||||
if (PlatformUtils().isMobile) {
|
if (PlatformUtils().isMobile) {
|
||||||
if ((androidInfo.version.sdkInt ?? 0) >= 33) {
|
if ((androidInfo.version.sdkInt) >= 33) {
|
||||||
final photos = await Permissions.checkPermission(
|
final photos = await Permissions.checkPermission(
|
||||||
context,
|
context,
|
||||||
Permission.photos.value,
|
Permission.photos.value,
|
||||||
|
|
@ -309,137 +310,6 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
|
||||||
child: errorDisplay(context, theme),
|
child: errorDisplay(context, theme),
|
||||||
));
|
));
|
||||||
|
|
||||||
Widget _renderLocalImage(String smallImage, dynamic heroTag,
|
|
||||||
double? positionRadio, TUITheme? theme, String? originImage) {
|
|
||||||
double? currentPositionRadio = positionRadio;
|
|
||||||
File imgF = File(smallImage);
|
|
||||||
|
|
||||||
bool isExist = imgF.existsSync();
|
|
||||||
if (!isExist) {
|
|
||||||
return errorDisplay(context, theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
Image image = Image.file(imgF);
|
|
||||||
|
|
||||||
String showImage = (originImage != null && File(originImage).existsSync())
|
|
||||||
? originImage
|
|
||||||
: smallImage;
|
|
||||||
|
|
||||||
image.image
|
|
||||||
.resolve(const ImageConfiguration())
|
|
||||||
.addListener(ImageStreamListener((image, synchronousCall) {
|
|
||||||
if (image.image.width != 0 && image.image.height != 0) {
|
|
||||||
currentPositionRadio = image.image.width / image.image.height;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
final message = widget.message;
|
|
||||||
final preloadImage = model.preloadImageMap[
|
|
||||||
message.seq! + message.timestamp.toString() + (message.msgID ?? "")];
|
|
||||||
|
|
||||||
final isDesktopScreen =
|
|
||||||
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
|
||||||
|
|
||||||
return Stack(
|
|
||||||
alignment: AlignmentDirectional.topStart,
|
|
||||||
children: [
|
|
||||||
if (!isDesktopScreen && currentPositionRadio != null)
|
|
||||||
AspectRatio(
|
|
||||||
aspectRatio: currentPositionRadio!,
|
|
||||||
child: Container(
|
|
||||||
decoration: const BoxDecoration(color: Colors.transparent),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
getImage(
|
|
||||||
InkWell(
|
|
||||||
onTap: () {
|
|
||||||
if (PlatformUtils().isDesktop) {
|
|
||||||
if(PlatformUtils().isWindows){
|
|
||||||
OpenFile.open(showImage);
|
|
||||||
} else{
|
|
||||||
launchUrl(Uri.file(showImage));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Navigator.of(context).push(
|
|
||||||
PageRouteBuilder(
|
|
||||||
opaque: false, // set to false
|
|
||||||
pageBuilder: (_, __, ___) => ImageScreen(
|
|
||||||
imageProvider: FileImage(File(showImage)),
|
|
||||||
heroTag: heroTag,
|
|
||||||
messageID: widget.message.msgID,
|
|
||||||
downloadFn: () async {
|
|
||||||
return await _saveImg(theme!);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
constraints:
|
|
||||||
const BoxConstraints(minWidth: 20, minHeight: 20),
|
|
||||||
child: Hero(
|
|
||||||
tag: heroTag,
|
|
||||||
child: preloadImage != null
|
|
||||||
? RawImage(
|
|
||||||
image: preloadImage,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
)
|
|
||||||
: Image.file(
|
|
||||||
File(smallImage),
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
imageElem: null)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
if (!PlatformUtils().isWeb) {
|
|
||||||
if ((widget.message.msgID != null && widget.message.msgID != '') &&
|
|
||||||
(widget.message.imageElem!.imageList![0]!.localUrl == null ||
|
|
||||||
widget.message.imageElem!.imageList![0]!.localUrl!.isEmpty)) {
|
|
||||||
_messageService.downloadMessage(
|
|
||||||
msgID: widget.message.msgID!,
|
|
||||||
messageType: 3,
|
|
||||||
imageType: 0,
|
|
||||||
isSnapshot: false);
|
|
||||||
_messageService.downloadMessage(
|
|
||||||
msgID: widget.message.msgID!,
|
|
||||||
messageType: 3,
|
|
||||||
imageType: 1,
|
|
||||||
isSnapshot: false);
|
|
||||||
_messageService.downloadMessage(
|
|
||||||
msgID: widget.message.msgID!,
|
|
||||||
messageType: 3,
|
|
||||||
imageType: 2,
|
|
||||||
isSnapshot: false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 先暂时下掉用网络图片计算尺寸比例的feature,在没有找到准确的判断图片是否被打击前
|
|
||||||
// setOnlineImageRatio();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setOnlineImageRatio() {
|
|
||||||
if (networkImagePositionRadio == null) {
|
|
||||||
V2TimImage? smallImg = getImageFromList(V2TimImageTypesEnum.small);
|
|
||||||
V2TimImage? originalImg = getImageFromList(V2TimImageTypesEnum.original);
|
|
||||||
Image image = Image.network(smallImg?.url ?? originalImg?.url ?? "");
|
|
||||||
|
|
||||||
image.image
|
|
||||||
.resolve(const ImageConfiguration())
|
|
||||||
.addListener(ImageStreamListener((ImageInfo info, bool _) {
|
|
||||||
if (info.image.width != 0 && info.image.height != 0) {
|
|
||||||
setState(() {
|
|
||||||
networkImagePositionRadio = (info.image.width / info.image.height);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _renderNetworkImage(
|
Widget _renderNetworkImage(
|
||||||
dynamic heroTag, double? positionRadio, TUITheme? theme,
|
dynamic heroTag, double? positionRadio, TUITheme? theme,
|
||||||
{String? path, V2TimImage? originalImg, V2TimImage? smallImg}) {
|
{String? path, V2TimImage? originalImg, V2TimImage? smallImg}) {
|
||||||
|
|
@ -458,12 +328,32 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
|
||||||
getImage(
|
getImage(
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
if (PlatformUtils().isWeb) {
|
||||||
|
launchUrl(
|
||||||
|
Uri.parse(widget.message.imageElem?.path ?? ""),
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (isDesktopScreen) {
|
if (isDesktopScreen) {
|
||||||
onTIMCallback(TIMCallback(
|
if (TencentUtils.checkString(widget
|
||||||
infoCode: 6660414,
|
.message.imageElem!.imageList![0]!.localUrl) !=
|
||||||
infoRecommendText: TIM_t("正在下载中"),
|
null &&
|
||||||
type: TIMCallbackType.INFO
|
File(widget.message.imageElem!.imageList![0]!.localUrl!)
|
||||||
));
|
.existsSync()) {
|
||||||
|
if (PlatformUtils().isWindows) {
|
||||||
|
OpenFile.open(
|
||||||
|
widget.message.imageElem!.imageList![0]!.localUrl);
|
||||||
|
} else {
|
||||||
|
launchUrl(Uri.file(widget
|
||||||
|
.message.imageElem!.imageList![0]!.localUrl!));
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
onTIMCallback(TIMCallback(
|
||||||
|
infoCode: 6660414,
|
||||||
|
infoRecommendText: TIM_t("正在下载中"),
|
||||||
|
type: TIMCallbackType.INFO));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
PageRouteBuilder(
|
PageRouteBuilder(
|
||||||
|
|
@ -516,6 +406,152 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _renderLocalImage(String smallImage, dynamic heroTag,
|
||||||
|
double? positionRadio, TUITheme? theme, String? originImage) {
|
||||||
|
double? currentPositionRadio = positionRadio;
|
||||||
|
File imgF = File(smallImage);
|
||||||
|
|
||||||
|
bool isExist = imgF.existsSync();
|
||||||
|
if (!isExist) {
|
||||||
|
return errorDisplay(context, theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
Image image = Image.file(imgF);
|
||||||
|
|
||||||
|
String showImage = (originImage != null && File(originImage).existsSync())
|
||||||
|
? originImage
|
||||||
|
: smallImage;
|
||||||
|
|
||||||
|
image.image
|
||||||
|
.resolve(const ImageConfiguration())
|
||||||
|
.addListener(ImageStreamListener((image, synchronousCall) {
|
||||||
|
if (image.image.width != 0 && image.image.height != 0) {
|
||||||
|
currentPositionRadio = image.image.width / image.image.height;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
final message = widget.message;
|
||||||
|
final preloadImage = model.preloadImageMap[
|
||||||
|
message.seq! + message.timestamp.toString() + (message.msgID ?? "")];
|
||||||
|
|
||||||
|
final isDesktopScreen =
|
||||||
|
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
||||||
|
|
||||||
|
return Stack(
|
||||||
|
alignment: AlignmentDirectional.topStart,
|
||||||
|
children: [
|
||||||
|
if (!isDesktopScreen && currentPositionRadio != null)
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: currentPositionRadio!,
|
||||||
|
child: Container(
|
||||||
|
decoration: const BoxDecoration(color: Colors.transparent),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
getImage(
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
if (PlatformUtils().isDesktop) {
|
||||||
|
if (PlatformUtils().isWindows) {
|
||||||
|
OpenFile.open(showImage);
|
||||||
|
} else {
|
||||||
|
launchUrl(Uri.file(showImage));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
PageRouteBuilder(
|
||||||
|
opaque: false, // set to false
|
||||||
|
pageBuilder: (_, __, ___) => ImageScreen(
|
||||||
|
imageProvider: FileImage(File(showImage)),
|
||||||
|
heroTag: heroTag,
|
||||||
|
messageID: widget.message.msgID,
|
||||||
|
downloadFn: () async {
|
||||||
|
return await _saveImg(theme!);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
constraints:
|
||||||
|
const BoxConstraints(minWidth: 20, minHeight: 20),
|
||||||
|
child: Hero(
|
||||||
|
tag: heroTag,
|
||||||
|
child: preloadImage != null
|
||||||
|
? RawImage(
|
||||||
|
image: preloadImage,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
)
|
||||||
|
: Image.file(
|
||||||
|
File(smallImage),
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
imageElem: null)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
if (!PlatformUtils().isWeb &&
|
||||||
|
TencentUtils.checkString(widget.message.msgID) != null) {
|
||||||
|
if (TencentUtils.checkString(
|
||||||
|
widget.message.imageElem!.imageList![0]!.localUrl) ==
|
||||||
|
null ||
|
||||||
|
!File(widget.message.imageElem!.imageList![0]!.localUrl!)
|
||||||
|
.existsSync()) {
|
||||||
|
_messageService.downloadMessage(
|
||||||
|
msgID: widget.message.msgID!,
|
||||||
|
messageType: 3,
|
||||||
|
imageType: 0,
|
||||||
|
isSnapshot: false);
|
||||||
|
}
|
||||||
|
if (TencentUtils.checkString(
|
||||||
|
widget.message.imageElem!.imageList![1]!.localUrl) ==
|
||||||
|
null ||
|
||||||
|
!File(widget.message.imageElem!.imageList![1]!.localUrl!)
|
||||||
|
.existsSync()) {
|
||||||
|
_messageService.downloadMessage(
|
||||||
|
msgID: widget.message.msgID!,
|
||||||
|
messageType: 3,
|
||||||
|
imageType: 1,
|
||||||
|
isSnapshot: false);
|
||||||
|
}
|
||||||
|
if (TencentUtils.checkString(
|
||||||
|
widget.message.imageElem!.imageList![2]!.localUrl) ==
|
||||||
|
null ||
|
||||||
|
!File(widget.message.imageElem!.imageList![2]!.localUrl!)
|
||||||
|
.existsSync()) {
|
||||||
|
_messageService.downloadMessage(
|
||||||
|
msgID: widget.message.msgID!,
|
||||||
|
messageType: 3,
|
||||||
|
imageType: 2,
|
||||||
|
isSnapshot: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 先暂时下掉用网络图片计算尺寸比例的feature,在没有找到准确的判断图片是否被打击前
|
||||||
|
// setOnlineImageRatio();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOnlineImageRatio() {
|
||||||
|
if (networkImagePositionRadio == null) {
|
||||||
|
V2TimImage? smallImg = getImageFromList(V2TimImageTypesEnum.small);
|
||||||
|
V2TimImage? originalImg = getImageFromList(V2TimImageTypesEnum.original);
|
||||||
|
Image image = Image.network(smallImg?.url ?? originalImg?.url ?? "");
|
||||||
|
|
||||||
|
image.image
|
||||||
|
.resolve(const ImageConfiguration())
|
||||||
|
.addListener(ImageStreamListener((ImageInfo info, bool _) {
|
||||||
|
if (info.image.width != 0 && info.image.height != 0) {
|
||||||
|
setState(() {
|
||||||
|
networkImagePositionRadio = (info.image.width / info.image.height);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool isNeedShowLocalPath() {
|
bool isNeedShowLocalPath() {
|
||||||
final current = (DateTime.now().millisecondsSinceEpoch / 1000).ceil();
|
final current = (DateTime.now().millisecondsSinceEpoch / 1000).ceil();
|
||||||
final timeStamp = widget.message.timestamp ?? current;
|
final timeStamp = widget.message.timestamp ?? current;
|
||||||
|
|
@ -572,8 +608,7 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
|
||||||
smallImg: smallImg, originalImg: originalImg);
|
smallImg: smallImg, originalImg: originalImg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if ((smallImg?.url ?? originalImg?.url) != null &&
|
||||||
(smallImg?.url ?? originalImg?.url) != null &&
|
|
||||||
(smallImg?.url ?? originalImg?.url)!.isNotEmpty) {
|
(smallImg?.url ?? originalImg?.url)!.isNotEmpty) {
|
||||||
return _renderNetworkImage(heroTag, positionRadio, theme,
|
return _renderNetworkImage(heroTag, positionRadio, theme,
|
||||||
smallImg: smallImg, originalImg: originalImg);
|
smallImg: smallImg, originalImg: originalImg);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart';
|
||||||
import 'package:tencent_extended_text/extended_text.dart';
|
import 'package:extended_text/extended_text.dart';
|
||||||
import 'package:tencent_im_base/tencent_im_base.dart';
|
import 'package:tencent_im_base/tencent_im_base.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
||||||
|
|
@ -302,7 +302,7 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
|
||||||
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
||||||
customEmojiStickerList: widget.customEmojiStickerList,
|
customEmojiStickerList: widget.customEmojiStickerList,
|
||||||
isEnableTextSelection:
|
isEnableTextSelection:
|
||||||
widget.chatModel.chatConfig.isEnableTextSelection);
|
widget.chatModel.chatConfig.isEnableTextSelection ?? false);
|
||||||
return Container(
|
return Container(
|
||||||
padding: widget.textPadding ?? EdgeInsets.all(isDesktopScreen ? 12 : 10),
|
padding: widget.textPadding ?? EdgeInsets.all(isDesktopScreen ? 12 : 10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,7 @@ class _TIMUIKitSoundElemState extends TIMUIKitState<TIMUIKitSoundElem> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return InkWell(
|
return GestureDetector(
|
||||||
onTap: () => _playSound(),
|
onTap: () => _playSound(),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: widget.textPadding ?? const EdgeInsets.all(10),
|
padding: widget.textPadding ?? const EdgeInsets.all(10),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
|
||||||
import 'package:tencent_extended_text/extended_text.dart';
|
import 'package:extended_text/extended_text.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
||||||
|
|
@ -170,7 +170,7 @@ class _TIMUIKitTextElemState extends TIMUIKitState<TIMUIKitTextElem> {
|
||||||
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
||||||
customEmojiStickerList: widget.customEmojiStickerList,
|
customEmojiStickerList: widget.customEmojiStickerList,
|
||||||
isEnableTextSelection:
|
isEnableTextSelection:
|
||||||
widget.chatModel.chatConfig.isEnableTextSelection);
|
widget.chatModel.chatConfig.isEnableTextSelection ?? false);
|
||||||
final borderRadius = widget.isFromSelf
|
final borderRadius = widget.isFromSelf
|
||||||
? const BorderRadius.only(
|
? const BorderRadius.only(
|
||||||
topLeft: Radius.circular(10),
|
topLeft: Radius.circular(10),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
|
||||||
import 'package:tencent_extended_text/extended_text.dart';
|
import 'package:extended_text/extended_text.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
||||||
|
|
@ -127,7 +127,7 @@ class _TIMUIKitTextTranslationElemState
|
||||||
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
||||||
customEmojiStickerList: widget.customEmojiStickerList,
|
customEmojiStickerList: widget.customEmojiStickerList,
|
||||||
isEnableTextSelection:
|
isEnableTextSelection:
|
||||||
widget.chatModel.chatConfig.isEnableTextSelection);
|
widget.chatModel.chatConfig.isEnableTextSelection ?? false);
|
||||||
|
|
||||||
return TencentUtils.checkString(translateText) != null
|
return TencentUtils.checkString(translateText) != null
|
||||||
? Container(
|
? Container(
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:loading_animation_widget/loading_animation_widget.dart';
|
import 'package:loading_animation_widget/loading_animation_widget.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
|
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
|
||||||
import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.dart';
|
import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart';
|
||||||
|
|
@ -40,7 +38,6 @@ class TIMUIKitVideoElem extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TIMUIKitVideoElemState extends TIMUIKitState<TIMUIKitVideoElem> {
|
class _TIMUIKitVideoElemState extends TIMUIKitState<TIMUIKitVideoElem> {
|
||||||
final TUIChatGlobalModel globalModel = serviceLocator<TUIChatGlobalModel>();
|
|
||||||
final MessageService _messageService = serviceLocator<MessageService>();
|
final MessageService _messageService = serviceLocator<MessageService>();
|
||||||
late V2TimVideoElem stateElement = widget.message.videoElem!;
|
late V2TimVideoElem stateElement = widget.message.videoElem!;
|
||||||
|
|
||||||
|
|
@ -112,13 +109,13 @@ class _TIMUIKitVideoElemState extends TIMUIKitState<TIMUIKitVideoElem> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (!kIsWeb && stateElement.snapshotUrl == null ||
|
return (!PlatformUtils().isWeb && stateElement.snapshotUrl == null ||
|
||||||
widget.message.status == MessageStatus.V2TIM_MSG_STATUS_SENDING)
|
widget.message.status == MessageStatus.V2TIM_MSG_STATUS_SENDING)
|
||||||
? (stateElement.snapshotPath!.isNotEmpty
|
? (stateElement.snapshotPath!.isNotEmpty
|
||||||
? Image.file(File(stateElement.snapshotPath!), fit: BoxFit.fitWidth)
|
? Image.file(File(stateElement.snapshotPath!), fit: BoxFit.fitWidth)
|
||||||
: Image.file(File(stateElement.localSnapshotUrl!),
|
: Image.file(File(stateElement.localSnapshotUrl!),
|
||||||
fit: BoxFit.fitWidth))
|
fit: BoxFit.fitWidth))
|
||||||
: (kIsWeb ||
|
: (PlatformUtils().isWeb ||
|
||||||
stateElement.localSnapshotUrl == null ||
|
stateElement.localSnapshotUrl == null ||
|
||||||
stateElement.localSnapshotUrl == "")
|
stateElement.localSnapshotUrl == "")
|
||||||
? Image.network(stateElement.snapshotUrl!, fit: BoxFit.fitWidth)
|
? Image.network(stateElement.snapshotUrl!, fit: BoxFit.fitWidth)
|
||||||
|
|
@ -127,9 +124,9 @@ class _TIMUIKitVideoElemState extends TIMUIKitState<TIMUIKitVideoElem> {
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadMessageDetailAndSave() async {
|
downloadMessageDetailAndSave() async {
|
||||||
if (widget.message.msgID != null && widget.message.msgID != '') {
|
if (TencentUtils.checkString(widget.message.msgID) != null) {
|
||||||
if (widget.message.videoElem!.videoUrl == null ||
|
if (TencentUtils.checkString(widget.message.videoElem!.videoUrl) ==
|
||||||
widget.message.videoElem!.videoUrl == '') {
|
null) {
|
||||||
final response = await _messageService.getMessageOnlineUrl(
|
final response = await _messageService.getMessageOnlineUrl(
|
||||||
msgID: widget.message.msgID!);
|
msgID: widget.message.msgID!);
|
||||||
if (response.data != null) {
|
if (response.data != null) {
|
||||||
|
|
@ -140,16 +137,19 @@ class _TIMUIKitVideoElemState extends TIMUIKitState<TIMUIKitVideoElem> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!PlatformUtils().isWeb) {
|
if (!PlatformUtils().isWeb) {
|
||||||
if (widget.message.videoElem!.localVideoUrl == null ||
|
if (TencentUtils.checkString(widget.message.videoElem!.localVideoUrl) ==
|
||||||
widget.message.videoElem!.localVideoUrl == '') {
|
null ||
|
||||||
|
!File(widget.message.videoElem!.localVideoUrl!).existsSync()) {
|
||||||
_messageService.downloadMessage(
|
_messageService.downloadMessage(
|
||||||
msgID: widget.message.msgID!,
|
msgID: widget.message.msgID!,
|
||||||
messageType: 5,
|
messageType: 5,
|
||||||
imageType: 0,
|
imageType: 0,
|
||||||
isSnapshot: false);
|
isSnapshot: false);
|
||||||
}
|
}
|
||||||
if (widget.message.videoElem!.localSnapshotUrl == null ||
|
if (TencentUtils.checkString(
|
||||||
widget.message.videoElem!.localSnapshotUrl == '') {
|
widget.message.videoElem!.localSnapshotUrl) ==
|
||||||
|
null ||
|
||||||
|
!File(widget.message.videoElem!.localSnapshotUrl!).existsSync()) {
|
||||||
_messageService.downloadMessage(
|
_messageService.downloadMessage(
|
||||||
msgID: widget.message.msgID!,
|
msgID: widget.message.msgID!,
|
||||||
messageType: 5,
|
messageType: 5,
|
||||||
|
|
@ -172,33 +172,40 @@ class _TIMUIKitVideoElemState extends TIMUIKitState<TIMUIKitVideoElem> {
|
||||||
final heroTag =
|
final heroTag =
|
||||||
"${widget.message.msgID ?? widget.message.id ?? widget.message.timestamp ?? DateTime.now().millisecondsSinceEpoch}${widget.isFrom}";
|
"${widget.message.msgID ?? widget.message.id ?? widget.message.timestamp ?? DateTime.now().millisecondsSinceEpoch}${widget.isFrom}";
|
||||||
|
|
||||||
return InkWell(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
if (PlatformUtils().isWeb) {
|
||||||
|
launchUrl(
|
||||||
|
Uri.parse(widget.message.videoElem?.videoPath ?? ""),
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (PlatformUtils().isDesktop) {
|
if (PlatformUtils().isDesktop) {
|
||||||
final videoElem = widget.message.videoElem;
|
final videoElem = widget.message.videoElem;
|
||||||
if (videoElem != null) {
|
if (videoElem != null) {
|
||||||
final localVideoUrl = TencentUtils.checkString(videoElem.localVideoUrl);
|
final localVideoUrl =
|
||||||
|
TencentUtils.checkString(videoElem.localVideoUrl);
|
||||||
final videoPath = TencentUtils.checkString(videoElem.videoPath);
|
final videoPath = TencentUtils.checkString(videoElem.videoPath);
|
||||||
final videoUrl = videoElem.videoUrl;
|
final videoUrl = videoElem.videoUrl;
|
||||||
|
|
||||||
if (localVideoUrl != null) {
|
if (localVideoUrl != null) {
|
||||||
if(PlatformUtils().isWindows){
|
if (PlatformUtils().isWindows) {
|
||||||
OpenFile.open(localVideoUrl);
|
OpenFile.open(localVideoUrl);
|
||||||
} else{
|
} else {
|
||||||
launchUrl(Uri.file(localVideoUrl));
|
launchUrl(Uri.file(localVideoUrl));
|
||||||
}
|
}
|
||||||
} else if (videoPath != null) {
|
} else if (videoPath != null) {
|
||||||
if(PlatformUtils().isWindows){
|
if (PlatformUtils().isWindows) {
|
||||||
OpenFile.open(videoPath);
|
OpenFile.open(videoPath);
|
||||||
} else{
|
} else {
|
||||||
launchUrl(Uri.file(videoPath));
|
launchUrl(Uri.file(videoPath));
|
||||||
}
|
}
|
||||||
} else if (TencentUtils.isTextNotEmpty(videoUrl)) {
|
} else if (TencentUtils.isTextNotEmpty(videoUrl)) {
|
||||||
onTIMCallback(TIMCallback(
|
onTIMCallback(TIMCallback(
|
||||||
infoCode: 6660414,
|
infoCode: 6660414,
|
||||||
infoRecommendText: TIM_t("正在下载中"),
|
infoRecommendText: TIM_t("正在下载中"),
|
||||||
type: TIMCallbackType.INFO
|
type: TIMCallbackType.INFO));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -227,15 +234,14 @@ class _TIMUIKitVideoElemState extends TIMUIKitState<TIMUIKitVideoElem> {
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(5)),
|
borderRadius: const BorderRadius.all(Radius.circular(5)),
|
||||||
child: LayoutBuilder(builder:
|
child: LayoutBuilder(builder:
|
||||||
(BuildContext context, BoxConstraints constraints) {
|
(BuildContext context, BoxConstraints constraints) {
|
||||||
double positionRadio = 0.56;
|
double? positionRadio;
|
||||||
if (stateElement.snapshotWidth != null &&
|
if ((stateElement.snapshotWidth) != null &&
|
||||||
stateElement.snapshotHeight != null &&
|
stateElement.snapshotHeight != null &&
|
||||||
stateElement.snapshotWidth != 0 &&
|
stateElement.snapshotWidth != 0 &&
|
||||||
stateElement.snapshotHeight != 0) {
|
stateElement.snapshotHeight != 0) {
|
||||||
positionRadio = (stateElement.snapshotWidth! /
|
positionRadio = (stateElement.snapshotWidth! /
|
||||||
stateElement.snapshotHeight!);
|
stateElement.snapshotHeight!);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxWidth: PlatformUtils().isWeb
|
maxWidth: PlatformUtils().isWeb
|
||||||
|
|
@ -244,52 +250,49 @@ class _TIMUIKitVideoElemState extends TIMUIKitState<TIMUIKitVideoElem> {
|
||||||
maxHeight: min(constraints.maxHeight * 0.8, 300),
|
maxHeight: min(constraints.maxHeight * 0.8, 300),
|
||||||
minHeight: 20,
|
minHeight: 20,
|
||||||
minWidth: 20),
|
minWidth: 20),
|
||||||
child: AspectRatio(
|
child: Stack(
|
||||||
aspectRatio: positionRadio,
|
children: <Widget>[
|
||||||
child: Stack(
|
if (positionRadio != null &&
|
||||||
children: <Widget>[
|
(stateElement.snapshotUrl != null ||
|
||||||
if (stateElement.snapshotUrl != null ||
|
stateElement.snapshotUrl != null))
|
||||||
stateElement.snapshotUrl != null)
|
AspectRatio(
|
||||||
AspectRatio(
|
aspectRatio: positionRadio,
|
||||||
aspectRatio: positionRadio,
|
child: Container(
|
||||||
child: Container(
|
decoration: const BoxDecoration(
|
||||||
decoration: const BoxDecoration(
|
color: Colors.transparent),
|
||||||
color: Colors.transparent),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: generateSnapshot(theme,
|
|
||||||
stateElement.snapshotHeight ?? 100))
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
if (widget.message.status !=
|
Row(
|
||||||
MessageStatus
|
children: [
|
||||||
.V2TIM_MSG_STATUS_SENDING &&
|
Expanded(
|
||||||
(stateElement.snapshotUrl != null ||
|
child: generateSnapshot(theme,
|
||||||
stateElement.snapshotPath != null) &&
|
stateElement.snapshotHeight ?? 100))
|
||||||
stateElement.videoPath != null ||
|
],
|
||||||
stateElement.videoUrl != null)
|
),
|
||||||
Positioned.fill(
|
if (widget.message.status !=
|
||||||
// alignment: Alignment.center,
|
MessageStatus.V2TIM_MSG_STATUS_SENDING &&
|
||||||
child: Center(
|
(stateElement.snapshotUrl != null ||
|
||||||
child: Image.asset('images/play.png',
|
stateElement.snapshotPath != null) &&
|
||||||
package: 'tencent_cloud_chat_uikit',
|
stateElement.videoPath != null ||
|
||||||
height: 64)),
|
stateElement.videoUrl != null)
|
||||||
),
|
Positioned.fill(
|
||||||
Positioned(
|
// alignment: Alignment.center,
|
||||||
right: 10,
|
child: Center(
|
||||||
bottom: 10,
|
child: Image.asset('images/play.png',
|
||||||
child: Text(
|
package: 'tencent_cloud_chat_uikit',
|
||||||
MessageUtils.formatVideoTime(widget
|
height: 64)),
|
||||||
.message.videoElem?.duration ??
|
),
|
||||||
0)
|
Positioned(
|
||||||
.toString(),
|
right: 10,
|
||||||
style: const TextStyle(
|
bottom: 10,
|
||||||
color: Colors.white, fontSize: 12))),
|
child: Text(
|
||||||
],
|
MessageUtils.formatVideoTime(
|
||||||
),
|
widget.message.videoElem?.duration ??
|
||||||
|
0)
|
||||||
|
.toString(),
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white, fontSize: 12))),
|
||||||
|
],
|
||||||
));
|
));
|
||||||
}),
|
}),
|
||||||
))),
|
))),
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ class _AtMemberPanelState extends TIMUIKitState<AtMemberPanel> {
|
||||||
width: 24,
|
width: 24,
|
||||||
child: Avatar(
|
child: Avatar(
|
||||||
faceUrl: memberItem.faceUrl ?? "",
|
faceUrl: memberItem.faceUrl ?? "",
|
||||||
|
type: 1,
|
||||||
showName: showName),
|
showName: showName),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:tencent_im_base/tencent_im_base.dart';
|
import 'package:tencent_im_base/tencent_im_base.dart';
|
||||||
import 'package:tencent_wechat_camera_picker/tencent_wechat_camera_picker.dart';
|
import 'package:wechat_camera_picker/wechat_camera_picker.dart';
|
||||||
|
|
||||||
class IntlCameraPickerTextDelegate extends CameraPickerTextDelegate {
|
class IntlCameraPickerTextDelegate extends CameraPickerTextDelegate {
|
||||||
/// Confirm string for the confirm button.
|
/// Confirm string for the confirm button.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
// ignore_for_file: file_names
|
// ignore_for_file: file_names
|
||||||
|
|
||||||
import 'package:tencent_extended_text_field/extended_text_field.dart';
|
import 'package:extended_text_field/extended_text_field.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/http_text.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/http_text.dart';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:tencent_extended_text/extended_text.dart';
|
import 'package:extended_text/extended_text.dart';
|
||||||
import 'package:tim_ui_kit_sticker_plugin/constant/emoji.dart';
|
import 'package:tim_ui_kit_sticker_plugin/constant/emoji.dart';
|
||||||
|
|
||||||
///emoji/image text
|
///emoji/image text
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/common/utils.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/common/utils.dart';
|
||||||
import 'package:tencent_extended_text/extended_text.dart';
|
import 'package:extended_text/extended_text.dart';
|
||||||
|
|
||||||
class HttpText extends SpecialText {
|
class HttpText extends SpecialText {
|
||||||
HttpText(TextStyle? textStyle, SpecialTextGestureTapCallback? onTap,
|
HttpText(TextStyle? textStyle, SpecialTextGestureTapCallback? onTap,
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ class AtText extends StatefulWidget {
|
||||||
final Function(
|
final Function(
|
||||||
V2TimGroupMemberFullInfo memberInfo, TapDownDetails? tapDetails)?
|
V2TimGroupMemberFullInfo memberInfo, TapDownDetails? tapDetails)?
|
||||||
onChooseMember;
|
onChooseMember;
|
||||||
|
final bool canAtAll;
|
||||||
|
|
||||||
// some Group type cant @all
|
// some Group type cant @all
|
||||||
final String? groupType;
|
final String? groupType;
|
||||||
|
|
@ -32,6 +33,7 @@ class AtText extends StatefulWidget {
|
||||||
this.groupMemberList,
|
this.groupMemberList,
|
||||||
this.closeFunc,
|
this.closeFunc,
|
||||||
this.onChooseMember,
|
this.onChooseMember,
|
||||||
|
this.canAtAll = false,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -116,7 +118,7 @@ class _AtTextState extends TIMUIKitState<AtText> {
|
||||||
groupType: widget.groupType ?? "",
|
groupType: widget.groupType ?? "",
|
||||||
memberList: searchMemberList ?? [],
|
memberList: searchMemberList ?? [],
|
||||||
onTapMemberItem: _onTapMemberItem,
|
onTapMemberItem: _onTapMemberItem,
|
||||||
canAtAll: true,
|
canAtAll: widget.canAtAll,
|
||||||
canSlideDelete: false,
|
canSlideDelete: false,
|
||||||
touchBottomCallBack: () {
|
touchBottomCallBack: () {
|
||||||
// Get all by once, unnecessary to load more
|
// Get all by once, unnecessary to load more
|
||||||
|
|
@ -130,6 +132,7 @@ class _AtTextState extends TIMUIKitState<AtText> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
desktopWidget: mentionedMembersBody(),
|
desktopWidget: mentionedMembersBody(),
|
||||||
defaultWidget: Scaffold(
|
defaultWidget: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
|
@ -137,8 +140,7 @@ class _AtTextState extends TIMUIKitState<AtText> {
|
||||||
iconTheme: IconThemeData(
|
iconTheme: IconThemeData(
|
||||||
color: theme.appbarTextColor,
|
color: theme.appbarTextColor,
|
||||||
),
|
),
|
||||||
backgroundColor: theme.appbarBgColor ??
|
backgroundColor: theme.appbarBgColor ?? theme.primaryColor,
|
||||||
theme.primaryColor,
|
|
||||||
leading: Row(
|
leading: Row(
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_call_invite_list.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_call_invite_list.dart';
|
||||||
import 'package:tencent_wechat_camera_picker/tencent_wechat_camera_picker.dart';
|
import 'package:wechat_camera_picker/wechat_camera_picker.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
|
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
||||||
|
|
@ -360,7 +360,7 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
||||||
if (PlatformUtils().isMobile){
|
if (PlatformUtils().isMobile){
|
||||||
if(PlatformUtils().isAndroid){
|
if(PlatformUtils().isAndroid){
|
||||||
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||||
if ((androidInfo.version.sdkInt ?? 0) >= 33) {
|
if ((androidInfo.version.sdkInt) >= 33) {
|
||||||
final videos = await Permissions.checkPermission(
|
final videos = await Permissions.checkPermission(
|
||||||
context,Permission.videos.value,
|
context,Permission.videos.value,
|
||||||
theme,
|
theme,
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ class _SendSoundMessageState extends TIMUIKitState<SendSoundMessage> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
Overlay.of(context)?.insert(overlayEntry!);
|
Overlay.of(context).insert(overlayEntry!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,6 @@ typedef CustomStickerPanel = Widget Function({
|
||||||
double? height,
|
double? height,
|
||||||
});
|
});
|
||||||
|
|
||||||
GlobalKey<_InputTextFieldState> inputTextFieldState = GlobalKey();
|
|
||||||
|
|
||||||
class TIMUIKitInputTextField extends StatefulWidget {
|
class TIMUIKitInputTextField extends StatefulWidget {
|
||||||
/// conversation id
|
/// conversation id
|
||||||
final String conversationID;
|
final String conversationID;
|
||||||
|
|
@ -59,7 +57,7 @@ class TIMUIKitInputTextField extends StatefulWidget {
|
||||||
/// hint text for textField widget
|
/// hint text for textField widget
|
||||||
final String? hintText;
|
final String? hintText;
|
||||||
|
|
||||||
/// config for more pannel
|
/// config for more panel
|
||||||
final MorePanelConfig? morePanelConfig;
|
final MorePanelConfig? morePanelConfig;
|
||||||
|
|
||||||
/// show send audio icon
|
/// show send audio icon
|
||||||
|
|
@ -133,15 +131,13 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
int? currentCursor;
|
int? currentCursor;
|
||||||
bool isAddingAtSearchWords = false;
|
bool isAddingAtSearchWords = false;
|
||||||
double inputWidth = 900;
|
double inputWidth = 900;
|
||||||
|
Map<String, V2TimGroupMemberFullInfo> mentionedMembersMap = {};
|
||||||
Map<String, V2TimGroupMemberFullInfo> memberInfoMap = {};
|
|
||||||
|
|
||||||
late TextEditingController textEditingController;
|
late TextEditingController textEditingController;
|
||||||
final TUIConversationViewModel conversationModel =
|
final TUIConversationViewModel conversationModel =
|
||||||
serviceLocator<TUIConversationViewModel>();
|
serviceLocator<TUIConversationViewModel>();
|
||||||
final TUISelfInfoViewModel selfModel = serviceLocator<TUISelfInfoViewModel>();
|
final TUISelfInfoViewModel selfModel = serviceLocator<TUISelfInfoViewModel>();
|
||||||
MuteStatus muteStatus = MuteStatus.none;
|
MuteStatus muteStatus = MuteStatus.none;
|
||||||
|
bool _isComposingText = false;
|
||||||
int latestSendEditStatusTime = DateTime.now().millisecondsSinceEpoch;
|
int latestSendEditStatusTime = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
|
||||||
setCurrentCursor(int? value) {
|
setCurrentCursor(int? value) {
|
||||||
|
|
@ -160,7 +156,9 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop) {
|
if (TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop) {
|
||||||
focusNode.unfocus();
|
textEditingController.selection = TextSelection.fromPosition(TextPosition(
|
||||||
|
offset: currentCursor ?? textEditingController.text.length));
|
||||||
|
focusNode.requestFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,18 +166,13 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
return text.replaceAll(RegExp(r'\ufeff'), "");
|
return text.replaceAll(RegExp(r'\ufeff'), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
getShowName(message) {
|
|
||||||
return TencentUtils.checkStringWithoutSpace(message?.friendRemark) ??
|
|
||||||
TencentUtils.checkStringWithoutSpace(message?.nickName) ??
|
|
||||||
TencentUtils.checkStringWithoutSpace(message?.userID);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSetDraftText([String? id, ConvType? convType]) async {
|
handleSetDraftText([String? id, ConvType? convType]) async {
|
||||||
String convID = id ?? widget.conversationID;
|
String convID = id ?? widget.conversationID;
|
||||||
String conversationID =
|
String conversationID = convID.contains("@TOPIC#")
|
||||||
(convType ?? widget.conversationType) == ConvType.c2c
|
? convID
|
||||||
|
: ((convType ?? widget.conversationType) == ConvType.c2c
|
||||||
? "c2c_$convID"
|
? "c2c_$convID"
|
||||||
: "group_$convID";
|
: "group_$convID");
|
||||||
String text = textEditingController.text;
|
String text = textEditingController.text;
|
||||||
String? draftText = _filterU200b(text);
|
String? draftText = _filterU200b(text);
|
||||||
|
|
||||||
|
|
@ -258,7 +251,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
|
|
||||||
List<String> getUserIdFromMemberInfoMap() {
|
List<String> getUserIdFromMemberInfoMap() {
|
||||||
List<String> userList = [];
|
List<String> userList = [];
|
||||||
memberInfoMap.forEach((String key, V2TimGroupMemberFullInfo info) {
|
mentionedMembersMap.forEach((String key, V2TimGroupMemberFullInfo info) {
|
||||||
userList.add(info.userID);
|
userList.add(info.userID);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -278,7 +271,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
convType: convType,
|
convType: convType,
|
||||||
atUserIDList: getUserIdFromMemberInfoMap()),
|
atUserIDList: getUserIdFromMemberInfoMap()),
|
||||||
context);
|
context);
|
||||||
} else if (memberInfoMap.isNotEmpty) {
|
} else if (mentionedMembersMap.isNotEmpty) {
|
||||||
widget.model.sendTextAtMessage(
|
widget.model.sendTextAtMessage(
|
||||||
text: text,
|
text: text,
|
||||||
convType: widget.conversationType,
|
convType: widget.conversationType,
|
||||||
|
|
@ -293,7 +286,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
textEditingController.clear();
|
textEditingController.clear();
|
||||||
currentCursor = null;
|
currentCursor = null;
|
||||||
lastText = "";
|
lastText = "";
|
||||||
memberInfoMap = {};
|
mentionedMembersMap = {};
|
||||||
|
|
||||||
goDownBottom();
|
goDownBottom();
|
||||||
_handleSendEditStatus("", false);
|
_handleSendEditStatus("", false);
|
||||||
|
|
@ -320,11 +313,6 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void onModelChanged() {
|
void onModelChanged() {
|
||||||
if (widget.model.repliedMessage != null) {
|
|
||||||
narrowTextFieldKey.currentState?.showKeyboard = true;
|
|
||||||
focusNode.requestFocus();
|
|
||||||
_addDeleteTag();
|
|
||||||
} else {}
|
|
||||||
if (widget.model.editRevokedMsg != "") {
|
if (widget.model.editRevokedMsg != "") {
|
||||||
narrowTextFieldKey.currentState?.showKeyboard = true;
|
narrowTextFieldKey.currentState?.showKeyboard = true;
|
||||||
focusNode.requestFocus();
|
focusNode.requestFocus();
|
||||||
|
|
@ -336,13 +324,6 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_addDeleteTag() {
|
|
||||||
final originalText = textEditingController.text;
|
|
||||||
textEditingController.text = zeroWidthSpace + originalText;
|
|
||||||
textEditingController.selection = TextSelection.fromPosition(
|
|
||||||
TextPosition(offset: textEditingController.text.length));
|
|
||||||
}
|
|
||||||
|
|
||||||
_onCursorChange() {
|
_onCursorChange() {
|
||||||
final selection = textEditingController.selection;
|
final selection = textEditingController.selection;
|
||||||
currentCursor = selection.baseOffset;
|
currentCursor = selection.baseOffset;
|
||||||
|
|
@ -362,24 +343,32 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
}
|
}
|
||||||
|
|
||||||
_longPressToAt(String? userID, String? nickName) {
|
_longPressToAt(String? userID, String? nickName) {
|
||||||
final memberInfo = V2TimGroupMemberFullInfo(
|
if (TencentUtils.checkString(userID) == null) {
|
||||||
userID: userID ?? "",
|
focusNode.requestFocus();
|
||||||
nickName: nickName,
|
} else {
|
||||||
);
|
final memberInfo = widget.model.groupMemberList
|
||||||
final showName = _getShowName(memberInfo);
|
?.firstWhere((element) => element?.userID == userID) ??
|
||||||
memberInfoMap["@$showName"] = memberInfo;
|
V2TimGroupMemberFullInfo(
|
||||||
String text = "${textEditingController.text}@$showName ";
|
userID: userID ?? "",
|
||||||
//please do not delete space
|
nickName: nickName,
|
||||||
focusNode.requestFocus();
|
);
|
||||||
textEditingController.text = text;
|
final showName = _getShowName(memberInfo);
|
||||||
textEditingController.selection =
|
mentionedMembersMap["@$showName"] = memberInfo;
|
||||||
TextSelection.fromPosition(TextPosition(offset: text.length - 1));
|
String text = "${textEditingController.text}@$showName ";
|
||||||
lastText = text;
|
//please do not delete space
|
||||||
|
focusNode.requestFocus();
|
||||||
|
textEditingController.text = text;
|
||||||
|
textEditingController.selection =
|
||||||
|
TextSelection.fromPosition(TextPosition(offset: text.length));
|
||||||
|
lastText = text;
|
||||||
|
_isComposingText = false;
|
||||||
|
narrowTextFieldKey.currentState?.showKeyboard = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldRemoveAtTag(String atTag, String deletedChar) {
|
bool shouldRemoveAtTag(String atTag, String deletedChar) {
|
||||||
final atMemberArray = [];
|
final atMemberArray = [];
|
||||||
memberInfoMap.forEach((key, value) {
|
mentionedMembersMap.forEach((key, value) {
|
||||||
atMemberArray.add(key);
|
atMemberArray.add(key);
|
||||||
});
|
});
|
||||||
for (String member in atMemberArray) {
|
for (String member in atMemberArray) {
|
||||||
|
|
@ -403,7 +392,11 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
final Offset caretPosition =
|
final Offset caretPosition =
|
||||||
textPainter.getOffsetForCaret(lastLineOffset, Rect.zero);
|
textPainter.getOffsetForCaret(lastLineOffset, Rect.zero);
|
||||||
final dx = min(inputWidth - 180, caretPosition.dx + 16);
|
final dx = min(inputWidth - 180, caretPosition.dx + 16);
|
||||||
final dy = max(24, 110 - caretPosition.dy).toDouble();
|
final dy = max(
|
||||||
|
24,
|
||||||
|
18 * widget.model.chatConfig.desktopMessageInputFieldLines -
|
||||||
|
caretPosition.dy)
|
||||||
|
.toDouble();
|
||||||
|
|
||||||
return Offset(dx, dy);
|
return Offset(dx, dy);
|
||||||
}
|
}
|
||||||
|
|
@ -417,11 +410,11 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
parseAtList.add(str);
|
parseAtList.add(str);
|
||||||
}
|
}
|
||||||
for (String? key in parseAtList) {
|
for (String? key in parseAtList) {
|
||||||
if (key != null && memberInfoMap[key] != null) {
|
if (key != null && mentionedMembersMap[key] != null) {
|
||||||
map[key] = memberInfoMap[key]!;
|
map[key] = mentionedMembersMap[key]!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memberInfoMap = map;
|
mentionedMembersMap = map;
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleAtText(String text, TUIChatSeparateViewModel model) async {
|
_handleAtText(String text, TUIChatSeparateViewModel model) async {
|
||||||
|
|
@ -464,16 +457,21 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
parseAtList.add(str);
|
parseAtList.add(str);
|
||||||
}
|
}
|
||||||
for (String? key in parseAtList) {
|
for (String? key in parseAtList) {
|
||||||
if (key != null && memberInfoMap[key] != null) {
|
if (key != null && mentionedMembersMap[key] != null) {
|
||||||
map[key] = memberInfoMap[key]!;
|
map[key] = mentionedMembersMap[key]!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memberInfoMap = map;
|
mentionedMembersMap = map;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final int selfRole = widget.model.selfMemberInfo?.role ?? 0;
|
||||||
|
final bool canAtAll =
|
||||||
|
(selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN ||
|
||||||
|
selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER);
|
||||||
|
|
||||||
if (isDesktopScreen) {
|
if (isDesktopScreen) {
|
||||||
final atPlace = text.lastIndexOf("@");
|
final atPlace = text.lastIndexOf("@");
|
||||||
final keyword = text.substring(atPlace + 1);
|
final keyword = text.substring(atPlace + 1);
|
||||||
|
|
@ -484,16 +482,54 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
model.atPositionY = atPosition.dy;
|
model.atPositionY = atPosition.dy;
|
||||||
isAddingAtSearchWords = true;
|
isAddingAtSearchWords = true;
|
||||||
}
|
}
|
||||||
final List<V2TimGroupMemberFullInfo?> showAtMemberList =
|
List<V2TimGroupMemberFullInfo> showAtMemberList = (model
|
||||||
(model.groupMemberList ?? []).where((element) {
|
.groupMemberList ??
|
||||||
final friendRemark = element?.friendRemark ?? "";
|
[])
|
||||||
final nickName = element?.nickName ?? "";
|
.where((element) {
|
||||||
final showName = TencentUtils.checkString(friendRemark) ??
|
final showName = (TencentUtils.checkStringWithoutSpace(
|
||||||
TencentUtils.checkString(nickName) ??
|
element?.friendRemark) ??
|
||||||
TencentUtils.checkString(element?.userID) ??
|
TencentUtils.checkStringWithoutSpace(element?.nameCard) ??
|
||||||
"";
|
TencentUtils.checkStringWithoutSpace(element?.nickName) ??
|
||||||
return showName.contains(keyword);
|
TencentUtils.checkStringWithoutSpace(element?.userID) ??
|
||||||
}).toList();
|
"")
|
||||||
|
.toLowerCase();
|
||||||
|
return element != null &&
|
||||||
|
showName.contains(keyword.toLowerCase()) &&
|
||||||
|
TencentUtils.checkString(showName) != null &&
|
||||||
|
element.userID != widget.model.selfMemberInfo?.userID;
|
||||||
|
})
|
||||||
|
.whereType<V2TimGroupMemberFullInfo>()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
showAtMemberList.sort(
|
||||||
|
(V2TimGroupMemberFullInfo userA, V2TimGroupMemberFullInfo userB) {
|
||||||
|
final isUserAIsGroupAdmin = userA.role == 300;
|
||||||
|
final isUserAIsGroupOwner = userA.role == 400;
|
||||||
|
|
||||||
|
final isUserBIsGroupAdmin = userB.role == 300;
|
||||||
|
final isUserBIsGroupOwner = userB.role == 400;
|
||||||
|
|
||||||
|
final String userAName = _getShowName(userA);
|
||||||
|
final String userBName = _getShowName(userB);
|
||||||
|
|
||||||
|
if (isUserAIsGroupOwner != isUserBIsGroupOwner) {
|
||||||
|
return isUserAIsGroupOwner ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUserAIsGroupAdmin != isUserBIsGroupAdmin) {
|
||||||
|
return isUserAIsGroupAdmin ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return userAName.compareTo(userBName);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (canAtAll && showAtMemberList.isNotEmpty && keyword.isEmpty) {
|
||||||
|
showAtMemberList = [
|
||||||
|
V2TimGroupMemberFullInfo(
|
||||||
|
userID: "__kImSDK_MesssageAtALL__", nickName: TIM_t("所有人")),
|
||||||
|
...showAtMemberList
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
model.activeAtIndex = 0;
|
model.activeAtIndex = 0;
|
||||||
model.showAtMemberList = showAtMemberList;
|
model.showAtMemberList = showAtMemberList;
|
||||||
|
|
@ -514,12 +550,13 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
groupMemberList: model.groupMemberList,
|
groupMemberList: model.groupMemberList,
|
||||||
groupInfo: model.groupInfo,
|
groupInfo: model.groupInfo,
|
||||||
groupID: groupID,
|
groupID: groupID,
|
||||||
|
canAtAll: canAtAll,
|
||||||
groupType: widget.groupType),
|
groupType: widget.groupType),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final showName = _getShowName(memberInfo);
|
final showName = _getShowName(memberInfo);
|
||||||
if (memberInfo != null) {
|
if (memberInfo != null) {
|
||||||
memberInfoMap["@$showName"] = memberInfo;
|
mentionedMembersMap["@$showName"] = memberInfo;
|
||||||
textEditingController.text = "$text$showName ";
|
textEditingController.text = "$text$showName ";
|
||||||
lastText = "$text$showName ";
|
lastText = "$text$showName ";
|
||||||
}
|
}
|
||||||
|
|
@ -547,103 +584,125 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
bool? isAddToCursorPosition = false}) {
|
bool? isAddToCursorPosition = false}) {
|
||||||
if (memberInfo != null) {
|
if (memberInfo != null) {
|
||||||
final String showName = _getShowName(memberInfo);
|
final String showName = _getShowName(memberInfo);
|
||||||
memberInfoMap["@$showName"] = memberInfo;
|
mentionedMembersMap["@$showName"] = memberInfo;
|
||||||
replaceAtTag(showName);
|
replaceAtTag(showName);
|
||||||
widget.model.showAtMemberList = [];
|
widget.model.showAtMemberList = [];
|
||||||
widget.model.activeAtIndex = -1;
|
widget.model.activeAtIndex = -1;
|
||||||
|
focusNode.requestFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeyEventResult handleDesktopKeyEvent(FocusNode node, RawKeyEvent event) {
|
||||||
|
final activeIndex = widget.model.activeAtIndex;
|
||||||
|
final showMemberList = widget.model.showAtMemberList;
|
||||||
|
if (event.runtimeType == RawKeyDownEvent) {
|
||||||
|
if (event.physicalKey == PhysicalKeyboardKey.backspace) {
|
||||||
|
if (textEditingController.text.isEmpty && lastText.isEmpty) {
|
||||||
|
widget.model.repliedMessage = null;
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
}
|
||||||
|
} else if ((event.isShiftPressed ||
|
||||||
|
event.isAltPressed ||
|
||||||
|
event.isControlPressed ||
|
||||||
|
event.isMetaPressed) &&
|
||||||
|
event.physicalKey == PhysicalKeyboardKey.enter) {
|
||||||
|
final offset = textEditingController.selection.baseOffset;
|
||||||
|
textEditingController.text =
|
||||||
|
'${lastText.substring(0, offset)}\n${lastText.substring(offset)}';
|
||||||
|
textEditingController.selection =
|
||||||
|
TextSelection.fromPosition(TextPosition(offset: offset + 1));
|
||||||
|
lastText = textEditingController.text;
|
||||||
|
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
} else if (event.physicalKey == PhysicalKeyboardKey.enter) {
|
||||||
|
if (!_isComposingText) {
|
||||||
|
if (!isAddingAtSearchWords || widget.model.showAtMemberList.isEmpty) {
|
||||||
|
onSubmitted();
|
||||||
|
} else {
|
||||||
|
isAddingAtSearchWords = false;
|
||||||
|
final V2TimGroupMemberFullInfo? memberInfo =
|
||||||
|
showMemberList[activeIndex];
|
||||||
|
if (memberInfo != null) {
|
||||||
|
handleAtMember(
|
||||||
|
memberInfo: memberInfo, isAddToCursorPosition: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.isKeyPressed(LogicalKeyboardKey.arrowUp) &&
|
||||||
|
isAddingAtSearchWords &&
|
||||||
|
showMemberList.isNotEmpty) {
|
||||||
|
final newIndex = max(activeIndex - 1, 0);
|
||||||
|
widget.model.activeAtIndex = newIndex;
|
||||||
|
widget.atMemberPanelScroll?.scrollToIndex(newIndex,
|
||||||
|
preferPosition: AutoScrollPosition.middle);
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.isKeyPressed(LogicalKeyboardKey.arrowDown) &&
|
||||||
|
isAddingAtSearchWords &&
|
||||||
|
showMemberList.isNotEmpty) {
|
||||||
|
final newIndex = min(activeIndex + 1, showMemberList.length - 1);
|
||||||
|
widget.model.activeAtIndex = newIndex;
|
||||||
|
widget.atMemberPanelScroll?.scrollToIndex(newIndex,
|
||||||
|
preferPosition: AutoScrollPosition.middle);
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return KeyEventResult.ignored;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
if (PlatformUtils().isWeb || PlatformUtils().isDesktop) {
|
if (PlatformUtils().isWeb || PlatformUtils().isDesktop) {
|
||||||
focusNode = FocusNode(
|
focusNode = FocusNode(
|
||||||
onKey: (node, event) {
|
onKey: (node, event) => handleDesktopKeyEvent(node, event),
|
||||||
final activeIndex = widget.model.activeAtIndex;
|
|
||||||
final showMemberList = widget.model.showAtMemberList;
|
|
||||||
if (event.runtimeType == RawKeyDownEvent) {
|
|
||||||
if (event.physicalKey == PhysicalKeyboardKey.backspace) {
|
|
||||||
if (textEditingController.text.isEmpty && lastText.isEmpty) {
|
|
||||||
widget.model.repliedMessage = null;
|
|
||||||
return KeyEventResult.handled;
|
|
||||||
}
|
|
||||||
} else if ((event.isShiftPressed ||
|
|
||||||
event.isAltPressed ||
|
|
||||||
event.isControlPressed ||
|
|
||||||
event.isMetaPressed) &&
|
|
||||||
event.physicalKey == PhysicalKeyboardKey.enter) {
|
|
||||||
final offset = textEditingController.selection.baseOffset;
|
|
||||||
textEditingController.text =
|
|
||||||
'${lastText.substring(0, offset)}\n${lastText.substring(offset)}';
|
|
||||||
textEditingController.selection =
|
|
||||||
TextSelection.fromPosition(TextPosition(offset: offset + 1));
|
|
||||||
lastText = textEditingController.text;
|
|
||||||
|
|
||||||
return KeyEventResult.handled;
|
|
||||||
} else if (event.physicalKey == PhysicalKeyboardKey.enter) {
|
|
||||||
if (!isAddingAtSearchWords ||
|
|
||||||
widget.model.showAtMemberList.isEmpty) {
|
|
||||||
onSubmitted();
|
|
||||||
} else {
|
|
||||||
isAddingAtSearchWords = false;
|
|
||||||
final V2TimGroupMemberFullInfo? memberInfo =
|
|
||||||
showMemberList[activeIndex];
|
|
||||||
if (memberInfo != null) {
|
|
||||||
handleAtMember(
|
|
||||||
memberInfo: memberInfo, isAddToCursorPosition: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return KeyEventResult.handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.isKeyPressed(LogicalKeyboardKey.arrowUp) &&
|
|
||||||
isAddingAtSearchWords &&
|
|
||||||
showMemberList.isNotEmpty) {
|
|
||||||
final newIndex = max(activeIndex - 1, 0);
|
|
||||||
widget.model.activeAtIndex = newIndex;
|
|
||||||
widget.atMemberPanelScroll?.scrollToIndex(newIndex,
|
|
||||||
preferPosition: AutoScrollPosition.middle);
|
|
||||||
return KeyEventResult.handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.isKeyPressed(LogicalKeyboardKey.arrowDown) &&
|
|
||||||
isAddingAtSearchWords &&
|
|
||||||
showMemberList.isNotEmpty) {
|
|
||||||
final newIndex = min(activeIndex + 1, showMemberList.length - 1);
|
|
||||||
widget.model.activeAtIndex = newIndex;
|
|
||||||
widget.atMemberPanelScroll?.scrollToIndex(newIndex,
|
|
||||||
preferPosition: AutoScrollPosition.middle);
|
|
||||||
return KeyEventResult.handled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return KeyEventResult.ignored;
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
focusNode = FocusNode();
|
focusNode = FocusNode();
|
||||||
}
|
}
|
||||||
textEditingController =
|
textEditingController =
|
||||||
widget.controller?.textEditingController ?? TextEditingController();
|
widget.controller?.textEditingController ?? TextEditingController();
|
||||||
if (widget.controller != null) {
|
|
||||||
widget.controller?.addListener(() {
|
|
||||||
final actionType = widget.controller?.actionType;
|
|
||||||
if (actionType == ActionType.longPressToAt) {
|
|
||||||
_longPressToAt(
|
|
||||||
widget.controller?.atUserID, widget.controller?.atUserName);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
widget.model.addListener(onModelChanged);
|
|
||||||
if (widget.initText != null) {
|
if (widget.initText != null) {
|
||||||
textEditingController.text = widget.initText!;
|
textEditingController.text = widget.initText!;
|
||||||
}
|
}
|
||||||
|
if (widget.controller != null) {
|
||||||
|
widget.controller?.addListener(controllerHandler);
|
||||||
|
}
|
||||||
|
widget.model.addListener(onModelChanged);
|
||||||
final AppLocale appLocale = I18nUtils.findDeviceLocale(null);
|
final AppLocale appLocale = I18nUtils.findDeviceLocale(null);
|
||||||
languageType =
|
languageType =
|
||||||
(appLocale == AppLocale.zhHans || appLocale == AppLocale.zhHant)
|
(appLocale == AppLocale.zhHans || appLocale == AppLocale.zhHant)
|
||||||
? 'zh'
|
? 'zh'
|
||||||
: 'en';
|
: 'en';
|
||||||
|
textEditingController.addListener(() {
|
||||||
|
_isComposingText = textEditingController.value.composing.start != -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
controllerHandler() {
|
||||||
|
final actionType = widget.controller?.actionType;
|
||||||
|
if (actionType == ActionType.longPressToAt) {
|
||||||
|
_longPressToAt(
|
||||||
|
widget.controller?.atUserID, widget.controller?.atUserName);
|
||||||
|
} else if (actionType == ActionType.setTextField) {
|
||||||
|
final newText = widget.controller?.inputText ?? "";
|
||||||
|
textEditingController.text = newText;
|
||||||
|
textEditingController.selection = TextSelection.fromPosition(
|
||||||
|
TextPosition(offset: textEditingController.text.length));
|
||||||
|
lastText = textEditingController.text;
|
||||||
|
return;
|
||||||
|
} else if (actionType == ActionType.requestFocus) {
|
||||||
|
focusNode.requestFocus();
|
||||||
|
return;
|
||||||
|
} else if (actionType == ActionType.handleAtMember) {
|
||||||
|
handleAtMember(memberInfo: widget.controller?.groupMemberFullInfo);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -651,18 +710,27 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
if (widget.conversationID != oldWidget.conversationID) {
|
if (widget.conversationID != oldWidget.conversationID) {
|
||||||
handleSetDraftText(oldWidget.conversationID, oldWidget.conversationType);
|
handleSetDraftText(oldWidget.conversationID, oldWidget.conversationType);
|
||||||
|
mentionedMembersMap.clear();
|
||||||
if (oldWidget.initText != widget.initText) {
|
if (oldWidget.initText != widget.initText) {
|
||||||
textEditingController.text = widget.initText ?? "";
|
textEditingController.text = widget.initText ?? "";
|
||||||
} else {
|
} else {
|
||||||
textEditingController.clear();
|
textEditingController.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (widget.initText != oldWidget.initText &&
|
||||||
|
TencentUtils.checkString(widget.initText) != null) {
|
||||||
|
textEditingController.text = widget.initText!;
|
||||||
|
focusNode.requestFocus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
handleSetDraftText();
|
handleSetDraftText();
|
||||||
widget.model.removeListener(onModelChanged);
|
widget.model.removeListener(onModelChanged);
|
||||||
|
if (widget.controller != null) {
|
||||||
|
widget.controller?.removeListener(controllerHandler);
|
||||||
|
}
|
||||||
focusNode.dispose();
|
focusNode.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
@ -738,6 +806,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||||
|
final theme = value.theme;
|
||||||
final TUIChatSeparateViewModel model =
|
final TUIChatSeparateViewModel model =
|
||||||
Provider.of<TUIChatSeparateViewModel>(context);
|
Provider.of<TUIChatSeparateViewModel>(context);
|
||||||
|
|
||||||
|
|
@ -763,6 +832,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
inputWidth = constraints.maxWidth;
|
inputWidth = constraints.maxWidth;
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
defaultWidget: TIMUIKitTextFieldLayoutNarrow(
|
defaultWidget: TIMUIKitTextFieldLayoutNarrow(
|
||||||
onEmojiSubmitted: onEmojiSubmitted,
|
onEmojiSubmitted: onEmojiSubmitted,
|
||||||
onCustomEmojiFaceSubmitted: onCustomEmojiFaceSubmitted,
|
onCustomEmojiFaceSubmitted: onCustomEmojiFaceSubmitted,
|
||||||
|
|
@ -798,6 +868,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
showMorePanel: widget.showMorePanel,
|
showMorePanel: widget.showMorePanel,
|
||||||
customEmojiStickerList: widget.customEmojiStickerList),
|
customEmojiStickerList: widget.customEmojiStickerList),
|
||||||
desktopWidget: TIMUIKitTextFieldLayoutWide(
|
desktopWidget: TIMUIKitTextFieldLayoutWide(
|
||||||
|
theme: theme,
|
||||||
currentConversation: widget.currentConversation,
|
currentConversation: widget.currentConversation,
|
||||||
onEmojiSubmitted: onEmojiSubmitted,
|
onEmojiSubmitted: onEmojiSubmitted,
|
||||||
onCustomEmojiFaceSubmitted: onCustomEmojiFaceSubmitted,
|
onCustomEmojiFaceSubmitted: onCustomEmojiFaceSubmitted,
|
||||||
|
|
@ -825,7 +896,6 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
||||||
handleAtText: (text) {
|
handleAtText: (text) {
|
||||||
_handleAtText(text, model);
|
_handleAtText(text, model);
|
||||||
},
|
},
|
||||||
handleSoftKeyBoardDelete: _handleSoftKeyBoardDelete,
|
|
||||||
onSubmitted: onSubmitted,
|
onSubmitted: onSubmitted,
|
||||||
goDownBottom: goDownBottom,
|
goDownBottom: goDownBottom,
|
||||||
showSendAudio: widget.showSendAudio,
|
showSendAudio: widget.showSendAudio,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,21 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:tencent_im_base/tencent_im_base.dart';
|
||||||
|
|
||||||
enum ActionType { hideAllPanel, longPressToAt }
|
enum ActionType {
|
||||||
|
hideAllPanel,
|
||||||
|
longPressToAt,
|
||||||
|
setTextField,
|
||||||
|
requestFocus,
|
||||||
|
handleAtMember
|
||||||
|
}
|
||||||
|
|
||||||
class TIMUIKitInputTextFieldController extends ChangeNotifier {
|
class TIMUIKitInputTextFieldController extends ChangeNotifier {
|
||||||
TextEditingController? textEditingController = TextEditingController();
|
TextEditingController? textEditingController = TextEditingController();
|
||||||
ActionType? actionType;
|
ActionType? actionType;
|
||||||
String? atUserName;
|
String? atUserName;
|
||||||
String? atUserID;
|
String? atUserID;
|
||||||
|
String inputText = "";
|
||||||
|
V2TimGroupMemberFullInfo? groupMemberFullInfo;
|
||||||
|
|
||||||
TIMUIKitInputTextFieldController([TextEditingController? controller]) {
|
TIMUIKitInputTextFieldController([TextEditingController? controller]) {
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
|
|
@ -26,4 +35,21 @@ class TIMUIKitInputTextFieldController extends ChangeNotifier {
|
||||||
atUserID = userID;
|
atUserID = userID;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTextField(String text) {
|
||||||
|
inputText = text;
|
||||||
|
actionType = ActionType.setTextField;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
requestFocus() {
|
||||||
|
actionType = ActionType.requestFocus;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAtMember(V2TimGroupMemberFullInfo? memberInfo) {
|
||||||
|
actionType = ActionType.handleAtMember;
|
||||||
|
groupMemberFullInfo = memberInfo;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_emoji_panel.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_emoji_panel.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_send_sound_message.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_send_sound_message.dart';
|
||||||
import 'package:tencent_extended_text_field/extended_text_field.dart';
|
import 'package:extended_text_field/extended_text_field.dart';
|
||||||
import 'package:tencent_keyboard_visibility/tencent_keyboard_visibility.dart';
|
import 'package:tencent_keyboard_visibility/tencent_keyboard_visibility.dart';
|
||||||
|
|
||||||
GlobalKey<_TIMUIKitTextFieldLayoutNarrowState> narrowTextFieldKey = GlobalKey();
|
GlobalKey<_TIMUIKitTextFieldLayoutNarrowState> narrowTextFieldKey = GlobalKey();
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:fc_native_video_thumbnail_for_us/fc_native_video_thumbnail_for_us.dart';
|
import 'package:fc_native_video_thumbnail_for_us/fc_native_video_thumbnail_for_us.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:pasteboard/pasteboard.dart';
|
import 'package:pasteboard/pasteboard.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
|
|
@ -26,7 +27,7 @@ import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_emoji_panel.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_emoji_panel.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/drag_widget.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/widgets/drag_widget.dart';
|
||||||
import 'package:tencent_extended_text_field/extended_text_field.dart';
|
import 'package:extended_text_field/extended_text_field.dart';
|
||||||
import 'package:universal_html/html.dart' as html;
|
import 'package:universal_html/html.dart' as html;
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
@ -39,6 +40,7 @@ class DesktopControlBarItem {
|
||||||
final String item;
|
final String item;
|
||||||
final IconData? icon;
|
final IconData? icon;
|
||||||
final String? imgPath;
|
final String? imgPath;
|
||||||
|
final String? svgPath;
|
||||||
final Color? color;
|
final Color? color;
|
||||||
final ValueChanged<Offset?> onClick;
|
final ValueChanged<Offset?> onClick;
|
||||||
final String? showName;
|
final String? showName;
|
||||||
|
|
@ -49,10 +51,13 @@ class DesktopControlBarItem {
|
||||||
this.icon,
|
this.icon,
|
||||||
this.color,
|
this.color,
|
||||||
this.imgPath,
|
this.imgPath,
|
||||||
|
this.svgPath,
|
||||||
required this.onClick,
|
required this.onClick,
|
||||||
this.showName,
|
this.showName,
|
||||||
this.size})
|
this.size})
|
||||||
: assert(icon != null || imgPath != null);
|
: assert(icon != null ||
|
||||||
|
TencentUtils.checkString(imgPath) != null ||
|
||||||
|
TencentUtils.checkString(svgPath) != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
class DesktopControlBarConfig {
|
class DesktopControlBarConfig {
|
||||||
|
|
@ -81,7 +86,7 @@ class TIMUIKitTextFieldLayoutWide extends StatefulWidget {
|
||||||
final Function(String, bool) handleSendEditStatus;
|
final Function(String, bool) handleSendEditStatus;
|
||||||
final VoidCallback backSpaceText;
|
final VoidCallback backSpaceText;
|
||||||
final ValueChanged<String> addStickerToText;
|
final ValueChanged<String> addStickerToText;
|
||||||
|
final TUITheme theme;
|
||||||
final ValueChanged<String> handleAtText;
|
final ValueChanged<String> handleAtText;
|
||||||
|
|
||||||
/// Whether to use the default emoji
|
/// Whether to use the default emoji
|
||||||
|
|
@ -125,8 +130,6 @@ class TIMUIKitTextFieldLayoutWide extends StatefulWidget {
|
||||||
/// show send audio icon
|
/// show send audio icon
|
||||||
final bool showSendAudio;
|
final bool showSendAudio;
|
||||||
|
|
||||||
final VoidCallback handleSoftKeyBoardDelete;
|
|
||||||
|
|
||||||
/// on text changed
|
/// on text changed
|
||||||
final void Function(String)? onChanged;
|
final void Function(String)? onChanged;
|
||||||
|
|
||||||
|
|
@ -168,7 +171,6 @@ class TIMUIKitTextFieldLayoutWide extends StatefulWidget {
|
||||||
this.onChanged,
|
this.onChanged,
|
||||||
required this.handleSendEditStatus,
|
required this.handleSendEditStatus,
|
||||||
required this.handleAtText,
|
required this.handleAtText,
|
||||||
required this.handleSoftKeyBoardDelete,
|
|
||||||
this.repliedMessage,
|
this.repliedMessage,
|
||||||
this.forbiddenText,
|
this.forbiddenText,
|
||||||
required this.onSubmitted,
|
required this.onSubmitted,
|
||||||
|
|
@ -179,7 +181,8 @@ class TIMUIKitTextFieldLayoutWide extends StatefulWidget {
|
||||||
this.hintText,
|
this.hintText,
|
||||||
required this.customEmojiStickerList,
|
required this.customEmojiStickerList,
|
||||||
this.controller,
|
this.controller,
|
||||||
required this.currentConversation})
|
required this.currentConversation,
|
||||||
|
required this.theme})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -199,6 +202,7 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
double? bottomPadding;
|
double? bottomPadding;
|
||||||
late ScrollController _scrollController;
|
late ScrollController _scrollController;
|
||||||
late FocusNode textFocusNode;
|
late FocusNode textFocusNode;
|
||||||
|
late List<DesktopControlBarItem> defaultControlBarItems;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -214,6 +218,27 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
textFocusNode = FocusNode();
|
textFocusNode = FocusNode();
|
||||||
widget.focusNode.requestFocus();
|
widget.focusNode.requestFocus();
|
||||||
_scrollController = ScrollController();
|
_scrollController = ScrollController();
|
||||||
|
try {
|
||||||
|
if (PlatformUtils().isWeb) {
|
||||||
|
html.window.addEventListener('paste', (event) {
|
||||||
|
_handlePaste(event as html.ClipboardEvent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
generateDefaultControlBarItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _handlePaste(html.ClipboardEvent event) async {
|
||||||
|
try {
|
||||||
|
if (event.clipboardData!.files!.isNotEmpty) {
|
||||||
|
html.File imageFile = event.clipboardData!.files![0];
|
||||||
|
sendFileUseJs(imageFile);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print("Paste image failed: ${e.toString()}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hideAllPanel() {
|
hideAllPanel() {
|
||||||
|
|
@ -324,6 +349,8 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
addText: (int unicode) {
|
addText: (int unicode) {
|
||||||
final newText = String.fromCharCode(unicode);
|
final newText = String.fromCharCode(unicode);
|
||||||
widget.addStickerToText(newText);
|
widget.addStickerToText(newText);
|
||||||
|
entry?.remove();
|
||||||
|
entry = null;
|
||||||
},
|
},
|
||||||
addCustomEmojiText: ((String singleEmojiName) {
|
addCustomEmojiText: ((String singleEmojiName) {
|
||||||
String? emojiName = singleEmojiName.split('.png')[0];
|
String? emojiName = singleEmojiName.split('.png')[0];
|
||||||
|
|
@ -335,6 +362,8 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
}
|
}
|
||||||
final newText = '[$emojiName]';
|
final newText = '[$emojiName]';
|
||||||
widget.addStickerToText(newText);
|
widget.addStickerToText(newText);
|
||||||
|
entry?.remove();
|
||||||
|
entry = null;
|
||||||
}),
|
}),
|
||||||
defaultCustomEmojiStickerList:
|
defaultCustomEmojiStickerList:
|
||||||
widget.isUseDefaultEmoji ? ConstData.emojiList : [])
|
widget.isUseDefaultEmoji ? ConstData.emojiList : [])
|
||||||
|
|
@ -349,7 +378,7 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
Overlay.of(context)?.insert(entry!);
|
Overlay.of(context).insert(entry!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -363,12 +392,12 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
color: const Color(0x7F000000),
|
color: const Color(0x7F000000),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
Overlay.of(context)?.insert(entry!);
|
Overlay.of(context).insert(entry!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_removeOverlay() {
|
_removeOverlay() {
|
||||||
entry!.remove();
|
entry?.remove();
|
||||||
entry = null;
|
entry = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -399,20 +428,19 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
convID: convID,
|
convID: convID,
|
||||||
convType: convType),
|
convType: convType),
|
||||||
context);
|
context);
|
||||||
return;
|
} else {
|
||||||
|
File file = File(result.files.single.path!);
|
||||||
|
final int size = file.lengthSync();
|
||||||
|
final String savePath = file.path;
|
||||||
|
|
||||||
|
MessageUtils.handleMessageError(
|
||||||
|
model.sendFileMessage(
|
||||||
|
filePath: savePath,
|
||||||
|
size: size,
|
||||||
|
convID: convID,
|
||||||
|
convType: convType),
|
||||||
|
context);
|
||||||
}
|
}
|
||||||
|
|
||||||
File file = File(result.files.single.path!);
|
|
||||||
final int size = file.lengthSync();
|
|
||||||
final String savePath = file.path;
|
|
||||||
|
|
||||||
MessageUtils.handleMessageError(
|
|
||||||
model.sendFileMessage(
|
|
||||||
filePath: savePath,
|
|
||||||
size: size,
|
|
||||||
convID: convID,
|
|
||||||
convType: convType),
|
|
||||||
context);
|
|
||||||
} else {
|
} else {
|
||||||
throw TypeError();
|
throw TypeError();
|
||||||
}
|
}
|
||||||
|
|
@ -424,10 +452,11 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
|
|
||||||
List<Widget> generateBarIcons(
|
List<Widget> generateBarIcons(
|
||||||
List<DesktopControlBarItem> items, TUITheme theme) {
|
List<DesktopControlBarItem> items, TUITheme theme) {
|
||||||
|
final defaultItems = defaultControlBarItems.map((e) => e.item);
|
||||||
return items.map((e) {
|
return items.map((e) {
|
||||||
final GlobalKey key = GlobalKey();
|
final GlobalKey key = GlobalKey();
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(right: 6),
|
margin: const EdgeInsets.only(right: 10),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
final alignBox =
|
final alignBox =
|
||||||
|
|
@ -445,20 +474,39 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
textStyle: TextStyle(fontSize: 12, color: theme.white),
|
textStyle: TextStyle(fontSize: 12, color: theme.white),
|
||||||
message: e.showName,
|
message: e.showName,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration:
|
decoration: BoxDecoration(borderRadius: BorderRadius.circular(2)),
|
||||||
BoxDecoration(borderRadius: BorderRadius.circular(2)),
|
padding: const EdgeInsets.all(4),
|
||||||
padding: const EdgeInsets.all(4),
|
child: () {
|
||||||
child: e.imgPath != null
|
if (TencentUtils.checkString(e.svgPath) != null) {
|
||||||
? Image.asset(
|
return SvgPicture.asset(
|
||||||
e.imgPath!,
|
e.svgPath!,
|
||||||
key: key,
|
package: defaultItems.contains(e.item)
|
||||||
width: e.size ?? 20,
|
? 'tencent_cloud_chat_uikit'
|
||||||
height: e.size ?? 20,
|
: null,
|
||||||
)
|
key: key,
|
||||||
: Icon(e.icon,
|
width: e.size ?? 16,
|
||||||
key: key,
|
height: e.size ?? 16,
|
||||||
color: e.color ?? hexToColor("646a73"),
|
);
|
||||||
size: e.size ?? 20)),
|
}
|
||||||
|
if (TencentUtils.checkString(e.imgPath) != null) {
|
||||||
|
return Image.asset(
|
||||||
|
e.imgPath!,
|
||||||
|
package: defaultItems.contains(e.item)
|
||||||
|
? 'tencent_cloud_chat_uikit'
|
||||||
|
: null,
|
||||||
|
key: key,
|
||||||
|
width: e.size ?? 16,
|
||||||
|
height: e.size ?? 16,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Icon(
|
||||||
|
e.icon,
|
||||||
|
key: key,
|
||||||
|
color: e.color ?? hexToColor("646a73"),
|
||||||
|
size: e.size ?? 20,
|
||||||
|
);
|
||||||
|
}(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -658,10 +706,11 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_sendImageWithConfirmation(String file) async {
|
_sendImageWithConfirmation(
|
||||||
|
{String? fileName, Size? fileSize, required String filePath}) async {
|
||||||
final option1 = widget.currentConversation.showName ??
|
final option1 = widget.currentConversation.showName ??
|
||||||
(widget.conversationType == ConvType.group ? TIM_t("群聊") : TIM_t("对方"));
|
(widget.conversationType == ConvType.group ? TIM_t("群聊") : TIM_t("对方"));
|
||||||
final size = await ScreenshotHelper.getImageSize(file);
|
final size = fileSize ?? await ScreenshotHelper.getImageSize(filePath);
|
||||||
|
|
||||||
TUIKitWidePopup.showPopupWindow(
|
TUIKitWidePopup.showPopupWindow(
|
||||||
operationKey: TUIKitWideModalOperationKey.beforeSendScreenShot,
|
operationKey: TUIKitWideModalOperationKey.beforeSendScreenShot,
|
||||||
|
|
@ -679,12 +728,19 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
height: min(360, size.height / 2),
|
height: min(360, size.height / 2),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
launchUrl(Uri.file(file));
|
launchUrl(PlatformUtils().isWeb
|
||||||
|
? Uri.parse(filePath)
|
||||||
|
: Uri.file(filePath));
|
||||||
},
|
},
|
||||||
child: Image.file(
|
child: PlatformUtils().isWeb
|
||||||
File(file),
|
? Image.network(
|
||||||
height: min(360, size.height / 2),
|
filePath,
|
||||||
),
|
height: min(360, size.height / 2),
|
||||||
|
)
|
||||||
|
: Image.file(
|
||||||
|
File(filePath),
|
||||||
|
height: min(360, size.height / 2),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
|
|
@ -703,7 +759,8 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
MessageUtils.handleMessageError(
|
MessageUtils.handleMessageError(
|
||||||
widget.model.sendImageMessage(
|
widget.model.sendImageMessage(
|
||||||
imagePath: file,
|
imagePath: filePath,
|
||||||
|
imageName: fileName,
|
||||||
convID: widget.conversationID,
|
convID: widget.conversationID,
|
||||||
convType: widget.conversationType),
|
convType: widget.conversationType),
|
||||||
context);
|
context);
|
||||||
|
|
@ -720,12 +777,11 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
_sendScreenShot() async {
|
_sendScreenShot() async {
|
||||||
final file = await ScreenshotHelper.captureScreen();
|
final file = await ScreenshotHelper.captureScreen();
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
_sendImageWithConfirmation(file);
|
_sendImageWithConfirmation(filePath: file);
|
||||||
} else {}
|
} else {}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> generateControlBar(
|
generateDefaultControlBarItems() {
|
||||||
TUIChatSeparateViewModel model, TUITheme theme) {
|
|
||||||
final DesktopControlBarConfig config =
|
final DesktopControlBarConfig config =
|
||||||
widget.model.chatConfig.desktopControlBarConfig ??
|
widget.model.chatConfig.desktopControlBarConfig ??
|
||||||
DesktopControlBarConfig();
|
DesktopControlBarConfig();
|
||||||
|
|
@ -735,9 +791,9 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
item: "face",
|
item: "face",
|
||||||
showName: TIM_t("表情"),
|
showName: TIM_t("表情"),
|
||||||
onClick: (offset) {
|
onClick: (offset) {
|
||||||
_sendEmoji(offset, theme);
|
_sendEmoji(offset, widget.theme);
|
||||||
},
|
},
|
||||||
icon: Icons.mood),
|
svgPath: "images/svg/send_face.svg"),
|
||||||
if (config.showScreenshotButton && PlatformUtils().isDesktop)
|
if (config.showScreenshotButton && PlatformUtils().isDesktop)
|
||||||
DesktopControlBarItem(
|
DesktopControlBarItem(
|
||||||
item: "screenShot",
|
item: "screenShot",
|
||||||
|
|
@ -745,15 +801,15 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
onClick: (offset) {
|
onClick: (offset) {
|
||||||
_sendScreenShot();
|
_sendScreenShot();
|
||||||
},
|
},
|
||||||
icon: Icons.cut_outlined),
|
svgPath: "images/svg/send_screenshot.svg"),
|
||||||
if (config.showSendFileButton)
|
if (config.showSendFileButton)
|
||||||
DesktopControlBarItem(
|
DesktopControlBarItem(
|
||||||
item: "file",
|
item: "file",
|
||||||
showName: TIM_t("文件"),
|
showName: TIM_t("文件"),
|
||||||
onClick: (offset) {
|
onClick: (offset) {
|
||||||
_sendFile(widget.model, theme);
|
_sendFile(widget.model, widget.theme);
|
||||||
},
|
},
|
||||||
icon: Icons.file_copy_outlined),
|
svgPath: "images/svg/send_file.svg"),
|
||||||
if (config.showSendImageButton)
|
if (config.showSendImageButton)
|
||||||
DesktopControlBarItem(
|
DesktopControlBarItem(
|
||||||
item: "photo",
|
item: "photo",
|
||||||
|
|
@ -762,10 +818,10 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
if (PlatformUtils().isWeb) {
|
if (PlatformUtils().isWeb) {
|
||||||
_sendImageFileOnWeb(widget.model);
|
_sendImageFileOnWeb(widget.model);
|
||||||
} else {
|
} else {
|
||||||
_sendMediaMessage(widget.model, theme, FileType.image);
|
_sendMediaMessage(widget.model, widget.theme, FileType.image);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
icon: Icons.image_outlined),
|
svgPath: "images/svg/send_image.svg"),
|
||||||
if (config.showSendVideoButton)
|
if (config.showSendVideoButton)
|
||||||
DesktopControlBarItem(
|
DesktopControlBarItem(
|
||||||
item: "video",
|
item: "video",
|
||||||
|
|
@ -774,10 +830,10 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
if (PlatformUtils().isWeb) {
|
if (PlatformUtils().isWeb) {
|
||||||
_sendVideoFileOnWeb(widget.model);
|
_sendVideoFileOnWeb(widget.model);
|
||||||
} else {
|
} else {
|
||||||
_sendMediaMessage(widget.model, theme, FileType.video);
|
_sendMediaMessage(widget.model, widget.theme, FileType.video);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
icon: Icons.video_library_outlined),
|
svgPath: "images/svg/send_video.svg"),
|
||||||
if (config.showMessageHistoryButton)
|
if (config.showMessageHistoryButton)
|
||||||
DesktopControlBarItem(
|
DesktopControlBarItem(
|
||||||
item: "history",
|
item: "history",
|
||||||
|
|
@ -801,19 +857,41 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
onTapConversation: (V2TimConversation conversation,
|
onTapConversation: (V2TimConversation conversation,
|
||||||
V2TimMessage? message) {},
|
V2TimMessage? message) {},
|
||||||
),
|
),
|
||||||
theme: theme);
|
theme: widget.theme);
|
||||||
},
|
},
|
||||||
icon: Icons.chat_outlined),
|
svgPath: "images/svg/message_history.svg"),
|
||||||
|
];
|
||||||
|
defaultControlBarItems = itemsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> generateControlBar(
|
||||||
|
TUIChatSeparateViewModel model, TUITheme theme) {
|
||||||
|
final List<DesktopControlBarItem> itemsList = [
|
||||||
|
...defaultControlBarItems,
|
||||||
...(widget.model.chatConfig.additionalDesktopControlBarItems ?? [])
|
...(widget.model.chatConfig.additionalDesktopControlBarItems ?? [])
|
||||||
];
|
];
|
||||||
|
|
||||||
return generateBarIcons(itemsList, theme);
|
return generateBarIcons(itemsList, theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendFileUseJs(html.File file) {
|
||||||
|
final mimeType = file.type.split('/');
|
||||||
|
final type = mimeType[0];
|
||||||
|
final blobUrl = html.Url.createObjectUrl(file);
|
||||||
|
if (type == 'image') {
|
||||||
|
_sendImageWithConfirmation(
|
||||||
|
filePath: blobUrl,
|
||||||
|
fileName: file.name,
|
||||||
|
fileSize: const Size(500, 500));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _handleKeyEvent(RawKeyEvent event) async {
|
Future<void> _handleKeyEvent(RawKeyEvent event) async {
|
||||||
if ((event.isKeyPressed(LogicalKeyboardKey.controlLeft) &&
|
if (PlatformUtils().isDesktop &&
|
||||||
event.logicalKey == LogicalKeyboardKey.keyV) ||
|
((event.isKeyPressed(LogicalKeyboardKey.controlLeft) &&
|
||||||
(event.isMetaPressed && event.logicalKey == LogicalKeyboardKey.keyV)) {
|
event.logicalKey == LogicalKeyboardKey.keyV) ||
|
||||||
|
(event.isMetaPressed &&
|
||||||
|
event.logicalKey == LogicalKeyboardKey.keyV))) {
|
||||||
final bytes = await Pasteboard.image;
|
final bytes = await Pasteboard.image;
|
||||||
if (bytes != null) {
|
if (bytes != null) {
|
||||||
String directory;
|
String directory;
|
||||||
|
|
@ -838,7 +916,7 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
await scDirectory.create(recursive: true);
|
await scDirectory.create(recursive: true);
|
||||||
}
|
}
|
||||||
await file.writeAsBytes(bytes.toList());
|
await file.writeAsBytes(bytes.toList());
|
||||||
_sendImageWithConfirmation(filePath);
|
_sendImageWithConfirmation(filePath: filePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -857,10 +935,6 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
}
|
}
|
||||||
widget.handleAtText(value);
|
widget.handleAtText(value);
|
||||||
widget.handleSendEditStatus(value, true);
|
widget.handleSendEditStatus(value, true);
|
||||||
final isEmpty = value.isEmpty;
|
|
||||||
if (isEmpty) {
|
|
||||||
widget.handleSoftKeyBoardDelete();
|
|
||||||
}
|
|
||||||
}, const Duration(milliseconds: 80));
|
}, const Duration(milliseconds: 80));
|
||||||
|
|
||||||
final MediaQueryData data = MediaQuery.of(context);
|
final MediaQueryData data = MediaQuery.of(context);
|
||||||
|
|
@ -873,7 +947,7 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
focusNode: textFocusNode,
|
focusNode: textFocusNode,
|
||||||
onKey: _handleKeyEvent,
|
onKey: _handleKeyEvent,
|
||||||
child: Container(
|
child: Container(
|
||||||
color: widget.backgroundColor,
|
color: widget.backgroundColor ?? theme.desktopChatMessageInputBgColor,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
|
@ -899,7 +973,8 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 35,
|
height: 35,
|
||||||
color: theme.weakBackgroundColor,
|
color: widget.backgroundColor ??
|
||||||
|
theme.desktopChatMessageInputBgColor,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
TIM_t(widget.forbiddenText!),
|
TIM_t(widget.forbiddenText!),
|
||||||
|
|
@ -913,45 +988,43 @@ class _TIMUIKitTextFieldLayoutWideState
|
||||||
)),
|
)),
|
||||||
if (widget.forbiddenText == null)
|
if (widget.forbiddenText == null)
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Scrollbar(
|
child: ExtendedTextField(
|
||||||
controller: _scrollController,
|
scrollController: _scrollController,
|
||||||
child: ExtendedTextField(
|
autofocus: true,
|
||||||
autofocus: true,
|
maxLines: widget
|
||||||
maxLines: 6,
|
.model.chatConfig.desktopMessageInputFieldLines,
|
||||||
minLines: 6,
|
minLines: widget
|
||||||
focusNode: widget.focusNode,
|
.model.chatConfig.desktopMessageInputFieldLines,
|
||||||
onChanged: debounceFunc,
|
focusNode: widget.focusNode,
|
||||||
keyboardType: TextInputType.multiline,
|
onChanged: debounceFunc,
|
||||||
textInputAction: PlatformUtils().isAndroid
|
keyboardType: TextInputType.multiline,
|
||||||
? TextInputAction.newline
|
onEditingComplete: () {
|
||||||
: TextInputAction.send,
|
// // widget.onSubmitted();
|
||||||
onEditingComplete: () {
|
},
|
||||||
widget.onSubmitted();
|
textAlignVertical: TextAlignVertical.top,
|
||||||
},
|
style: const TextStyle(fontSize: 14),
|
||||||
textAlignVertical: TextAlignVertical.top,
|
decoration: InputDecoration(
|
||||||
style: const TextStyle(fontSize: 14),
|
hoverColor: hexToColor("fafafa"),
|
||||||
decoration: InputDecoration(
|
border: InputBorder.none,
|
||||||
hoverColor: hexToColor("fafafa"),
|
hintStyle: const TextStyle(
|
||||||
border: InputBorder.none,
|
color: Color(0xffAEA4A3),
|
||||||
hintStyle: const TextStyle(
|
|
||||||
color: Color(0xffAEA4A3),
|
|
||||||
),
|
|
||||||
fillColor: hexToColor("fafafa"),
|
|
||||||
filled: true,
|
|
||||||
isDense: true,
|
|
||||||
hintText: widget.hintText ?? '',
|
|
||||||
),
|
),
|
||||||
controller: widget.textEditingController,
|
fillColor: widget.backgroundColor ??
|
||||||
specialTextSpanBuilder: PlatformUtils().isWeb
|
theme.desktopChatMessageInputBgColor ??
|
||||||
? null
|
hexToColor("fafafa"),
|
||||||
: DefaultSpecialTextSpanBuilder(
|
filled: true,
|
||||||
isUseDefaultEmoji:
|
isDense: true,
|
||||||
widget.isUseDefaultEmoji,
|
hintText: widget.hintText ?? '',
|
||||||
customEmojiStickerList:
|
),
|
||||||
widget.customEmojiStickerList,
|
controller: widget.textEditingController,
|
||||||
showAtBackground: true,
|
specialTextSpanBuilder: PlatformUtils().isWeb
|
||||||
)),
|
? null
|
||||||
),
|
: DefaultSpecialTextSpanBuilder(
|
||||||
|
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
||||||
|
customEmojiStickerList:
|
||||||
|
widget.customEmojiStickerList,
|
||||||
|
showAtBackground: true,
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,10 @@ class TIMUIKitChat extends StatefulWidget {
|
||||||
/// Avatar and name in message reaction tap callback.
|
/// Avatar and name in message reaction tap callback.
|
||||||
final void Function(String userID, TapDownDetails tapDetails)? onTapAvatar;
|
final void Function(String userID, TapDownDetails tapDetails)? onTapAvatar;
|
||||||
|
|
||||||
|
/// Avatar and name in message reaction secondary tap callback.
|
||||||
|
final void Function(String userID, TapDownDetails tapDetails)?
|
||||||
|
onSecondaryTapAvatar;
|
||||||
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
"Nickname will not shows in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
|
"Nickname will not shows in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
|
||||||
|
|
||||||
|
|
@ -145,12 +149,13 @@ class TIMUIKitChat extends StatefulWidget {
|
||||||
this.conversationShowName,
|
this.conversationShowName,
|
||||||
this.abstractMessageBuilder,
|
this.abstractMessageBuilder,
|
||||||
this.onTapAvatar,
|
this.onTapAvatar,
|
||||||
@Deprecated("Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
|
@Deprecated(
|
||||||
this.showNickName = false,
|
"Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
|
||||||
|
this.showNickName = false,
|
||||||
this.showTotalUnReadCount = false,
|
this.showTotalUnReadCount = false,
|
||||||
this.messageItemBuilder,
|
this.messageItemBuilder,
|
||||||
@Deprecated("Please use [extraTipsActionItemBuilder] instead")
|
@Deprecated("Please use [extraTipsActionItemBuilder] instead")
|
||||||
this.exteraTipsActionItemBuilder,
|
this.exteraTipsActionItemBuilder,
|
||||||
this.extraTipsActionItemBuilder,
|
this.extraTipsActionItemBuilder,
|
||||||
this.draftText,
|
this.draftText,
|
||||||
this.textFieldHintText,
|
this.textFieldHintText,
|
||||||
|
|
@ -170,7 +175,8 @@ class TIMUIKitChat extends StatefulWidget {
|
||||||
this.topFixWidget = const SizedBox(),
|
this.topFixWidget = const SizedBox(),
|
||||||
this.textFieldBuilder,
|
this.textFieldBuilder,
|
||||||
this.customEmojiStickerList = const [],
|
this.customEmojiStickerList = const [],
|
||||||
this.customAppBar})
|
this.customAppBar,
|
||||||
|
this.onSecondaryTapAvatar})
|
||||||
: super(key: key) {
|
: super(key: key) {
|
||||||
startTime = DateTime.now().millisecondsSinceEpoch;
|
startTime = DateTime.now().millisecondsSinceEpoch;
|
||||||
}
|
}
|
||||||
|
|
@ -239,7 +245,9 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
|
||||||
model = TUIChatSeparateViewModel();
|
model = TUIChatSeparateViewModel();
|
||||||
model.abstractMessageBuilder = widget.abstractMessageBuilder;
|
model.abstractMessageBuilder = widget.abstractMessageBuilder;
|
||||||
model.onTapAvatar = widget.onTapAvatar;
|
model.onTapAvatar = widget.onTapAvatar;
|
||||||
|
textFieldController.requestFocus();
|
||||||
Future.delayed(const Duration(milliseconds: 50), () {
|
Future.delayed(const Duration(milliseconds: 50), () {
|
||||||
|
textFieldController.requestFocus();
|
||||||
try {
|
try {
|
||||||
autoController.jumpTo(
|
autoController.jumpTo(
|
||||||
autoController.position.minScrollExtent,
|
autoController.position.minScrollExtent,
|
||||||
|
|
@ -317,6 +325,7 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
|
||||||
return TIMUIKitChatProviderScope(
|
return TIMUIKitChatProviderScope(
|
||||||
model: model,
|
model: model,
|
||||||
groupID: widget.groupID,
|
groupID: widget.groupID,
|
||||||
|
scrollController: autoController,
|
||||||
textFieldController: textFieldController,
|
textFieldController: textFieldController,
|
||||||
conversationID: _getConvID(),
|
conversationID: _getConvID(),
|
||||||
conversationType: _getConvType(),
|
conversationType: _getConvType(),
|
||||||
|
|
@ -331,6 +340,8 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
|
||||||
Provider.of<TUIChatGlobalModel>(context, listen: true);
|
Provider.of<TUIChatGlobalModel>(context, listen: true);
|
||||||
|
|
||||||
widget.controller?.model = model;
|
widget.controller?.model = model;
|
||||||
|
widget.controller?.textFieldController = textFieldController;
|
||||||
|
widget.controller?.scrollController = autoController;
|
||||||
List<V2TimGroupApplication> filteredApplicationList = [];
|
List<V2TimGroupApplication> filteredApplicationList = [];
|
||||||
if (widget.conversationType == ConvType.group &&
|
if (widget.conversationType == ConvType.group &&
|
||||||
widget.onDealWithGroupApplication != null) {
|
widget.onDealWithGroupApplication != null) {
|
||||||
|
|
@ -431,11 +442,8 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
|
||||||
tongueItemBuilder: widget.tongueItemBuilder,
|
tongueItemBuilder: widget.tongueItemBuilder,
|
||||||
onLongPressForOthersHeadPortrait:
|
onLongPressForOthersHeadPortrait:
|
||||||
(String? userId, String? nickName) {
|
(String? userId, String? nickName) {
|
||||||
if (widget.conversationType !=
|
textFieldController.longPressToAt(
|
||||||
ConvType.c2c) {
|
nickName, userId);
|
||||||
textFieldController.longPressToAt(
|
|
||||||
nickName, userId);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
mainHistoryListConfig:
|
mainHistoryListConfig:
|
||||||
widget.mainHistoryListConfig,
|
widget.mainHistoryListConfig,
|
||||||
|
|
@ -445,6 +453,8 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
|
||||||
widget.exteraTipsActionItemBuilder,
|
widget.exteraTipsActionItemBuilder,
|
||||||
conversationType: _getConvType(),
|
conversationType: _getConvType(),
|
||||||
scrollController: autoController,
|
scrollController: autoController,
|
||||||
|
onSecondaryTapAvatar:
|
||||||
|
widget.onSecondaryTapAvatar,
|
||||||
onTapAvatar: widget.onTapAvatar,
|
onTapAvatar: widget.onTapAvatar,
|
||||||
// ignore: deprecated_member_use_from_same_package
|
// ignore: deprecated_member_use_from_same_package
|
||||||
showNickName: widget.showNickName,
|
showNickName: widget.showNickName,
|
||||||
|
|
@ -463,7 +473,6 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
|
||||||
: (widget.textFieldBuilder != null
|
: (widget.textFieldBuilder != null
|
||||||
? widget.textFieldBuilder!(context)
|
? widget.textFieldBuilder!(context)
|
||||||
: TIMUIKitInputTextField(
|
: TIMUIKitInputTextField(
|
||||||
key: inputTextFieldState,
|
|
||||||
atMemberPanelScroll:
|
atMemberPanelScroll:
|
||||||
atMemberPanelScroll,
|
atMemberPanelScroll,
|
||||||
groupType:
|
groupType:
|
||||||
|
|
@ -483,7 +492,8 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
|
||||||
scrollController: autoController,
|
scrollController: autoController,
|
||||||
conversationID: _getConvID(),
|
conversationID: _getConvID(),
|
||||||
conversationType: _getConvType(),
|
conversationType: _getConvType(),
|
||||||
initText: widget.draftText,
|
initText: widget.draftText ??
|
||||||
|
widget.conversation.draftText,
|
||||||
hintText: widget.textFieldHintText,
|
hintText: widget.textFieldHintText,
|
||||||
showMorePanel: widget.config
|
showMorePanel: widget.config
|
||||||
?.isAllowShowMorePanel ??
|
?.isAllowShowMorePanel ??
|
||||||
|
|
@ -508,9 +518,8 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
|
||||||
),
|
),
|
||||||
AtMemberPanel(
|
AtMemberPanel(
|
||||||
atMemberPanelScroll: atMemberPanelScroll,
|
atMemberPanelScroll: atMemberPanelScroll,
|
||||||
onSelectMember: (member) => inputTextFieldState
|
onSelectMember: (member) =>
|
||||||
.currentState
|
textFieldController.handleAtMember(member),
|
||||||
?.handleAtMember(memberInfo: member),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -555,6 +564,8 @@ class TIMUIKitChatProviderScope extends StatelessWidget {
|
||||||
|
|
||||||
final bool? isBuild;
|
final bool? isBuild;
|
||||||
|
|
||||||
|
final AutoScrollController? scrollController;
|
||||||
|
|
||||||
TIMUIKitChatProviderScope(
|
TIMUIKitChatProviderScope(
|
||||||
{Key? key,
|
{Key? key,
|
||||||
this.child,
|
this.child,
|
||||||
|
|
@ -568,13 +579,16 @@ class TIMUIKitChatProviderScope extends StatelessWidget {
|
||||||
required this.conversationType,
|
required this.conversationType,
|
||||||
this.controller,
|
this.controller,
|
||||||
this.config,
|
this.config,
|
||||||
this.lifeCycle})
|
this.lifeCycle,
|
||||||
|
this.scrollController})
|
||||||
: super(key: key) {
|
: super(key: key) {
|
||||||
if (isBuild ?? false) {
|
if (isBuild ?? false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
model ??= TUIChatSeparateViewModel();
|
model ??= TUIChatSeparateViewModel();
|
||||||
controller?.model = model;
|
controller?.model = model;
|
||||||
|
controller?.textFieldController = textFieldController;
|
||||||
|
controller?.scrollController = scrollController;
|
||||||
if (config != null) {
|
if (config != null) {
|
||||||
model?.chatConfig = config!;
|
model?.chatConfig = config!;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ class TIMUIKitChatConfig {
|
||||||
/// Whether to use the default emoji
|
/// Whether to use the default emoji
|
||||||
final bool isUseDefaultEmoji;
|
final bool isUseDefaultEmoji;
|
||||||
|
|
||||||
/// Is show avatar on history message list.
|
/// Whether shows avatar on history message list.
|
||||||
/// [Default]: true.
|
/// [Default]: true.
|
||||||
final bool isShowAvatar;
|
final bool isShowAvatar;
|
||||||
|
|
||||||
|
|
@ -157,13 +157,13 @@ class TIMUIKitChatConfig {
|
||||||
final List<MessageHoverControlItem>? additionalDesktopMessageHoverBarItem;
|
final List<MessageHoverControlItem>? additionalDesktopMessageHoverBarItem;
|
||||||
|
|
||||||
/// This list contains additional items that are displayed
|
/// This list contains additional items that are displayed
|
||||||
/// on the control bar on desktop (macOS, Windows, and desktop version of Web).
|
/// on the message sending area control bar on desktop (macOS, Windows, and desktop version of Web).
|
||||||
/// Use `desktopControlBarConfig` to configure whether or not to show the default control items.
|
/// Use `desktopControlBarConfig` to configure whether or not to show the default control items.
|
||||||
final List<DesktopControlBarItem>? additionalDesktopControlBarItems;
|
final List<DesktopControlBarItem>? additionalDesktopControlBarItems;
|
||||||
|
|
||||||
/// This configuration is used for the control bar
|
/// This configuration is used for the control bar
|
||||||
/// on desktop (macOS, Windows, and desktop version of Web).
|
/// on desktop (macOS, Windows, and desktop version of Web).
|
||||||
/// Use `desktopControlBarConfig` to add additional items to the desktop control bar, in addition to the default ones.
|
/// Use `desktopControlBarConfig` to add additional items to the desktop message sending area control bar, in addition to the default ones.
|
||||||
final DesktopControlBarConfig? desktopControlBarConfig;
|
final DesktopControlBarConfig? desktopControlBarConfig;
|
||||||
|
|
||||||
/// Controls whether users are allowed to mention another user in the group by long-pressing on their avatar.
|
/// Controls whether users are allowed to mention another user in the group by long-pressing on their avatar.
|
||||||
|
|
@ -178,15 +178,24 @@ class TIMUIKitChatConfig {
|
||||||
/// [Default]: true on Desktop while false on Mobile.
|
/// [Default]: true on Desktop while false on Mobile.
|
||||||
final bool? isEnableTextSelection;
|
final bool? isEnableTextSelection;
|
||||||
|
|
||||||
|
/// Controls whether enable the control bar shows when hovering a message on Desktop.
|
||||||
|
/// [Default]: true.
|
||||||
|
final bool isUseMessageHoverBarOnDesktop;
|
||||||
|
|
||||||
|
/// Define the lines in the text message input field on Desktop.
|
||||||
|
final int desktopMessageInputFieldLines;
|
||||||
|
|
||||||
const TIMUIKitChatConfig(
|
const TIMUIKitChatConfig(
|
||||||
{this.onTapLink,
|
{this.onTapLink,
|
||||||
this.timeDividerConfig,
|
this.timeDividerConfig,
|
||||||
this.isAutoReportRead = true,
|
this.isAutoReportRead = true,
|
||||||
this.faceURIPrefix,
|
this.faceURIPrefix,
|
||||||
this.faceURISuffix,
|
this.faceURISuffix,
|
||||||
this.textHeight = 1.3,
|
this.textHeight = 1.3,
|
||||||
|
this.desktopMessageInputFieldLines = 6,
|
||||||
this.isAtWhenReply = true,
|
this.isAtWhenReply = true,
|
||||||
this.notificationAndroidSound = "",
|
this.notificationAndroidSound = "",
|
||||||
|
this.isUseMessageHoverBarOnDesktop = true,
|
||||||
this.isSupportMarkdownForTextMessage = false,
|
this.isSupportMarkdownForTextMessage = false,
|
||||||
this.notificationExt,
|
this.notificationExt,
|
||||||
this.isUseMessageReaction = true,
|
this.isUseMessageReaction = true,
|
||||||
|
|
@ -212,7 +221,7 @@ class TIMUIKitChatConfig {
|
||||||
this.desktopControlBarConfig,
|
this.desktopControlBarConfig,
|
||||||
this.isAllowLongPressMessage = true,
|
this.isAllowLongPressMessage = true,
|
||||||
this.isAllowClickAvatar = true,
|
this.isAllowClickAvatar = true,
|
||||||
this.isEnableTextSelection,
|
this.isEnableTextSelection,
|
||||||
this.additionalDesktopMessageHoverBarItem,
|
this.additionalDesktopMessageHoverBarItem,
|
||||||
this.isShowGroupReadingStatus = true,
|
this.isShowGroupReadingStatus = true,
|
||||||
this.isReportGroupReadingStatus = true,
|
this.isReportGroupReadingStatus = true,
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ class MultiSelectPanel extends TIMUIKitStatelessWidget {
|
||||||
Provider.of<TUIChatSeparateViewModel>(context);
|
Provider.of<TUIChatSeparateViewModel>(context);
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
desktopWidget: Container(
|
desktopWidget: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: theme.selectPanelBgColor ?? theme.primaryColor,
|
color: theme.selectPanelBgColor ?? theme.primaryColor,
|
||||||
|
|
|
||||||
|
|
@ -64,8 +64,6 @@ class TIMUIKitConversation extends StatefulWidget {
|
||||||
final bool isShowOnlineStatus;
|
final bool isShowOnlineStatus;
|
||||||
|
|
||||||
/// Control if shows the identifier that the conversation has a draft text, inputted in previous.
|
/// Control if shows the identifier that the conversation has a draft text, inputted in previous.
|
||||||
/// Also, you have better specifying the `draftText` field for `TIMUIKitChat`, from the `draftText` in `V2TimConversation`,
|
|
||||||
/// to meet the identifier shows here.
|
|
||||||
final bool isShowDraft;
|
final bool isShowDraft;
|
||||||
|
|
||||||
const TIMUIKitConversation(
|
const TIMUIKitConversation(
|
||||||
|
|
@ -375,7 +373,7 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
|
||||||
: isPined
|
: isPined
|
||||||
? theme.conversationItemPinedBgColor
|
? theme.conversationItemPinedBgColor
|
||||||
: theme.conversationItemBgColor,
|
: theme.conversationItemBgColor,
|
||||||
child: InkWell(
|
child: GestureDetector(
|
||||||
child: TIMUIKitConversationItem(
|
child: TIMUIKitConversationItem(
|
||||||
isCurrent: isCurrent,
|
isCurrent: isCurrent,
|
||||||
isShowDraft: widget.isShowDraft,
|
isShowDraft: widget.isShowDraft,
|
||||||
|
|
@ -402,11 +400,12 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
desktopWidget: AutoScrollTag(
|
desktopWidget: AutoScrollTag(
|
||||||
key: ValueKey(conversationItem.conversationID),
|
key: ValueKey(conversationItem.conversationID),
|
||||||
controller: _autoScrollController,
|
controller: _autoScrollController,
|
||||||
index: index,
|
index: index,
|
||||||
child: GestureDetector(
|
child: InkWell(
|
||||||
onSecondaryTapDown: (details) {
|
onSecondaryTapDown: (details) {
|
||||||
TUIKitWidePopup.showPopupWindow(
|
TUIKitWidePopup.showPopupWindow(
|
||||||
operationKey: TUIKitWideModalOperationKey
|
operationKey: TUIKitWideModalOperationKey
|
||||||
|
|
@ -450,6 +449,7 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
defaultWidget: SlidableAutoCloseBehavior(
|
defaultWidget: SlidableAutoCloseBehavior(
|
||||||
child: EasyRefresh(
|
child: EasyRefresh(
|
||||||
header: CustomizeBallPulseHeader(color: theme.primaryColor),
|
header: CustomizeBallPulseHeader(color: theme.primaryColor),
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ class _AddGroupMemberPageState extends TIMUIKitState<AddGroupMemberPage> {
|
||||||
final TUITheme theme = value.theme;
|
final TUITheme theme = value.theme;
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
desktopWidget: Container(
|
desktopWidget: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: ContactList(
|
child: ContactList(
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ class _DeleteGroupMemberPageState extends TIMUIKitState<DeleteGroupMemberPage> {
|
||||||
final TUITheme theme = value.theme;
|
final TUITheme theme = value.theme;
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
desktopWidget: Container(
|
desktopWidget: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: GroupProfileMemberList(
|
child: GroupProfileMemberList(
|
||||||
|
|
|
||||||
|
|
@ -376,7 +376,7 @@ class _GroupProfileGroupManagePageState
|
||||||
size: 16),
|
size: 16),
|
||||||
onClick: () {
|
onClick: () {
|
||||||
widget.model.muteGroupMember(
|
widget.model.muteGroupMember(
|
||||||
e?.userID ?? "",
|
e.userID,
|
||||||
false,
|
false,
|
||||||
serverTime);
|
serverTime);
|
||||||
onClose();
|
onClose();
|
||||||
|
|
@ -409,6 +409,7 @@ class _GroupProfileGroupManagePageState
|
||||||
}
|
}
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
desktopWidget: managePage(),
|
desktopWidget: managePage(),
|
||||||
defaultWidget: Scaffold(
|
defaultWidget: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
|
@ -501,6 +502,7 @@ Widget _buildListItem(BuildContext context, V2TimGroupMemberFullInfo memberInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
desktopWidget: nameItem(),
|
desktopWidget: nameItem(),
|
||||||
defaultWidget: SingleChildScrollView(
|
defaultWidget: SingleChildScrollView(
|
||||||
child: Slidable(endActionPane: endActionPane, child: nameItem())));
|
child: Slidable(endActionPane: endActionPane, child: nameItem())));
|
||||||
|
|
@ -724,7 +726,7 @@ class _GroupProfileSetManagerPageState
|
||||||
Icons.remove_circle_outline,
|
Icons.remove_circle_outline,
|
||||||
size: 16),
|
size: 16),
|
||||||
onClick: () {
|
onClick: () {
|
||||||
_removeAdmin(context, e!);
|
_removeAdmin(context, e);
|
||||||
onClose();
|
onClose();
|
||||||
}),
|
}),
|
||||||
]));
|
]));
|
||||||
|
|
@ -758,6 +760,7 @@ class _GroupProfileSetManagerPageState
|
||||||
}
|
}
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
desktopWidget: adminPage(),
|
desktopWidget: adminPage(),
|
||||||
defaultWidget: Scaffold(
|
defaultWidget: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
|
@ -876,6 +879,7 @@ class _GroupProfileAddAdminState extends TIMUIKitState<GroupProfileAddAdmin> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
desktopWidget: Container(
|
desktopWidget: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: addAdminPage(),
|
child: addAdminPage(),
|
||||||
|
|
|
||||||
|
|
@ -76,11 +76,11 @@ class TIMUIKitProfile extends StatefulWidget {
|
||||||
/// The life cycle hooks for user profile business logic
|
/// The life cycle hooks for user profile business logic
|
||||||
final ProfileLifeCycle? lifeCycle;
|
final ProfileLifeCycle? lifeCycle;
|
||||||
|
|
||||||
/// If the loading user is self.
|
/// Whether the specify user is current logged in user.
|
||||||
/// Default: [false].
|
/// Default: [false].
|
||||||
final bool isSelf;
|
final bool isSelf;
|
||||||
|
|
||||||
/// Is use the small card mode on Desktop. Usually shows on the Chat page.
|
/// Whether use the small card mode on Desktop. Usually shows on the Chat page.
|
||||||
final bool smallCardMode;
|
final bool smallCardMode;
|
||||||
|
|
||||||
const TIMUIKitProfile(
|
const TIMUIKitProfile(
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ class TIMUIKitProfileUserInfoCard extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
defaultWidget: TIMUIKitProfileUserInfoCardNarrow(
|
defaultWidget: TIMUIKitProfileUserInfoCardNarrow(
|
||||||
userInfo: userInfo,
|
userInfo: userInfo,
|
||||||
isJumpToPersonalProfile: isJumpToPersonalProfile,
|
isJumpToPersonalProfile: isJumpToPersonalProfile,
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ class TIMUIKitSearchItem extends TIMUIKitStatelessWidget {
|
||||||
final TUITheme theme = value.theme;
|
final TUITheme theme = value.theme;
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
defaultWidget: GestureDetector(
|
defaultWidget: GestureDetector(
|
||||||
onTap: onClick,
|
onTap: onClick,
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,16 @@ class LinkPreviewEntry {
|
||||||
/// get the text message with hyperlinks
|
/// get the text message with hyperlinks
|
||||||
static LinkPreviewText? getHyperlinksText(String messageText, bool isMarkdown,
|
static LinkPreviewText? getHyperlinksText(String messageText, bool isMarkdown,
|
||||||
{Function(String)? onLinkTap,
|
{Function(String)? onLinkTap,
|
||||||
bool? isEnableTextSelection,
|
bool isEnableTextSelection = false,
|
||||||
bool isUseDefaultEmoji = false,
|
bool isUseDefaultEmoji = false,
|
||||||
List customEmojiStickerList = const []}) {
|
List customEmojiStickerList = const []}) {
|
||||||
return ({TextStyle? style}) {
|
return ({TextStyle? style}) {
|
||||||
return isMarkdown
|
return isMarkdown
|
||||||
? LinkTextMarkdown(
|
? LinkTextMarkdown(
|
||||||
messageText: messageText, style: style, onLinkTap: onLinkTap)
|
isEnableTextSelection: isEnableTextSelection,
|
||||||
|
messageText: replaceSingleNewlineWithTwo(messageText),
|
||||||
|
style: style,
|
||||||
|
onLinkTap: onLinkTap)
|
||||||
: LinkText(
|
: LinkText(
|
||||||
isEnableTextSelection: isEnableTextSelection,
|
isEnableTextSelection: isEnableTextSelection,
|
||||||
messageText: messageText,
|
messageText: messageText,
|
||||||
|
|
@ -27,6 +30,13 @@ class LinkPreviewEntry {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String replaceSingleNewlineWithTwo(String inputText) {
|
||||||
|
return inputText.replaceAllMapped(
|
||||||
|
RegExp(r'(?<!\n)\n(?!\n)'),
|
||||||
|
(match) => '\n\n',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// get the [LinkPreviewContent] with preview widget and website information for the first link.
|
/// get the [LinkPreviewContent] with preview widget and website information for the first link.
|
||||||
/// If you provide `onUpdateMessage(String linkInfoJson)`, it can save the link info to local custom data than call updating the message on UI automatically.
|
/// If you provide `onUpdateMessage(String linkInfoJson)`, it can save the link info to local custom data than call updating the message on UI automatically.
|
||||||
static Future<LinkPreviewContent?> getFirstLinkPreviewContent(
|
static Future<LinkPreviewContent?> getFirstLinkPreviewContent(
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
// ignore_for_file: deprecated_member_use
|
// ignore_for_file: deprecated_member_use
|
||||||
|
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
|
import 'package:extended_text/extended_text.dart';
|
||||||
import 'package:tencent_extended_text/extended_text.dart';
|
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
|
|
@ -20,14 +19,21 @@ class LinkTextMarkdown extends TIMStatelessWidget {
|
||||||
/// text style for default words
|
/// text style for default words
|
||||||
final TextStyle? style;
|
final TextStyle? style;
|
||||||
|
|
||||||
|
final bool? isEnableTextSelection;
|
||||||
|
|
||||||
const LinkTextMarkdown(
|
const LinkTextMarkdown(
|
||||||
{Key? key, required this.messageText, this.onLinkTap, this.style})
|
{Key? key,
|
||||||
|
required this.messageText,
|
||||||
|
this.isEnableTextSelection,
|
||||||
|
this.onLinkTap,
|
||||||
|
this.style})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget timBuild(BuildContext context) {
|
Widget timBuild(BuildContext context) {
|
||||||
return MarkdownBody(
|
return MarkdownBody(
|
||||||
data: messageText,
|
data: messageText,
|
||||||
|
selectable: isEnableTextSelection ?? false,
|
||||||
styleSheet: MarkdownStyleSheet.fromTheme(ThemeData(
|
styleSheet: MarkdownStyleSheet.fromTheme(ThemeData(
|
||||||
textTheme: TextTheme(
|
textTheme: TextTheme(
|
||||||
bodyText2: style ?? const TextStyle(fontSize: 16.0))))
|
bodyText2: style ?? const TextStyle(fontSize: 16.0))))
|
||||||
|
|
@ -40,9 +46,9 @@ class LinkTextMarkdown extends TIMStatelessWidget {
|
||||||
String title,
|
String title,
|
||||||
) {
|
) {
|
||||||
if (onLinkTap != null) {
|
if (onLinkTap != null) {
|
||||||
onLinkTap!(link);
|
onLinkTap!(href ?? "");
|
||||||
} else {
|
} else {
|
||||||
LinkUtils.launchURL(context, link);
|
LinkUtils.launchURL(context, href ?? "");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -130,15 +136,8 @@ class LinkText extends TIMStatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget timBuild(BuildContext context) {
|
Widget timBuild(BuildContext context) {
|
||||||
final isDesktopScreen =
|
return ExtendedText(_getContentSpan(messageText, context), softWrap: true,
|
||||||
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
onSpecialTextTap: (dynamic parameter) {
|
||||||
return
|
|
||||||
// Text.rich(
|
|
||||||
// TextSpan(children: [..._getContentSpan(messageText, context)]),
|
|
||||||
// style: style ?? const TextStyle(fontSize: 16.0),
|
|
||||||
// );
|
|
||||||
ExtendedText(_getContentSpan(messageText, context), softWrap: true,
|
|
||||||
onSpecialTextTap: (dynamic parameter) {
|
|
||||||
if (parameter.toString().startsWith('\$')) {
|
if (parameter.toString().startsWith('\$')) {
|
||||||
if (onLinkTap != null) {
|
if (onLinkTap != null) {
|
||||||
onLinkTap!((parameter.toString()).replaceAll('\$', ''));
|
onLinkTap!((parameter.toString()).replaceAll('\$', ''));
|
||||||
|
|
@ -148,14 +147,11 @@ class LinkText extends TIMStatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selectionEnabled: isEnableTextSelection != null
|
style: style ?? const TextStyle(fontSize: 16.0),
|
||||||
? isEnableTextSelection!
|
specialTextSpanBuilder: DefaultSpecialTextSpanBuilder(
|
||||||
: isDesktopScreen,
|
isUseDefaultEmoji: isUseDefaultEmoji,
|
||||||
style: style ?? const TextStyle(fontSize: 16.0),
|
customEmojiStickerList: customEmojiStickerList,
|
||||||
specialTextSpanBuilder: DefaultSpecialTextSpanBuilder(
|
showAtBackground: true,
|
||||||
isUseDefaultEmoji: isUseDefaultEmoji,
|
));
|
||||||
customEmojiStickerList: customEmojiStickerList,
|
|
||||||
showAtBackground: true,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -305,6 +305,7 @@ class MergerMessageScreenState extends TIMUIKitState<MergerMessageScreen> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
desktopWidget: Container(
|
desktopWidget: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: messageListPage(),
|
child: messageListPage(),
|
||||||
|
|
|
||||||
|
|
@ -362,6 +362,7 @@ class _MessageReadReceiptState extends TIMUIKitState<MessageReadReceipt> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
desktopWidget: pageBody(),
|
desktopWidget: pageBody(),
|
||||||
defaultWidget: DefaultTabController(
|
defaultWidget: DefaultTabController(
|
||||||
length: 2,
|
length: 2,
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,7 @@ class TextInputBottomSheet {
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
Overlay.of(context)?.insert(entry!);
|
Overlay.of(context).insert(entry!);
|
||||||
} else {
|
} else {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
isScrollControlled: true, // !important
|
isScrollControlled: true, // !important
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,7 @@ class _SelectNewGroupOwner extends TIMUIKitState<SelectNewGroupOwner> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return TUIKitScreenUtils.getDeviceWidget(
|
return TUIKitScreenUtils.getDeviceWidget(
|
||||||
|
context: context,
|
||||||
defaultWidget: Scaffold(
|
defaultWidget: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
shadowColor: theme.weakBackgroundColor,
|
shadowColor: theme.weakBackgroundColor,
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:chewie/chewie.dart';
|
import 'package:chewie_for_us/chewie_for_us.dart';
|
||||||
import 'package:chewie/src/helpers/utils.dart';
|
import 'package:chewie_for_us/src/helpers/utils.dart';
|
||||||
import 'package:chewie/src/animated_play_pause.dart';
|
import 'package:chewie_for_us/src/animated_play_pause.dart';
|
||||||
import 'package:chewie/src/material/material_progress_bar.dart';
|
import 'package:chewie_for_us/src/material/material_progress_bar.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:loading_animation_widget/loading_animation_widget.dart';
|
import 'package:loading_animation_widget/loading_animation_widget.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||||
import 'package:universal_html/html.dart' as html;
|
import 'package:universal_html/html.dart' as html;
|
||||||
import 'package:chewie/chewie.dart';
|
import 'package:chewie_for_us/chewie_for_us.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/utils/permission.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/utils/permission.dart';
|
||||||
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
||||||
|
|
@ -89,7 +89,7 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
|
||||||
} else {
|
} else {
|
||||||
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||||
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||||
if ((androidInfo.version.sdkInt ?? 0) >= 33) {
|
if ((androidInfo.version.sdkInt) >= 33) {
|
||||||
final videos = await Permissions.checkPermission(
|
final videos = await Permissions.checkPermission(
|
||||||
context,Permission.videos.value,
|
context,Permission.videos.value,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -88,142 +88,145 @@ class TUIKitWidePopup {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
entry = OverlayEntry(builder: (BuildContext context) {
|
entry = OverlayEntry(builder: (BuildContext context) {
|
||||||
return TUIKitDragArea(
|
return Material(
|
||||||
backgroundColor: isDarkBackground ? const Color(0x7F000000) : null,
|
color: Colors.transparent,
|
||||||
closeFun: () {
|
child: TUIKitDragArea(
|
||||||
if (entry != null) {
|
backgroundColor: isDarkBackground ? const Color(0x7F000000) : null,
|
||||||
entry?.remove();
|
closeFun: () {
|
||||||
entry = null;
|
if (entry != null) {
|
||||||
}
|
entry?.remove();
|
||||||
},
|
entry = null;
|
||||||
initOffset: offset ??
|
}
|
||||||
(width != null && height != null
|
},
|
||||||
? Offset(MediaQuery.of(context).size.width * 0.5 - width / 2,
|
initOffset: offset ??
|
||||||
MediaQuery.of(context).size.height * 0.5 - height / 2)
|
(width != null && height != null
|
||||||
: null),
|
? Offset(MediaQuery.of(context).size.width * 0.5 - width / 2,
|
||||||
child: Container(
|
MediaQuery.of(context).size.height * 0.5 - height / 2)
|
||||||
width: width,
|
: null),
|
||||||
height: height,
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
width: width,
|
||||||
borderRadius:
|
height: height,
|
||||||
borderRadius ?? const BorderRadius.all(Radius.circular(16)),
|
decoration: BoxDecoration(
|
||||||
color: theme?.wideBackgroundColor ?? const Color(0xFFffffff),
|
borderRadius:
|
||||||
border: isDarkBackground
|
borderRadius ?? const BorderRadius.all(Radius.circular(16)),
|
||||||
? Border.all(
|
color: theme?.wideBackgroundColor ?? const Color(0xFFffffff),
|
||||||
width: 2,
|
border: isDarkBackground
|
||||||
color:
|
? Border.all(
|
||||||
theme?.weakBackgroundColor ?? const Color(0xFFbebebe),
|
width: 2,
|
||||||
)
|
color:
|
||||||
: null,
|
theme?.weakBackgroundColor ?? const Color(0xFFbebebe),
|
||||||
boxShadow: isDarkBackground
|
)
|
||||||
? null
|
: null,
|
||||||
: const [
|
boxShadow: isDarkBackground
|
||||||
BoxShadow(
|
? null
|
||||||
color: Color(0xFFbebebe),
|
: const [
|
||||||
offset: Offset(3, 3),
|
BoxShadow(
|
||||||
blurRadius: 10,
|
color: Color(0xFFbebebe),
|
||||||
spreadRadius: 1,
|
offset: Offset(3, 3),
|
||||||
),
|
blurRadius: 10,
|
||||||
],
|
spreadRadius: 1,
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
if (title != null)
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: hexToColor("f5f6f7"),
|
|
||||||
borderRadius: const BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(16),
|
|
||||||
topRight: Radius.circular(16)),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
title,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
color: theme?.darkTextColor ??
|
|
||||||
const Color(0xFF444444)),
|
|
||||||
),
|
|
||||||
InkWell(
|
|
||||||
onTap: () {
|
|
||||||
if (onSubmit != null) {
|
|
||||||
onSubmit();
|
|
||||||
}
|
|
||||||
entry?.remove();
|
|
||||||
entry = null;
|
|
||||||
},
|
|
||||||
child: onSubmit != null
|
|
||||||
? (submitWidget ?? const Icon(Icons.check))
|
|
||||||
: const Icon(Icons.close),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (title != null)
|
],
|
||||||
SizedBox(
|
),
|
||||||
height: 1,
|
child: Column(
|
||||||
child: Container(
|
children: [
|
||||||
color: theme?.weakDividerColor ?? const Color(0xFFE5E6E9),
|
if (title != null)
|
||||||
),
|
Container(
|
||||||
),
|
padding: const EdgeInsets.all(16),
|
||||||
if (height != null && width != null)
|
decoration: BoxDecoration(
|
||||||
Expanded(child: child(() {
|
color: hexToColor("f5f6f7"),
|
||||||
entry?.remove();
|
borderRadius: const BorderRadius.only(
|
||||||
entry = null;
|
topLeft: Radius.circular(16),
|
||||||
})),
|
topRight: Radius.circular(16)),
|
||||||
if (height == null || width == null)
|
),
|
||||||
child(() {
|
child: Row(
|
||||||
entry?.remove();
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
entry = null;
|
mainAxisSize: MainAxisSize.max,
|
||||||
}),
|
children: [
|
||||||
if (onCancel != null || onConfirm != null)
|
Text(
|
||||||
Container(
|
title,
|
||||||
padding: const EdgeInsets.only(bottom: 16),
|
style: TextStyle(
|
||||||
child: Row(
|
fontSize: 18,
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
color: theme?.darkTextColor ??
|
||||||
children: [
|
const Color(0xFF444444)),
|
||||||
if (onCancel != null)
|
|
||||||
Container(
|
|
||||||
margin: const EdgeInsets.only(right: 16),
|
|
||||||
child: OutlinedButton(
|
|
||||||
onPressed: () {
|
|
||||||
entry?.remove();
|
|
||||||
entry = null;
|
|
||||||
onCancel();
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
TIM_t("取消"),
|
|
||||||
style: TextStyle(
|
|
||||||
color:
|
|
||||||
theme?.weakTextColor ?? Colors.black),
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
if (onConfirm != null)
|
InkWell(
|
||||||
Container(
|
onTap: () {
|
||||||
margin: const EdgeInsets.only(right: 16),
|
if (onSubmit != null) {
|
||||||
child: ElevatedButton(
|
onSubmit();
|
||||||
onPressed: () {
|
}
|
||||||
entry?.remove();
|
entry?.remove();
|
||||||
entry = null;
|
entry = null;
|
||||||
onConfirm();
|
},
|
||||||
},
|
child: onSubmit != null
|
||||||
child: Text(
|
? (submitWidget ?? const Icon(Icons.check))
|
||||||
TIM_t("确定"),
|
: const Icon(Icons.close),
|
||||||
style: TextStyle(color: theme?.primaryColor),
|
)
|
||||||
)),
|
],
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
),
|
||||||
)
|
if (title != null)
|
||||||
],
|
SizedBox(
|
||||||
),
|
height: 1,
|
||||||
));
|
child: Container(
|
||||||
|
color: theme?.weakDividerColor ?? const Color(0xFFE5E6E9),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (height != null && width != null)
|
||||||
|
Expanded(child: child(() {
|
||||||
|
entry?.remove();
|
||||||
|
entry = null;
|
||||||
|
})),
|
||||||
|
if (height == null || width == null)
|
||||||
|
child(() {
|
||||||
|
entry?.remove();
|
||||||
|
entry = null;
|
||||||
|
}),
|
||||||
|
if (onCancel != null || onConfirm != null)
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.only(bottom: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
if (onCancel != null)
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.only(right: 16),
|
||||||
|
child: OutlinedButton(
|
||||||
|
onPressed: () {
|
||||||
|
entry?.remove();
|
||||||
|
entry = null;
|
||||||
|
onCancel();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
TIM_t("取消"),
|
||||||
|
style: TextStyle(
|
||||||
|
color:
|
||||||
|
theme?.weakTextColor ?? Colors.black),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
if (onConfirm != null)
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.only(right: 16),
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
entry?.remove();
|
||||||
|
entry = null;
|
||||||
|
onConfirm();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
TIM_t("确定"),
|
||||||
|
style: TextStyle(color: theme?.primaryColor),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
Overlay.of(context)?.insert(entry!);
|
Overlay.of(context).insert(entry!);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
632
pubspec.lock
29
pubspec.yaml
|
|
@ -1,6 +1,6 @@
|
||||||
name: tencent_cloud_chat_uikit
|
name: tencent_cloud_chat_uikit
|
||||||
description: A powerful chat UI component library and business logic for Tencent Cloud Chat, creating seamless in-app chat modules for delightful user experiences.
|
description: A powerful chat UI component library and business logic for Tencent Cloud Chat, creating seamless in-app chat modules for delightful user experiences.
|
||||||
version: 2.0.0
|
version: 2.1.0
|
||||||
homepage: https://www.tencentcloud.com/products/im?from=pub
|
homepage: https://www.tencentcloud.com/products/im?from=pub
|
||||||
repository: https://github.com/TencentCloud/tc-chat-uikit-flutter
|
repository: https://github.com/TencentCloud/tc-chat-uikit-flutter
|
||||||
documentation: https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html
|
documentation: https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html
|
||||||
|
|
@ -14,23 +14,23 @@ platforms:
|
||||||
windows:
|
windows:
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.17.0 <3.0.0"
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
flutter: ">=3.0.0"
|
flutter: ">=3.10.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
adaptive_action_sheet: ^2.0.1
|
adaptive_action_sheet: ^2.0.1
|
||||||
provider: ^6.0.1
|
provider: ^6.0.1
|
||||||
intl: ^0.17.0
|
intl: ^0.18.0
|
||||||
get_it: ^7.2.0
|
get_it: ^7.2.0
|
||||||
dotted_border: ^2.0.0+2
|
dotted_border: ^2.0.0+2
|
||||||
flutter_svg: ^1.0.0
|
flutter_svg: ^1.0.0
|
||||||
image_picker: ^0.8.5+3
|
image_picker: ^0.8.5+3
|
||||||
file_picker: ^5.2.9
|
file_picker: ^5.3.0
|
||||||
tencent_super_tooltip: ^0.0.1
|
tencent_super_tooltip: ^0.0.1
|
||||||
video_player: ^2.4.2
|
video_player: ^2.4.2
|
||||||
chewie: ^1.3.2
|
chewie_for_us: ^1.5.0
|
||||||
flutter_slidable_for_tencent_im: ^1.4.0
|
flutter_slidable_for_tencent_im: ^1.4.0
|
||||||
flutter_plugin_record_plus: ^0.0.15
|
flutter_plugin_record_plus: ^0.0.15
|
||||||
azlistview_all_platforms: ^2.1.2
|
azlistview_all_platforms: ^2.1.2
|
||||||
|
|
@ -41,13 +41,13 @@ dependencies:
|
||||||
cached_network_image: ^3.2.0
|
cached_network_image: ^3.2.0
|
||||||
shared_preferences: ^2.0.13
|
shared_preferences: ^2.0.13
|
||||||
scroll_to_index: ^2.1.1
|
scroll_to_index: ^2.1.1
|
||||||
wechat_assets_picker: ^7.2.0
|
wechat_assets_picker: ^8.5.0
|
||||||
tencent_wechat_camera_picker: ^3.6.5
|
wechat_camera_picker: ^3.8.0
|
||||||
flutter_easyrefresh: ^2.2.1
|
flutter_easyrefresh: ^2.2.1
|
||||||
extended_image: ^6.0.2+1
|
extended_image: ^8.0.1
|
||||||
tencent_extended_text_field: ^1.0.0
|
extended_text_field: ^12.0.0
|
||||||
tencent_extended_text: ^1.0.0
|
extended_text: ^11.0.0
|
||||||
package_info_plus: ^1.4.0
|
package_info_plus: ^4.0.1
|
||||||
loading_animation_widget: ^1.1.0+3
|
loading_animation_widget: ^1.1.0+3
|
||||||
permission_handler: ^10.2.0
|
permission_handler: ^10.2.0
|
||||||
tuple: ^2.0.0
|
tuple: ^2.0.0
|
||||||
|
|
@ -64,14 +64,14 @@ dependencies:
|
||||||
tencent_open_file: ^4.0.10
|
tencent_open_file: ^4.0.10
|
||||||
tencent_keyboard_visibility: ^1.0.1
|
tencent_keyboard_visibility: ^1.0.1
|
||||||
tim_ui_kit_sticker_plugin: ^2.0.1
|
tim_ui_kit_sticker_plugin: ^2.0.1
|
||||||
tencent_im_base: ^1.0.51
|
tencent_im_base: ^1.0.57
|
||||||
fc_native_video_thumbnail_for_us: ^0.4.8+1
|
fc_native_video_thumbnail_for_us: ^0.4.8+1
|
||||||
audioplayers: ^3.0.1
|
audioplayers: ^3.0.1
|
||||||
path: ^1.8.1
|
path: ^1.8.1
|
||||||
tencent_cloud_uikit_core: ^1.0.2
|
tencent_cloud_uikit_core: ^1.0.2
|
||||||
pasteboard: ^0.2.0
|
pasteboard: ^0.2.0
|
||||||
desktop_drop: ^0.4.1
|
desktop_drop: ^0.4.1
|
||||||
device_info_plus: ^4.1.3
|
device_info_plus: ^9.0.1
|
||||||
cross_file: ^0.3.3+4
|
cross_file: ^0.3.3+4
|
||||||
diff_match_patch: ^0.4.1
|
diff_match_patch: ^0.4.1
|
||||||
|
|
||||||
|
|
@ -89,6 +89,7 @@ flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
assets:
|
assets:
|
||||||
- images/
|
- images/
|
||||||
|
- images/svg/
|
||||||
# - assets/custom_face_resource/4349/
|
# - assets/custom_face_resource/4349/
|
||||||
|
|
||||||
# To add assets to your package, add an assets section, like this:
|
# To add assets to your package, add an assets section, like this:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
}
|
||||||