diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8a6acde..86bae52 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,158 @@
+## 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.
+
+The main feature of this new 2.0.0 version is Desktop Support. Tencent Cloud Chat UIKit now supports all platforms, including iOS, Android, Web, Windows, and macOS, which has resulted in significant changes to the codebase. The UI has been improved to adapt to screens of various widths, with different layouts for both wide and narrow screens.
+
+In addition, there are some significant changes compared to version 2.0.0-preview.7.
+
+### New Features
+
+* Added drag and drop support for multiple files in `TIMUIKitChat`, allowing direct sending.
+* Introduced functionality to open files or their containing folder (using `Finder` on `macOS` or `Explorer` on `Windows`) for file messages via the message operation tooltip menu on desktop.
+* Implemented text selection and copying in messages on desktop.
+* Added group joining application processing on Desktop.
+* Introduced `isAutoReportRead` to `TIMUIKitChatConfig` for controlling read status reporting.
+
+### Improvements
+
+* Enhanced group members selection panel for mentioning someone in a group chat.
+* Refined image display ratio on Desktop.
+* The Reply or Quote button is now labeled as `Reply` when `isAtWhenReply` is set to true, and `Quote` otherwise.
+* @ member tags can now be deleted at once.
+
+### Bug Fixes
+
+* Fixed UI layout issue causing the `translate` button to display on two lines.
+* Addressed an issue causing the mute status not to change when switching to another conversation.
+* Fixed several issues causing bugs when opening files.
+* Resolved an issue causing secondary confirmation modal UI layout to be over-width on Desktop.
+* Fixed an issue causing UI layout errors on the profile page.
+* Addressed an issue where the `chatMessageItemFromSelfBgColor` configuration did not work.
+* Fixed an issue preventing files from being opened when the path contained Chinese characters on Windows.
+* Resolved an issue preventing images from being pasted and sent directly with Ctrl + V on Windows.
+* Fixed an issue causing errors in the muting members list.
+
+## 2.0.0-preview.7
+
+### New Features
+
+* Added `additionalMessageToolTips` to `ToolTipsConfig`. This new property allows developers to add additional message operation tooltip items, apart from the default ones. The previous `additionalItemBuilder` has been replaced by this new property. With `additionalMessageToolTips`, developers only need to specify the data for the tooltip items, rather than providing a whole widget. This makes usage easier, as you no longer need to worry about the UI display.
+* Added `isPreloadMessagesAfterInit` to `TIMUIKitConfig`, allows determines whether TUIKit should preload some messages after initialization for faster message display.
+
+### Improvements
+
+* Message operation menu shows when long-pressing messages will not show if nothing operation item includes and do not use message sticker reaction module.
+* Renamed `desktopMessageHoverBar` to `additionalDesktopMessageHoverBarItem` in `TIMUIKitChatConfig` to control only the addition of extra operation items displayed on the hover bar of messages on desktop (macOS, Windows, and desktop version of Web), without affecting the default ones. Previously, it controlled the entire message hover bar, including covering the default items.
+* Renamed `showWideScreenModalFunc` to `showDesktopModalFunc` in `TIMUIKitConfig` for better clarity.
+* Upgraded several dependencies to their latest versions, including `ffi` to ^2.0.1, `file_picker` to ^5.2.9 and `device_info_plus` to ^8.2.0.
+* Added support for the new permission authorization schema on Android 13 and `targetSdkVersion` greater than 33.
+* Corrected the `textHight` to `textHeight` in `TIMUIKitChatConfig`, and modified the default value to 1.3.
+
+### Bug Fixes
+
+* Fixed an issue where the `showVideoCall` and `showVoiceCall` configuration options were not working.
+* Fixed potential `Windows` platform deployment prohibition issue.
+* Fixed an issue that may cause `setLocalCustomData` to be triggered repeatedly.
+
+## 2.0.0-preview.6
+
+### Improvements
+
+* Permission requests now feature a gray translucent overlay for secondary confirmations on first-time requests, which was reintroduced after being removed in version 2.0.0-preview.4. Additionally, the overlay can now be successfully hidden once the permission authorization is complete.".
+* Time Divider on Message List: The default 12-hour display has been changed to a 24-hour display.
+* Message translation now targets the language of TUIKit instead of relying on the system language directly. The language of TUIKit can be set as the system language automatically or defined by the user. For more information, please refer to this documentation: https://www.tencentcloud.com/document/product/1047/52154.
+* Optimized the animation for message text input area.
+
+### Bug Fixes
+
+* Fixed an issue where the `Voice Call` and `Video Call` buttons were not working in group chat.
+* Fixed several null-safety issues.
+* Fixed a layout problem for the message operation menu when not using the message sticker reaction module.
+* Addressed a problem where the time ago display was not correct on the conversation item.
+* Fixed an issue where stickers could not be clicked in some cases.
+* Resolved an overflow error that occurred when opening the sticker panel.
+
+## 2.0.0-preview.5
+
+### New Features
+
+* New Chat Configuration: `isAllowLongPressAvatarToAt`. This option controls whether users are allowed to mention another user in the group by long-pressing on their avatar.
+
+### Improvements
+
+* Improved tool bar configuration on desktop: The tool bar can now be customized using `desktopControlBarConfig` for embedded default items and `additionalDesktopControlBarItems` for additional tool items. These configurations come from TIMUIKitChatConfig.
+* Renamed the `wideMessageHoverBar` configuration option to `desktopMessageHoverBar` for better clarity.
+* Eliminated the dependency on `fluttertoast`. All necessary customer reminders are now triggered through the `onTUIKitCallbackListener` info callback in your project. For more information, please see: https://www.tencentcloud.com/document/product/1047/50054#how-do-i-get-an-api-call-error.2Fflutter-layer-error.2Fpop-up-prompt-message.3F.3Ca-id.3D.22callback.22.3E.3C.2Fa.3E.
+* Eliminated other six unnecessary dependency packages to reduce the size and improve performance.
+* Improved the clarity of the `sendMessage` function in `TIMUIKitChatController` by replacing the use of `convID` to represent both `userID` and `groupID` with separate parameters.
+
+### Bug Fixes
+
+* Fixed an issue where the message operation menu may show inaccurately when the message is too long.
+* Fixed a problem where the message operation menu had the potential to be too wide for certain types of messages, causing display issues.
+* Corrected an issue where the button to remove group members was not functioning correctly.
+* Addressed a problem where the message item could exceed the pixel limit and appear too wide.
+* Fixed a bug where certain JSON decoding operations could potentially fail.
+* Fixed an issue with sound messages on iOS devices playing only through earpiece instead of speaker by default.
+
+## 2.0.0-preview.4
+
+### New Features
+
+* New Chat Configuration: `TIMUIKitChatConfig` now includes `offlinePushInfo`, which allows for customization of the entire `offlinePushInfo` for each message. This field has a higher priority than the previous separate configuration fields for this object.
+* New Color Configuration: Added `appbarTextColor` and `appbarBgColor` to configure the color for the Appbar. Also added `selectPanelBgColor` and `selectPanelTextIconColor` to configure the color of the messages multi-select panel.
+
+### Improvements
+
+* Improved Group Management: Muting members on Work Group is now not allowed.
+* Improved Avatar: Ensured that the avatar can be as small as possible while still covering the entire target box.
+* Permission Requests: Removed the gray translucent overlay for secondary confirmations on first-time permission requests.
+
+### Bug Fixes
+
+* Fixed an issue where the color defined by `chatBgColor` could not cover the entire chat screen when messages did not cover the whole page.
+* Fixed an issue where the history message list could not be scrolled in some cases.
+* Fixed an issue where the ratio of sending messages was incorrect, resulting in the wrong position of the read status label on the left.
+* Fixed an issue where loading messages could fail when the number of messages equaled the specified count.
+
+## 2.0.0-preview.3
+
+### New Features
+
+* Integrated Callkit: The Calls button no longer needs to be added to `MorePanelConfig`. If `tencent_calls_uikit` is installed, the Video Call and Voice Call buttons will be displayed automatically.
+* Paste Images on Desktop: Users can now paste an image on the text field on Desktop to send it.
+* Screenshot Capture on Desktop: Users can now capture a screenshot on Desktop and send it.
+
+### Improvements
+
+* Improved Compatibility: The TUIKit is now compatible with Flutter versions 3.0.0 to 3.7.7.
+
+### Bug Fixes
+
+* Fixed an issue where the `businessID` type may not be correct.
+* Fixed an issue where the `chatMessageItemFromSelfBgColor` configuration was not taking effect.
+
+## 2.0.0-preview.2
+
+### New Features
+
+* Added support for opening files locally from file messages.
+
+## 2.0.0-preview.1
+
+### New Features
+
+* Desktop Support: Tencent Cloud Chat UIKit now supports all platforms, including iOS, Android, Web, Windows, and macOS, resulting in significant changes to the codebase. The UI has been enhanced to adapt to screens of various widths, with different layouts for both wide and narrow screens.
+* Information Copy: The ability to copy information, such as Group ID, from the screen has been added.
+
+### Improvements
+
+* Improved group management logic, with non-administrators no longer able to access the management interface.
+* Optimized cursor positioning when sending messages.
+* Improved and optimized scrollbar functionality.
+* Enhanced clickable URL support in messages, with URLs now supporting both with and without the "https://" prefix.
+
## 1.7.0+1
* Fix: An issue that caused errors on mentioning all members.
diff --git a/LICENSE b/LICENSE
index 3e1a561..8482e60 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,201 +1,25 @@
-Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
+Copyright © 2013-2023 Tencent Cloud. All Rights Reserved.
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
- 1. Definitions.
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [2023] [Tencent]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
\ No newline at end of file
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/README.md b/README.md
index 6708341..2a06226 100644
--- a/README.md
+++ b/README.md
@@ -63,123 +63,167 @@ More languages:
-
+
TUIKit has Chat SDK, UI components and basic business logic inside. You can choose our pure Chat SDK tencent-cloud-chat-sdk if you tend to build the UI yourself.
-Official Documentation
+
+Official Documentation
-## Experience DEMO
+## Preview Version Release Notes
-You can experience our Chat and Voice/Video Call modules via the following demos.
+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.
-**Those following versions of demo has been build by the same Flutter project with our SDKs and extensions.**
+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.
-**Also, taking a look of the screenshots of TUIKit from [here](https://www.tencentcloud.com/document/product/1047/50059?from=pub) are suggested.**
+## Check Out Our Sample Apps
+
+Experience our Chat and Voice/Video Call modules by trying out our sample apps.
+
+**These apps have been created using the same Flutter project as our SDKs and extensions.**
+
+| Platform | Link | Remark |
+|---------|---------|---------|
+| Android / iOS | | Scan to download app for both Android and iOS. Automatically identifies platform. |
+| Web | | Supports both desktop and mobile browsers and automatically adjusts its layout accordingly. Same website as link below. |
+| Web | [Visit Now](https://comm.qq.com/flutter/#/) | Supports both desktop and mobile browsers and automatically adjusts its layout accordingly. Same website as previous QR code. |
+| macOS | [Download Now](https://comm.qq.com/im_demo_download/macos_flutter.dmg) | The macOS version of our sample app. Control-click the app icon, then choose "Open" from the shortcut menu. |
+| Windows | [Download Now](https://comm.qq.com/im_demo_download/windows_flutter.appx) | The Windows version of our sample app, which is a UWP (Universal Windows Platform) application. |
+| Linux | Coming Soon... | Will be available later this year. |
+
+**Take a look at the screenshots of
+TUIKit [here](https://www.tencentcloud.com/document/product/1047/50059?from=pub) to get an idea of
+what to expect.**
## Introduction to TUIKit
-[TUIKit](https://www.tencentcloud.com/document/product/1047/50059?from=pub) is a set of official UI components for Tencent Cloud Chat SDK, with chat business logic around it. It provides components such as the conversation, chat, relationship chain, and group.
+Tencent Cloud Chat SDK comes
+with [TUIKit](https://www.tencentcloud.com/document/product/1047/50059?from=pub), which is an
+official set of UI components that have chat business logic built-in. TUIKit includes components
+like conversation, chat, relationship chain, and group.
-You can use these UI components to build your APP with the In-APP chat module quickly and easily.
+Developers can use these UI components to quickly and easily add In-APP chat modules to their mobile
+applications.

-Currently, Flutter [TUIKit](https://www.tencentcloud.com/document/product/1047/50059?from=pub) contains the following main components:
+Currently, Flutter [TUIKit](https://www.tencentcloud.com/document/product/1047/50059?from=pub)
+contains the following main components:
- [TIMUIKitCore](https://comm.qq.com/im/doc/flutter/en/TUIKit/TIMUIKitCore/readme.html): Core entry
-- [TIMUIKitConversation](https://www.tencentcloud.com/document/product/1047/50059?from=pub#timuikitconversation): Conversation list
-- [TIMUIKitChat](https://www.tencentcloud.com/document/product/1047/50059?from=pub#timuikitchat): Chat module, includes historical message list and message sending area, with some other features like message reaction and URL preview, etc.
-- [TIMUIKitContact](https://www.tencentcloud.com/document/product/1047/50059?from=pub#relationship-chain-components): Contacts list
-- [TIMUIKitProfile](https://www.tencentcloud.com/document/product/1047/50059?from=pub#timuikitprofile): User profile and relationship management
-- [TIMUIKitGroupProfile](https://www.tencentcloud.com/document/product/1047/50059?from=pub#timuikitgroupprofile): Group profile and management
-- [TIMUIKitGroup](https://www.tencentcloud.com/document/product/1047/50059?from=pub#relationship-chain-components): The list of group self joined
-- [TIMUIKitBlackList](https://www.tencentcloud.com/document/product/1047/50059?from=pub#relationship-chain-components): The list of user been blocked
-- [TIMUIKitNewContact](https://www.tencentcloud.com/document/product/1047/50059?from=pub#relationship-chain-components): New contacts application list
-- [TIMUIKitSearch](https://www.tencentcloud.com/document/product/1047/50036?from=pub): Search globally
-- [TIMUIKitSearchMsgDetail](https://www.tencentcloud.com/document/product/1047/50036?from=pub): Search in specific conversation
+- [TIMUIKitConversation](https://www.tencentcloud.com/document/product/1047/50059?from=pub#timuikitconversation):
+ Conversation list
+- [TIMUIKitChat](https://www.tencentcloud.com/document/product/1047/50059?from=pub#timuikitchat):
+ Chat module, includes historical message list and message sending area, with some other features
+ like message reaction and URL preview, etc.
+- [TIMUIKitContact](https://www.tencentcloud.com/document/product/1047/50059?from=pub#relationship-chain-components):
+ Contacts list
+- [TIMUIKitProfile](https://www.tencentcloud.com/document/product/1047/50059?from=pub#timuikitprofile):
+ User profile and relationship management
+- [TIMUIKitGroupProfile](https://www.tencentcloud.com/document/product/1047/50059?from=pub#timuikitgroupprofile):
+ Group profile and management
+- [TIMUIKitGroup](https://www.tencentcloud.com/document/product/1047/50059?from=pub#relationship-chain-components):
+ The list of group self joined
+- [TIMUIKitBlackList](https://www.tencentcloud.com/document/product/1047/50059?from=pub#relationship-chain-components):
+ The list of user been blocked
+- [TIMUIKitNewContact](https://www.tencentcloud.com/document/product/1047/50059?from=pub#relationship-chain-components):
+ New contacts application list
+- [TIMUIKitSearch](https://www.tencentcloud.com/document/product/1047/50036?from=pub): Search
+ globally
+- [TIMUIKitSearchMsgDetail](https://www.tencentcloud.com/document/product/1047/50036?from=pub):
+ Search in specific conversation
-Also, there are some other useful components and widgets, that can help to build your APP, and meet your business needs, including group entry application list and group member list, etc.
+In addition to these components, there are other useful components and widgets available to help
+developers meet their business needs, such as group entry application list and group member list.
-For the source code of the project in the figure above, see [im-flutter-uikit](https://github.com/TencentCloud/tc-chat-demo-flutter). The project is open source and can be used directly.
+For the source code of the project shown in the image above, please refer
+to [chat-demo-flutter](https://github.com/TencentCloud/chat-demo-flutter). This project is open
+source and can be directly used by developers.
-## Supported Platforms
+## Compatible Platforms
+
+The platforms are compatible with the deployment of our Chat UIKit.
- Android
- iOS
-- Web(After version of 0.1.4)
+- Web (version 0.1.4 and later)
+- Windows (version 2.0.0 and later)
+- macOS (version 2.0.0 and later)
## Get Started
-**[Please refer this documents](https://www.tencentcloud.com/document/product/1047/45907?from=pub), for a completed and detailed get started guide.**
+Please refer to [this document](https://www.tencentcloud.com/document/product/1047/45907?from=pub) for a complete and detailed guide on getting started.
## Directions
-The following guide describes how to use Flutter TUIKit to build a simple Chat APP quickly.
+The following guide describes how to quickly build a simple chat application using Flutter TUIKit.
-**You may refer to the appendix, if willing to know about the detail and parameter for each widgets.**
+Refer to the appendix if you want to learn about the details and parameters of each widget.
-> If you tend to add this Flutter [TUIKit](https://www.tencentcloud.com/document/product/1047/50059?from=pub) to your existing application directly, you may refer to [this documentation](https://www.tencentcloud.com/document/product/1047/51456). Add the Flutter module to your existing app, coding once and deploying to all platforms. It could reduce your workload, to adding chat and call modules to your existing, to a large extent.
+> If you want to directly add Flutter TUIKit to your existing application, refer to [this document](https://www.tencentcloud.com/document/product/1047/51456). You can add the Flutter module to your existing application, code once, and deploy to all platforms. This can significantly reduce the workload of adding chat and call modules to your existing application.
-### Step 0. Create two accounts for testing
+### Step 0: Create two accounts for testing
-[Signed up](https://www.tencentcloud.com/document/product/378/17985?from=pub) and [log in](https://www.tencentcloud.com/document/product/378/36004?from=pub) to the [Tencent IM console](https://console.tencentcloud.com/im?from=pub).
+Sign up and log in to the [Tencent Cloud Chat console](https://console.tencentcloud.com/im?from=pub).
-[Create an application](https://www.tencentcloud.com/document/product/1047/34577?from=pub) and enter in.
+Create an application and enter it.
-Select [Auxiliary Tools](https://console.tencentcloud.com/im-detail/tool-usersig?from=pub) > UserSig Generation and Verification on the left sidebar. Generate two pairs of "UserID" and the corresponding "UserSig", and copy the "key" information. [Refer to here.](https://www.tencentcloud.com/document/product/1047/34580?from=pub#usersig-generation-and-verification)
+Select Auxiliary Tools > UserSig Generation and Verification on the left sidebar. Generate two pairs of "UserID" and the corresponding "UserSig," and copy the "key" information. Refer to [this document](https://www.tencentcloud.com/document/product/1047/34580?from=pub#usersig-generation-and-verification).
-Tips: You may create "user1" and "user2" here.
+Tips: You can create "user1" and "user2" here.
-> Note:
+> Note:
>
-> The correct `UserSig` distribution method is to integrate the calculation code of `UserSig` into your server and provide an application-oriented API. When `UserSig` is needed, your application can send a request to the business server for a dynamic `UserSig`. For more information, see [How do I calculate UserSig on the server?](https://www.tencentcloud.com/document/product/1047/34385?from=pub).
+> The correct way to distribute `UserSig` is to integrate the calculation code for `UserSig` into your server and provide an application-oriented API. When `UserSig` is needed, your application can send a request to the business server for a dynamic `UserSig.` For more information, see [How do I calculate UserSig on the server?](https://www.tencentcloud.com/document/product/1047/34385?from=pub).
-### Step 1. Create a Flutter app and add permission configuration
+### Step 1: Create a Flutter app and add permission configuration
-Quickly create a Flutter APP by referring to [Flutter documentation](https://docs.flutter.dev/get-started/install).
+Create a Flutter app quickly by following the [Flutter documentation](https://docs.flutter.dev/get-started/install).
-TUIKit needs the permissions of shooting/album/recording/network for basic messaging function. You need to declare in the permission manually to use the relevant capabilities normally.
+TUIKit needs the permissions of shooting/album/recording/network for basic messaging functions. You need to declare these permissions manually to use the relevant capabilities normally.
#### Android
-Open `android/app/src/main/AndroidManifest.xml`, add the following lines between `` and ` `.
+Open `android/app/src/main/AndroidManifest.xml` and add the following lines between `` and ` `.
```xml
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
```
#### iOS
-Open `ios/Podfile`, add the following lines to the end of the file.
+Open `ios/Podfile` and add the following lines to the end of the file.
```pod
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
+ target.build_configurations.each do |config|
+ config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64'
+ config.build_settings['ENABLE_BITCODE'] = 'NO'
+ config.build_settings["ONLY_ACTIVE_ARCH"] = "NO"
+ end
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
@@ -192,7 +236,7 @@ post_install do |installer|
end
```
-### Step 2. Install dependencies
+### Step 2: Install dependencies
Add `tencent_cloud_chat_uikit` under `dependencies` in the `pubspec.yaml` file, or run the following command:
@@ -200,15 +244,15 @@ Add `tencent_cloud_chat_uikit` under `dependencies` in the `pubspec.yaml` file,
flutter pub add tencent_cloud_chat_uikit
```
-It supports Android and iOS as default, if you are also willing to use it on the Web, please refer to the following guide.
+It supports Android and iOS by default. If you also want to use it on the web, refer to the following guide.
-#### Web Supports
+#### Web Support
-Version of 0.1.4 or later are required for web supports.
+Version 0.1.4 or later is required to support web.
-> If your existing Flutter project does not support Web, run `flutter create .` in the project root directory to add web support.
+If your existing Flutter project does not support web, run `flutter create .` in the project root directory to add web support.
-Install JS dependencies to `web/` by `npm` or `yarn`.
+Install JavaScript dependencies to `web/` using `npm` or `yarn`.
```shell
cd web
@@ -220,7 +264,7 @@ npm i tim-js-sdk
npm i tim-upload-plugin
```
-Open `web/index.html` , add the following two lines between `` and `` to import them.
+Open `web/index.html` and add the following two lines between `` and `` to import them.
```html
@@ -229,35 +273,35 @@ Open `web/index.html` , add the following two lines between `` and ` **You may also better to register a callback function for `onTUIKitCallbackListener` here, please refer to the appendix.**
+> **You may also want to register a callback function for `onTUIKitCallbackListener` here. Refer to the appendix.**
-### Step 4. Get the signature and log in
+### Step 4: Get the signature and log in
-Now, you can log in one of the testing accounts, generated on Step 0, to start the IM module.
+You can now log in one of the testing accounts generated in Step 0 to start the Tencent Cloud Chat module.
-Log in by `_coreInstance.login` .
+Log in using `_coreInstance.login`.
```dart
/// main.dart
@@ -267,16 +311,16 @@ final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
_coreInstance.login(userID: userID, userSig: userSig);
```
-Caveat: Importing UserSig to your application is ONLY for Debugging purposes and cannot be applied for the Release version. Before publishing your app, you should generate your UserSig from your server. Refers to:
+Note: Importing UserSig to your application is only for debugging purposes and cannot be used for the release version. Before publishing your app, you should generate your UserSig from your server. Refer to:
-### Step 5. Implementing the conversation list page
+## Step 5. Implementing the conversation list page
-You can take the conversation (channel) list page as the homepage of your Chat module, covering the conversation with all users and groups that have chat records.
+You can use the conversation (channel) list page as the homepage of your Chat module, which includes all conversations with users and groups that have chat records.
-
+```markdown
+
-You can create a `Conversation` class, with `TIMUIKitConversation` on its `body`, to render the conversation list.
-The only parameter you need to provide at least is `onTapItem` callback, aimed at navigating to the Chat page for each conversation. The `Chat` class will be introduced in the next step.
+You can create a `Conversation` class, with `TIMUIKitConversation` as its body, to render the conversation list. You only need to provide the `onTapItem` callback, which allows users to navigate to the Chat page for each conversation. In the next step, we'll introduce the `Chat` class.
```dart
import 'package:flutter/material.dart';
@@ -284,6 +328,7 @@ import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
class Conversation extends StatelessWidget {
const Conversation({Key? key}) : super(key: key);
+
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -298,9 +343,10 @@ class Conversation extends StatelessWidget {
Navigator.push(
context,
MaterialPageRoute(
- builder: (context) => Chat(
- selectedConversation: selectedConv,
- ),
+ builder: (context) =>
+ Chat(
+ selectedConversation: selectedConv,
+ ),
));
},
),
@@ -308,15 +354,14 @@ class Conversation extends StatelessWidget {
}
}
```
+## Step 6. Implementing the chat page
-### Step 6. Implementing the chat page
+The chat page consists of the main message list and a message sending bar at the bottom.
-The chat page is composed of the main historical message list and a message sending bar at the bottom.
+```markdown
+
-
-
-You can create a `Chat` class, with `TIMUIKitChat` on its `body`, to render the chat page.
-It is recommended to provide a `onTapAvatar` callback function, for navigating to the profile page for current contact, which will be introduced in the next step.
+You can create a `Chat` class, with `TIMUIKitChat` as its body, to render the chat page. We recommend providing an `onTapAvatar` callback function to navigate to the profile page for the current contact, which we'll introduce in the next step.
```dart
import 'package:flutter/material.dart';
@@ -324,42 +369,43 @@ import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
class Chat extends StatelessWidget {
final V2TimConversation selectedConversation;
+
const Chat({Key? key, required this.selectedConversation}) : super(key: key);
+
String? _getConvID() {
return selectedConversation.type == 1
? selectedConversation.userID
: selectedConversation.groupID;
}
+
@override
Widget build(BuildContext context) {
return TIMUIKitChat(
- conversationID: _getConvID() ?? '', // groupID or UserID
- conversationType: selectedConversation.type ?? 1, // Conversation type
- conversationShowName: selectedConversation.showName ?? "", // Conversation display name
+ conversationID: _getConvID() ?? '',
+ conversationType: selectedConversation.type ?? 1,
+ conversationShowName: selectedConversation.showName ?? "",
onTapAvatar: (_) {
- Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) => UserProfile(userID: userID),
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => UserProfile(userID: userID),
));
- }, // Callback for the clicking of the message sender profile photo. This callback can be used with `TIMUIKitProfile`.
+ },
);
-}
+ }
```
+## Step 7. Implementing the user profile page
-### Step 7. Implementing the user profile page
+This page shows the profile of a specific user and maintains the relationship between the current logged-in user and the other user.
-This page can show the profile of a specific user and maintain the relationship between the current login user and it.
+```markdown
+
-
+You can create a `UserProfile` class, with `TIMUIKitProfile` as its body, to render the user profile page.
-Here, you can create a `UserProfile` class, with `TIMUIKitProfile` on its `body`, to render the profile page.
+The only parameter you have to provide is `userID`, while this component automatically generates the profile and relationship maintenance page based on the existence of friendship.
-The only parameter you have to provide at least is 'userID', while this component can generate the profile and relationship maintenance page based on the existence of friendship automatically.
-
-> TIPS
->
-> Please give priority to use `profileWidgetBuilder`, to customize some profile widgets, with `profileWidgetsOrder`, determine the vertical sequence, if you tend to customize this page. If this method could not meet your business needs, you may consider using `builder` instead.
+> **TIP**: Please use `profileWidgetBuilder` first to customize some profile widgets and determine their vertical sequence using `profileWidgetsOrder` if you want to customize this page. If this method cannot meet your business needs, you may consider using `builder` instead.
```dart
import 'package:flutter/material.dart';
@@ -367,34 +413,35 @@ import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
class UserProfile extends StatelessWidget {
final String userID;
+
const UserProfile({required this.userID, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
- title: const Text(
- "Message",
- style: TextStyle(color: Colors.black),
- ),
- ),
- body: TIMUIKitProfile(
- userID: widget.userID,
- ),
+ title: const Text(
+ "Message",
+ style: TextStyle(color: Colors.black),
+ ),
+ ),
+ body: TIMUIKitProfile(
+ userID: widget.userID,
+ ),
);
}
}
```
-Now, your app can send/receive messages, show the conversation list, and deal with the contact friendship.
-
-You can use others components from TUIKit continually to implement the complete Chat module quickly and easily.
+Now your app can send and receive messages, display the conversation list, and manage contact friendships. You can use other components from TUIKit to quickly and easily implement the complete Chat module.
## FAQs
### Do I need to integrate Chat SDK after integrating TUIKit?
-No. You don't need to integrate Chat SDK again. If you want to use Chat SDK related APIs, you can get them via `TIMUIKitCore.getSDKInstance()`. This method is recommended to ensure Chat SDK version consistency.
+No. You don't need to integrate Chat SDK again. If you want to use Chat SDK related APIs, you can
+get them via `TIMUIKitCore.getSDKInstance()`. This method is recommended to ensure Chat SDK version
+consistency.
### Why did force quit occur when I sent voice, image, file or other messages?
@@ -404,11 +451,13 @@ Refers to Step 1 above.
### What should I do if clicking Build And Run for an Android device triggers an error, stating no available device is found?
-Check that the device is not occupied by other resources. Alternatively, click Build to generate an APK package, drag it to the simulator, and run it.
+Check that the device is not occupied by other resources. Alternatively, click Build to generate an
+APK package, drag it to the simulator, and run it.
### What should I do if an error occurs during the first run for an iOS device?
-If an error occurs after the configuration, click **Product > Clean Build Folder** , clean the product, and run `pod install` or `flutter run` again.
+If an error occurs after the configuration, click **Product > Clean Build Folder** , clean the
+product, and run `pod install` or `flutter run` again.

@@ -416,33 +465,33 @@ If an error occurs after the configuration, click **Product > Clean Build Folder

-Turn on Airplane Mode on your Apple Watch, and go to **Settings > Bluetooth** on your iPhone to turn off Bluetooth.
+Turn on Airplane Mode on your Apple Watch, and go to **Settings > Bluetooth** on your iPhone to turn
+off Bluetooth.
Restart Xcode (if opened) and run `flutter run` again.
-### Issue with Flutter environment?
-
-If you want to check the Flutter environment, run `flutter doctor` to detect whether the Flutter environment is ready.
-
### What should I do when an error occurs on an Android device after TUIKit is imported into the application automatically generated by Flutter?

-1. Open `android\app\src\main\AndroidManifest.xml` and complete `xmlns:tools="http://schemas.android.com/tools" / android:label="@string/android_label" / tools:replace="android:label"` as follows.
+1. Open `android\app\src\main\AndroidManifest.xml` and
+ complete `xmlns:tools="http://schemas.android.com/tools" / android:label="@string/android_label" / tools:replace="android:label"`
+ as follows.
```xml
+
-
+
```
-2. Open `android\app\build.gradle` and complete `minSdkVersion` and `targetSdkVersion` in `defaultConfig`.
+2. Open `android\app\build.gradle` and complete `minSdkVersion` and `targetSdkVersion`
+ in `defaultConfig`.
```gradle
defaultConfig {
@@ -456,422 +505,8 @@ defaultConfig {
## Contact Us
-Please do not hesitate to contact us in the following place, if you have any further questions or tend to learn more about the use cases.
-
-- Telegram Group:
-- WhatsApp Group:
-- QQ Group: 788910197, chat in Chinese
-
-Our Website:
-
----
-
-## Appendix: Overview for each widgets
-
-### TIMUIKitCore
-
-`TIMUIKitCore` provides two static methods, including `getInstance` and `getSDKInstance`。
-
-- `getInstance`: Used for get the instance of `CoreServicesImpl`.
-- `getSDKInstance`: Used for get the instance of Chat SDK.
-
-`CoreServicesImpl` is the main class of `TUIKit` , providing the methods includes initialization, logging in and out, getting user information, etc.
-
-```dart
-import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
-
-final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
-final V2TIMManager _sdkInstance = TIMUIKitCore.getSDKInstance();
-
-// init
-_coreInstance.init(
- language: LanguageEnum?, // Specify the displaying language among English / Chinese, Traditional / Chinese, Simplified. If this field is not provided, the default is the system language
- onTUIKitCallbackListener: ValueChanged, // The callback listener for information from TUIKit, includes errors from SDK API/ the info needs to reminds users/ errors from Flutter. You can reminds users up to your business needs, the description below for details.
- sdkAppID: 0, // sdkAppID from Tencent IM console
- loglevel: LogLevelEnum.V2TIM_LOG_DEBUG,
- listener: V2TimSDKListener());
-// unInit
-_coreInstance.unInit();
-
-// login
-_coreInstance.login(
- userID: 0, // user ID
- userSig: "" // [How do I calculate UserSig on the server?](https://www.tencentcloud.com/document/product/1047/34385?from=pub)
-)
-
-// logout
-_coreInstance.logout();
-
-// getUsersInfo
-_coreInstance.getUsersInfo(userIDList: ["123", "456"]);
-
-// setOfflinePushConfig
-_coreInstance.setOfflinePushConfig(
- businessID: businessID, // The business from Tencent IM console, for each platform of devices
- token: token, // The token from manufactors when registering the offline push
-)
-
-// setSelfInfo
-_coreInstance.setSelfInfo(userFullInfo: userFullInfo) // set self userinfo
-
-// setTheme
-_coreInstance.setTheme(TUITheme theme: theme) // set theme color
-/*
- TUITheme(
- // Primary color
- final Color? primaryColor;
-
- // Secondary color
- final Color? secondaryColor;
-
- // Info color, for secondary operations or prompts
- final Color? infoColor;
-
- // Weak background color, lighter than the main background color, used to fill gaps or shadows
- final Color? weakBackgroundColor;
-
- // Weak divider line color, for dividing lines or borders
- final Color? weakDividerColor;
-
- // Weak text color
- final Color? weakTextColor;
-
- // Dark text color
- final Color? darkTextColor;
-
- // Used for AppBar or Panels
- final Color? lightPrimaryColor;
-
- // Text color
- final Color? textColor;
-
- // Warning color for dangerous operation
- final Color? cautionColor;
-
- // Group owner identification color
- final Color? ownerColor;
-
- // Group administrator identification color
- final Color? adminColor;)
-*/
-```
-
-#### `onTUIKitCallbackListener`
-
-This listener is used to get information including: errors form SDK API / errors form Flutter / some remind information that may need to pop up to prompt users.
-
-Determine the type by `TIMCallbackType`.
-
-> You may refer to our [DEMO](https://github.com/TencentCloud/tc-chat-demo-flutter/lib/src/pages/app.dart) for the codes in this part, and modifying up to your business needs.
-
-##### Errors form SDK API(`TIMCallbackType.API_ERROR`)
-
-In this scenario, SDK API original `errorMsg` and `errorCode` are provided.
-
-[Error codes listed here](https://www.tencentcloud.com/document/product/1047/34348?from=pub)
-
-#### Errors form Flutter(`TIMCallbackType.FLUTTER_ERROR`)
-
-This error is captured by listening Flutter natively throwing an exception, providing `stackTrace` (from `FlutterError.onError`) or `catchError` (from try-catch) when the error occurs.
-
-#### Remind information(`TIMCallbackType.INFO`)
-
-It is suggest to pup up to prompt users for this kind of messages.
-
-Provide the `infoCode` info code to help you determine the current scene, and provide the default prompt recommendation `infoRecommendText`.
-
-You can directly pop up our recommendations, or you can customize the recommendations according to the scene code. The language of recommendation text is adaptive according to the system language or the language you specified, do not judge the scene according to the recommendation language.
-
-The rules for info code are as follows:
-
-The info code consists of seven digits, the first five digits determine the components of the scene, and the last two digits determine the specific performance of the scene.
-
-| The first five digits | Corresponding widget |
-| ---------- | ---------------------- |
-| 66601 | `TIMUIKitAddFriend` |
-| 66602 | `TIMUIKitAddGroup` |
-| 66603 | `TIMUIKitBlackList` |
-| 66604 | `TIMUIKitChat` |
-| 66605 | `TIMUIKitContact` |
-| 66606 | `TIMUIKitConversation` |
-| 66607 | `TIMUIKitGroup` |
-| 66608 | `TIMUIKitGroupProfile` |
-| 66609 | `TIMUIKitNewContact` |
-| 66610 | `TIMUIKitGroupProfile` |
-| 66611 | `TIMUIKitNewContact` |
-| 66612 | `TIMUIKitProfile` |
-| 66613 | `TIMUIKitSearch` |
-| 66614 | General Widget |
-
-All info codes are listed below:
-
-| `infoCode` | Recommendation prompt `infoRecommendText` | Scene description |
-| ----------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
-| 6660101 | Contact request sent | User requests to add another user as a contact. |
-| 6660102 | This user is your contact. | When a user applies to add another user who is already a contact, the callback of `onTapAlreadyFriendsItem` is triggered. |
-| 6660201 | Group request sent | Users apply to join a group chat that requires the approval of the administrator.|
-| 6660202 | You are already in this group | When a user applies to join a group, it is determined that the user is already a member of the current group, triggering the callback of `onTapExistGroup`. |
-| 6660401 | Failed to locate the original message | When the user needs to jump to the @ message or reference the message, the target message is not found in the message list. |
-| 6660402 | Video saved successfully | After clicking on the video message in the message list, and chooses to save the video. |
-| 6660403 | Failed to save the video | After clicking on the video message in the message list, and chooses to save the video. |
-| 6660404 | Message too short | The user sent an overly short voice message. |
-| 6660405 | Sending failed. The video cannot exceed 100 MB. | The user attempted to send a video larger than 100MB. |
-| 6660406 | Image saved successfully | After clicking on the image in the message list, the user chooses to save the picture. |
-| 6660407 | Failed to save the image | After clicking on the image in the message list, the user chooses to save the picture. |
-| 6660408 | Copied | The user chooses to copy the text message in the pop-up window. |
-| 6660409 | Not implemented | The user selects a non-implemented function in the pop-up. window |
-| 6660410 | You are receiving other files | When the user clicks the download file message, the previous download task has not yet been completed. |
-| 6660411 | Receiving | User clicks to download file message. |
-| 6660412 | Video is available with .mp4 only | The user sent a video message in non-mp4 format |
-| 6660413 | Added to download queue and waiting | Added to the queue to be downloaded, while other files are downloading |
-| 6661001 | Modification failed due to network disconnection | When users try to modify group data in a non-network environment. |
-| 6661002 | Failed to view the group members due to network disconnection | When users try to modify group data in a non-network environment. |
-| 6661003 | Admin role canceled successfully | The user removes the other users from the administrator in the group. |
-| 6661201 | Modification failed due to network disconnection | When a user tries to modify his or her contact information without a network environment. |
-| 6661202 | Added successfully | Add other users as contact on the profile page and automatically add them successfully without verification. |
-| 6661203 | Request sent successfully | Add other users as contact on the profile page, and the other user's settings need to be verified. |
-| 6661204 | The user is blocked | Add other users as contacts on the profile page, who are on their own blocklist. |
-| 6661205 | Added failed | Add other users as contact on the profile page, but failed, probably because the other party is forbidden to add contact. |
-| 6661206 | Deleted successfully | Delete other users as contact on the profile page and succeed. |
-| 6661207 | Deleted failed | Delete other users as contact on the profile page. Failed. |
-| 6661401 | The input cannot be empty | When the user is entering information, an empty string is entered. |
-| 6661402 | Please provide a life cycle hook navigating back to home or other pages. | When users quit the group or dissolve the group, they did not provide a way to return to the home page. |
-| 6661403 | Insufficient disk storage space, it is recommended to clean up to obtain a better experience | After the login is successful, the device storage space will be automatically detected. If there is less than 1GB, it will be prompted. |
-
-### TIMUIKitConversation
-
-`TIMUIKitConversation` shows the conversation list.
-
-The corresponding controller: `TIMUIKitConversationController` is also provided.
-
-```dart
-import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
-
-final TIMUIKitConversationController _controller =
- TIMUIKitConversationController();
-
-void _handleOnConvItemTaped(V2TimConversation? selectedConv) {
- // You can jump to the chat interface here.
-}
-
-List _itemSlidableBuilder(
- V2TimConversation conversationItem) {
- return [
- ConversationItemSlidablePanel(
- onPressed: (context) {
- _clearHistory(conversationItem);
- },
- backgroundColor: hexToColor("006EFF"),
- foregroundColor: Colors.white,
- label: 'Clear conversaation',
- autoClose: true,
- ),
- ConversationItemSlidablePanel(
- onPressed: (context) {
- _pinConversation(conversationItem);
- },
- backgroundColor: hexToColor("FF9C19"),
- foregroundColor: Colors.white,
- label: conversationItem.isPinned! ? 'unpined' : 'pinned',
- )
- ];
- }
-
-TIMUIKitConversation(
- lifeCycle: ConversationLifeCycle(), // The lifecycle hook
- onTapItem: _handleOnConvItemTaped, // Callback of clicking conversation, can navigating to chat page
- itemSlidableBuilder: _itemSlidableBuilder, // Operation items for conversation Item sliding to the left, conversation topping, etc.
- controller: _controller, // Conversation component controller, through which you can get conversation data, set conversation data, pin conversation to top and other operations
- itembuilder: (conversationItem) {} // Used to customize the conversation item. Can be combined with TIMUIKitConversationController to implement business logic.
- conversationCollector: (conversation) {} // Conversation collector, which can customize whether the conversation is displayed
- lastMessageBuilder: (V2TimMessage, List) {} // Customize the second line of the conversation item, which is generally used to show the last message
-)
-```
-
----
-
-### TIMUIKitChat
-
-`TIMUIKitChat` is the main chat component that provides the display of message list and the ability to send messages.
-
-It also supports custom display of various message types.
-
-Additionally, it can be combined with `TIMUIKitChatController` to realize local storage and pre-rendering of messages, etc.
-
-Currently supported message parsing:
-
-- Text message.
-- Image message.
-- Video message.
-- Voice message.
-- Group message.
-- Merge message.
-- File message.
-
-```dart
-import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
-
-TIMUIKitChat(
- lifeCycle: ChatLifeCycle(), // Lifecycle hook for TIMUIKitChat
- conversationID: "", // User ID or Group ID
- conversationType: ConversationType, // 1 is c2c chat, 2 is group chat
- conversationShowName: "",
- appBarActions: [], // appBar operation item, which can be used to jump to the page of group details and personal details, etc.
- onTapAvatar: _onTapAvatar, // callback function for clicking the avatar, which can be used to jump to the user profile.
- messageItemBuilder: (MessageItemBuilder) {
- // Message item layout constructor, you can choose to customize part of the message type or message row layout.
- },
- extraTipsActionItemBuilder: (message) {
- // The configuration for the menu, opend by long pressed messages
- },
- morePanelConfig: MorePanelConfig(), // The config for more panel area
- appBarConfig: AppBar(), // The config for AppBar
- mainHistoryListConfig: ListView(), // Additional config for ListView of historical message list
- textFieldHintText: "", // The hint of inputTextField
- draftText: "", // The draft of inputting message
- initFindingMsg: 0, // The message been jumped
- config: TIMUIKitChatConfig(), // The config for the whole TIMUIKitChat
- onDealWithGroupApplication: (String groupID){
- // Navigating to the pages for the page of [TIMUIKitGroupApplicationList] or other pages to handle joining group application for specific group
- }
-)
-```
-
----
-
-### TIMUIKitProfile
-
-`TIMUIKitProfile` shows the detail information for a user, and manage the relationship.
-
-```dart
-TIMUIKitProfile(
- userID: "",
- controller: TIMUIKitProfileController(), // Profile Controller
- profileWidgetBuilder: ProfileWidgetBuilder(), // Customized some kinds of item.
- profileWidgetsOrder: List, // Determine the vertical sequence for those profile widgets.
- builder: (
- BuildContext context,
- V2TimFriendInfo friendInfo,
- V2TimConversation conversation,
- int friendType,
- bool isMute) {
- // Customized the whole page. `profileWidgetBuilder` and `profileWidgetsOrder` will no longer works if you define this.
- },
- lifeCycle: ProfileLifeCycle(),// Lifecycle hook for TIMUIKitProfile
-)
-```
-
----
-
-### TIMUIKitGroupProfile
-
-`TIMUIKitGroupProfile` shows the details of a group and can manage this group.
-
-```dart
-TIMUIKitGroupProfile(
- groupID: "",
- profileWidgetBuilder: GroupProfileWidgetBuilder(), // Customized some kinds of item.
- profileWidgetsOrder: List, // Determine the vertical sequence for those profile widgets.
- builder: (BuildContext context, V2TimGroupInfo groupInfo, List groupMemberList){
- // Customized the whole page. `profileWidgetBuilder` and `profileWidgetsOrder` will no longer works if you define this.
- },
- lifeCycle: GroupProfileLifeCycle, // Lifecycle hook for TIMUIKitGroupProfile
-)
-```
-
----
-
-### TIMUIKitBlackList
-
-`TIMUIKitBlackList` shows the list of blocked users.
-
-```dart
-TIMUIKitBlackList(
- onTapItem: (_) {}, // Callback of clicking the item
- emptyBuilder: () {} // The builder when no user listed
- itemBuilder: () {} // Customized the user item
- lifeCycle: BlockListLifeCycle(), // Lifecycle hook for TIMUIKitBlackList
-)
-```
-
----
-
-### TIMUIKitGroup
-
-`TIMUIKitGroup` shows the list of joined group.
-
-```dart
-TIMUIKitGroup(
- onTapItem: (_) {}, // Callback of clicking the item
- emptyBuilder: () {} // The builder when no group listed
- itemBuilder: () {} // Customized the group item
-)
-```
-
----
-
-### TIMUIKitContact
-
-`TIMUIKitContact` shows the list of contacts.
-
-```dart
-import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
-
-TIMUIKitContact(
- lifeCycle: FriendListLifeCycle(), // Lifecycle hook for TIMUIKitContact
- topList: [
- TopListItem(name: "New Contact", id: "newContact"),
- TopListItem(name: "Group", id: "groupList"),
- TopListItem(name: "Blocklist", id: "blackList")
- ], // Top list array
- topListItemBuilder: _topListBuilder, // The builder for top list array
- onTapItem: (item) { }, // Callback of clicking a contact
- emptyBuilder: (context) => const Center(
- child: Text("No cantact"),
- ), // The builder when no contact listed
- );
-```
-
-### TIMUIKitSearch
-
-`TIMUIKitSearch` is a global search widget. Global search supports search for "contacts" / "groups" / "chat records".
-`TIMUIKitSearchMsgDetail` is an intra-conversation search component that can search for chat records for a specific conversation.
-
-```dart
-import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
-
-// Search globally
-TIMUIKitSearch(
- onTapConversation: _handleOnConvItemTapedWithPlace, // Function(V2TimConversation, V2TimMessage? message), navigating to specific message from specific conversation
- onEnterConversation: (V2TimConversation conversation, String initKeyword){}, // Navigating to search in specific conversation. Please navigate to TIMUIKitSearchMsgDetail manually
-);
-
-// Search inside a specific conversation
-TIMUIKitSearchMsgDetail(
- currentConversation: conversation!,
- onTapConversation: onTapConversation,
- keyword: initKeyword ?? "",
- );
-```
-
-## What's more
-
-In addition to Flutter SDK, we have numerous SDKs that covering all platforms. The following platforms can communicate with each other and provide services across devices and platforms.
-
-| Platform | Introduction | Demo | Download | UI Components library |
-| --- | --- | --- | --- | --- |
-| Android | Compatible with JDK 1.6 and Android SDK version 14 and later | [Get](https://www.tencentcloud.com/document/product/1047/34279) | [Get](https://github.com/TencentCloud/TIMSDK/tree/master/Android/IMSDK) | [Get](https://www.tencentcloud.com/document/product/1047/50062) |
-| iOS | Compatible with iOS 8.0 and later | [Get](https://www.tencentcloud.com/document/product/1047/34279) | [Get](https://github.com/TencentCloud/TIMSDK/tree/master/iOS/IMSDK) | [Get](https://www.tencentcloud.com/document/product/1047/50062) |
-| Mac | Compatible with OS X 10.10 and later | - | [Get](https://github.com/TencentCloud/TIMSDK/tree/master/Mac/IMSDK) | - |
-| Windows | C and C++ are included. Compatible with Windows 7, Windows 8 and 8.1, and Windows 10. Both 32-bit and 64-bit programs can be connected | - | [Get](https://github.com/TencentCloud/TIMSDK/tree/master/Windows/IMSDK) | - |
-| Web | Supports Internet Explorer 11+, Chrome 7+, Firefox 3.6+, Opera 12+ and Safari 6+ | [Get](https://www.tencentcloud.com/document/product/1047/34279) | [Get](https://www.npmjs.com/package/tim-js-sdk) | [Get](https://www.tencentcloud.com/document/product/1047/50061) |
-| Unity | Supports 2020.2.7f1c1 or later | - | [Get](https://www.tencentcloud.com/document/product/1047/46263) | - |
-| Flutter | Supports Flutter 2 & dart 2.12 and later, deploying to Android, iOS, Web, macOS and Windows. | [Get](https://www.tencentcloud.com/document/product/1047/34279) | Here | [Get](https://pub.dev/packages/tencent_cloud_chat_uikit) |
-| Electron | Electron SDK | - | [Get](https://github.com/tencentyun/im_electron_demo) | - |
-
-## Contact Us
-
-Please do not hesitate to contact us in the following place, if you have any further questions or tend to learn more about the use cases.
+Please do not hesitate to contact us in the following place, if you have any further questions or
+tend to learn more about the use cases.
- Telegram Group:
- WhatsApp Group:
diff --git a/doc/社交场景最佳实践.md b/doc/社交场景最佳实践.md
deleted file mode 100644
index 118f1d6..0000000
--- a/doc/社交场景最佳实践.md
+++ /dev/null
@@ -1,509 +0,0 @@
-
-# 社交场景最佳实践方案
-
-社交模块是目前主流应用程序最常见的功能之一。有了社交模块,用户在您的应用内,可以自由的交流互动,并添加好友,关注其他用户等等。
-
-这可在很大程度上,促进您应用程序的活跃度,吸引用户留存,获取更多新用户,并可拓展您应用的业务范围。让更多用户花更多时间在您的应用程序上。
-
-例如,
-
-- 交友软件,其核心便是社交聊天模块,用于匹配对话及用户关系链维护,让更多的用户相聚与相识;
-- 音乐软件,可用社交模块让乐迷及粉丝群体实时沟通,打造音乐社区文化;
-- 教育软件,可用社交模块打通 "学校-教师-家长" 循环,促进家校互动,形成家校社三合力,更大程度发挥教育影响作用,保证教育的一致性与连贯性;
-- 电子游戏,特别是RPG类型,内置的聊天模块让玩家能在线组局,一起作战,并组建工会,创造游戏内社区,提升游戏社交属性,丰富体验,提升活跃度;
-- 医疗软件,聊天及社群模块让患者间得以互助交流,一起战胜病魔,走出病情,让大家看到希望;
-- 导航软件,堵车交流群,让归心似箭的旅行者们,交流前方最新情报,不再无聊干等,发发段子调侃一下,为拥堵的出行,带来一些希望与欢乐。
-
-因此,社交聊天模块可谓是您应用程序不可或缺的能力。
-
-本文将以腾讯云IM为基础,梳理现有应用在接入社交场景过程中常见需求,给出解决实现方案。以及可能遇到的问题、需要注意的细节点等,希望能帮助客户朋友们快速的理解业务、实现需求。
-
->? 本文档主要介绍社交场景的通用SDK实现方案,文中示例截图来自于我们的[Flutter TUIKit](https://cloud.tencent.com/document/product/269/70747),您可根据需要,选用我们提供的全部平台SDK或TUIKit。
-
-## 用户
-
-腾讯云IM支持托管维护用户信息与用户资料。您可直接将您应用的用户资料存储与我们的服务内,并通过相关API进行读取/更新/维护操作。
-
-对于社交场景,常见用户资料可分为基本信息资料和其他信息资料。
-
-|基本信息|其他信息|
-|---|---|
-|用户名,性别,生日,所在地,个性签名,昵称等|其他社交模块内需要的资料|
-
-### 导入现有用户数据
-
-如果您需要给您现有应用,添加社交能力。可直接使用我们的服务端API,快速将您现有用户数据,完整导入至腾讯云IM中。
-
-导入完成后,现有用户可直接使用其原有身份数据,和其他用户发起会话,一起聊天,开启社交之旅。
-
-[导入多个账号 - 服务端API](https://cloud.tencent.com/document/product/269/4919)
-
-### 用户在线状态
-
-腾讯云IM支持自动上报并让其他用户获取[在线状态信息](https://cloud.tencent.com/document/product/269/3665)。
-
-状态包括:前台运行状态 / 后台运行状态 / 未登录状态。
-
-利用这一能力,您可让用户看到其他用户的在线状态,增强互动性。
-
-此外,您还可使用这一能力,针对您的业务场景,做许多功能拓展。例如,交友软件,能够优先推荐匹配在线的用户,让聊天可进行的更顺畅,缘分更快相聚。
-
-| 会话列表用户在线状态 | 通讯录用户在线状态 |
-|---------|---------|
-| | |
-
-#### 获取用户在线状态
-
-在客户端上, 您可调用 [`getUserStatus`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMManager/getUserStatus.html) 方法,[批量查询其他用户的在线状态](https://cloud.tencent.com/document/product/269/75511#.E6.9F.A5.E8.AF.A2.E7.94.A8.E6.88.B7.E7.8A.B6.E6.80.81)。
-
-此外,在服务端上,也可[通过REST API,获取用户状态](https://cloud.tencent.com/document/product/269/2566)。
-
-#### 订阅用户在线状态变更
-
-其他用户的在线状态总是实时在变化的,您可在客户端上,调用 [`subscribeUserStatus`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMManager/subscribeUserStatus.html) 方法,[批量订阅其他用户的在线状态](https://cloud.tencent.com/document/product/269/75511#.E8.AE.A2.E9.98.85.E7.94.A8.E6.88.B7.E7.8A.B6.E6.80.81)。当发生变化时,将通过回调函数通知您,您可根据其,修改界面UI并完成其他您的业务操作。
-
-## 好友
-
-好友管理,又称关系链管理,是社交场景的基础。众多会话/聊天特性,都要依赖于好友关系状态。有了好友关系链能力,众多的用户才能得以串联起来,互动形成整体。
-
-例如微信/QQ只允许好友间发起一对一单聊;交友软件则常常可在非好友的情况下,进行有限度的聊天;在线娱乐社区软件,则常常不需要好友关系即可会话。
-
-因此,您需要根据您的应用使用场景,确定好友及关系链管理的用法。
-
-| 通讯录 |
-|---------|
-| |
-
-### 好友关系
-
-腾讯云IM支持单个用户添加最多3000个好友。
-
-#### 好友关系类型
-
-好友关系类别包含单向好友和双向好友。
-
-- 双向好友:用户 A 的好友表中有用户 B,B 的好友表中也有 A。
-- 单向好友:用户 A 的好友表中有用户 B,但 B 的好友表中却没有 A。
-
-#### 添加好友验证方式
-
-一回合加好友:如果帐号 A 设置的加好友验证方式是 [AllowType_Type_AllowAny](https://cloud.tencent.com/document/product/269/1500#.E6.A0.87.E9.85.8D.E8.B5.84.E6.96.99.E5.AD.97.E6.AE.B5),那么任何人添加 A 为好友都可直接添加成功,这种一个请求就添加好友成功的场景称作一回合加好友。
-
-两回合加好友:如果帐号 A 设置的加好友验证方式是 [AllowType_Type_NeedConfirm](https://cloud.tencent.com/document/product/269/1500#.E6.A0.87.E9.85.8D.E8.B5.84.E6.96.99.E5.AD.97.E6.AE.B5),那么任何人添加 A,A 都会收到一个请求加好友验证消息,这是第一个回合,然后 A 对这个请求加好友验证消息进行同意操作时,这是第二个回合,这种需要验证的加好友场景就被称为两回合加好友。
-
-### 非好友发消息
-
-对于某些场景,需要非好友关系也能发送消息。例如对于交友软件,常常允许匹配到的陌生人,发送若干条消息打招呼。
-
-这需要您在[腾讯云IM的控制台](https://console.cloud.tencent.com/im/login-message),关闭 “好友关系检查” 功能。
-
-
-
-如果您需要针对陌生人发消息的数量加以限制,可在您的业务层实现。发送若干条消息后,不再允许发送即可。
-
-## 群组
-
-有的时候,仅一对一单聊无法满足您的社交场景要求。
-
-例如对于音乐类型app,优质的乐迷群及粉丝群,有助于打造音乐社区文化,吸引用户留存。
-
-### 群组类型
-
-腾讯云IM支持多种类型的群,为便于理解,在这里以常见的群聊举例。
-
-#### 微信群 - 好友工作群 Work
-
-Work群,类似普通微信群。创建后仅支持已在群内的好友邀请加群,且无需被邀请方同意或群主审批。
-
-该群适合用于打造好友间互动拉入,产生的群。
-
-#### QQ群 - 陌生人社交群 Public
-
-Public群,类似QQ群。创建后群主可指定群管理员,用户搜索群 ID 发起加群申请后,需要群主或管理员审批通过(可选)才能入群。
-
-该群适合用于打造兴趣社区,用户在您的app中,发现好玩的兴趣群组,可按需主动加入。
-
-#### 会议群 - Meeting
-
-创建后可随意进出,且支持查看入群前消息。
-
-适合用于音视频会议场景、在线教育场景等与实时音视频产品结合的场景。
-
-#### 直播群 - AVChatRoom
-
-创建后可随意进出,没有群成员数量上限,但不支持历史消息存储。
-
-适合与直播产品结合,用于弹幕聊天场景。
-
-#### Discord - 社群 Community
-
-创建后可随意进出,最多支持10w人,支持历史消息存储,用户搜索群 ID 发起加群申请后,无需管理员审批即可进群。[详情可查看此文档](https://cloud.tencent.com/document/product/269/83870)。
-
-### 群资料
-
-群资料主要包括 群组本身的资料 和 群成员资料。
-
-#### 群组本身的资料
-
-群组资料是指单个群组维度的属性,包括群名称、简介、公告、群主等,以及群组维度自定义字段。
-
-##### 获取群资料
-
-在客户端上,可调用 [`getGroupsInfo`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMGroupManager/getGroupsInfo.html) 方法,获取特定群组资料详情。
-
-##### 修改群资料
-
-可修改群组名称、群组简介、群组公告、群组头像、群名片,修改加群选项、群纬度自定义字段、用户群内身份、群成员维度自定义字段和接收群消息选项等信息。
-
-在客户端上,可调用 [`setGroupInfo`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMGroupManager/setGroupInfo.html) 方法,修改特定群组资料详情。
-
-#### 群成员资料
-
-群成员资料主要包括,特定成员,在群内的备注名/角色/禁言状态/自定义字段信息。在强社交场景的群内,会非常实用。
-
-##### 获取群成员资料
-
-可通过客户端 [`getGroupMembersInfo`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMGroupManager/getGroupMembersInfo.html) 方法或 [`getGroupMemberList`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMGroupManager/getGroupMemberList.html) 方法获取此信息。
-
-##### 修改群成员资料
-
-不同的群成员资料,调用不同的API方法修改。[详情可查看此处](https://cloud.tencent.com/document/product/269/37411#.E7.BE.A4.E6.88.90.E5.91.98)。
-
-## 会话
-
-一个会话,您可理解为同某个特定用户的单聊,或一个群聊的消息集合。当用户创建了一个单聊或群聊,当其中有消息的收发时,对应的会话就随之创建。
-
-在腾讯云IM层面,每个会话都是一个 [`V2TIMConversation`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimConversation.html) 类的实例,包括了 `会话类型 / 会话ID / 用户ID / 群ID / 显示名称 / 头像 / 最后一条消息 / 草稿 / 群聊类型 / 消息接收方式 / 群 @ 信息列表 / 是否置顶 / 标记列表 / 所属分组信息 / 自定义数据` 信息。
-
-### 会话列表
-
-会话列表,您可以理解成微信软件的首页。即,所有会话的集合。方便用户找到目标会话。
-
-会话列表功能主要分为获取会话列表、处理会话列表更新。
-
-| 会话列表 |
-|---------|
-| |
-
-#### 获取会话列表
-
-您可在客户端上调用 [`getConversationList`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMConversationManager/getConversationList.html)。该接口获取的是本地缓存的会话,如果服务器会话有更新,SDK 内部会自动同步,然后在 [`V2TIMConversationListener`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Listener/V2TimConversationListener.html) 通知回调。
-
-如果您的应用场景会产生较多的会话数,考虑到加载效率、网络省流,我们建议您采用分页拉取的方式。每次分页拉取的数量建议不超过 100 个。[具体方案可参考此处。](https://cloud.tencent.com/document/product/269/75366#.E5.88.86.E9.A1.B5.E6.8B.89.E5.8F.96)
-
-#### 会话列表实时更新
-
-当会话信息发生变化,例如收到一条新消息/设置消息草稿/出现一个新的会话,都会导致会话列表发生更新。
-
-如果需要实时获取更新信息,需要通过客户端的 [`addConversationListener`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMConversationManager/addConversationListener.html) 方法,添加会话监听器。收到更新触发后,更改UI。
-
-### 会话草稿
-
-在发送消息时,可能会遇到消息尚未编辑完,就要切换至其它聊天窗口的情况。
-
-这些未编辑完的消息可通过 [`setConversationDraft`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMConversationManager/setConversationDraft.html) 接口保存,以便于下次回到这个聊天界面时,通过 [`V2TIMConversation`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimConversation.html) 对象的 [`draftText`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimConversation.html#drafttext) 字段,获取到尚未编辑完的内容,继续编辑。
-
-此类草稿信息,仅保存在本地。
-
-### 置顶会话
-
-会话置顶指的是把单聊或者群聊会话固定在会话列表的最顶部,不会被其他会话更新挤到底部,方便用户查找。
-
-在社交场景中,用户常常需要将一些重要的人或群置顶。这在我们使用微信的过程中,很普遍。
-
-置顶状态会存储在服务器,切换终端设备后,置顶状态会同步到新设备上。
-
-| 置顶的会话,注意最上方第一条 |
-|---------|
-| |
-
-置顶会话,通过客户端 [`pinConversation`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMConversationManager/pinConversation.html) 即可。
-
-调用 [`getConversationList`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMConversationManager/getConversationList.html) 获取会话列表时,该接口返回的会话列表中,置顶的会话在前,未置顶的会话在后。您可以通过 [`V2TIMConversation`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimConversation.html) 对象的 [`isPinned`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimConversation.html#ispinned) 字段,检查会话有没有置顶。
-
-### 会话标记
-
-在某些社交场景下,您可能需要对会话进行标记,例如 "会话标星"、"会话折叠"、"会话隐藏"、“会话标记未读”。
-
-直接在客户端调用 [`markConversation`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMConversationManager/markConversation.html) 方法,即可标记,或取消标记一条会话。
-
-标记后,在后续通过 [`getConversationListByFilter`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMConversationManager/getConversationListByFilter.html) 方法请求会话列表时,可按照标记,过滤获取指定会话。
-
-## 消息
-
-消息是社交模块的灵魂。众多各种类型的消息,组成了一个个会话,使得用户与用户之间,紧密的串联在一起。
-
-腾讯云IM中,一对一单聊消息与群聊消息,用法在大部分场景中都类似,下面着重介绍几点。
-
-### 消息类型
-
-腾讯云IM支持多种类型的消息,如下:
-
-| 功能类型 | 功能描述 |
-| ------------ | ------------------------------------------------------------ |
-| 文本消息 | 消息内容是普通文本 |
-| 表情消息 | 表情消息为开发者自定义,可传入表情资源路径 |
-| 地理位置消息 | 消息内容为地理位置标题、经度、纬度信息 |
-| 图片消息 | 消息内容为图片的 URL 地址、尺寸、图片大小等信息,最大支持大小为28M的图片 |
-| 语音消息 | 消息内容为语音文件的 URL 地址、大小、时长等信息,最大支持大小为28M的语音文件 |
-| 文件消息 | 消息内容为文件的 URL 地址、大小、格式等信息,格式不限,最大支持大小为100M的文件 |
-| 短视频消息 | 消息内容为短视频文件的 URL 地址、时长、大小、格式等信息,最大支持大小为100M的短视频文件 |
-| 自定义消息 | 开发者自定义的消息类型,例如红包消息、石头剪刀布等形式的消息 |
-
-### 消息回复
-
-回复一条消息,既支持使用文字内容,发一条新消息,引用原消息;也支持使用[Emoji表情回应](https://cloud.tencent.com/document/product/269/85906)。
-
-| 引用回复文本 | [表情回应](https://cloud.tencent.com/document/product/269/85906) |
-|---------|---------|
-| | |
-
-#### 引用回复文本
-
-此方案效果和微信中,长按一条消息,选择 “引用”,效果一致。
-
-引用消息,实际上,在腾讯云IM SDK层面,也是一条普通文本消息。文本消息的主体,则是回复的文字内容。
-
-为了展示原消息的引用,需要在发送文本消息的时候,将原消息的信息,传入新消息的 [`cloudCustomData`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimMessage.html#cloudcustomdata) 字段中。例如,我们的TUIKit,为了实现这个功能,传入了如下JSON。
-
-```json
-"messageReply": {
- "messageID": 原消息的ID,
- "messageAbstract": 原消息的描述,用于显示在消息列表气泡中,
- "messageSender": 原消息的发送者,建议使用备注名或昵称,
- "messageType": 原消息类型,
- "version": 协议版本
-}
-```
-
-在消息列表中展示时,从 [`cloudCustomData`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimMessage.html#cloudcustomdata) 字段中,提取出上述JSON信息,直接用于拼接展示 `"${messageSender}: ${messageAbstract}"` 即可。
-
-如需支持点击展示引用消息的区域,跳转至被引用原始消息。可根据上述JSON中的 `messageID` 字段,在消息列表中,找到这条消息,跳转即可。如果消息当前不存在于数组中,可直接调用 [`getHistoryMessageList`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/getHistoryMessageList.html),参数传入 `messageID`,获取本条消息及其上下文消息。
-
-#### 表情回应
-
-在回复特定的单条消息时,你不仅可以直接引用原消息并回复,还可使用Emoji表情回应,高效表达“好的”、“赞同”、“很棒”、“哭笑不得”、“加油”等多种信息,大大降低沟通成本,解决多人聊天中消息冗杂的问题。
-
-通常,若干个用户,可对同一条消息,或多条消息,点击一个或若干个回应表情。[在显示上](https://cloud.tencent.com/document/product/269/85906#.E5.B1.95.E7.A4.BA.E8.A1.A8.E6.83.85.E5.9B.9E.E5.BA.94),这些回应信息,常常承载在不同的气泡中,以表情为首,后面跟着若干个名字。如本章节图片所示。
-
-这些名称,需要支持点击,并跳转至用户Profile详情页中。若名字过多,还需要加以折叠,[通过新窗口详情页展示](https://cloud.tencent.com/document/product/269/85906#.E5.B1.95.E7.A4.BA.E8.A1.A8.E6.83.85.E5.9B.9E.E5.BA.94)。
-
-[发送表情回应](https://cloud.tencent.com/document/product/269/85906#.E5.8F.91.E9.80.81.E8.A1.A8.E6.83.85.E5.9B.9E.E5.BA.94),则可放置于消息的长按菜单中。
-
-| 发送表情回应 [TUIKit](https://cloud.tencent.com/document/product/269/85906) | 表情回应详情 [TUIKit](https://cloud.tencent.com/document/product/269/85906) |
-|---------|---------|
-| | |
-
-**下面介绍实施细节:**
-
-表情回应的数据,存储于消息的 [`cloudCustomData`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimMessage.html#cloudcustomdata) 字段中,可以如下JSON格式示例。其中的 `key` 字段,如采用Emoji Unicode表情字符,可直接传入 Unicode;若采用图片小表情,可传入路径或文件名。
-
-```json
-messageReactions: [
- {
- key: "表情名称1",
- users: ["用户1", "用户2", ...]
- },
- {
- key: "表情名称2",
- users: ["用户1", "用户2", ...]
- },
- ...
-]
-```
-
-本部分代码可根据您的需求,加以修改。
-
-展示时,渲染遍历上述结构体即可。
-
-发送表情回应,则直接在客户端调用 [`modifyMessage`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/modifyMessage.html) 方法,修改消息本身的 [`cloudCustomData`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimMessage.html#cloudcustomdata) 字段即可。将当前用户头像,添加或从上述结构体中移除,完成回应或取消回应。
-
-调用方法修改后,所有用户的 [`V2TimAdvancedMsgListener`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Listener/V2TimAdvancedMsgListener.html) => [`onRecvMessageModified`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Callback/OnRecvMessageModified.html) 监听器会触发,您可依此修改消息UI,展示最新表情回应内容。
-
-### 删除消息
-
-删除消息分为两种:删除本地消息和删除云端消息。
-
-删除云端消息会在删除本地消息的基础上,同步删除云端存储的消息,且无法恢复。
-
-删除本地消息,在客户端调用 [`deleteMessageFromLocalStorage`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/deleteMessageFromLocalStorage.html) 方法。需要注意的是,如果程序卸载重装,依旧能获取到被删除的消息。
-
-删除云端存储的消息,在客户端调用 [`deleteMessages`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/deleteMessages.html) 方法。此方法不支持多端同步,即无法自动删除,其他设备上,已经存在的消息。
-
-### 搜索消息
-
-搜索能力是社交场景中的重要一环。
-
-用户们常常需要,在特定会话中,亦或是全局中,快速准确搜索到某条消息。此外,也可作为社交活动运营工具,增加相关内容的引导,简洁高效。
-
-您可在客户端,调用 [`searchLocalMessages`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/searchLocalMessages.html) 方法,并传入以 [`V2TIMMessageSearchParam`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimMessageSearchParam.html) 对象封装的关键词信息,即可完成搜索。
-
-如果您希望在全部会话范围内搜索,只需要将 [`V2TIMMessageSearchParam`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimMessageSearchParam.html) 中的 [`conversationID`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimMessageSearchParam.html#conversationid) 设置为空或者不设置即可。
-
-| 全局搜索 | 会话内搜索 |
-|---------|---------|
-| | |
-
-### 转发消息
-
-在日常生活聊天或工作场景中,将一个会话中的消息,合并或逐条转发至另一个会话,是个非常高频且基础的操作。
-
-| 合并转发消息 | 合并消息详细内容 |
-|---------|---------|
-| | |
-
-逐条转发消息,需要先在客户端调用 [`createForwardMessage`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/createForwardMessage.html) 方法创建一条和原消息内容完全一样的转发消息,再调用 [`sendMessage`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/sendMessage.html) 方法把转发消息发送出去。
-
-合并转发消息,同样需要先创建后转发。需要额外注意的是,在客户端调用 [`createMergerMessage`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/createMergerMessage.html) 方法创建一条合并消息时,需要设置原始消息列表,合并消息标题、合并消息摘要等信息。
-
-若想转发至多个接收者,遍历调用 [`sendMessage`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/sendMessage.html) 方法即可。
-
-对于接收者端,若想展示上方右侧图片的合并消息详情,需要当用户点击合并消息的时候再调用 [`downloadMergerMessage`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/downloadMergerMessage.html) 方法下载合并消息列表 UI 展示。
-
-### 撤回消息
-
-消息撤回是目前社交软件中必备的功能。
-
-发送方可撤回一条已经发送成功的消息。默认情况下,发送者只能撤回 2 分钟以内的消息,此配置可按需修改。
-
-撤回方在客户端,调用 [`revokeMessage`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/revokeMessage.html) 方法,接收方会收到消息撤回通知 [`onRecvMessageRevoked`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Callback/OnRecvMessageRevokedCallback.html)。通知中包含了撤回消息的 msgID,您可根据这个 msgID 判断 UI 层是哪一条消息撤回了,然后把对应的消息气泡切换成 "消息已被撤回" 状态。
-
-### 消息翻译
-
-对于国际化的聊天场景,消息翻译功能必不可少,可大大提升跨语言交流效率。社交场景中,大型群聊内,有不同语言的交流存在,是非常之常见的。
-
-| 消息翻译 |
-|---------|
-| |
-
-对于文本类型的消息,您可在客户端上调用 [`translateText`](https://im.sdk.qcloud.com/doc/zh-cn/classcom_1_1tencent_1_1imsdk_1_1v2_1_1V2TIMMessageManager.html#a1e1806c27bc7b76a3b816492ed9cbe5c) 方法,将待翻译文本列表和目标语言传至我们的服务端。原语言可由您自行判断,也可由我们判断。
-
-翻译结果返回后,我们建议您将其通过客户端的 [`setLocalCustomData`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/setLocalCustomData.html) 方法,存放于消息的 [`localCustomData`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimMessage.html#localcustomdata) 字段中,以便于后续直接展示,避免用户重复翻译,多次发送翻译请求。
-
-我们支持众多语言的互相翻译,[所有支持的语言可查看此处](https://cloud.tencent.com/document/product/269/85380#.E6.96.87.E6.9C.AC.E7.BF.BB.E8.AF.91.E8.AF.AD.E8.A8.80.E6.94.AF.E6.8C.81)。
-
-### 消息已读回执
-
-单聊和群聊均支持消息已读回执功能,操作步骤一致。
-
-| 单聊,[TUIKit](https://cloud.tencent.com/document/product/269/85905)中以文字承载 | 群聊,[TUIKit](https://cloud.tencent.com/document/product/269/85905)中以圆圈承载 |
-|---------|---------|
-| | |
-
-
-是否启用此功能,可根据您的社交业务需求决定。
-
-例如对于类似微信的熟人社交,已读回执的用处可能不是非常大;但是对于陌生人交友场景,已读回执则十分重要,帮助用户来确认,对方是否愿意跟自己聊下去,是否已读不回;对于工作聊天场景,群已读回执还能发挥更大的作用,可便捷看到群内哪些人已读哪些人未读,帮助发送者确认信息传递效率。
-
-**具体用法如下:**
-
-发送端创建消息后,先通过消息对象 [`V2TIMMessage`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimMessage.html) 的 [`needReadReceipt`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimMessage.html#needreadreceipt) 字段设置这条消息需要已读回执,再发送消息到会话中。
-
-接收端收到消息后,根据消息对象 [`V2TIMMessage`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimMessage.html) 的 [`needReadReceipt`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Message/V2TimMessage.html#needreadreceipt) 字段判断消息是否需要已读回执。如果需要,当用户查看消息后,调用 `sendMessageReadReceipts` 方法发送消息已读回执。
-
-接收端发送消息已读回执后,发送端可在 [`V2TIMAdvancedMsgListener`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Class/Listener/V2TimAdvancedMsgListener.html) 的 [`onRecvMessageReadReceipts`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Callback/OnRecvMessageReadReceipts.html) 中收到已读回执通知,在通知中更新 UI,例如更新某条消息为 “2 人已读”。
-
-此外,发送端也可主动请求消息已读回执信息。发送端从其他界面进入消息列表后,先请求获取历史消息,再调用 [`getMessageReadReceipts`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/getMessageReadReceipts.html) 方法请求获取消息已读回执信息。
-
-群聊场景的消息已读回执,通常需要能够查看详情,显示群内哪些人已读,哪些人未读。当用户点击已读回执角标时,可调用 [`getGroupMessageReadMemberList`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/getGroupMessageReadMemberList.html) 方法分页拉取消息已读或未读群成员列表。
-
-| 已读 群成员 | 未读 群成员 |
-|---------|---------|
-| | |
-
-### 群内@消息
-
-相信大家已经很熟悉,在群聊交流过程中,如果需要提及或提醒某些群成员,我们可直接 @ 他们。所有的社交聊天软件,都有这个基础功能。
-
-当用户输入 @ 字符后,弹出群成员选择界面。选择完需要 @ 的成员后以 “@A @B @C......” 形式显示在输入框,并可继续编辑消息内容,完成消息发送。
-
-| 监听 @ 字符选择群成员 | 编辑群 @ 消息发送 | 收到群 @ 消息 |
-|---------|---------|---------|
-| | | |
-
->? 图一:在聊天界面监听到输入框输入 "@" 字符后,可跳转到群成员选择界面,选择需要 @ 的群成员。
->图二:在群成员选择完成后,重新返回聊天界面,继续编辑群 @ 消息发送。
->图三:如果有消息 @ 我,自己会收到会话更新,可在会话 Cell 展示 “有人@我” 信息。
-
-由于实现方案内容较多,[您可查看此文档](https://cloud.tencent.com/document/product/269/75349),获取详情。
-
-### 消息漫游
-
-如果用户有多台设备,或者同时使用电脑和手机登录您的应用程序,用户们希望看到,无论在哪一端,历史消息都能尽可能完整。能从提供的历史消息上下文中,快速无障碍的加入聊天,满足社交场景高频率聊天的要求。
-
-为了保证交流的连续性与流畅性,我们提供一套消息漫游存储能力,即用户更换终端的情况下,也可以获取到跟其他用户或者某个群的聊天记录,可以达到类似QQ软件的效果。
-
-默认情况下,单聊消息和群聊消息有7天漫游,超过漫游时长的消息会被删除。此外,还支持在控制台修改消息漫游时长,延长消息漫游时长是增值服务。
-
-以下截图演示了消息在手机和电脑之间漫游。*图片来自Flutter TUIKit,一套代码完成电脑桌面端/Web端/移动端应用的开发。*
-
-| 电脑端 | 手机移动端 |
-|---------|---------|
-| | |
-
-### 更多丰富的消息形态
-
-我们默认提供的消息类型,可满足您大部分的聊天场景需求。但是对于社交软件来说,仅有这些还远远不够。
-
-红包/送礼物/投票/发送匹配度/闪照等等一系列创新玩法,让您app的社交场景模块变得更加丰富多彩。
-
-因此,您可使用我们提供的**自定义消息**能力,来发挥您的想象力,尽情创造激动人心的玩法及贴合您业务需求的功能。
-
-发送自定义消息分两步:
-
-- 调用 [`createCustomMessage`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/createCustomMessage.html) 创建自定义消息,传入消息体。
-- 调用 [`sendMessage`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/sendMessage.html) 发送消息。
-
-消息体中,您可以JSON格式,传入任何符合您需求的数据。例如,包含一个字段控制消息形态类型,还包含一个字段控制消息当前数据。
-
-如果是投票/红包等类型的消息,如果您想实时更新投票数据/红包领取信息,可将此类信息放在消息体中,在客户端上,通过 [`modifyMessage`](https://comm.qq.com/im/doc/flutter/zh/SDKAPI/Api/V2TIMMessageManager/modifyMessage.html) 方法实时修改。
-
-## 更多能力
-
-### 内容审核
-
-在社交场景中,用户很可能会发送不合规的消息。特别是陌生人交友软件,黄色不良内容消息更是频频出现。诱导性暗示图片/裸聊等令人反感的内容,不仅严重损害了用户们的身心健康,更很有可能违法并导致应用被监管部门查封。
-
-我们支持内容审核(反垃圾信息)功能,可针对不安全、不适宜的内容进行自动识别、处理,为您的产品体验和业务安全保驾护航。
-
-目前有以下三种内容审核方式。
-
-| 方式 | 介绍 |
-|---------|---------|
-| [本地审核](https://cloud.tencent.com/document/product/269/79139#bdsh) | 在客户端本地检测由即时通信 SDK 发送的文本内容,支持对已配置的敏感词进行拦截或者替换处理。此功能通过在 IM 开启服务并配置词库的方式实现。 |
-| [云端审核](https://cloud.tencent.com/document/product/269/79139#ydsh) | 在服务端检测由单聊、群聊、资料场景中产生的文本、图片、音频、视频内容,支持针对不同场景的不同内容分别配置审核策略,并对识别出的不安全内容进行拦截。 |
-| [第三方回调服务](https://cloud.tencent.com/document/product/269/79139#dsf) | 如果您已接入第三方内容审核服务,您可以使用 第三方回调配置 来实现。 |
-
-您可按需使用如上三种内容审核工具,保证业务安全运行。
-
-### 离线推送
-
-社交场景下,用户需要随时都能够得知最新的消息,以加快聊天效率,促进社交关系的形成。
-
-由于移动端设备的性能与电量有限,当 App 处于后台时,为了避免维持长连接而导致的过多资源消耗,我们推荐您使用各厂商提供的系统级推送通道来进行消息通知。系统级的推送通道相比第三方推送拥有更稳定的系统级长连接,可以做到随时接受推送消息,且资源消耗大幅降低。
-
-我们目前原生支持的厂商系统有:苹果iOS/Google FCM/OPPO/VIVO/华为/小米/魅族/荣耀。
-
-理论上,集成系统原生的离线推送,需要手动对接各个厂商的SDK,手动注册服务/获取Token/承载点击回调页面,非常之复杂。
-
-因此,我们提供了如下几款离线推送插件,封装了上述厂商的原生SDK,大大降低了使用上手成本。您可直接按照文档配置。开箱即用。
-
-- [Native](https://cloud.tencent.com/document/product/269/74285)
-- [Flutter](https://cloud.tencent.com/document/product/269/74605)
-- [uni-app](https://cloud.tencent.com/document/product/269/79124)
-
-
-
-### 音视频通话
-
-许多时候,仅靠文字和图片还是不足以抒发我们内心的情感,可能打字聊天一小时,也比不是直接打一通视频电话来的爽快。
-
-特别是对于社交场景下的用户们,他们一定有很多想法想要交流与诉说。
-
-因此,我们也强烈推荐您,再集成我们的音视频通话能力,一步到位,完成一对一或多人群组的音频/视频通话,并且支持离线唤起能力。
-
-[详情可查看此文档](https://cloud.tencent.com/document/product/269/84296)。
-
-
-
-以上,就是使用腾讯云IM实现社交场景常见需求的解决方案。快来使用腾讯云IM打造属于您的社交产品吧~
diff --git a/example/.metadata b/example/.metadata
index 5a02328..212e4a0 100644
--- a/example/.metadata
+++ b/example/.metadata
@@ -1,10 +1,36 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
-# This file should be version controlled and should not be manually edited.
+# This file should be version controlled.
version:
- revision: 7e9793dee1b85a243edd0e06cb1658e98b077561
+ revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
channel: stable
project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
+ base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
+ - platform: linux
+ create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
+ base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
+ - platform: macos
+ create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
+ base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
+ - platform: windows
+ create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
+ base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/example/1400187352_3130303435333633/im.db b/example/1400187352_3130303435333633/im.db
new file mode 100644
index 0000000..84687bc
Binary files /dev/null and b/example/1400187352_3130303435333633/im.db differ
diff --git a/example/1400187352_3130303435333633/im.db-shm b/example/1400187352_3130303435333633/im.db-shm
new file mode 100644
index 0000000..722a46c
Binary files /dev/null and b/example/1400187352_3130303435333633/im.db-shm differ
diff --git a/example/1400187352_3130303435333633/im.db-wal b/example/1400187352_3130303435333633/im.db-wal
new file mode 100644
index 0000000..07d73d7
Binary files /dev/null and b/example/1400187352_3130303435333633/im.db-wal differ
diff --git a/example/1400187352_3130303435333633/msg_0.db b/example/1400187352_3130303435333633/msg_0.db
new file mode 100644
index 0000000..84687bc
Binary files /dev/null and b/example/1400187352_3130303435333633/msg_0.db differ
diff --git a/example/1400187352_3130303435333633/msg_0.db-shm b/example/1400187352_3130303435333633/msg_0.db-shm
new file mode 100644
index 0000000..015c0c0
Binary files /dev/null and b/example/1400187352_3130303435333633/msg_0.db-shm differ
diff --git a/example/1400187352_3130303435333633/msg_0.db-wal b/example/1400187352_3130303435333633/msg_0.db-wal
new file mode 100644
index 0000000..c5a8d0a
Binary files /dev/null and b/example/1400187352_3130303435333633/msg_0.db-wal differ
diff --git a/example/imsdk_C.mmap2 b/example/imsdk_C.mmap2
new file mode 100755
index 0000000..e5b999e
Binary files /dev/null and b/example/imsdk_C.mmap2 differ
diff --git a/example/imsdk_C_20221229.xlog b/example/imsdk_C_20221229.xlog
new file mode 100644
index 0000000..c27d095
Binary files /dev/null and b/example/imsdk_C_20221229.xlog differ
diff --git a/example/imsdk_api_report b/example/imsdk_api_report
new file mode 100644
index 0000000..3bd4f45
Binary files /dev/null and b/example/imsdk_api_report differ
diff --git a/example/imsdk_config_1400187352 b/example/imsdk_config_1400187352
new file mode 100644
index 0000000..a41f4d5
Binary files /dev/null and b/example/imsdk_config_1400187352 differ
diff --git a/example/imsdk_sensitive_word_1400187352 b/example/imsdk_sensitive_word_1400187352
new file mode 100644
index 0000000..b8faf10
--- /dev/null
+++ b/example/imsdk_sensitive_word_1400187352
@@ -0,0 +1,2 @@
+
+Hϼhѽ#%-a
\ No newline at end of file
diff --git a/example/ios/Podfile b/example/ios/Podfile
index 1e8c3c9..313ea4a 100644
--- a/example/ios/Podfile
+++ b/example/ios/Podfile
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
-# platform :ios, '9.0'
+platform :ios, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
index 68bacaa..4bd8450 100644
--- a/example/ios/Podfile.lock
+++ b/example/ios/Podfile.lock
@@ -1,17 +1,18 @@
PODS:
- - camera (0.0.1):
+ - audioplayers_darwin (0.0.1):
- Flutter
- - connectivity_plus (0.0.1):
+ - camera_avfoundation (0.0.1):
- Flutter
- - ReachabilitySwift
- - DKImagePickerController/Core (4.3.3):
+ - disk_space (0.0.1):
+ - Flutter
+ - DKImagePickerController/Core (4.3.4):
- DKImagePickerController/ImageDataManager
- DKImagePickerController/Resource
- - DKImagePickerController/ImageDataManager (4.3.3)
- - DKImagePickerController/PhotoGallery (4.3.3):
+ - DKImagePickerController/ImageDataManager (4.3.4)
+ - DKImagePickerController/PhotoGallery (4.3.4):
- DKImagePickerController/Core
- DKPhotoGallery
- - DKImagePickerController/Resource (4.3.3)
+ - DKImagePickerController/Resource (4.3.4)
- DKPhotoGallery (0.0.17):
- DKPhotoGallery/Core (= 0.0.17)
- DKPhotoGallery/Model (= 0.0.17)
@@ -35,11 +36,18 @@ PODS:
- DKPhotoGallery/Resource (0.0.17):
- SDWebImage
- SwiftyGif
+ - fc_native_video_thumbnail_for_us (0.0.1):
+ - Flutter
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- Flutter (1.0.0)
- - flutter_plugin_record (0.0.1):
+ - flutter_image_compress (1.0.0):
+ - Flutter
+ - Mantle
+ - SDWebImage
+ - SDWebImageWebPCoder
+ - flutter_plugin_record_plus (0.0.1):
- Flutter
- fluttertoast (0.0.2):
- Flutter
@@ -52,69 +60,98 @@ PODS:
- Flutter
- image_picker_ios (0.0.1):
- Flutter
- - libwebp (1.2.1):
- - libwebp/demux (= 1.2.1)
- - libwebp/mux (= 1.2.1)
- - libwebp/webp (= 1.2.1)
- - libwebp/demux (1.2.1):
+ - libwebp (1.2.4):
+ - libwebp/demux (= 1.2.4)
+ - libwebp/mux (= 1.2.4)
+ - libwebp/webp (= 1.2.4)
+ - libwebp/demux (1.2.4):
- libwebp/webp
- - libwebp/mux (1.2.1):
+ - libwebp/mux (1.2.4):
- libwebp/demux
- - libwebp/webp (1.2.1)
- - open_file (0.0.1):
- - Flutter
+ - libwebp/webp (1.2.4)
+ - Mantle (2.2.0):
+ - Mantle/extobjc (= 2.2.0)
+ - Mantle/extobjc (2.2.0)
- package_info_plus (0.4.5):
- Flutter
- - path_provider_ios (0.0.1):
+ - pasteboard (0.0.1):
- Flutter
- - "permission_handler (5.1.0+2)":
+ - path_provider_foundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
+ - permission_handler_apple (9.0.4):
- Flutter
- photo_manager (2.0.0):
- Flutter
- FlutterMacOS
- - ReachabilitySwift (5.0.0)
- - SDWebImage (5.12.5):
- - SDWebImage/Core (= 5.12.5)
- - SDWebImage/Core (5.12.5)
- - shared_preferences_ios (0.0.1):
+ - ReactiveObjC (3.1.1)
+ - SDWebImage (5.15.5):
+ - SDWebImage/Core (= 5.15.5)
+ - SDWebImage/Core (5.15.5)
+ - SDWebImageWebPCoder (0.11.0):
+ - libwebp (~> 1.0)
+ - SDWebImage/Core (~> 5.15)
+ - shared_preferences_foundation (0.0.1):
- Flutter
+ - FlutterMacOS
- sqflite (0.0.2):
- Flutter
- FMDB (>= 2.7.5)
- - SwiftyGif (5.4.3)
- - tencent_im_sdk_plugin (1.0.5):
+ - SwiftyGif (5.4.4)
+ - tencent_cloud_chat_sdk (5.1.2):
- Flutter
- HydraAsync
- - TXIMSDK_Plus_iOS (= 6.1.2155.1)
+ - TXIMSDK_Plus_iOS (= 7.1.3925)
+ - tencent_cloud_uikit_core (0.0.1):
+ - Flutter
+ - TUICore (= 7.1.3925)
+ - tencent_open_file (0.0.1):
+ - Flutter
- Toast (4.0.0)
- - TXIMSDK_Plus_iOS (6.1.2155.1)
+ - TUICore (7.1.3925):
+ - ReactiveObjC
+ - SDWebImage
+ - TUICore/ImSDK_Plus (= 7.1.3925)
+ - TUICore/Base (7.1.3925):
+ - ReactiveObjC
+ - SDWebImage
+ - TUICore/ImSDK_Plus (7.1.3925):
+ - ReactiveObjC
+ - SDWebImage
+ - TUICore/Base
+ - TXIMSDK_Plus_iOS (= 7.1.3925)
+ - TXIMSDK_Plus_iOS (7.1.3925)
+ - url_launcher_ios (0.0.1):
+ - Flutter
- video_player_avfoundation (0.0.1):
- Flutter
- - video_thumbnail (0.0.1):
- - Flutter
- - libwebp
- wakelock (0.0.1):
- Flutter
DEPENDENCIES:
- - camera (from `.symlinks/plugins/camera/ios`)
- - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
+ - audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/ios`)
+ - camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`)
+ - disk_space (from `.symlinks/plugins/disk_space/ios`)
+ - fc_native_video_thumbnail_for_us (from `.symlinks/plugins/fc_native_video_thumbnail_for_us/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`)
- - flutter_plugin_record (from `.symlinks/plugins/flutter_plugin_record/ios`)
+ - flutter_image_compress (from `.symlinks/plugins/flutter_image_compress/ios`)
+ - flutter_plugin_record_plus (from `.symlinks/plugins/flutter_plugin_record_plus/ios`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- - open_file (from `.symlinks/plugins/open_file/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
- - permission_handler (from `.symlinks/plugins/permission_handler/ios`)
+ - pasteboard (from `.symlinks/plugins/pasteboard/ios`)
+ - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
+ - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
- - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`)
+ - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`)
- - tencent_im_sdk_plugin (from `.symlinks/plugins/tencent_im_sdk_plugin/ios`)
+ - tencent_cloud_chat_sdk (from `.symlinks/plugins/tencent_cloud_chat_sdk/ios`)
+ - tencent_cloud_uikit_core (from `.symlinks/plugins/tencent_cloud_uikit_core/ios`)
+ - tencent_open_file (from `.symlinks/plugins/tencent_open_file/ios`)
+ - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`)
- - video_thumbnail (from `.symlinks/plugins/video_thumbnail/ios`)
- wakelock (from `.symlinks/plugins/wakelock/ios`)
SPEC REPOS:
@@ -124,83 +161,104 @@ SPEC REPOS:
- FMDB
- HydraAsync
- libwebp
- - ReachabilitySwift
+ - Mantle
+ - ReactiveObjC
- SDWebImage
+ - SDWebImageWebPCoder
- SwiftyGif
- Toast
+ - TUICore
- TXIMSDK_Plus_iOS
EXTERNAL SOURCES:
- camera:
- :path: ".symlinks/plugins/camera/ios"
- connectivity_plus:
- :path: ".symlinks/plugins/connectivity_plus/ios"
+ audioplayers_darwin:
+ :path: ".symlinks/plugins/audioplayers_darwin/ios"
+ camera_avfoundation:
+ :path: ".symlinks/plugins/camera_avfoundation/ios"
+ disk_space:
+ :path: ".symlinks/plugins/disk_space/ios"
+ fc_native_video_thumbnail_for_us:
+ :path: ".symlinks/plugins/fc_native_video_thumbnail_for_us/ios"
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
Flutter:
:path: Flutter
- flutter_plugin_record:
- :path: ".symlinks/plugins/flutter_plugin_record/ios"
+ flutter_image_compress:
+ :path: ".symlinks/plugins/flutter_image_compress/ios"
+ flutter_plugin_record_plus:
+ :path: ".symlinks/plugins/flutter_plugin_record_plus/ios"
fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios"
image_gallery_saver:
:path: ".symlinks/plugins/image_gallery_saver/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
- open_file:
- :path: ".symlinks/plugins/open_file/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
- path_provider_ios:
- :path: ".symlinks/plugins/path_provider_ios/ios"
- permission_handler:
- :path: ".symlinks/plugins/permission_handler/ios"
+ pasteboard:
+ :path: ".symlinks/plugins/pasteboard/ios"
+ path_provider_foundation:
+ :path: ".symlinks/plugins/path_provider_foundation/ios"
+ permission_handler_apple:
+ :path: ".symlinks/plugins/permission_handler_apple/ios"
photo_manager:
:path: ".symlinks/plugins/photo_manager/ios"
- shared_preferences_ios:
- :path: ".symlinks/plugins/shared_preferences_ios/ios"
+ shared_preferences_foundation:
+ :path: ".symlinks/plugins/shared_preferences_foundation/ios"
sqflite:
:path: ".symlinks/plugins/sqflite/ios"
- tencent_im_sdk_plugin:
- :path: ".symlinks/plugins/tencent_im_sdk_plugin/ios"
+ tencent_cloud_chat_sdk:
+ :path: ".symlinks/plugins/tencent_cloud_chat_sdk/ios"
+ tencent_cloud_uikit_core:
+ :path: ".symlinks/plugins/tencent_cloud_uikit_core/ios"
+ tencent_open_file:
+ :path: ".symlinks/plugins/tencent_open_file/ios"
+ url_launcher_ios:
+ :path: ".symlinks/plugins/url_launcher_ios/ios"
video_player_avfoundation:
:path: ".symlinks/plugins/video_player_avfoundation/ios"
- video_thumbnail:
- :path: ".symlinks/plugins/video_thumbnail/ios"
wakelock:
:path: ".symlinks/plugins/wakelock/ios"
SPEC CHECKSUMS:
- camera: 9993f92f2c793e87b65e35f3a23c70582afb05b1
- connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
- DKImagePickerController: 72fd378f244cef3d27288e0aebf217a4467e4012
+ audioplayers_darwin: 877d9a4d06331c5c374595e46e16453ac7eafa40
+ camera_avfoundation: 07c77549ea54ad95d8581be86617c094a46280d9
+ disk_space: e94d34bbdf77954adfb39e60bde9cc5c7233eda6
+ DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
- file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1
- Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
- flutter_plugin_record: 562ded56f3a109d769e72c3ef52ef20d835493d4
- fluttertoast: 16fbe6039d06a763f3533670197d01fc73459037
+ fc_native_video_thumbnail_for_us: 69559e6500bff0f6340f044ec0847366fa6f6233
+ file_picker: 817ab1d8cd2da9d2da412a417162deee3500fc95
+ Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
+ flutter_image_compress: 5a5e9aee05b6553048b8df1c3bc456d0afaac433
+ flutter_plugin_record_plus: 79b8e13ee7ed9a94f6c067018653599528cee1fc
+ fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
HydraAsync: 8d589bd725b0224f899afafc9a396327405f8063
image_gallery_saver: 259eab68fb271cfd57d599904f7acdc7832e7ef2
image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb
- libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc
- open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d
+ libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
+ Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
- path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
- permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0
+ pasteboard: 982969ebaa7c78af3e6cc7761e8f5e77565d9ce0
+ path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
+ permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
photo_manager: 4f6810b7dfc4feb03b461ac1a70dacf91fba7604
- ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
- SDWebImage: 0905f1b7760fc8ac4198cae0036600d67478751e
- shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
+ ReactiveObjC: 011caa393aa0383245f2dcf9bf02e86b80b36040
+ SDWebImage: fd7e1a22f00303e058058278639bf6196ee431fe
+ SDWebImageWebPCoder: 295a6573c512f54ad2dd58098e64e17dcf008499
+ shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
- SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
- tencent_im_sdk_plugin: c68993c62fd0198cd47132a055a06c9cef33b8e3
+ SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
+ tencent_cloud_chat_sdk: 17f2ddd7de43495312603e7c9dac04d76352e246
+ tencent_cloud_uikit_core: 2c4ccb41c33b45b5c69750b9774fa389fc20cdb2
+ tencent_open_file: 1261db508715b8f43ef3b7e31c90824838038165
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
- TXIMSDK_Plus_iOS: 2b0e9440eacdb49f385c90a23ad6558013f0cac6
+ TUICore: edc9e0911f6a04224620d7098ff9d7f4b1f0291a
+ TXIMSDK_Plus_iOS: 3edf95acc3dff794287ea858b5205ed6f4dd339f
+ url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2
video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff
- video_thumbnail: c4e2a3c539e247d4de13cd545344fd2d26ffafd1
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
-PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
+PODFILE CHECKSUM: 7368163408c647b7eb699d0d788ba6718e18fb8d
-COCOAPODS: 1.11.3
+COCOAPODS: 1.12.0
diff --git a/example/lib/TIMUIKitChatExample.dart b/example/lib/TIMUIKitChatExample.dart
index baeed5c..d82bb9f 100644
--- a/example/lib/TIMUIKitChatExample.dart
+++ b/example/lib/TIMUIKitChatExample.dart
@@ -3,7 +3,6 @@
import 'package:example/TIMUIKitGroupProfileExample.dart';
import 'package:example/TIMUIKitProfileExample.dart';
import 'package:flutter/material.dart';
-import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
class TIMUIKitChatExample extends StatelessWidget {
@@ -12,21 +11,60 @@ class TIMUIKitChatExample extends StatelessWidget {
const TIMUIKitChatExample({Key? key, this.selectedConversation})
: super(key: key);
- String? _getConversationID() {
- if(selectedConversation != null){
- return selectedConversation!.type == 1
- ? selectedConversation!.userID
- : selectedConversation!.groupID;
- }
- return null;
+ Widget renderCustomStickerPanel({
+ sendTextMessage,
+ sendFaceMessage,
+ deleteText,
+ addCustomEmojiText,
+ addText,
+ List defaultCustomEmojiStickerList = const [],
+ double? height,
+ double? width
+ }) {
+ final defaultEmojiList =
+ defaultCustomEmojiStickerList.map((customEmojiPackage) {
+ return CustomStickerPackage(
+ name: customEmojiPackage.name,
+ baseUrl: "assets/custom_face_resource/${customEmojiPackage.name}",
+ isEmoji: customEmojiPackage.isEmoji,
+ isDefaultEmoji: true,
+ stickerList: customEmojiPackage.list
+ .asMap()
+ .keys
+ .map((idx) =>
+ CustomSticker(index: idx, name: customEmojiPackage.list[idx]))
+ .toList(),
+ menuItem: CustomSticker(
+ index: 0,
+ name: customEmojiPackage.icon,
+ ));
+ }).toList();
+ return StickerPanel(
+ sendTextMsg: sendTextMessage,
+ sendFaceMsg: (index, data) =>
+ sendFaceMessage(index + 1, (data.split("/")[3]).split("@")[0]),
+ deleteText: deleteText,
+ addText: addText,
+ addCustomEmojiText: addCustomEmojiText,
+ customStickerPackageList: [
+ ...defaultEmojiList,
+ ]);
}
@override
Widget build(BuildContext context) {
return TIMUIKitChat(
+ conversation: selectedConversation ??
+ V2TimConversation(
+ conversationID: "c2c_10040818",
+ userID: "10040818",
+ showName: "Test Chat",
+ type: 1),
+ customStickerPanel: renderCustomStickerPanel,
config: const TIMUIKitChatConfig(
// 仅供演示,非全部配置项,实际使用中,可只传和默认项不同的参数,无需传入所有开关
isAllowClickAvatar: true,
+ isUseDefaultEmoji: true,
isAllowLongPressMessage: true,
isShowReadingStatus: true,
isShowGroupReadingStatus: true,
@@ -38,12 +76,6 @@ class TIMUIKitChatExample extends StatelessWidget {
GroupReceiptAllowType.public
],
),
- conversation: selectedConversation ??
- V2TimConversation(
- conversationID: "c2c_10040818",
- userID: "10040818",
- showName: "Test Chat",
- type: 1),
appBarConfig: AppBar(
actions: [
IconButton(
diff --git a/example/lib/TIMUIKitSearchExample.dart b/example/lib/TIMUIKitSearchExample.dart
index d547e53..078e6ae 100644
--- a/example/lib/TIMUIKitSearchExample.dart
+++ b/example/lib/TIMUIKitSearchExample.dart
@@ -10,8 +10,6 @@ class TIMUIKitSearchExample extends StatelessWidget {
Widget build(BuildContext context) {
return TIMUIKitSearch(
onTapConversation: (conv, message) {
- print(conv.toJson());
- print(message!.toJson());
},
onEnterConversation: (V2TimConversation conversation, String keyword) {},
);
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 10fee08..eb814ac 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -39,7 +39,7 @@ class MyApp extends StatelessWidget {
// is not restarted.
primarySwatch: Colors.blue,
),
- home: const MyHomePage(title: 'Tencent IM UIKit'),
+ home: const MyHomePage(title: 'Tencent Cloud Chat UIKit'),
);
}
}
@@ -67,7 +67,7 @@ class _MyHomePageState extends State {
}
String getUserID() {
- return const String.fromEnvironment('LOGINUSERID', defaultValue: "");
+ return const String.fromEnvironment('LOGINUSERID', defaultValue: "10045363");
}
String getSecret() {
diff --git a/example/linux/.gitignore b/example/linux/.gitignore
new file mode 100644
index 0000000..d3896c9
--- /dev/null
+++ b/example/linux/.gitignore
@@ -0,0 +1 @@
+flutter/ephemeral
diff --git a/example/linux/CMakeLists.txt b/example/linux/CMakeLists.txt
new file mode 100644
index 0000000..74c66dd
--- /dev/null
+++ b/example/linux/CMakeLists.txt
@@ -0,0 +1,138 @@
+# Project-level configuration.
+cmake_minimum_required(VERSION 3.10)
+project(runner LANGUAGES CXX)
+
+# The name of the executable created for the application. Change this to change
+# the on-disk name of your application.
+set(BINARY_NAME "example")
+# The unique GTK application identifier for this application. See:
+# https://wiki.gnome.org/HowDoI/ChooseApplicationID
+set(APPLICATION_ID "com.example.example")
+
+# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
+# versions of CMake.
+cmake_policy(SET CMP0063 NEW)
+
+# Load bundled libraries from the lib/ directory relative to the binary.
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Root filesystem for cross-building.
+if(FLUTTER_TARGET_PLATFORM_SYSROOT)
+ set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+endif()
+
+# Define build configuration options.
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+endif()
+
+# Compilation settings that should be applied to most targets.
+#
+# Be cautious about adding new options here, as plugins use this function by
+# default. In most cases, you should add new options to specific targets instead
+# of modifying this function.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_14)
+ target_compile_options(${TARGET} PRIVATE -Wall -Werror)
+ target_compile_options(${TARGET} PRIVATE "$<$>:-O3>")
+ target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
+endfunction()
+
+# Flutter library and tool build rules.
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+
+add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
+
+# Define the application target. To change its name, change BINARY_NAME above,
+# not the value here, or `flutter run` will no longer work.
+#
+# Any new source files that you add to the application should be added here.
+add_executable(${BINARY_NAME}
+ "main.cc"
+ "my_application.cc"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+)
+
+# Apply the standard set of build settings. This can be removed for applications
+# that need different build settings.
+apply_standard_settings(${BINARY_NAME})
+
+# Add dependency libraries. Add any application-specific dependencies here.
+target_link_libraries(${BINARY_NAME} PRIVATE flutter)
+target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
+
+# Run the Flutter tool portions of the build. This must not be removed.
+add_dependencies(${BINARY_NAME} flutter_assemble)
+
+# Only the install-generated bundle's copy of the executable will launch
+# correctly, since the resources must in the right relative locations. To avoid
+# people trying to run the unbundled copy, put it in a subdirectory instead of
+# the default top-level location.
+set_target_properties(${BINARY_NAME}
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
+)
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# By default, "installing" just makes a relocatable bundle in the build
+# directory.
+set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+# Start with a clean build bundle directory every time.
+install(CODE "
+ file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
+ " COMPONENT Runtime)
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
+ install(FILES "${bundled_library}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endforeach(bundled_library)
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+ install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
diff --git a/example/linux/flutter/CMakeLists.txt b/example/linux/flutter/CMakeLists.txt
new file mode 100644
index 0000000..d5bd016
--- /dev/null
+++ b/example/linux/flutter/CMakeLists.txt
@@ -0,0 +1,88 @@
+# This file controls Flutter-level build steps. It should not be edited.
+cmake_minimum_required(VERSION 3.10)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+
+# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
+# which isn't available in 3.10.
+function(list_prepend LIST_NAME PREFIX)
+ set(NEW_LIST "")
+ foreach(element ${${LIST_NAME}})
+ list(APPEND NEW_LIST "${PREFIX}${element}")
+ endforeach(element)
+ set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
+endfunction()
+
+# === Flutter Library ===
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
+pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
+
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "fl_basic_message_channel.h"
+ "fl_binary_codec.h"
+ "fl_binary_messenger.h"
+ "fl_dart_project.h"
+ "fl_engine.h"
+ "fl_json_message_codec.h"
+ "fl_json_method_codec.h"
+ "fl_message_codec.h"
+ "fl_method_call.h"
+ "fl_method_channel.h"
+ "fl_method_codec.h"
+ "fl_method_response.h"
+ "fl_plugin_registrar.h"
+ "fl_plugin_registry.h"
+ "fl_standard_message_codec.h"
+ "fl_standard_method_codec.h"
+ "fl_string_codec.h"
+ "fl_value.h"
+ "fl_view.h"
+ "flutter_linux.h"
+)
+list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
+target_link_libraries(flutter INTERFACE
+ PkgConfig::GTK
+ PkgConfig::GLIB
+ PkgConfig::GIO
+)
+add_dependencies(flutter flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CMAKE_CURRENT_BINARY_DIR}/_phony_
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
+ ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+)
diff --git a/example/linux/flutter/generated_plugin_registrant.cc b/example/linux/flutter/generated_plugin_registrant.cc
new file mode 100644
index 0000000..2e68889
--- /dev/null
+++ b/example/linux/flutter/generated_plugin_registrant.cc
@@ -0,0 +1,27 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#include "generated_plugin_registrant.h"
+
+#include
+#include
+#include
+#include
+
+void fl_register_plugins(FlPluginRegistry* registry) {
+ g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
+ fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
+ audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
+ g_autoptr(FlPluginRegistrar) desktop_drop_registrar =
+ fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin");
+ desktop_drop_plugin_register_with_registrar(desktop_drop_registrar);
+ g_autoptr(FlPluginRegistrar) pasteboard_registrar =
+ fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin");
+ pasteboard_plugin_register_with_registrar(pasteboard_registrar);
+ g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
+ fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
+ url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
+}
diff --git a/example/linux/flutter/generated_plugin_registrant.h b/example/linux/flutter/generated_plugin_registrant.h
new file mode 100644
index 0000000..e0f0a47
--- /dev/null
+++ b/example/linux/flutter/generated_plugin_registrant.h
@@ -0,0 +1,15 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#ifndef GENERATED_PLUGIN_REGISTRANT_
+#define GENERATED_PLUGIN_REGISTRANT_
+
+#include
+
+// Registers Flutter plugins.
+void fl_register_plugins(FlPluginRegistry* registry);
+
+#endif // GENERATED_PLUGIN_REGISTRANT_
diff --git a/example/linux/flutter/generated_plugins.cmake b/example/linux/flutter/generated_plugins.cmake
new file mode 100644
index 0000000..fa2eba0
--- /dev/null
+++ b/example/linux/flutter/generated_plugins.cmake
@@ -0,0 +1,27 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+ audioplayers_linux
+ desktop_drop
+ pasteboard
+ url_launcher_linux
+)
+
+list(APPEND FLUTTER_FFI_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
+
+foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
+endforeach(ffi_plugin)
diff --git a/example/linux/main.cc b/example/linux/main.cc
new file mode 100644
index 0000000..e7c5c54
--- /dev/null
+++ b/example/linux/main.cc
@@ -0,0 +1,6 @@
+#include "my_application.h"
+
+int main(int argc, char** argv) {
+ g_autoptr(MyApplication) app = my_application_new();
+ return g_application_run(G_APPLICATION(app), argc, argv);
+}
diff --git a/example/linux/my_application.cc b/example/linux/my_application.cc
new file mode 100644
index 0000000..0ba8f43
--- /dev/null
+++ b/example/linux/my_application.cc
@@ -0,0 +1,104 @@
+#include "my_application.h"
+
+#include
+#ifdef GDK_WINDOWING_X11
+#include
+#endif
+
+#include "flutter/generated_plugin_registrant.h"
+
+struct _MyApplication {
+ GtkApplication parent_instance;
+ char** dart_entrypoint_arguments;
+};
+
+G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
+
+// Implements GApplication::activate.
+static void my_application_activate(GApplication* application) {
+ MyApplication* self = MY_APPLICATION(application);
+ GtkWindow* window =
+ GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
+
+ // Use a header bar when running in GNOME as this is the common style used
+ // by applications and is the setup most users will be using (e.g. Ubuntu
+ // desktop).
+ // If running on X and not using GNOME then just use a traditional title bar
+ // in case the window manager does more exotic layout, e.g. tiling.
+ // If running on Wayland assume the header bar will work (may need changing
+ // if future cases occur).
+ gboolean use_header_bar = TRUE;
+#ifdef GDK_WINDOWING_X11
+ GdkScreen* screen = gtk_window_get_screen(window);
+ if (GDK_IS_X11_SCREEN(screen)) {
+ const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
+ if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
+ use_header_bar = FALSE;
+ }
+ }
+#endif
+ if (use_header_bar) {
+ GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
+ gtk_widget_show(GTK_WIDGET(header_bar));
+ gtk_header_bar_set_title(header_bar, "example");
+ gtk_header_bar_set_show_close_button(header_bar, TRUE);
+ gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
+ } else {
+ gtk_window_set_title(window, "example");
+ }
+
+ gtk_window_set_default_size(window, 1280, 720);
+ gtk_widget_show(GTK_WIDGET(window));
+
+ g_autoptr(FlDartProject) project = fl_dart_project_new();
+ fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
+
+ FlView* view = fl_view_new(project);
+ gtk_widget_show(GTK_WIDGET(view));
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
+
+ fl_register_plugins(FL_PLUGIN_REGISTRY(view));
+
+ gtk_widget_grab_focus(GTK_WIDGET(view));
+}
+
+// Implements GApplication::local_command_line.
+static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
+ MyApplication* self = MY_APPLICATION(application);
+ // Strip out the first argument as it is the binary name.
+ self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
+
+ g_autoptr(GError) error = nullptr;
+ if (!g_application_register(application, nullptr, &error)) {
+ g_warning("Failed to register: %s", error->message);
+ *exit_status = 1;
+ return TRUE;
+ }
+
+ g_application_activate(application);
+ *exit_status = 0;
+
+ return TRUE;
+}
+
+// Implements GObject::dispose.
+static void my_application_dispose(GObject* object) {
+ MyApplication* self = MY_APPLICATION(object);
+ g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
+ G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
+}
+
+static void my_application_class_init(MyApplicationClass* klass) {
+ G_APPLICATION_CLASS(klass)->activate = my_application_activate;
+ G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
+ G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
+}
+
+static void my_application_init(MyApplication* self) {}
+
+MyApplication* my_application_new() {
+ return MY_APPLICATION(g_object_new(my_application_get_type(),
+ "application-id", APPLICATION_ID,
+ "flags", G_APPLICATION_NON_UNIQUE,
+ nullptr));
+}
diff --git a/example/linux/my_application.h b/example/linux/my_application.h
new file mode 100644
index 0000000..72271d5
--- /dev/null
+++ b/example/linux/my_application.h
@@ -0,0 +1,18 @@
+#ifndef FLUTTER_MY_APPLICATION_H_
+#define FLUTTER_MY_APPLICATION_H_
+
+#include
+
+G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
+ GtkApplication)
+
+/**
+ * my_application_new:
+ *
+ * Creates a new Flutter-based application.
+ *
+ * Returns: a new #MyApplication.
+ */
+MyApplication* my_application_new();
+
+#endif // FLUTTER_MY_APPLICATION_H_
diff --git a/example/macos/.gitignore b/example/macos/.gitignore
new file mode 100644
index 0000000..746adbb
--- /dev/null
+++ b/example/macos/.gitignore
@@ -0,0 +1,7 @@
+# Flutter-related
+**/Flutter/ephemeral/
+**/Pods/
+
+# Xcode-related
+**/dgph
+**/xcuserdata/
diff --git a/example/macos/Flutter/Flutter-Debug.xcconfig b/example/macos/Flutter/Flutter-Debug.xcconfig
new file mode 100644
index 0000000..4b81f9b
--- /dev/null
+++ b/example/macos/Flutter/Flutter-Debug.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/example/macos/Flutter/Flutter-Release.xcconfig b/example/macos/Flutter/Flutter-Release.xcconfig
new file mode 100644
index 0000000..5caa9d1
--- /dev/null
+++ b/example/macos/Flutter/Flutter-Release.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift
new file mode 100644
index 0000000..e63f903
--- /dev/null
+++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -0,0 +1,34 @@
+//
+// Generated file. Do not edit.
+//
+
+import FlutterMacOS
+import Foundation
+
+import audioplayers_darwin
+import desktop_drop
+import device_info_plus_macos
+import fc_native_video_thumbnail_for_us
+import package_info_plus_macos
+import pasteboard
+import path_provider_foundation
+import photo_manager
+import shared_preferences_foundation
+import sqflite
+import url_launcher_macos
+import wakelock_macos
+
+func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
+ AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
+ DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin"))
+ DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
+ FcNativeVideoThumbnailPlugin.register(with: registry.registrar(forPlugin: "FcNativeVideoThumbnailPlugin"))
+ FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
+ PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin"))
+ PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
+ PhotoManagerPlugin.register(with: registry.registrar(forPlugin: "PhotoManagerPlugin"))
+ SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
+ SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
+ UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
+ WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin"))
+}
diff --git a/example/macos/Podfile b/example/macos/Podfile
new file mode 100644
index 0000000..9ec46f8
--- /dev/null
+++ b/example/macos/Podfile
@@ -0,0 +1,40 @@
+platform :osx, '10.15'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_macos_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+ use_modular_headers!
+
+ flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_macos_build_settings(target)
+ end
+end
diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock
new file mode 100644
index 0000000..4b372e6
--- /dev/null
+++ b/example/macos/Podfile.lock
@@ -0,0 +1,68 @@
+PODS:
+ - FlutterMacOS (1.0.0)
+ - FMDB (2.7.5):
+ - FMDB/standard (= 2.7.5)
+ - FMDB/standard (2.7.5)
+ - package_info_plus_macos (0.0.1):
+ - FlutterMacOS
+ - path_provider_macos (0.0.1):
+ - FlutterMacOS
+ - photo_manager (2.0.0):
+ - Flutter
+ - FlutterMacOS
+ - shared_preferences_macos (0.0.1):
+ - FlutterMacOS
+ - sqflite (0.0.2):
+ - FlutterMacOS
+ - FMDB (>= 2.7.5)
+ - url_launcher_macos (0.0.1):
+ - FlutterMacOS
+ - wakelock_macos (0.0.1):
+ - FlutterMacOS
+
+DEPENDENCIES:
+ - FlutterMacOS (from `Flutter/ephemeral`)
+ - package_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos`)
+ - path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
+ - photo_manager (from `Flutter/ephemeral/.symlinks/plugins/photo_manager/macos`)
+ - shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`)
+ - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`)
+ - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
+ - wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`)
+
+SPEC REPOS:
+ trunk:
+ - FMDB
+
+EXTERNAL SOURCES:
+ FlutterMacOS:
+ :path: Flutter/ephemeral
+ package_info_plus_macos:
+ :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos
+ path_provider_macos:
+ :path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
+ photo_manager:
+ :path: Flutter/ephemeral/.symlinks/plugins/photo_manager/macos
+ shared_preferences_macos:
+ :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos
+ sqflite:
+ :path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos
+ url_launcher_macos:
+ :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
+ wakelock_macos:
+ :path: Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos
+
+SPEC CHECKSUMS:
+ FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
+ FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
+ package_info_plus_macos: f010621b07802a241d96d01876d6705f15e77c1c
+ path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
+ photo_manager: 4f6810b7dfc4feb03b461ac1a70dacf91fba7604
+ shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727
+ sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
+ url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
+ wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9
+
+PODFILE CHECKSUM: 0d3963a09fc94f580682bd88480486da345dc3f0
+
+COCOAPODS: 1.11.3
diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..1dad809
--- /dev/null
+++ b/example/macos/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,632 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 51;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
+ buildPhases = (
+ 33CC111E2044C6BF0003C045 /* ShellScript */,
+ );
+ dependencies = (
+ );
+ name = "Flutter Assemble";
+ productName = FLX;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
+ BCA39B6D459F9CBDE3F985BB /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6AB45B5BD0777F55845AF08A /* Pods_Runner.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 33CC111A2044C6BA0003C045;
+ remoteInfo = FLX;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 33CC110E2044A8840003C045 /* Bundle Framework */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Bundle Framework";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; };
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; };
+ 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; };
+ 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
+ 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; };
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; };
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; };
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; };
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; };
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; };
+ 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; };
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; };
+ 5E1960EFBF841FBF2ED9CB42 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
+ 613D89C488B7E8A9AABFE80A /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+ 6AB45B5BD0777F55845AF08A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; };
+ 9055C8A896F8B9969E9A9A75 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 33CC10EA2044A3C60003C045 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ BCA39B6D459F9CBDE3F985BB /* Pods_Runner.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 33BA886A226E78AF003329D5 /* Configs */ = {
+ isa = PBXGroup;
+ children = (
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
+ );
+ path = Configs;
+ sourceTree = "";
+ };
+ 33CC10E42044A3C60003C045 = {
+ isa = PBXGroup;
+ children = (
+ 33FAB671232836740065AC1E /* Runner */,
+ 33CEB47122A05771004F2AC0 /* Flutter */,
+ 33CC10EE2044A3C60003C045 /* Products */,
+ D73912EC22F37F3D000D13A0 /* Frameworks */,
+ 35CB9EEBE37DA168A0AC063E /* Pods */,
+ );
+ sourceTree = "";
+ };
+ 33CC10EE2044A3C60003C045 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10ED2044A3C60003C045 /* example.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 33CC11242044D66E0003C045 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */,
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */,
+ 33CC10F72044A3C60003C045 /* Info.plist */,
+ );
+ name = Resources;
+ path = ..;
+ sourceTree = "";
+ };
+ 33CEB47122A05771004F2AC0 /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
+ );
+ path = Flutter;
+ sourceTree = "";
+ };
+ 33FAB671232836740065AC1E /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */,
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */,
+ 33E51914231749380026EE4D /* Release.entitlements */,
+ 33CC11242044D66E0003C045 /* Resources */,
+ 33BA886A226E78AF003329D5 /* Configs */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ 35CB9EEBE37DA168A0AC063E /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 613D89C488B7E8A9AABFE80A /* Pods-Runner.debug.xcconfig */,
+ 9055C8A896F8B9969E9A9A75 /* Pods-Runner.release.xcconfig */,
+ 5E1960EFBF841FBF2ED9CB42 /* Pods-Runner.profile.xcconfig */,
+ );
+ name = Pods;
+ path = Pods;
+ sourceTree = "";
+ };
+ D73912EC22F37F3D000D13A0 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 6AB45B5BD0777F55845AF08A /* Pods_Runner.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 33CC10EC2044A3C60003C045 /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ DF30845ABEF3338382F8E5CB /* [CP] Check Pods Manifest.lock */,
+ 33CC10E92044A3C60003C045 /* Sources */,
+ 33CC10EA2044A3C60003C045 /* Frameworks */,
+ 33CC10EB2044A3C60003C045 /* Resources */,
+ 33CC110E2044A8840003C045 /* Bundle Framework */,
+ 3399D490228B24CF009A79C7 /* ShellScript */,
+ 2F97E070B313FE8332FEB41C /* [CP] Embed Pods Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */,
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 33CC10ED2044A3C60003C045 /* example.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 33CC10E52044A3C60003C045 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 0920;
+ LastUpgradeCheck = 1300;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 33CC10EC2044A3C60003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ LastSwiftMigration = 1100;
+ ProvisioningStyle = Automatic;
+ SystemCapabilities = {
+ com.apple.Sandbox = {
+ enabled = 1;
+ };
+ };
+ };
+ 33CC111A2044C6BA0003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Manual;
+ };
+ };
+ };
+ buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 33CC10E42044A3C60003C045;
+ productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 33CC10EC2044A3C60003C045 /* Runner */,
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 33CC10EB2044A3C60003C045 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 2F97E070B313FE8332FEB41C /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 3399D490228B24CF009A79C7 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
+ };
+ 33CC111E2044C6BF0003C045 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ Flutter/ephemeral/FlutterInputs.xcfilelist,
+ );
+ inputPaths = (
+ Flutter/ephemeral/tripwire,
+ );
+ outputFileListPaths = (
+ Flutter/ephemeral/FlutterOutputs.xcfilelist,
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
+ };
+ DF30845ABEF3338382F8E5CB /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 33CC10E92044A3C60003C045 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
+ targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 33CC10F52044A3C60003C045 /* Base */,
+ );
+ name = MainMenu.xib;
+ path = Runner;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 338D0CE9231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Profile;
+ };
+ 338D0CEA231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Profile;
+ };
+ 338D0CEB231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Profile;
+ };
+ 33CC10F92044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 33CC10FA2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 33CC10FC2044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 33CC10FD2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+ 33CC111C2044C6BA0003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 33CC111D2044C6BA0003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10F92044A3C60003C045 /* Debug */,
+ 33CC10FA2044A3C60003C045 /* Release */,
+ 338D0CE9231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10FC2044A3C60003C045 /* Debug */,
+ 33CC10FD2044A3C60003C045 /* Release */,
+ 338D0CEA231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC111C2044C6BA0003C045 /* Debug */,
+ 33CC111D2044C6BA0003C045 /* Release */,
+ 338D0CEB231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 33CC10E52044A3C60003C045 /* Project object */;
+}
diff --git a/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..fb7259e
--- /dev/null
+++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/example/macos/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..21a3cc1
--- /dev/null
+++ b/example/macos/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/example/macos/Runner/AppDelegate.swift b/example/macos/Runner/AppDelegate.swift
new file mode 100644
index 0000000..d53ef64
--- /dev/null
+++ b/example/macos/Runner/AppDelegate.swift
@@ -0,0 +1,9 @@
+import Cocoa
+import FlutterMacOS
+
+@NSApplicationMain
+class AppDelegate: FlutterAppDelegate {
+ override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
+ return true
+ }
+}
diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..a2ec33f
--- /dev/null
+++ b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+ "images" : [
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_16.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_64.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_128.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_1024.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
new file mode 100644
index 0000000..82b6f9d
Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ
diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
new file mode 100644
index 0000000..13b35eb
Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ
diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
new file mode 100644
index 0000000..0a3f5fa
Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ
diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
new file mode 100644
index 0000000..bdb5722
Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ
diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
new file mode 100644
index 0000000..f083318
Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ
diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
new file mode 100644
index 0000000..326c0e7
Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ
diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
new file mode 100644
index 0000000..2f1632c
Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ
diff --git a/example/macos/Runner/Base.lproj/MainMenu.xib b/example/macos/Runner/Base.lproj/MainMenu.xib
new file mode 100644
index 0000000..80e867a
--- /dev/null
+++ b/example/macos/Runner/Base.lproj/MainMenu.xib
@@ -0,0 +1,343 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/macos/Runner/Configs/AppInfo.xcconfig b/example/macos/Runner/Configs/AppInfo.xcconfig
new file mode 100644
index 0000000..8b42559
--- /dev/null
+++ b/example/macos/Runner/Configs/AppInfo.xcconfig
@@ -0,0 +1,14 @@
+// Application-level settings for the Runner target.
+//
+// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
+// future. If not, the values below would default to using the project name when this becomes a
+// 'flutter create' template.
+
+// The application's name. By default this is also the title of the Flutter window.
+PRODUCT_NAME = example
+
+// The application's bundle identifier
+PRODUCT_BUNDLE_IDENTIFIER = com.example.example
+
+// The copyright displayed in application information
+PRODUCT_COPYRIGHT = Copyright © 2022 com.example. All rights reserved.
diff --git a/example/macos/Runner/Configs/Debug.xcconfig b/example/macos/Runner/Configs/Debug.xcconfig
new file mode 100644
index 0000000..36b0fd9
--- /dev/null
+++ b/example/macos/Runner/Configs/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Debug.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/example/macos/Runner/Configs/Release.xcconfig b/example/macos/Runner/Configs/Release.xcconfig
new file mode 100644
index 0000000..dff4f49
--- /dev/null
+++ b/example/macos/Runner/Configs/Release.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Release.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/example/macos/Runner/Configs/Warnings.xcconfig b/example/macos/Runner/Configs/Warnings.xcconfig
new file mode 100644
index 0000000..42bcbf4
--- /dev/null
+++ b/example/macos/Runner/Configs/Warnings.xcconfig
@@ -0,0 +1,13 @@
+WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
+GCC_WARN_UNDECLARED_SELECTOR = YES
+CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
+CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
+CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
+CLANG_WARN_PRAGMA_PACK = YES
+CLANG_WARN_STRICT_PROTOTYPES = YES
+CLANG_WARN_COMMA = YES
+GCC_WARN_STRICT_SELECTOR_MATCH = YES
+CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
+CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
+GCC_WARN_SHADOW = YES
+CLANG_WARN_UNREACHABLE_CODE = YES
diff --git a/example/macos/Runner/DebugProfile.entitlements b/example/macos/Runner/DebugProfile.entitlements
new file mode 100644
index 0000000..9f56413
--- /dev/null
+++ b/example/macos/Runner/DebugProfile.entitlements
@@ -0,0 +1,12 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.network.server
+
+
+
diff --git a/example/macos/Runner/Info.plist b/example/macos/Runner/Info.plist
new file mode 100644
index 0000000..4789daa
--- /dev/null
+++ b/example/macos/Runner/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSMinimumSystemVersion
+ $(MACOSX_DEPLOYMENT_TARGET)
+ NSHumanReadableCopyright
+ $(PRODUCT_COPYRIGHT)
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/example/macos/Runner/MainFlutterWindow.swift b/example/macos/Runner/MainFlutterWindow.swift
new file mode 100644
index 0000000..2722837
--- /dev/null
+++ b/example/macos/Runner/MainFlutterWindow.swift
@@ -0,0 +1,15 @@
+import Cocoa
+import FlutterMacOS
+
+class MainFlutterWindow: NSWindow {
+ override func awakeFromNib() {
+ let flutterViewController = FlutterViewController.init()
+ let windowFrame = self.frame
+ self.contentViewController = flutterViewController
+ self.setFrame(windowFrame, display: true)
+
+ RegisterGeneratedPlugins(registry: flutterViewController)
+
+ super.awakeFromNib()
+ }
+}
diff --git a/example/macos/Runner/Release.entitlements b/example/macos/Runner/Release.entitlements
new file mode 100644
index 0000000..f7eba17
--- /dev/null
+++ b/example/macos/Runner/Release.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.network.client
+
+
+
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 8f6a22c..0b1b6c2 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
- sha256: "569ddca58d535e601dd1584afa117710abc999d036c0cd2c51777fb257df78e8"
+ sha256: "0c80aeab9bc807ab10022cd3b2f4cf2ecdf231949dc1ddd9442406a003f19201"
url: "https://pub.dev"
source: hosted
- version: "53.0.0"
+ version: "52.0.0"
adaptive_action_sheet:
dependency: transitive
description:
@@ -21,10 +21,10 @@ packages:
dependency: transitive
description:
name: analyzer
- sha256: "10927c4b7c7c88b1adbca278c3d5531db92e2f4b4abf04e2919a800af965f3f5"
+ sha256: cd8ee83568a77f3ae6b913a36093a1c9b1264e7cb7f834d9ddd2311dade9c1f4
url: "https://pub.dev"
source: hosted
- version: "5.5.0"
+ version: "5.4.0"
archive:
dependency: "direct main"
description:
@@ -37,10 +37,10 @@ packages:
dependency: transitive
description:
name: args
- sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440"
+ sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611"
url: "https://pub.dev"
source: hosted
- version: "2.4.0"
+ version: "2.3.2"
async:
dependency: transitive
description:
@@ -49,14 +49,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.10.0"
- azlistview:
+ audioplayers:
dependency: transitive
description:
- name: azlistview
- sha256: "93e865f11777a271b439f0d6b00799c0797e9daeec2e082a2e01373809c4b90d"
+ name: audioplayers
+ sha256: "16451eab798b23ad9307aef6f9ca62bb8fb06542af8810eead0d236d3fd40a42"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.1"
+ audioplayers_android:
+ dependency: transitive
+ description:
+ name: audioplayers_android
+ sha256: b2c833e6f718b6b030454e329931229afafe9327fdb002874dd544dc8bf2484d
url: "https://pub.dev"
source: hosted
version: "2.0.0"
+ audioplayers_darwin:
+ dependency: transitive
+ description:
+ name: audioplayers_darwin
+ sha256: e7a3c8759bf11ecfe4b20df338bf9f3d37c7719a5761c46a3833aba0ceeaacff
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.1"
+ audioplayers_linux:
+ dependency: transitive
+ description:
+ name: audioplayers_linux
+ sha256: e95b65e1f4d4764601dac5e65f8d8186fc29401043ab020f1dacec483d708707
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.4"
+ audioplayers_platform_interface:
+ dependency: transitive
+ description:
+ name: audioplayers_platform_interface
+ sha256: "178581a44cb685fd798d2108111d2e98cca3400e30b9c3a05546f124fb37f600"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.0.0"
+ audioplayers_web:
+ dependency: transitive
+ description:
+ name: audioplayers_web
+ sha256: "859ba09be2a57e57a787273f18c8cf0d9b61383870c5ee4b5632fe9adbc37edf"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.0"
+ audioplayers_windows:
+ dependency: transitive
+ description:
+ name: audioplayers_windows
+ sha256: "622e01c4c357c2aaf1b956c3a0f89d97c3cb40315c03f16e3b6c2a31ff9c38bc"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.3"
+ azlistview_all_platforms:
+ dependency: transitive
+ description:
+ name: azlistview_all_platforms
+ sha256: "47ce2204863e0c3e481ca2a3813096d9818b153f1f677e839503e33d36e97993"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.2"
boolean_selector:
dependency: transitive
description:
@@ -109,10 +165,10 @@ packages:
dependency: transitive
description:
name: camera_android
- sha256: e491c836147f60dd8a54cf3895fd2960e13b21b78a9d15b563a1b6c70894f142
+ sha256: "4cef01e8e78fe27c809a429bf74352ab94ab76b0c980e3ec708f1414614e3d9f"
url: "https://pub.dev"
source: hosted
- version: "0.10.4"
+ version: "0.10.3"
camera_avfoundation:
dependency: transitive
description:
@@ -125,10 +181,10 @@ packages:
dependency: transitive
description:
name: camera_platform_interface
- sha256: b632be28e61d00a233f67d98ea90fd7041956f27a1c65500188ee459be60e15f
+ sha256: "0eedd642d905ca24f1c483fe9ea0d0e7287b86a402845c28d24df28cc7b0ee6e"
url: "https://pub.dev"
source: hosted
- version: "2.4.0"
+ version: "2.3.4"
camera_web:
dependency: transitive
description:
@@ -225,6 +281,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.5"
+ desktop_drop:
+ dependency: transitive
+ description:
+ name: desktop_drop
+ sha256: "4ca4d960f4b11c032e9adfd2a0a8ac615bc3fddb4cbe73dcf840dd8077582186"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.4.1"
+ device_info_plus:
+ dependency: transitive
+ description:
+ name: device_info_plus
+ sha256: b809c4ed5f7fcdb325ccc70b80ad934677dc4e2aa414bf46859a42bfdfafcbb6
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.1.3"
+ 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:
+ dependency: transitive
+ description:
+ name: device_info_plus_platform_interface
+ sha256: "83fdba24fcf6846d3b10f10dfdc8b6c6d7ada5f8ed21d62ea2909c2dfa043773"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.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:
+ dependency: transitive
+ description:
+ name: diff_match_patch
+ sha256: "2efc9e6e8f449d0abe15be240e2c2a3bcd977c8d126cfd70598aee60af35c0a4"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.4.1"
disk_space:
dependency: transitive
description:
@@ -245,10 +365,10 @@ packages:
dependency: transitive
description:
name: extended_image
- sha256: a6b738d9b8d5513be72c545cc3e9c5c451fbee77c8db3cbec7c32ae85b82fb93
+ sha256: "5854d0d05ee0c687d1852af9db05f15cfe058520fa56f417075705c5bce965d4"
url: "https://pub.dev"
source: hosted
- version: "6.4.1"
+ version: "6.4.0"
extended_image_library:
dependency: transitive
description:
@@ -273,14 +393,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.12.6"
+ fc_native_video_thumbnail_for_us:
+ dependency: transitive
+ description:
+ name: fc_native_video_thumbnail_for_us
+ sha256: db6fa2998195ef5eadac690ae58d6a909ddb5b0283ebbbf9ae4e0e8f99a54902
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.4.8+1"
ffi:
dependency: transitive
description:
name: ffi
- sha256: "13a6ccf6a459a125b3fcdb6ec73bd5ff90822e071207c663bfd1f70062d51d18"
+ sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
url: "https://pub.dev"
source: hosted
- version: "1.2.1"
+ version: "2.0.1"
file:
dependency: transitive
description:
@@ -293,10 +421,10 @@ packages:
dependency: transitive
description:
name: file_picker
- sha256: "704259669b5e9cb24e15c11cfcf02caf5f20d30901b3916d60b6d1c2d647035f"
+ sha256: b85eb92b175767fdaa0c543bf3b0d1f610fe966412ea72845fe5ba7801e763ff
url: "https://pub.dev"
source: hosted
- version: "4.6.1"
+ version: "5.2.10"
file_utils:
dependency: transitive
description:
@@ -350,19 +478,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.4"
- flutter_localizations:
- dependency: transitive
- description: flutter
- source: sdk
- version: "0.0.0"
flutter_markdown:
dependency: transitive
description:
name: flutter_markdown
- sha256: "7b25c10de1fea883f3c4f9b8389506b54053cd00807beab69fd65c8653a2711f"
+ sha256: "818cf6c28377ba2c91ed283c96fd712e9c175dd2d2488eb7fc93b6afb9ad2e08"
url: "https://pub.dev"
source: hosted
- version: "0.6.14"
+ version: "0.6.13+1"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
@@ -387,14 +510,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
- flutter_spinkit:
- dependency: transitive
- description:
- name: flutter_spinkit
- sha256: "77a2117c0517ff909221f3160b8eb20052ab5216107581168af574ac1f05dff8"
- url: "https://pub.dev"
- source: hosted
- version: "5.1.0"
flutter_svg:
dependency: transitive
description:
@@ -481,18 +596,18 @@ packages:
dependency: transitive
description:
name: image_picker
- sha256: d39cc12402dab8365fe5b5370e64694ae0223d675c36b15ff0490b7cc3d32551
+ sha256: f98d76672d309c8b7030c323b3394669e122d52b307d2bbd8d06bd70f5b2aabe
url: "https://pub.dev"
source: hosted
- version: "0.8.6+2"
+ version: "0.8.6+1"
image_picker_android:
dependency: transitive
description:
name: image_picker_android
- sha256: "385f12ee9c7288575572c7873a332016ec45ebd092e1c2f6bd421b4a9ad21f1d"
+ sha256: b1cbfec0f5aef427a18eb573f5445af8c9c568626bf3388553e40c263d3f7368
url: "https://pub.dev"
source: hosted
- version: "0.8.5+6"
+ version: "0.8.5+5"
image_picker_for_web:
dependency: transitive
description:
@@ -505,10 +620,10 @@ packages:
dependency: transitive
description:
name: image_picker_ios
- sha256: "884ed71165bc01ffe1b0b7813e6fa17e1e9442da974656f99b79a292371303d6"
+ sha256: "39c013200046d14c58b71dc4fa3d00e425fc9c699d589136cd3ca018727c0493"
url: "https://pub.dev"
source: hosted
- version: "0.8.6+8"
+ version: "0.8.6+6"
image_picker_platform_interface:
dependency: transitive
description:
@@ -585,10 +700,10 @@ packages:
dependency: transitive
description:
name: markdown
- sha256: b3c60dee8c2af50ad0e6e90cceba98e47718a6ee0a7a6772c77846a0cc21f78b
+ sha256: c2b81e184067b41d0264d514f7cdaa2c02d38511e39d6521a1ccc238f6d7b3f2
url: "https://pub.dev"
source: hosted
- version: "7.0.1"
+ version: "6.0.1"
matcher:
dependency: transitive
description:
@@ -649,10 +764,10 @@ packages:
dependency: transitive
description:
name: package_info_plus
- sha256: "7a6114becdf042be2b0777d77ace954d4a205644171a1cbd8712976b9bbb837c"
+ sha256: f62d7253edc197fe3c88d7c2ddab82d68f555e778d55390ccc3537eca8e8d637
url: "https://pub.dev"
source: hosted
- version: "1.4.2"
+ version: "1.4.3+1"
package_info_plus_linux:
dependency: transitive
description:
@@ -689,10 +804,18 @@ packages:
dependency: transitive
description:
name: package_info_plus_windows
- sha256: a6040b8695c82f8dd3c3d4dfab7ef96fbe9c67aac21b9bcf5db272589ef84441
+ sha256: "79524f11c42dd9078b96d797b3cf79c0a2883a50c4920dc43da8562c115089bc"
url: "https://pub.dev"
source: hosted
- version: "1.0.5"
+ version: "2.1.0"
+ pasteboard:
+ dependency: transitive
+ description:
+ name: pasteboard
+ sha256: "1c8b6a8b3f1d12e55d4e9404433cda1b4abe66db6b17bc2d2fb5965772c04674"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.0"
path:
dependency: transitive
description:
@@ -745,10 +868,10 @@ packages:
dependency: transitive
description:
name: path_provider_linux
- sha256: "2e32f1640f07caef0d3cb993680f181c79e54a3827b997d5ee221490d131fbd9"
+ sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379
url: "https://pub.dev"
source: hosted
- version: "2.1.8"
+ version: "2.1.7"
path_provider_platform_interface:
dependency: transitive
description:
@@ -761,10 +884,10 @@ packages:
dependency: transitive
description:
name: path_provider_windows
- sha256: a34ecd7fb548f8e57321fd8e50d865d266941b54e6c3b7758cf8f37c24116905
+ sha256: d3f80b32e83ec208ac95253e0cd4d298e104fbc63cb29c5c69edaed43b0c69d6
url: "https://pub.dev"
source: hosted
- version: "2.0.7"
+ version: "2.1.6"
pedantic:
dependency: transitive
description:
@@ -829,14 +952,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.5.2"
- photo_view:
- dependency: transitive
- description:
- name: photo_view
- sha256: "8036802a00bae2a78fc197af8a158e3e2f7b500561ed23b4c458107685e645bb"
- url: "https://pub.dev"
- source: hosted
- version: "0.14.0"
platform:
dependency: transitive
description:
@@ -909,14 +1024,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
- scrollable_positioned_list:
+ scrollable_positioned_list_for_us:
dependency: transitive
description:
- name: scrollable_positioned_list
- sha256: "9566352ab9ba05794ee6c8864f154afba5d36c5637d0e3e32c615ba4ceb92772"
+ name: scrollable_positioned_list_for_us
+ sha256: b5bcbb35114902c004a4f98f2dbd5b0a5a7f80a0144a8b46297601e38fa5383d
url: "https://pub.dev"
source: hosted
- version: "0.2.3"
+ version: "0.4.2"
shared_preferences:
dependency: transitive
description:
@@ -1062,33 +1177,41 @@ packages:
dependency: transitive
description:
name: tencent_cloud_chat_sdk
- sha256: "32b7e40c5a1682b3ee85daa73504af3d0d4e60a93472b279591e769b15b4861c"
+ sha256: "765a93262a41080e155ce5b8a6ca20147a81c7d306f7f87444077c5eaae87e08"
url: "https://pub.dev"
source: hosted
- version: "5.0.9"
+ version: "5.1.5"
tencent_cloud_chat_uikit:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
- version: "1.7.0"
+ version: "2.0.0+1"
+ tencent_cloud_uikit_core:
+ dependency: transitive
+ description:
+ name: tencent_cloud_uikit_core
+ sha256: "829dfde0c4fbdae019ba233f7f2c299e7cbd18c3ae20ecfe3ab4a43084a33064"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.2"
tencent_extended_text:
dependency: transitive
description:
name: tencent_extended_text
- sha256: cf0d283c01a9e63f75666d8b5b1cabd463e18e51802bf1d093d7a65bd369b3d4
+ sha256: "27a2f7ee58ada480e295102471f1733a7402178a239d0c80a7aa33a134c641ef"
url: "https://pub.dev"
source: hosted
- version: "1.0.2+1"
+ version: "1.0.2"
tencent_extended_text_field:
dependency: transitive
description:
name: tencent_extended_text_field
- sha256: daa10f3775bfac1cc841b34275c2746ced7764f3b77222a93edb4c13bad1209b
+ sha256: d311c240983dbf78e31b58f91e425920a40d6564942813e692a3419bf5c9deb0
url: "https://pub.dev"
source: hosted
- version: "1.0.1+2"
+ version: "1.0.1"
tencent_extended_text_library:
dependency: transitive
description:
@@ -1101,26 +1224,34 @@ packages:
dependency: transitive
description:
name: tencent_im_base
- sha256: b5917ff0bae7c31d52f54932349fc400d3752719a1c5a2f0209258f85c7a6c07
+ sha256: "516356a80f43b94a6c0719b54e4c641cb1f164830b2b3e887d175ae862ebab3f"
url: "https://pub.dev"
source: hosted
- version: "1.0.26"
+ version: "1.0.51"
+ tencent_im_sdk_plugin_desktop:
+ dependency: "direct main"
+ description:
+ name: tencent_im_sdk_plugin_desktop
+ sha256: "5fe5ab0765183185fe4a1f94ce1fdc6ab0a450b8522011806549678edb52130d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.1.13"
tencent_im_sdk_plugin_platform_interface:
dependency: transitive
description:
name: tencent_im_sdk_plugin_platform_interface
- sha256: "7aff6a8495eae3efc3aed1ed944bab7a0ce7eb1035d09b3f3d7974bcb3d8b137"
+ sha256: "04043582f1af698b4abe12d53cd0f043466228fae712677688988d8ff7bfc1f1"
url: "https://pub.dev"
source: hosted
- version: "0.3.12"
+ version: "0.3.19"
tencent_im_sdk_plugin_web:
dependency: "direct main"
description:
name: tencent_im_sdk_plugin_web
- sha256: d83a2370398948bccdc3fa4aae33840bcee62d78faa288823a02d9e0e4ab677f
+ sha256: "6ddf543c6fb4a2c220c5cec3b9247b4353c3dc0f276e76b81c800ad4c92f9bb5"
url: "https://pub.dev"
source: hosted
- version: "0.3.7"
+ version: "0.3.9"
tencent_keyboard_visibility:
dependency: transitive
description:
@@ -1133,10 +1264,10 @@ packages:
dependency: transitive
description:
name: tencent_open_file
- sha256: "98cbffe55e3245a308fbf76e18c9a0f808e534c624bc1a1cc0a00bd63a418290"
+ sha256: "01f94f618da42e5593bbad0657fcd13cfc1c2360cca805d8cdfefe898cbe5429"
url: "https://pub.dev"
source: hosted
- version: "4.0.9+1"
+ version: "4.0.10"
tencent_super_tooltip:
dependency: transitive
description:
@@ -1149,10 +1280,10 @@ packages:
dependency: transitive
description:
name: tencent_wechat_camera_picker
- sha256: "6a6fd12d61ad2ef17273a226a165fe0b5e3ef5c7e49779de38503e4f4b6e3ef1"
+ sha256: "8f95b435c7a12a9221f00fe4354fb9c0f9313d79cc09ddb5902b7b418185092d"
url: "https://pub.dev"
source: hosted
- version: "3.6.5+2"
+ version: "3.6.5+1"
term_glyph:
dependency: transitive
description:
@@ -1173,18 +1304,18 @@ packages:
dependency: transitive
description:
name: tim_ui_kit_sticker_plugin
- sha256: cd5d6e2380deaf73e420602bbfe3da36c0f3253adc531f76e139efec80671175
+ sha256: "2a825d33076f319f6c1c87d58e2b0d650c9284ae4afd8efdc206f3e6f3582e64"
url: "https://pub.dev"
source: hosted
- version: "1.2.0"
+ version: "2.0.1"
transparent_image:
dependency: transitive
description:
name: transparent_image
- sha256: e8991d955a2094e197ca24c645efec2faf4285772a4746126ca12875e54ca02f
+ sha256: e566a616922a781489f4d91cc939b1b3203b6e4a093317805f2f82f0bb0f8dec
url: "https://pub.dev"
source: hosted
- version: "2.0.1"
+ version: "2.0.0"
tuple:
dependency: transitive
description:
@@ -1205,26 +1336,26 @@ packages:
dependency: transitive
description:
name: universal_html
- sha256: b5061c64c7c863c12e46279e032976f1c274f927fb3589b52b5928dcd2d52f7c
+ sha256: "5ff50b7c14d201421cf5230ec389a0591c4deb5c817c9d7ccca3b26fe5f31e34"
url: "https://pub.dev"
source: hosted
- version: "2.0.9"
+ version: "2.0.8"
universal_io:
dependency: transitive
description:
name: universal_io
- sha256: "06866290206d196064fd61df4c7aea1ffe9a4e7c4ccaa8fcded42dd41948005d"
+ sha256: "79f78ddad839ee3aae3ec7c01eb4575faf0d5c860f8e5223bc9f9c17f7f03cef"
url: "https://pub.dev"
source: hosted
- version: "2.2.0"
+ version: "2.0.4"
url_launcher:
dependency: transitive
description:
name: url_launcher
- sha256: e8f2efc804810c0f2f5b485f49e7942179f56eabcfe81dce3387fec4bb55876b
+ sha256: "698fa0b4392effdc73e9e184403b627362eb5fbf904483ac9defbb1c2191d809"
url: "https://pub.dev"
source: hosted
- version: "6.1.9"
+ version: "6.1.8"
url_launcher_android:
dependency: transitive
description:
@@ -1237,10 +1368,10 @@ packages:
dependency: transitive
description:
name: url_launcher_ios
- sha256: "0a5af0aefdd8cf820dd739886efb1637f1f24489900204f50984634c07a54815"
+ sha256: bb328b24d3bccc20bdf1024a0990ac4f869d57663660de9c936fb8c043edefe3
url: "https://pub.dev"
source: hosted
- version: "6.1.0"
+ version: "6.0.18"
url_launcher_linux:
dependency: transitive
description:
@@ -1337,14 +1468,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.13"
- video_thumbnail:
- dependency: transitive
- description:
- name: video_thumbnail
- sha256: "3455c189d3f0bb4e3fc2236475aa84fe598b9b2d0e08f43b9761f5bc44210016"
- url: "https://pub.dev"
- source: hosted
- version: "0.5.3"
wakelock:
dependency: transitive
description:
@@ -1381,10 +1504,10 @@ packages:
dependency: transitive
description:
name: wakelock_windows
- sha256: "108b1b73711f1664ee462e73af34a9286ff496e27d4d8371e2fb4da8fde4cdac"
+ sha256: "857f77b3fe6ae82dd045455baa626bc4b93cb9bb6c86bf3f27c182167c3a5567"
url: "https://pub.dev"
source: hosted
- version: "0.2.0"
+ version: "0.2.1"
watcher:
dependency: transitive
description:
@@ -1405,18 +1528,18 @@ packages:
dependency: transitive
description:
name: win32
- sha256: c0e3a4f7be7dae51d8f152230b86627e3397c1ba8c3fa58e63d44a9f3edc9cef
+ sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4
url: "https://pub.dev"
source: hosted
- version: "2.6.1"
+ version: "3.1.4"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
- sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1
+ sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86
url: "https://pub.dev"
source: hosted
- version: "1.0.0"
+ version: "0.2.0+3"
xml:
dependency: transitive
description:
@@ -1434,5 +1557,5 @@ packages:
source: hosted
version: "3.1.1"
sdks:
- dart: ">=2.19.0 <4.0.0"
+ dart: ">=2.19.0 <3.0.0"
flutter: ">=3.7.0"
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 4eecb12..f19a45a 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -36,8 +36,17 @@ dependencies:
cupertino_icons: ^1.0.2
tencent_cloud_chat_uikit:
path: ../../tim_ui_kit
- tencent_im_sdk_plugin_web: ^0.3.2
+ tencent_im_sdk_plugin_web: ^0.3.9
archive: ^3.3.0
+ tencent_im_sdk_plugin_desktop: ^0.1.13
+
+# dependency_overrides:
+# tencent_cloud_chat_sdk:
+# path: /Users/wangrunlin/Documents/GitHub/im-flutter-plugin/tencent_im_sdk_plugin
+# tencent_im_sdk_plugin_desktop:
+# path: /Users/wangrunlin/Documents/GitHub/im-flutter-plugin/tencent_im_sdk_plugin_desktop
+# tencent_im_sdk_plugin_web:
+# path: /Users/wangrunlin/Documents/GitHub/im-flutter-plugin/tencent_im_sdk_plugin_web
dev_dependencies:
flutter_test:
diff --git a/example/windows/flutter/generated_plugin_registrant.cc b/example/windows/flutter/generated_plugin_registrant.cc
index a0d0bbe..03ccc3e 100644
--- a/example/windows/flutter/generated_plugin_registrant.cc
+++ b/example/windows/flutter/generated_plugin_registrant.cc
@@ -6,10 +6,22 @@
#include "generated_plugin_registrant.h"
+#include
+#include
+#include
+#include
#include
#include
void RegisterPlugins(flutter::PluginRegistry* registry) {
+ AudioplayersWindowsPluginRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
+ DesktopDropPluginRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("DesktopDropPlugin"));
+ FcNativeVideoThumbnailForUsPluginCApiRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("FcNativeVideoThumbnailForUsPluginCApi"));
+ PasteboardPluginRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("PasteboardPlugin"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(
diff --git a/example/windows/flutter/generated_plugins.cmake b/example/windows/flutter/generated_plugins.cmake
index c20a586..ad68f93 100644
--- a/example/windows/flutter/generated_plugins.cmake
+++ b/example/windows/flutter/generated_plugins.cmake
@@ -3,6 +3,10 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
+ audioplayers_windows
+ desktop_drop
+ fc_native_video_thumbnail_for_us
+ pasteboard
permission_handler_windows
url_launcher_windows
)
diff --git a/images/folder_open.png b/images/folder_open.png
new file mode 100644
index 0000000..9557b37
Binary files /dev/null and b/images/folder_open.png differ
diff --git a/images/open_in_new.png b/images/open_in_new.png
new file mode 100644
index 0000000..b2193dd
Binary files /dev/null and b/images/open_in_new.png differ
diff --git a/images/video_icon.png b/images/video_icon.png
new file mode 100644
index 0000000..adeacdd
Binary files /dev/null and b/images/video_icon.png differ
diff --git a/lib/base_widgets/tim_ui_kit_state.dart b/lib/base_widgets/tim_ui_kit_state.dart
index d39284c..4ac9978 100644
--- a/lib/base_widgets/tim_ui_kit_state.dart
+++ b/lib/base_widgets/tim_ui_kit_state.dart
@@ -8,10 +8,6 @@ import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
class TIMUIKitState extends TIMState {
final CoreServicesImpl _coreServices = serviceLocator();
- @override
- initState() {
- super.initState();
- }
@override
void onTIMCallback(TIMCallback callbackValue) {
diff --git a/lib/business_logic/separate_models/tui_chat_model_tools.dart b/lib/business_logic/separate_models/tui_chat_model_tools.dart
index eee084c..521923e 100644
--- a/lib/business_logic/separate_models/tui_chat_model_tools.dart
+++ b/lib/business_logic/separate_models/tui_chat_model_tools.dart
@@ -1,13 +1,11 @@
import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart';
-import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
class TUIChatModelTools {
final TUIChatGlobalModel globalModel = serviceLocator();
final CoreServicesImpl _coreServices = serviceLocator();
- final MessageService _messageService = serviceLocator();
OfflinePushInfo buildMessagePushInfo(
V2TimMessage message, String convID, ConvType convType) {
@@ -15,6 +13,14 @@ class TUIChatModelTools {
return "{\"conversationID\": \"$convID\"}";
}
+ if (globalModel.chatConfig.offlinePushInfo != null) {
+ final customData =
+ globalModel.chatConfig.offlinePushInfo!(message, convID, convType);
+ if(customData != null){
+ return customData;
+ }
+ }
+
String title = globalModel.chatConfig.notificationTitle;
// If user provides null, use default ext.
@@ -109,35 +115,9 @@ class TUIChatModelTools {
});
if (targetIndex != null &&
- targetIndex != -1 &&
+ targetIndex > -1 &&
currentHistoryMsgList.isNotEmpty) {
- List response;
- if (currentHistoryMsgList.length > targetIndex + 2) {
- response = await _messageService.getHistoryMessageList(
- count: 1,
- getType: HistoryMsgGetTypeEnum.V2TIM_GET_LOCAL_NEWER_MSG,
- userID: conversationType == ConvType.c2c ? conversationID : null,
- groupID: conversationType == ConvType.group ? conversationID : null,
- lastMsgID: currentHistoryMsgList[targetIndex + 1].msgID);
- } else {
- response = await _messageService.getHistoryMessageList(
- count: 5,
- getType: HistoryMsgGetTypeEnum.V2TIM_GET_LOCAL_OLDER_MSG,
- userID: conversationType == ConvType.c2c ? conversationID : null,
- groupID: conversationType == ConvType.group ? conversationID : null,
- lastMsgID: currentHistoryMsgList.length - 3 < 0
- ? null
- : currentHistoryMsgList[currentHistoryMsgList.length - 3].msgID,
- );
- }
-
- try {
- return response.firstWhere((item) {
- return item.msgID == msgID;
- });
- } catch (e) {
- return null;
- }
+ return currentHistoryMsgList[targetIndex];
} else {
return null;
}
diff --git a/lib/business_logic/separate_models/tui_chat_separate_view_model.dart b/lib/business_logic/separate_models/tui_chat_separate_view_model.dart
index 9584351..a342a4a 100644
--- a/lib/business_logic/separate_models/tui_chat_separate_view_model.dart
+++ b/lib/business_logic/separate_models/tui_chat_separate_view_model.dart
@@ -2,10 +2,14 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
+
import 'package:flutter/cupertino.dart';
+
+// ignore: unnecessary_import
+import 'package:flutter/foundation.dart';
+import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:path_provider/path_provider.dart';
-import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/chat_life_cycle.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_model_tools.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
@@ -17,10 +21,7 @@ import 'package:tencent_cloud_chat_uikit/ui/constants/history_message_constant.d
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
import 'package:uuid/uuid.dart';
-enum LoadDirection {
- previous,
- latest
-}
+enum LoadDirection { previous, latest }
class TUIChatSeparateViewModel extends ChangeNotifier {
final FriendshipServices _friendshipServices =
@@ -39,7 +40,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
ConvType? conversationType;
bool haveMoreData = false;
bool haveMoreLatestData = false;
- String _currentSelectedMsgId = "";
+ String _currentPlayedMsgId = "";
String _editRevokedMsg = "";
GroupReceiptAllowType? _groupType;
List _multiSelectedMessageList = [];
@@ -51,11 +52,29 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
TIMUIKitChatConfig chatConfig = const TIMUIKitChatConfig();
ValueChanged? setInputField;
String Function(V2TimMessage message)? abstractMessageBuilder;
- Function(String userID)? onTapAvatar;
+ Function(String userID, TapDownDetails tapDetails)? onTapAvatar;
V2TimGroupMemberFullInfo? _currentChatUserInfo;
V2TimGroupInfo? _groupInfo;
String groupMemberListSeq = "0";
List? groupMemberList = [];
+ double atPositionX = 0.0;
+ double atPositionY = 0.0;
+ int _activeAtIndex = -1;
+ List _showAtMemberList = [];
+
+ int get activeAtIndex => _activeAtIndex;
+
+ set activeAtIndex(int value) {
+ _activeAtIndex = value;
+ notifyListeners();
+ }
+
+ List get showAtMemberList => _showAtMemberList;
+
+ set showAtMemberList(List value) {
+ _showAtMemberList = value;
+ notifyListeners();
+ }
V2TimGroupInfo? get groupInfo => _groupInfo;
@@ -78,10 +97,10 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
notifyListeners();
}
- String get currentSelectedMsgId => _currentSelectedMsgId;
+ String get currentPlayedMsgId => _currentPlayedMsgId;
- set currentSelectedMsgId(String value) {
- _currentSelectedMsgId = value;
+ set currentPlayedMsgId(String value) {
+ _currentPlayedMsgId = value;
notifyListeners();
}
@@ -167,6 +186,12 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
globalModel.refreshGroupApplicationList();
getGroupInfo(groupID ?? convID);
loadGroupMemberList(groupID: groupID ?? convID);
+ } else {
+ _groupType = null;
+ isGroupExist = true;
+ _groupInfo = null;
+ groupMemberList?.clear();
+ notifyListeners();
}
if (conversationType == ConvType.c2c) {
final List? friendRes =
@@ -208,36 +233,29 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
List msgList = [];
haveMoreData = false;
- final previousResponse = await _messageService.getHistoryMessageListWithComplete(
- count: 20,
- getType: HistoryMsgGetTypeEnum.V2TIM_GET_CLOUD_OLDER_MSG,
- userID: conversationType == ConvType.c2c ? conversationID : null,
- groupID: conversationType == ConvType.group ? conversationID : null,
- lastMsgSeq: max(seq, 0)
- );
+ final previousResponse =
+ await _messageService.getHistoryMessageListWithComplete(
+ count: 20,
+ getType: HistoryMsgGetTypeEnum.V2TIM_GET_CLOUD_OLDER_MSG,
+ userID: conversationType == ConvType.c2c ? conversationID : null,
+ groupID: conversationType == ConvType.group ? conversationID : null,
+ lastMsgSeq: max(seq, 0));
msgList = previousResponse?.messageList ?? [];
haveMoreData = !(previousResponse?.isFinished ?? false);
-
- // final latestResponse = await _messageService.getHistoryMessageListWithComplete(
- // count: 20,
- // getType: HistoryMsgGetTypeEnum.V2TIM_GET_CLOUD_NEWER_MSG,
- // userID: conversationType == ConvType.c2c ? conversationID : null,
- // groupID: conversationType == ConvType.group ? conversationID : null,
- // lastMsgSeq: max(seq - 1, 0)
- // );
- // msgList = [...(latestResponse?.messageList.reversed ?? []), ...msgList];
haveMoreLatestData = true;
- globalModel.setMessageListPosition(conversationID, HistoryMessagePosition.notShowLatest);
+ globalModel.setMessageListPosition(
+ conversationID, HistoryMessagePosition.notShowLatest);
msgList = await lifeCycle?.didGetHistoricalMessageList(msgList) ?? msgList;
- msgList.insert(0, V2TimMessage(
- userID: '',
- isSelf: false,
- elemType: 101,
- msgID: msgList[0].msgID,
- seq: msgList[0].seq,
- timestamp: 9999
- ));
+ msgList.insert(
+ 0,
+ V2TimMessage(
+ userID: '',
+ isSelf: false,
+ elemType: 101,
+ msgID: msgList[0].msgID,
+ seq: msgList[0].seq,
+ timestamp: 9999));
globalModel.setMessageList(conversationID, msgList,
needResetNewMessageCount: false);
@@ -253,99 +271,141 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
return haveMoreData;
}
- Future loadData({
- HistoryMsgGetTypeEnum? getType,
- int lastMsgSeq = -1,
- required int count,
- String? lastMsgID,
- LoadDirection direction = LoadDirection.previous,
+ // 加载聊天记录
+ Future loadChatRecord({
+ HistoryMsgGetTypeEnum? getType, // 获取聊天记录的方式
+ int lastMsgSeq = -1, // 上一条消息的消息序号
+ required int count, // 加载的消息数量
+ String? lastMsgID, // 最后一条消息的ID
+ LoadDirection direction =
+ LoadDirection.previous, // 加载的方向,previous表示向上加载,latest表示向下加载
}) async {
- // TODO: 这个函数写的也太复杂了,现在就先不动了,到时候2.0大版本得彻底改造下QAQ
- if(direction == LoadDirection.latest){
- haveMoreLatestData = false;
- }else{
- haveMoreData = false;
- }
- final currentHistoryMsgList = globalModel.messageListMap[conversationID];
- final response = await _messageService.getHistoryMessageListWithComplete(
+ try {
+ // 根据加载方向设置是否还能继续加载更多消息
+ direction == LoadDirection.latest
+ ? haveMoreLatestData = false
+ : haveMoreData = false;
+
+ // 获取当前聊天对话的历史消息列表
+ final currentRecordList = globalModel.messageListMap[conversationID];
+
+ // 调用MessageService获取聊天记录
+ final response = await _messageService.getHistoryMessageListWithComplete(
count: count,
- getType: getType ?? (direction == LoadDirection.previous
- ? HistoryMsgGetTypeEnum.V2TIM_GET_CLOUD_OLDER_MSG
- : HistoryMsgGetTypeEnum.V2TIM_GET_CLOUD_NEWER_MSG),
+ getType: getType ??
+ (direction == LoadDirection.previous
+ ? HistoryMsgGetTypeEnum.V2TIM_GET_CLOUD_OLDER_MSG
+ : HistoryMsgGetTypeEnum.V2TIM_GET_CLOUD_NEWER_MSG),
userID: conversationType == ConvType.c2c ? conversationID : null,
groupID: conversationType == ConvType.group ? conversationID : null,
lastMsgID: lastMsgID,
- lastMsgSeq: lastMsgSeq);
- if (response == null) {
- return false;
- }
+ lastMsgSeq: lastMsgSeq,
+ );
- if (lastMsgID != null && currentHistoryMsgList != null) {
- List messageList = response.messageList;
- List newList = [];
-
- if(direction == LoadDirection.latest){
- globalModel.receivedNewMessageCount = globalModel.receivedMessageListCount + messageList.length;
- messageList = messageList.reversed.toList();
- newList = [...messageList, ...currentHistoryMsgList];
- }else{
- newList = [...currentHistoryMsgList, ...messageList];
+ if (response == null) {
+ return false;
}
- final List msgList =
- await lifeCycle?.didGetHistoricalMessageList(newList) ?? newList;
- globalModel.setMessageList(conversationID, msgList,
- needResetNewMessageCount: false);
- } else {
- List messageList =
- await lifeCycle?.didGetHistoricalMessageList(response.messageList) ??
- response.messageList;
- if (globalModel.loadingMessage[conversationID] != null) {
- if (globalModel.loadingMessage[conversationID]!.isNotEmpty) {
- if(direction == LoadDirection.previous){
- messageList = [
- ...?globalModel.loadingMessage[conversationID],
- ...messageList
- ];
- }else{
- messageList = messageList.reversed.toList();
- messageList = [
- ...messageList,
- ...?globalModel.loadingMessage[conversationID],
- ];
+ // 根据加载方向更新是否还能继续加载更多消息
+ if (direction == LoadDirection.latest) {
+ haveMoreLatestData = !response.isFinished;
+ } else {
+ haveMoreData = !response.isFinished;
+ }
+ notifyListeners();
+
+ if (response.messageList.isEmpty) {
+ return false;
+ }
+
+ // 根据lastMsgID判断是否为分页加载
+ if (lastMsgID != null && currentRecordList != null) {
+ List messageList = response.messageList;
+ List newList = [];
+
+ // 根据加载方向拼接消息列表
+ if (direction == LoadDirection.latest) {
+ globalModel.receivedNewMessageCount =
+ globalModel.receivedMessageListCount + messageList.length;
+ messageList = messageList.reversed.toList();
+ newList = _combineMessageList(messageList, currentRecordList);
+ } else {
+ newList = _combineMessageList(currentRecordList, messageList);
+ }
+
+ // 处理新获取的消息列表后回调
+ final List msgList =
+ await lifeCycle?.didGetHistoricalMessageList(newList) ?? newList;
+
+ // 更新聊天记录到全局model
+ globalModel.setMessageList(
+ conversationID,
+ msgList,
+ needResetNewMessageCount: false,
+ );
+ } else {
+ // 处理新获取的消息列表后回调
+ List receivedList = await lifeCycle
+ ?.didGetHistoricalMessageList(response.messageList) ??
+ response.messageList;
+
+ // 根据加载方向拼接消息列表
+ if (globalModel.loadingMessage[conversationID]?.isNotEmpty ?? false) {
+ if (direction == LoadDirection.previous) {
+ receivedList = _combineMessageList(
+ globalModel.messageListMap[conversationID]!, receivedList);
+ } else {
+ receivedList = receivedList.reversed.toList();
+ receivedList = _combineMessageList(
+ receivedList, globalModel.messageListMap[conversationID]!);
}
} else {
globalModel.loadingMessage.remove(conversationID);
}
- }
- globalModel.setMessageList(conversationID, messageList,
- needResetNewMessageCount: false);
- }
- if (chatConfig.isShowGroupReadingStatus &&
- conversationType == ConvType.group &&
- response.messageList.isNotEmpty) {
- _getMsgReadReceipt(response.messageList);
- }
- if (chatConfig.isReportGroupReadingStatus &&
- conversationType == ConvType.group &&
- response.messageList.isNotEmpty) {
- _setMsgReadReceipt(response.messageList);
- }
-
- if(direction == LoadDirection.latest){
- haveMoreLatestData = !response.isFinished;
- if(!haveMoreLatestData){
- globalModel.setMessageListPosition(conversationID, HistoryMessagePosition.inTwoScreen);
+ // 更新聊天记录到全局model
+ globalModel.setMessageList(
+ conversationID,
+ receivedList,
+ needResetNewMessageCount: false,
+ );
}
- }else{
- haveMoreData = !response.isFinished;
+
+ // 获取已读未读状态
+ if (chatConfig.isShowGroupReadingStatus &&
+ conversationType == ConvType.group &&
+ response.messageList.isNotEmpty) {
+ _getMsgReadReceipt(response.messageList);
+ }
+ if (chatConfig.isReportGroupReadingStatus &&
+ conversationType == ConvType.group &&
+ response.messageList.isNotEmpty) {
+ _setMsgReadReceipt(response.messageList);
+ }
+
+ // 根据加载方向更新是否还能继续加载更多消息
+ if (direction == LoadDirection.latest && !haveMoreLatestData) {
+ globalModel.setMessageListPosition(
+ conversationID, HistoryMessagePosition.inTwoScreen);
+ }
+ notifyListeners();
+
+ return haveMoreData;
+ } catch (e) {
+ // ignore: avoid_print
+ print('loadChatRecord error: $e');
+ return false;
}
- return haveMoreData;
+ }
+
+// 拼接聊天记录
+ List _combineMessageList(
+ List first, List second) {
+ return [...first, ...second];
}
Future loadDataFromController({int? count}) {
- return loadData(
+ return loadChatRecord(
count: count ?? HistoryMessageDartConstant.getCount, //20
);
}
@@ -357,7 +417,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
_getMsgReadReceipt(List message) async {
final msgID = message
- .where((e) => (e.isSelf ?? false) && (e.needReadReceipt ?? false))
+ .where((e) => (e.isSelf ?? true) && (e.needReadReceipt ?? false))
.map((e) => e.msgID ?? '')
.toList();
if (msgID.isNotEmpty) {
@@ -374,10 +434,26 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
}
}
+ translateText(V2TimMessage message) async {
+ final String originText = message.textElem?.text ?? "";
+ final String deviceLocale = TIM_getCurrentDeviceLocale();
+ final String targetMessage = deviceLocale.split("-")[0];
+ final translatedText =
+ await _messageService.translateText(originText, targetMessage);
+
+ final LocalCustomDataModel localCustomData = LocalCustomDataModel.fromMap(
+ json.decode(TencentUtils.checkString(message.localCustomData) ?? "{}"));
+ localCustomData.translatedText = translatedText;
+ message.localCustomData = json.encode(localCustomData.toMap());
+ globalModel.onMessageModified(message);
+ TencentImSDKPlugin.v2TIMManager.v2TIMMessageManager.setLocalCustomData(
+ msgID: message.msgID!, localCustomData: message.localCustomData ?? "");
+ }
+
_setMsgReadReceipt(List message) async {
final msgIDList = List.empty(growable: true);
for (var item in message) {
- final isSelf = item.isSelf ?? false;
+ final isSelf = item.isSelf ?? true;
final needReadReceipt = item.needReadReceipt ?? false;
if (!isSelf && needReadReceipt && item.msgID != null) {
msgIDList.add(item.msgID!);
@@ -397,7 +473,6 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
markMessageAsRead() async {
globalModel.unreadCountForConversation = 0;
- // globalModel.receivedNewMessageCount = 0;
if (conversationType == ConvType.c2c) {
return _messageService.markC2CMessageAsRead(userID: conversationID);
}
@@ -472,15 +547,17 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
}
}
- Future updateMessageFromController({required String msgID}) async {
- V2TimMessage? newMessage = await tools.getExistingMessageByID(
- msgID: msgID,
- conversationType: conversationType ?? ConvType.c2c,
- conversationID: conversationID);
+ Future updateMessageFromController(
+ {required String msgID, V2TimMessage? message}) async {
+ V2TimMessage? newMessage = message ??
+ await tools.getExistingMessageByID(
+ msgID: msgID,
+ conversationType: conversationType ?? ConvType.c2c,
+ conversationID: conversationID);
if (newMessage != null) {
globalModel.onMessageModified(newMessage, conversationID);
} else {
- loadData(
+ loadChatRecord(
count: HistoryMessageDartConstant.getCount,
);
}
@@ -531,7 +608,9 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
})
: "",
);
- if (isEditStatusMessage == false && globalModel.getMessageListPosition(conversationID) != HistoryMessagePosition.notShowLatest) {
+ if (isEditStatusMessage == false &&
+ globalModel.getMessageListPosition(conversationID) !=
+ HistoryMessagePosition.notShowLatest) {
globalModel.updateMessage(
sendMsgRes, convID, id, convType, groupType, setInputField);
}
@@ -570,7 +649,8 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
}
}
- if(globalModel.getMessageListPosition(conversationID) != HistoryMessagePosition.notShowLatest) {
+ if (globalModel.getMessageListPosition(conversationID) !=
+ HistoryMessagePosition.notShowLatest) {
currentHistoryMsgList = [
lifeCycleMsg ?? messageInfoWithSender,
...currentHistoryMsgList
@@ -608,15 +688,16 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
}
}
- if(globalModel.getMessageListPosition(conversationID) != HistoryMessagePosition.notShowLatest) {
+ if (globalModel.getMessageListPosition(conversationID) !=
+ HistoryMessagePosition.notShowLatest) {
currentHistoryMsgList = [
lifeCycleMsg ?? messageInfoWithSender,
...currentHistoryMsgList
];
globalModel.setMessageList(conversationID, currentHistoryMsgList);
-
notifyListeners();
}
+
return _sendMessage(
convID: convID,
id: textATMessageInfo.id as String,
@@ -646,7 +727,9 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
return null;
}
}
- if(globalModel.getMessageListPosition(conversationID) != HistoryMessagePosition.notShowLatest) {
+
+ if (globalModel.getMessageListPosition(conversationID) !=
+ HistoryMessagePosition.notShowLatest) {
currentHistoryMsgList = [
lifeCycleMsg ?? messageInfoWithSender,
...currentHistoryMsgList
@@ -654,6 +737,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
globalModel.setMessageList(conversationID, currentHistoryMsgList);
notifyListeners();
}
+
return _sendMessage(
convID: convID,
id: textMessageInfo.id as String,
@@ -685,15 +769,17 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
return null;
}
}
- if(globalModel.getMessageListPosition(conversationID) != HistoryMessagePosition.notShowLatest) {
+
+ if (globalModel.getMessageListPosition(conversationID) !=
+ HistoryMessagePosition.notShowLatest) {
currentHistoryMsgList = [
lifeCycleMsg ?? messageInfoWithSender,
...currentHistoryMsgList
];
globalModel.setMessageList(conversationID, currentHistoryMsgList);
-
notifyListeners();
}
+
return _sendMessage(
convID: convID,
id: soundMessageInfo.id as String,
@@ -735,16 +821,22 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
}
}
- Future?> sendReplyMessage(
- {required String text,
- required String convID,
- required ConvType convType}) async {
+ Future?> sendReplyMessage({
+ required String text,
+ required String convID,
+ required ConvType convType,
+ List? atUserIDList,
+ }) async {
if (text.isEmpty) {
return null;
}
if (_repliedMessage != null) {
- final V2TimMsgCreateInfoResult? textMessageInfo =
+ V2TimMsgCreateInfoResult? textMessageInfo =
await _messageService.createTextMessage(text: text);
+ if (atUserIDList != null && atUserIDList.isNotEmpty) {
+ textMessageInfo = await _messageService.createTextAtMessage(
+ text: text, atUserList: atUserIDList);
+ }
final V2TimMessage? messageInfo = textMessageInfo!.messageInfo;
final receiver = convType == ConvType.c2c ? convID : '';
final groupID = convType == ConvType.group ? convID : '';
@@ -753,7 +845,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
: null;
if (messageInfo != null) {
V2TimMessage messageInfoWithSender =
- tools.setUserInfoForMessage(messageInfo, messageInfo.id!);
+ tools.setUserInfoForMessage(messageInfo, textMessageInfo.id!);
final hasNickName = _repliedMessage?.nickName != null &&
_repliedMessage?.nickName != "";
final cloudCustomData = {
@@ -843,10 +935,8 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
format: CompressFormat.jpeg, quality: 85);
image = result?.path;
}
- } catch (e) {
- // ignore: avoid_print
- print(e);
- }
+ // ignore: empty_catches
+ } catch (e) {}
}
final imageMessageInfo = await _messageService.createImageMessage(
imagePath: image ?? imagePath, inputElement: inputElement);
@@ -863,15 +953,17 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
return null;
}
}
- if(globalModel.getMessageListPosition(conversationID) != HistoryMessagePosition.notShowLatest){
- currentHistoryMsgList = [
- lifeCycleMsg ?? messageInfoWithSender,
- ...currentHistoryMsgList
- ];
- globalModel.setMessageList(conversationID, currentHistoryMsgList);
- notifyListeners();
+ if (globalModel.getMessageListPosition(conversationID) !=
+ HistoryMessagePosition.notShowLatest) {
+ currentHistoryMsgList = [
+ lifeCycleMsg ?? messageInfoWithSender,
+ ...currentHistoryMsgList
+ ];
+ globalModel.setMessageList(conversationID, currentHistoryMsgList);
+ notifyListeners();
}
+
return _sendMessage(
convID: convID,
messageInfo: lifeCycleMsg ?? messageInfoWithSender,
@@ -894,7 +986,9 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
List currentHistoryMsgList = getOriginMessageList();
final videoMessageInfo = await _messageService.createVideoMessage(
videoPath: videoPath,
- type: 'mp4',
+ type: videoPath != null
+ ? videoPath.split(".")[videoPath.split(".").length - 1]
+ : 'mp4',
duration: duration,
inputElement: inputElement,
snapshotPath: snapshotPath);
@@ -909,15 +1003,17 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
return null;
}
}
- if(globalModel.getMessageListPosition(conversationID) != HistoryMessagePosition.notShowLatest) {
+
+ if (globalModel.getMessageListPosition(conversationID) !=
+ HistoryMessagePosition.notShowLatest) {
currentHistoryMsgList = [
lifeCycleMsg ?? messageInfoWithSender,
...currentHistoryMsgList
];
globalModel.setMessageList(conversationID, currentHistoryMsgList);
-
notifyListeners();
}
+
return _sendMessage(
convID: convID,
messageInfo: lifeCycleMsg ?? messageInfoWithSender,
@@ -954,15 +1050,17 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
return null;
}
}
- if(globalModel.getMessageListPosition(conversationID) != HistoryMessagePosition.notShowLatest) {
+
+ if (globalModel.getMessageListPosition(conversationID) !=
+ HistoryMessagePosition.notShowLatest) {
currentHistoryMsgList = [
lifeCycleMsg ?? messageInfoWithSender,
...currentHistoryMsgList
];
globalModel.setMessageList(conversationID, currentHistoryMsgList);
-
notifyListeners();
}
+
return _sendMessage(
convID: convID,
messageInfo: lifeCycleMsg ?? messageInfoWithSender,
@@ -995,13 +1093,14 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
return null;
}
}
- if(globalModel.getMessageListPosition(conversationID) != HistoryMessagePosition.notShowLatest) {
+
+ if (globalModel.getMessageListPosition(conversationID) !=
+ HistoryMessagePosition.notShowLatest) {
currentHistoryMsgList = [
lifeCycleMsg ?? messageInfoWithSender,
...currentHistoryMsgList
];
globalModel.setMessageList(conversationID, currentHistoryMsgList);
-
notifyListeners();
}
return _sendMessage(
@@ -1126,10 +1225,12 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
}
// 注意重发消息需要先删除之前发送失败的图
- Future?> reSendFailMessage(
- {required V2TimMessage message,
- required String convID,
- required ConvType convType}) async {
+ Future?> reSendFailMessage({
+ required V2TimMessage message,
+ required String convID,
+ required ConvType convType,
+ List? atUserIDList,
+ }) async {
await deleteMsg(message.msgID ?? "",
id: message.id, webMessageInstance: message.messageFromWeb);
int messageType = message.elemType;
@@ -1138,7 +1239,11 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
String text = message.textElem!.text!;
if (_repliedMessage != null) {
res = await sendReplyMessage(
- text: text, convID: convID, convType: convType);
+ text: text,
+ convID: convID,
+ convType: convType,
+ atUserIDList: atUserIDList,
+ );
} else {
res = await sendTextMessage(
text: text, convID: convID, convType: convType);
@@ -1204,7 +1309,8 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
}
}
- if(globalModel.getMessageListPosition(conversationID) != HistoryMessagePosition.notShowLatest){
+ if (globalModel.getMessageListPosition(conversationID) !=
+ HistoryMessagePosition.notShowLatest) {
currentHistoryMsgList = [
lifeCycleMsg ?? messageInfoWithSender,
...currentHistoryMsgList
@@ -1213,7 +1319,6 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
notifyListeners();
}
-
return _sendMessage(
convID: convID,
id: textMessageInfo.id as String,
@@ -1235,8 +1340,16 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
final messageInfoWithSender = messageInfo.sender == null
? tools.setUserInfoForMessage(messageInfo, messageInfo.id!)
: messageInfo;
- currentHistoryMsgList = [messageInfoWithSender, ...currentHistoryMsgList];
- globalModel.setMessageList(conversationID, currentHistoryMsgList);
+
+ if (globalModel.getMessageListPosition(conversationID) !=
+ HistoryMessagePosition.notShowLatest) {
+ currentHistoryMsgList = [
+ messageInfoWithSender,
+ ...currentHistoryMsgList
+ ];
+ globalModel.setMessageList(conversationID, currentHistoryMsgList);
+ }
+
return _sendMessage(
convID: conversationID,
id: messageInfo.id as String,
@@ -1316,26 +1429,6 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
}
}
- translateText(V2TimMessage message) async {
- final String originText = message.textElem?.text ?? "";
- final String deviceLocale =
- WidgetsBinding.instance?.window.locale.toLanguageTag() ?? "en";
- final String targetMessage = deviceLocale.split("-")[0];
- final translatedText =
- await _messageService.translateText(originText, targetMessage);
-
- final LocalCustomDataModel localCustomData = LocalCustomDataModel.fromMap(
- json.decode(TencentUtils.checkString(message.localCustomData) ?? "{}"));
- localCustomData.translatedText = translatedText;
- final result = await TencentImSDKPlugin.v2TIMManager.v2TIMMessageManager
- .setLocalCustomData(
- msgID: message.msgID!,
- localCustomData: json.encode(localCustomData.toMap()));
- if (result.code == 0 && TencentUtils.checkString(message.msgID) != null) {
- updateMessageFromController(msgID: message.msgID!);
- }
- }
-
updateMultiSelectStatus(bool isSelect) {
_isMultiSelect = isSelect;
if (!isSelect) {
diff --git a/lib/business_logic/separate_models/tui_group_profile_model.dart b/lib/business_logic/separate_models/tui_group_profile_model.dart
index 47c11de..153ba22 100644
--- a/lib/business_logic/separate_models/tui_group_profile_model.dart
+++ b/lib/business_logic/separate_models/tui_group_profile_model.dart
@@ -26,7 +26,7 @@ class TUIGroupProfileModel extends ChangeNotifier {
List? _groupMemberList;
String _groupMemberListSeq = "0";
V2TimGroupInfo? _groupInfo;
- Function(String userID)? onClickUser;
+ Function(String userID, TapDownDetails? tapDetails)? onClickUser;
GroupProfileLifeCycle? get lifeCycle => _lifeCycle;
@@ -180,7 +180,6 @@ class TUIGroupProfileModel extends ChangeNotifier {
setGroupNotification(String notification) async {
if (_groupInfo != null) {
- _groupInfo?.notification = notification;
final response = await _groupServices.setGroupInfo(
info: V2TimGroupInfo.fromJson({
"groupID": _groupID,
@@ -189,6 +188,7 @@ class TUIGroupProfileModel extends ChangeNotifier {
}));
if (response.code == 0) {
notifyListeners();
+ _groupInfo?.notification = notification;
}
}
}
@@ -327,7 +327,7 @@ class TUIGroupProfileModel extends ChangeNotifier {
Future muteGroupMember(
String userID, bool isMute, int? serverTime) async {
- final muteTime = serverTime != null ? serverTime + 9999 : 51556926 * 10;
+ const muteTime = 315360000;
final res = await _groupServices.muteGroupMember(
groupID: _groupID, userID: userID, seconds: isMute ? muteTime : 0);
if (res.code == 0) {
@@ -335,7 +335,7 @@ class TUIGroupProfileModel extends ChangeNotifier {
_groupMemberList!.indexWhere((e) => e!.userID == userID);
if (targetIndex != -1) {
final targetElem = _groupMemberList![targetIndex];
- targetElem?.muteUntil = isMute ? muteTime : 0;
+ targetElem?.muteUntil = isMute ? (serverTime ?? 0) + muteTime : 0;
_groupMemberList![targetIndex] = targetElem;
}
notifyListeners();
diff --git a/lib/business_logic/separate_models/tui_profile_view_model.dart b/lib/business_logic/separate_models/tui_profile_view_model.dart
index cdb5ee6..6e517e5 100644
--- a/lib/business_logic/separate_models/tui_profile_view_model.dart
+++ b/lib/business_logic/separate_models/tui_profile_view_model.dart
@@ -31,6 +31,11 @@ class TUIProfileViewModel extends ChangeNotifier {
return _userProfile;
}
+ set userProfile(UserProfile? value) {
+ _userProfile = value;
+ notifyListeners();
+ }
+
bool? get isDisturb {
return _isDisturb;
}
@@ -48,6 +53,9 @@ class TUIProfileViewModel extends ChangeNotifier {
}
loadData({required String userID, bool isNeedConversation = true}) async {
+ if(userID.isEmpty){
+ return;
+ }
V2TimFriendInfo? friendUserInfo;
V2TimConversation? conversation;
final userInfoList =
@@ -159,8 +167,6 @@ class TUIProfileViewModel extends ChangeNotifier {
if (res.code == 0) {
_userProfile?.friendInfo!.userProfile!.allowType = allowType;
notifyListeners();
- } else {
- print("${res.code},${res.desc}");
}
return res;
}
@@ -175,8 +181,6 @@ class TUIProfileViewModel extends ChangeNotifier {
if (res.code == 0) {
_userProfile?.friendInfo!.userProfile!.gender = gender;
notifyListeners();
- } else {
- print("${res.code},${res.desc}");
}
return res;
@@ -192,8 +196,6 @@ class TUIProfileViewModel extends ChangeNotifier {
if (res.code == 0) {
_userProfile?.friendInfo!.userProfile!.nickName = nickName;
notifyListeners();
- } else {
- print("${res.code},${res.desc}");
}
return res;
@@ -208,8 +210,6 @@ class TUIProfileViewModel extends ChangeNotifier {
if (res.code == 0) {
_userProfile?.friendInfo!.userProfile!.selfSignature = selfSignature;
notifyListeners();
- } else {
- print("${res.code},${res.desc}");
}
return res;
}
@@ -297,8 +297,6 @@ class TUIProfileViewModel extends ChangeNotifier {
});
notifyListeners();
- } else {
- print("${res.code},${res.desc}");
}
return res;
}
diff --git a/lib/business_logic/view_models/tui_chat_global_model.dart b/lib/business_logic/view_models/tui_chat_global_model.dart
index fbbd5cf..d75acca 100644
--- a/lib/business_logic/view_models/tui_chat_global_model.dart
+++ b/lib/business_logic/view_models/tui_chat_global_model.dart
@@ -18,7 +18,12 @@ import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart';
enum ConvType { none, c2c, group }
-enum HistoryMessagePosition { bottom, inTwoScreen, awayTwoScreen, notShowLatest }
+enum HistoryMessagePosition {
+ bottom,
+ inTwoScreen,
+ awayTwoScreen,
+ notShowLatest
+}
class CurrentConversation {
final String conversationID;
@@ -37,6 +42,7 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
final Map _preloadImageMap = {};
final Map _historyMessagePositionMap = {};
final List _currentConversationList = [];
+
Map get preloadImageMap => _preloadImageMap;
ChatLifeCycle? _lifeCycle;
@@ -49,8 +55,9 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
late V2TimAdvancedMsgListener advancedMsgListener;
int _unreadCountForConversation = 0;
- // use for generate a new sliver list to show received message list
- int _receivedNewMessageCount = 0;
+
+ // use for generate a new sliver list to show recived messag list
+ int _recivedNewMessageCount = 0;
TIMUIKitChatConfig chatConfig = const TIMUIKitChatConfig();
List? _groupApplicationList;
String Function(V2TimMessage message)? _abstractMessageBuilder;
@@ -58,7 +65,7 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
Map.from({}); // 0 normal 1 sending
final Map _c2cMessageFromUserActiveMap = Map.from({});
final Map _c2cMessageActiveTimer = Map.from({});
- bool _showC2cMessageEditStatus = true;
+ bool _showC2cMessageEditStaus = true;
final Map _c2cMessageStatusShowTimer = Map.from({});
Map loadingMessage = {};
@@ -73,8 +80,8 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
onRecvNewMessage: (V2TimMessage newMsg) {
_onReceiveNewMsg(newMsg);
},
- onSendMessageProgress: (V2TimMessage message, int progress) {
- _onSendMessageProgress(message, progress);
+ onSendMessageProgress: (V2TimMessage messagae, int progress) {
+ _onSendMessageProgress(messagae, progress);
},
onRecvMessageReadReceipts: (List receiptList) {
_onReceiveMessageReadReceipts(receiptList);
@@ -87,12 +94,14 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
onMessageDownloadProgressCallback(messageProgress);
},
);
- // Future.delayed(const Duration(milliseconds: 200)).then((value) => initMessageMapFromLocal());
}
bool get isDownloading => _isDownloading;
+
bool get hasWaiting => _waitingDownloadList.isNotEmpty;
+
Map get currentDownLoad => _waitingDownloadList.first;
+
void addWaitingList(String msgID) {
print("add to waiting list success");
bool contains = false;
@@ -114,43 +123,28 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
}
downloadFile() async {
- bool hasWait = _waitingDownloadList.isNotEmpty;
- print(_waitingDownloadList);
- print(_isDownloading);
- print(!hasWaiting);
- if (_isDownloading || !hasWait) {
- print("no download return");
+ if (_isDownloading || _waitingDownloadList.isEmpty) {
return;
}
- Map nextDownLoad = _waitingDownloadList.first;
- String downLoadMsgID = nextDownLoad["msgID"] ?? "";
- if (downLoadMsgID.isEmpty) {
+ final nextDownload = _waitingDownloadList.first;
+ final msgID = nextDownload["msgID"] ?? "";
+ if (msgID.isEmpty || _messageListProgressMap[msgID] == 100) {
return;
}
- if (_messageListProgressMap[downLoadMsgID] == 100) {
- return;
- }
- // await Dio().download(downLoadMsgUrl, downLoadMsgSavePath,
- // onReceiveProgress: (recv, total) {
- // if (total != -1) {
- // // print((received / total * 100).toStringAsFixed(0) + "%");
- // int progrss = (recv / total * 100).ceil();
- // if (progrss > 1) {
- // setMessageProgress(downLoadMsgID, progrss);
- // }
- // //you can build progressbar feature too
- // }
- // });
- await _messageService.downloadMessage(
- msgID: downLoadMsgID, messageType: 6, imageType: 0, isSnapshot: false);
+
_isDownloading = true;
- // startDownloadProcess(context, theme, received);
+ await _messageService.downloadMessage(
+ msgID: msgID,
+ messageType: 6,
+ imageType: 0,
+ isSnapshot: false,
+ );
+
print("start another download");
- // downloadFile();
}
- int getReceived(msgID) {
+ int getRecevied(msgID) {
return messageListProgressMap[msgID] ?? 0;
}
@@ -179,11 +173,11 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
}
int get receivedMessageListCount {
- return _receivedNewMessageCount;
+ return _recivedNewMessageCount;
}
set receivedNewMessageCount(int value) {
- _receivedNewMessageCount = value;
+ _recivedNewMessageCount = value;
}
int get unreadCountForConversation => _unreadCountForConversation;
@@ -222,7 +216,7 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
}
setShowC2cEditStatus(bool show) {
- _showC2cMessageEditStatus = show;
+ _showC2cMessageEditStaus = show;
}
/// set edit status from chats
@@ -379,14 +373,14 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
_totalUnreadCount = 0;
_groupApplicationList?.clear();
_totalUnreadCount = 0;
- _receivedNewMessageCount = 0;
+ _recivedNewMessageCount = 0;
_messageReadReceiptMap.clear();
_messageListProgressMap.clear();
notifyListeners();
}
clearRecivedNewMessageCount() {
- _receivedNewMessageCount = 0;
+ _recivedNewMessageCount = 0;
}
_preLoadImage(List msgList) {
@@ -457,17 +451,18 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
_editStatusCheck(V2TimMessage msg) {
bool isStatusMessage = false;
- if (msg.customElem != null && msg.groupID == null) {
+ if (msg.customElem != null &&
+ TencentUtils.checkString(msg.groupID) == null) {
V2TimCustomElem customElem = msg.customElem!;
String sender = msg.sender ?? "";
if (customElem.data!.isNotEmpty) {
try {
Map? data = json.decode(customElem.data ?? "");
if (data != null) {
- String businessID = data["businessID"];
+ var businessID = data["businessID"];
int? userAction = data["userAction"];
String? actionParam = data["actionParam"];
- if (businessID == "user_typing_status") {
+ if (businessID.toString() == "user_typing_status") {
int? typingStatus = data["typingStatus"];
if (sender != "") {
if (typingStatus != null) {
@@ -501,7 +496,6 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
try {
Map data = json.decode(msg.cloudCustomData ?? "");
Map? messageFeature = data["messageFeature"];
- print(data);
if (messageFeature != null) {
int needTyping = messageFeature["needTyping"];
if (needTyping == 1) {
@@ -532,7 +526,7 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
}
sendEditStatusMessage(bool isEditing, String toUser) async {
- if (!_showC2cMessageEditStatus) {
+ if (!_showC2cMessageEditStaus) {
return;
}
if (!(_c2cMessageFromUserActiveMap[toUser] ?? false)) {
@@ -598,19 +592,27 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
}
_checkFromUserisActive(msgComing);
- final convID = newMsg.userID ?? newMsg.groupID;
+ final convID = TencentUtils.checkString(newMsg.userID) ?? newMsg.groupID;
+ final convType = TencentUtils.checkString(newMsg.groupID) != null
+ ? ConvType.group
+ : ConvType.c2c;
if (convID != null && convID == currentSelectedConv) {
final position = getMessageListPosition(convID);
- if(position == HistoryMessagePosition.notShowLatest){
+ if (position == HistoryMessagePosition.notShowLatest) {
return;
}
if (position == HistoryMessagePosition.bottom &&
unreadCountForConversation == 0) {
- markMessageAsRead(
- convID: convID,
- convType: currentSelectedConvType!,
- );
- _receivedNewMessageCount = 0;
+ _unreadCountForConversation = 0;
+ if (chatConfig.isAutoReportRead) {
+ Future.delayed(const Duration(seconds: 1), () {
+ markMessageAsRead(
+ convID: convID,
+ convType: convType,
+ );
+ });
+ }
+ _recivedNewMessageCount = 0;
final currentMsg = _messageListMap[convID] ?? [];
_messageListMap[convID] = [newMsg, ...currentMsg];
notifyListeners();
@@ -619,14 +621,17 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
if (needReadReceipt &&
messageID != null &&
msgComing.groupID != null &&
- chatConfig.isReportGroupReadingStatus) {
+ chatConfig.isReportGroupReadingStatus &&
+ chatConfig.isAutoReportRead) {
// only group message send message read receipt
- sendMessageReadReceipts([messageID]);
+ Future.delayed(const Duration(seconds: 1), () {
+ sendMessageReadReceipts([messageID]);
+ });
}
} else {
if (convID == currentSelectedConv) {
unreadCountForConversation++;
- _receivedNewMessageCount++;
+ _recivedNewMessageCount++;
final currentMsg = _messageListMap[convID] ?? [];
_messageListMap[convID] = [newMsg, ...currentMsg];
notifyListeners();
@@ -667,7 +672,9 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
onMessageModified(V2TimMessage modifiedMessage, [String? convID]) async {
modifiedMessage.id = DateTime.now().millisecondsSinceEpoch.toString();
- final activeMessageList = _messageListMap[convID ?? currentSelectedConv];
+ final String? exactId = TencentUtils.checkString(modifiedMessage.userID) ??
+ TencentUtils.checkString(modifiedMessage.groupID);
+ final activeMessageList = _messageListMap[convID ?? exactId];
if (activeMessageList == null || activeMessageList.isEmpty) {
return;
}
@@ -675,7 +682,7 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
await _lifeCycle?.modifiedMessageWillMount(modifiedMessage) ??
modifiedMessage;
final msgID = newMsg.msgID;
- _messageListMap[currentSelectedConv] = activeMessageList.map((item) {
+ _messageListMap[convID ?? exactId ?? ""] = activeMessageList.map((item) {
if (item.msgID == msgID) {
return newMsg;
}
@@ -690,7 +697,7 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
final isNotEmpty = _messageListMap[convID]?.isNotEmpty;
if (isNotEmpty != null && isNotEmpty) {
_messageListMap[convID] = _messageListMap[convID]!.map((element) {
- final isSelf = element.isSelf ?? false;
+ final isSelf = element.isSelf ?? true;
final isPeerRead = element.isPeerRead ?? false;
if (isSelf && !isPeerRead) {
element.isPeerRead = true;
@@ -711,9 +718,8 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
}
}
notifyListeners();
- } catch (e) {
- print(e);
- }
+ // ignore: empty_catches
+ } catch (e) {}
}
_onSendMessageProgress(V2TimMessage messagae, int progress) {
@@ -721,11 +727,23 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
}
onMessageDownloadProgressCallback(
- V2TimMessageDownloadProgress messageProgress) {
+ V2TimMessageDownloadProgress messageProgress) async {
if (messageProgress.isFinish) {
setMessageProgress(messageProgress.msgID, 100);
setFileMessageLocation(messageProgress.msgID, messageProgress.path);
downloadFile();
+ if (messageProgress.type == 0) {
+ final messages = await _messageService
+ .findMessages(messageIDList: [messageProgress.msgID]);
+ final V2TimMessage? message = messages?.first;
+ if (message != null) {
+ updateAsyncMessage(
+ message,
+ TencentUtils.checkString(message.userID) ??
+ TencentUtils.checkString(message.groupID) ??
+ "");
+ }
+ }
return;
}
if (messageProgress.isError) {
@@ -761,11 +779,9 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
required String convID,
required ConvType convType,
}) async {
- _unreadCountForConversation = 0;
if (convType == ConvType.c2c) {
return _messageService.markC2CMessageAsRead(userID: convID);
}
-
_messageService.markGroupMessageAsRead(groupID: convID);
}
@@ -888,7 +904,7 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
{bool needResetNewMessageCount = true}) {
_messageListMap[conversationID] = messageList;
if (needResetNewMessageCount) {
- _receivedNewMessageCount = 0;
+ _recivedNewMessageCount = 0;
}
notifyListeners();
}
@@ -926,6 +942,7 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
groupType != null ? GroupReceptAllowType.values[groupType.index] : null;
if (chatConfig.isShowGroupReadingStatus &&
convType == ConvType.group &&
+ sendMsgRes.data?.msgID != null &&
((chatConfig.groupReadReceiptPermissionList != null &&
chatConfig.groupReadReceiptPermissionList!
.contains(groupType)) ||
@@ -967,6 +984,7 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
if (listWithTimestamp.isEmpty ||
(listWithTimestamp[listWithTimestamp.length - 1].timestamp !=
null &&
+ item.timestamp != null &&
(item.timestamp! -
listWithTimestamp[listWithTimestamp.length - 1]
.timestamp! >
diff --git a/lib/business_logic/view_models/tui_conversation_view_model.dart b/lib/business_logic/view_models/tui_conversation_view_model.dart
index 7a7ef52..6d8985f 100644
--- a/lib/business_logic/view_models/tui_conversation_view_model.dart
+++ b/lib/business_logic/view_models/tui_conversation_view_model.dart
@@ -1,6 +1,7 @@
// ignore_for_file: unnecessary_getters_setters
import 'package:flutter/material.dart';
+import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/conversation_life_cycle.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
@@ -29,6 +30,8 @@ List removeDuplicates(
}
class TUIConversationViewModel extends ChangeNotifier {
+ final TUISelfInfoViewModel selfInfoViewModel =
+ serviceLocator();
final ConversationService _conversationService =
serviceLocator();
final FriendshipServices _friendshipServices =
@@ -39,23 +42,16 @@ class TUIConversationViewModel extends ChangeNotifier {
late V2TimConversationListener _conversationListener;
List _conversationList = [];
static V2TimConversation? _selectedConversation;
+
bool _haveMoreData = true;
int _totalUnReadCount = 0;
String? _scrollToConversation;
+ final TUIChatGlobalModel globalChatModel =
+ serviceLocator();
+
String _nextSeq = "0";
ConversationLifeCycle? _lifeCycle;
- String? get scrollToConversation => _scrollToConversation;
-
- set scrollToConversation(String? value) {
- _scrollToConversation = value;
- notifyListeners();
- }
-
- void clearScrollToConversation(){
- _scrollToConversation = null;
- }
-
List get conversationList {
if (PlatformUtils().isWeb) {
try {
@@ -69,9 +65,8 @@ class TUIConversationViewModel extends ChangeNotifier {
.toList();
_conversationList.removeWhere((element) => element?.isPinned == true);
_conversationList = [...pinnedConversation, ..._conversationList];
+ // ignore: empty_catches
} catch (e) {
- // ignore: avoid_print
- print(e);
}
} else {
_conversationList.sort((a, b) => b!.orderkey!.compareTo(a!.orderkey!));
@@ -79,6 +74,17 @@ class TUIConversationViewModel extends ChangeNotifier {
return _conversationList;
}
+ String? get scrollToConversation => _scrollToConversation;
+
+ set scrollToConversation(String? value) {
+ _scrollToConversation = value;
+ notifyListeners();
+ }
+
+ void clearScrollToConversation() {
+ _scrollToConversation = null;
+ }
+
bool get haveMoreData {
return _haveMoreData;
}
@@ -98,6 +104,11 @@ class TUIConversationViewModel extends ChangeNotifier {
notifyListeners();
}
+ set selectedConversation(V2TimConversation? value) {
+ _selectedConversation = value;
+ notifyListeners();
+ }
+
V2TimConversation? get selectedConversation {
return _selectedConversation;
}
@@ -125,7 +136,9 @@ class TUIConversationViewModel extends ChangeNotifier {
loadInitConversation() async {
await loadData(count: 40);
- _chatGlobalModel.initMessageMapFromLocalDatabase(_conversationList);
+ if (selfInfoViewModel.globalConfig?.isPreloadMessagesAfterInit ?? true) {
+ _chatGlobalModel.initMessageMapFromLocalDatabase(_conversationList);
+ }
}
initConversation() async {
@@ -165,6 +178,7 @@ class TUIConversationViewModel extends ChangeNotifier {
void setSelectedConversation(V2TimConversation conversation) {
_selectedConversation = conversation;
+ notifyListeners();
}
Future pinConversation({
@@ -182,6 +196,9 @@ class TUIConversationViewModel extends ChangeNotifier {
false) {
return null;
}
+
+ globalChatModel.setMessageList(convID, []);
+
if (convType == 1) {
return _messageService.clearC2CHistoryMessage(userID: convID);
} else {
diff --git a/lib/business_logic/view_models/tui_search_view_model.dart b/lib/business_logic/view_models/tui_search_view_model.dart
index 2d90d1a..2bc479d 100644
--- a/lib/business_logic/view_models/tui_search_view_model.dart
+++ b/lib/business_logic/view_models/tui_search_view_model.dart
@@ -1,5 +1,6 @@
// ignore_for_file: constant_identifier_names
+import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_services.dart';
@@ -91,10 +92,13 @@ class TUISearchViewModel extends ChangeNotifier {
type: KeywordListMatchType.V2TIM_KEYWORD_LIST_MATCH_TYPE_OR.index,
));
if (searchResult.code == 0 && searchResult.data != null) {
- totalMsgInConversationCount = searchResult.data!.totalCount!;
+ final messageSearchResultItems = searchResult
+ .data!.messageSearchResultItems!
+ .firstWhereOrNull((element) => element.conversationID == conversationId);
+ totalMsgInConversationCount = messageSearchResultItems?.messageCount ?? 0;
currentMsgListForConversation = [
...currentMsgListForConversation,
- ...(searchResult.data!.messageSearchResultItems?[0].messageList ?? [])
+ ...(messageSearchResultItems?.messageList ?? [])
];
}
notifyListeners();
diff --git a/lib/data_services/core/core_services_implements.dart b/lib/data_services/core/core_services_implements.dart
index e4f8504..14fd3af 100644
--- a/lib/data_services/core/core_services_implements.dart
+++ b/lib/data_services/core/core_services_implements.dart
@@ -1,7 +1,6 @@
// ignore_for_file: avoid_print
import 'package:flutter/cupertino.dart';
-import 'package:flutter/foundation.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_setting_model.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/listener_model/tui_group_listener_model.dart';
@@ -157,7 +156,7 @@ class CoreServicesImpl with CoreServices {
onCallback = onTUIKitCallbackListener;
}
setGlobalConfig(config);
- if (PlatformUtils().isIOS || PlatformUtils().isAndroid) {
+ if (!PlatformUtils().isWeb) {
didLoginSuccess();
}
}
@@ -251,8 +250,8 @@ class CoreServicesImpl with CoreServices {
}
tuiFriendShipViewModel.userStatusList = currentUserStatusList;
+ // ignore: empty_catches
} catch (e) {
- print(e);
}
}
@@ -265,7 +264,7 @@ class CoreServicesImpl with CoreServices {
_userSig = userSig;
V2TimCallback result = await TencentImSDKPlugin.v2TIMManager
.login(userID: userID, userSig: userSig);
- if (PlatformUtils().isIOS || PlatformUtils().isAndroid) {
+ if (!PlatformUtils().isWeb) {
didLoginSuccess();
}
if (result.code != 0) {
@@ -294,7 +293,7 @@ class CoreServicesImpl with CoreServices {
}
});
- if (!kIsWeb &&
+ if (PlatformUtils().isMobile &&
selfInfoViewModel.globalConfig?.isCheckDiskStorageSpace == true) {
final diskSpace = await DiskSpace.getFreeDiskSpace;
if (diskSpace != null && diskSpace < 1024) {
diff --git a/lib/data_services/core/tim_uikit_config.dart b/lib/data_services/core/tim_uikit_config.dart
index d347edf..cc8f38d 100644
--- a/lib/data_services/core/tim_uikit_config.dart
+++ b/lib/data_services/core/tim_uikit_config.dart
@@ -1,4 +1,6 @@
import 'package:flutter/cupertino.dart';
+import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_wide_modal_operation_key.dart';
+import 'package:tencent_im_base/theme/tui_theme.dart';
class TIMUIKitConfig {
/// Control if show online status of friends or contacts.
@@ -18,8 +20,34 @@ class TIMUIKitConfig {
/// The configuration of border radius for all the avatar shows in TUIKit.
final BorderRadius? defaultAvatarBorderRadius;
- const TIMUIKitConfig({
+ /// You can use this function to customize the Modal that shows on desktop.
+ /// Do not specified or return `false` will use our default implementation.
+ final Future Function(
+ TUIKitWideModalOperationKey operationKey,
+ BuildContext context,
+ Widget Function(VoidCallback closeFunc) child,
+ TUITheme? theme,
+ double? width,
+ double? height,
+ Offset? offset,
+ String? initText,
+ BorderRadius? borderRadius,
+ bool? isDarkBackground,
+ String? title,
+ VoidCallback? onSubmit,
+ Widget? submitWidget,
+ VoidCallback? onConfirm,
+ VoidCallback? onCancel,
+ )? showDesktopModalFunc;
+
+ /// Determines whether TUIKit should preload some messages after initialization for faster message display,
+ /// with a default value of `true`, and backward-compatibility.
+ final bool isPreloadMessagesAfterInit;
+
+ const TIMUIKitConfig( {
this.defaultAvatarAssetPath,
+ this.showDesktopModalFunc,
+ this.isPreloadMessagesAfterInit = true,
this.defaultAvatarBorderRadius,
this.isCheckDiskStorageSpace = true,
this.isShowOnlineStatus = true,
diff --git a/lib/data_services/core/tim_uikit_wide_modal_operation_key.dart b/lib/data_services/core/tim_uikit_wide_modal_operation_key.dart
new file mode 100644
index 0000000..98627a4
--- /dev/null
+++ b/lib/data_services/core/tim_uikit_wide_modal_operation_key.dart
@@ -0,0 +1,43 @@
+
+enum TUIKitWideModalOperationKey{
+
+ /// You could use this value for your own Modal usage.
+ custom,
+
+ // The following values are used in TUIKit
+
+ conversationSecondaryMenu,
+ chooseCountry,
+ beforeSendScreenShot,
+ showUserProfileFromChat,
+ addNewContact,
+ showBlockedUsers,
+ chooseContacts,
+ addFriend,
+ addGroup,
+ chooseGroupType,
+ settings,
+ contactUs,
+ aboutUs,
+ showConditionsAndTerms,
+ secondaryClickUserAvatar,
+ forward,
+ messageReadDetails,
+ mergerMessageList,
+ chooseMentionedMembers,
+ chatHistory,
+ groupAddOpt,
+ setMute,
+ setUnmute,
+ setAdmins,
+ deleteAdmin,
+ groupMembersList,
+ addGroupMembers,
+ kickOffGroupMembers,
+ confirmDeleteMessages,
+ confirmClearChatHistory,
+ confirmExitGroup,
+ confirmDisbandGroup,
+ confirmGeneral,
+ unableToSendDueToFolders
+}
\ No newline at end of file
diff --git a/lib/data_services/group/group_services_implement.dart b/lib/data_services/group/group_services_implement.dart
index 986d58e..89c3b2d 100644
--- a/lib/data_services/group/group_services_implement.dart
+++ b/lib/data_services/group/group_services_implement.dart
@@ -105,6 +105,7 @@ class GroupServicesImpl extends GroupServices {
count: count,
offset: offset);
if (res.code != 0) {
+
_coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
diff --git a/lib/data_services/message/message_service_implement.dart b/lib/data_services/message/message_service_implement.dart
index a1ae652..405567a 100644
--- a/lib/data_services/message/message_service_implement.dart
+++ b/lib/data_services/message/message_service_implement.dart
@@ -127,14 +127,14 @@ class MessageServiceImpl extends MessageService {
lastMsgID: lastMsgID,
lastMsgSeq: lastMsgSeq,
messageTypeList: messageTypeList);
- final reponseMessageList = res.data;
+ final responseMessageList = res.data;
if (res.code != 0) {
_coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
}
- return reponseMessageList;
+ return responseMessageList;
}
@override
@@ -812,4 +812,5 @@ class MessageServiceImpl extends MessageService {
}
return result.data?[text] ?? "";
}
+
}
diff --git a/lib/i18n/strings.i18n.json b/lib/i18n/strings.i18n.json
index 689dba7..b441b8d 100644
--- a/lib/i18n/strings.i18n.json
+++ b/lib/i18n/strings.i18n.json
@@ -1 +1 @@
-{"k_1fdhj9g":"This version does not support the message","k_06pujtm":"Accept all friend requests","k_0gyhkp5":"Require approval for friend requests","k_121ruco":"Reject all friend requests","k_05nspni":"Custom field","k_03fchyy":"Group profile photo","k_03i9mfe":"Group introduction","k_03agq58":"Group name","k_039xqny":"Group notification","k_003tr0a":"Group owner","k_002wddw":"Mute","k_0got6f7":"Unmute","k_1uaqed6":"[Custom]","k_0z2z7rx":"[Voice]","k_0y39ngu":"[Emoji]","k_0y1a2my":"[Image]","k_0z4fib8":"[Video]","k_0y24mcg":"[Location]","k_0pewpd1":"[Chat history]","k_13s8d9p":"Unknown message","k_003qkx2":"Calendar","k_003n2pz":"Camera","k_03idjo0":"Contact","k_003ltgm":"Location","k_02k3k86":"Mic","k_003pm7l":"Album","k_15ao57x":"Album write","k_164m3jd":"Local storage","k_03r6qyx":"We need your approval to get information.","k_02noktt":"Reject","k_00043x4":"Agree","k_003qzac":"Yesterday","k_003r39d":"2 days ago","k_03fqp9o":"Sun","k_03ibg5h":"Mon","k_03i7hu1":"Tue","k_03iaiks":"Wed","k_03el9pa":"Thu","k_03i7ok1":"Fri","k_03efxyg":"Sat","k_003q7ba":"Afternoon","k_003q7bb":"Morning","k_003pu3h":"Now","k_002rflt":"Delete","k_1don84v":"Failed to locate the original message","k_003q5fi":"Copy","k_003prq0":"Forward","k_002r1h2":"Multiple-choice","k_003j708":"Reference","k_003pqpr":"Recall","k_03ezhho":"Copied","k_11ctfsz":"Not implemented","k_1hbjg5g":"[Group system message]","k_03tvswb":"[Unknown message]","k_155cj23":"You've recalled a message.","k_0gapun3":"Edit it again","k_0003z7x":"You","k_002wfe4":"Read","k_002wjlg":"Unread","k_003nevv":"Cancel","k_001nmhu":"Open with another app","k_105682d":"Failed to load the image","k_0pytyeu":"Image saved successfully","k_0akceel":"Failed to save the image","k_003rk1s":"Save","k_04a0awq":"[Voice message]","k_105c3y3":"Failed to load the video","k_176rzr7":"Chat history","k_002r305":"Send","k_003n8b0":"Shoot","k_003tnp0":"File","k_0ylosxn":"Custom message","k_0jhdhtp":"Sending failed. The video cannot exceed 100 MB.","k_0am7r68":"Slide up to cancel","k_13dsw4l":"Release to cancel","k_15jl6qw":"Too short","k_0gx7vl6":"Press and hold to talk","k_15dlafd":"One-by-one forward","k_15dryxy":"Combine and forward","k_1eyhieh":"Are you sure you want to delete the selected message?","k_118prbn":"Search globally","k_003kv3v":"Search","k_17fmlyf":"Clear chat","k_0dhesoz":"Unpin from top","k_002sk7x":"Pin to top","k_003ll77":"Draft","k_003kfai":"Unknown","k_13dq4an":"Automatic approval","k_0l13cde":"Admin approval","k_11y8c6a":"Disallow group joining","k_1kvyskd":"Modification failed due to network disconnection","k_16payqf":"Group joining mode","k_0vzvn8r":"Modify group name","k_003rzap":"OK","k_038lh6u":"Group management","k_0k5wyiy":"Set admin","k_0goiuwk":"Mute all","k_1g889xx":"If you mute all, only the group owner and admin can speak.","k_0wlrefq":"Add group members to mute","k_0goox5g":"Mute","k_08daijh":"Admin role canceled successfully","k_0k5u935":"Add admin","k_003ngex":"Complete","k_03enyx5":"Group member","k_03erpei":"Admin","k_0qi9tno":"Group owner and admin","k_0uj7208":"Failed to view the group members due to network disconnection","k_0ef2a12":"Modify my nickname in group","k_1aajych":"2–20 characters, including digits, letters, and underscores","k_137pab5":"My nickname in group","k_0ivim6d":"No group notice","k_03eq6cn":"Group notice","k_002vxya":"Modify","k_03gu05e":"Chat room","k_03b4f3p":"Meeting group","k_03avj1p":"Public group","k_03asq2g":"Work group","k_03b3hbi":"Unknown group","k_03es1ox":"Group type","k_003mz1i":"Agree","k_003lpre":"Reject","k_003qk66":"Profile photo","k_003lhvk":"Nickname","k_003ps50":"Account","k_15lx52z":"Status","k_003qgkp":"Gender","k_003m6hr":"Date of birth","k_0003v6a":"Male","k_00043x2":"Female","k_03bcjkv":"Not set","k_11s0gdz":"Modify nickname","k_0p3j4sd":"Allows only letters, digits, and underscores","k_15lyvdt":"Modify status","k_0vylzjp":"None","k_1hs7ese":"Modify it later","k_03exjk7":"Remarks","k_0s3skfd":"Add to blocklist","k_17fpl3y":"Pin chat to top","k_0p3b31s":"Modify remarks","k_0003y9x":"None","k_11zgnfs":"Profile","k_1tez2xl":"No status","k_0vjj2kp":"Group chat history","k_003n2rp":"Select","k_1m9exwh":"Recent contacts","k_119nwqr":"The input cannot be empty","k_0pzwbmg":"Video saved successfully","k_0aktupv":"Failed to save the video","k_1yemzyd":"Received a message","k_13sajrj":"Emoji message","k_13sjeb7":"File message","k_0yd2ft8":"Group notification","k_13s7mxn":"Image message","k_13satlt":"Location message","k_00bbtsx":"Combined message","k_13sqwu4":"Voice message","k_13sqjjp":"Video message","k_03iqsh4":" $s to ","k_191t5n4":"$opUserNickName changed ","k_1pg6aoj":"$opUserNickName quit group chat","k_1f6zt3v":"Invite $invitedMemberString to the group","k_0y7zd07":"Remove $invitedMemberString from the group","k_1d5mshh":"User $joinedMemberString joined the group","k_0yenqf0":"$userName was","k_0spotql":"Set $adminMember as admin","k_0pg5zzj":"System message: $operationType","k_1c7z88n":"[File] $fileName","k_1c3us5n":"The current group does not support @all","k_11k579v":"Invalid statements detected","k_0qba4ns":" attempted to access your $yoursItem","k_0oozw9x":"$diffMinutes minutes ago","k_13hzn00":"$yesterday, yesterday","k_0n9pyxz":"The user does not exist","k_1bjwemh":"Search by user ID","k_02owlq8":"My user ID: $userID","k_1wu8h4x":"Me: $showName","k_16758qw":"Add friend","k_1shx4d9":"Status: $selfSignature","k_0i553x0":"Enter verification information","k_031ocwx":"Enter remarks and list","k_003ojje":"Remarks","k_003lsav":"List","k_167bdvq":"My friends","k_156b4ut":"Friend request sent","k_1loix7s":"Group type: $groupType","k_1lqbsib":"The group chat does not exist","k_03h153m":"Search by group ID","k_0oxak3r":"Group request sent","k_1uh417q":"$displayName recalled a message","k_1aszp2k":"Are you sure you want to send the message again?","k_0h1ygf8":"Call initiated","k_0h169j0":"Call canceled","k_0h13jjk":"Call accepted","k_0h19hfx":"Call rejected","k_0obi9lh":"No answer","k_0ohzb9l":"Call duration: $callTime","k_0y9u662":"$appName currently does not support this file type. You can use another app to open and preview the file.","k_1ht1b80":"Receiving","k_0d5z4m5":"Select reminder receiver","k_1665ltg":"Initiate call","k_003kthh":"Photo","k_119ucng":"The image cannot be empty","k_0w9x8gw":"Selected successfully: $successPath","k_1np495n":"$messageString[Someone@me]","k_1m797yi":"$messageString[@all]","k_1uaov41":"Search for chat content","k_0bxm97s":"Admin ($adminNum/10)","k_0jayw3z":"Group members ($groupMemberNum members)","k_0h1svv1":"Delete group member","k_0h1g636":"Add group member","k_01yfa4o":"$memberCount members","k_0hpukyx":"View more group members","k_0qtsar0":"Mute notifications","k_03xd79d":"Status: $signature","k_1m9dftc":"All contacts","k_0em4gyz":"All group chats","k_002twmj":"Group chat","k_09kga0d":"More chat history","k_1ui5lzi":"$count messages are found","k_09khmso":"Related chat records","k_1kevf4k":"Chat history with $receiver","k_03ignw6":"All","k_03icaxo":"Custom","k_1969986":"[Voice Call]:$callingLastMsgShow","k_1960dlr":"[Video Call]:$callingLastMsgShow","k_1qbg9xc":"$option8 to ","k_1wq5ubm":"$option7 changed ","k_0y5pu80":"$option6 quit group chat","k_0nl7cmd":"Invite $option5 to the group","k_1ju5iqw":"Remove $option4 from the group","k_1ovt677":"User $option3 joined the group","k_0k05b8b":"$option2 was ","k_0wm4xeb":"System message: $option2","k_0nbq9v3":"Call duration: $option2","k_0i1kf53":"[File] $option2","k_1gnnby6":" attempted to access your $option2","k_1wh4atg":"$option2 minutes ago","k_07sh7g1":"$option2, yesterday","k_1pj8xzh":"My user ID: $option2","k_0py1evo":"Status: $option2","k_1kvj4i2":"$option2 recalled a message","k_1v0lbpp":"$option2 currently does not support this file type. You can use another app to open and preview the file.","k_0torwfz":"Selected successfully: $option2","k_0i1bjah":"$option1 recalled a message","k_1qzxh9q":"Call duration: $option3","k_0wrgmom":"[Voice Call]:$option1","k_06ix2f0":"[Video Call]:$option2","k_08o3z5w":"[File] $option1","k_0ezbepg":"$option2[Someone@me]","k_1ccnht1":"$option2[@all]","k_1k3arsw":"Admin ($option2/10)","k_1d4golg":"Group members ($option1 members)","k_1bg69nt":"$option1 members","k_00gjqxj":"Status: $option1","k_0c29cxr":"$option1 messages are found","k_1twk5rz":"Chat history with $option1","k_18o68ro":"Allow ","k_1onpf8u":" to access your camera to take photos, record videos, and make video calls.","k_17irga5":" to access your microphone to send voice messages, record videos, and make voice/video calls.","k_0572kc4":" to access your photos to send images and videos.","k_0slykws":" to access your album to save images and videos.","k_119pkcd":" to access your files to view, select and send files in a chat.","k_03c49qt":"Authorize now","k_0nt2uyg":"Back to the bottom","k_04l16at":"$option1 new messages","k_13p3w93":"Someone @ me","k_18w5uk6":"@ all","k_0jmujgh":"You are receiving other files","k_12s5ept":"Message details","k_0mxa4f4":"$option1 read","k_061tue3":"$option2 unread","k_1vn4xq1":"remove $adminMember from admin","k_0e35hsw":"Please allow us to use your camera to capture photos and videos sending to your friends and make video calls.","k_0dj6yr7":"Please allow us to use your microphone for sending voice message, make video/audio calls.","k_003qnsl":"Save","k_0s3rtpw":"Please allow us to access the media and files on your devices, in order to select and send to your friend, or save from them.","k_0tezv85":" Would like to access $option2","k_002rety":" permission. ","k_0gqewd3":"Later","k_03eq4s1":"Authorize Now","k_18qjstb":"Transfer Group","k_0on1aj2":"$option2 messages @ me","k_09j4izl":"[Someone @ me] ","k_1oqtjw0":"[@ all] ","k_1x5a9vb":"This is: $option1","k_14n31e7":"Add Group","k_08nc5j1":"Group type: $option1","k_1josu12":"$option1 group joining request(s)","k_0n2x5s0":"Verification message: $option2","k_03c1nx0":"Agreed","k_03aw9w8":"Rejected","k_038ryos":"Handle now","k_0gw8pum":"Add Group","k_1gcvfrj":"Please fill in the remarks","k_002v9zj":"确认","k_10oqrki":"轻触拍照","k_0f8b3ws":"加载失败","k_11cm5lm":"手动聚焦","k_002uzrd":"预览","k_003qkn3":"录像","k_003k6a7":"拍照","k_0bqpqco":"拍照按钮","k_1626ozl":"停止录像","k_003lvmu":"前置","k_003lued":"后置","k_003lwzh":"外置","k_002qzi3":"关闭","k_003pufb":"自动","k_0apm0ze":"拍照时闪光","k_157zog5":"始终闪光","k_0cfyqhy":"$option1 画面预览","k_0phctlz":"闪光模式: $option2","k_02vfqe0":"切换至 $option3 摄像头","k_0f0y9ex":"说话时间太短","k_0ln70tk":"无法打开URL","k_11a3jdv":"轻触拍照,长按摄像","k_1k18miv":"请传入离开群组生命周期函数,提供返回首页或其他页面的导航方法。","k_1fu9ahv":"全员禁言状态","k_0gmwbnd":"全员禁言中","k_0got2zr":"您被禁言","k_0y9jck8":"你必须自定义search bar,并处理点击跳转","k_0yum3tv":"如使用自定义区域,请在profileWidgetBuilder传入对应组件","k_09kalj0":"清空聊天记录","k_14j5iul":"删除并退出","k_125ru1w":"解散该群","k_0jtutmw":"退出后不会接收到此群聊消息","k_0jtzmqa":"解散后不会接收到此群聊消息","k_0r8fi93":"好友添加成功","k_02qw14e":"好友申请已发出","k_0n3md5x":"当前用户在黑名单","k_094phq4":"好友添加失败","k_129scag":"好友删除成功","k_129uzfn":"好友删除失败","k_1666isy":"清除好友","k_1679vrd":"加为好友","k_1ualc52":"看看对方带来的数据是啥","k_0szluvp":"设置对方在线状态","k_0f4rnf8":"该用户已是好友","k_1tdkom4":"您已是群成员","k_1p2lyuz":"对方正在输入中...","k_1g8wfpy":"...共$option1人","k_12rv9vw":"回应详情","k_0havgi0":"[查看详情 >>](${linkMessage.link})","k_0n9p7g8":"群组不存在","k_1tdh5vn":"您不是群成员","k_0h1q57v":"暂无群成员","k_0y5drq1":"[查看详情 >>]($option1)","k_03pjp61":"[表情消息]","k_1jpvzul":"[自定义消息]","k_03u3bh1":"[文件消息]","k_1odsnsw":"[群消息]","k_03sel4t":"[图片消息]","k_03sfw3r":"[位置消息]","k_03xpuwq":"[合并消息]","k_07ycxwo":"[没有元素]","k_03rc9vz":"[文本消息]","k_046uopf":"[视频消息]","k_0ehmsun":"设备存储空间不足,建议清理,以获得更好使用体验","k_003kmos":"图片","k_002s86q":"视频","k_06bk5ei":"视频消息仅限 mp4 格式","k_13opfxf":"Web网页端不支持搜索","k_1i0o0y2":"暂时仅限 Android/iOS 端","k_045dtzl":"$option1的聊天记录","k_0t0131u":"群资料信息","k_18ok8xz":"消息接收方式","k_03ax3ks":"群资料","k_0sqvoqo":"将 $option1 设置为管理员","k_1gbg1v8":"将 $option1 取消管理员","k_17k64g4":"群聊创建成功!","k_05mn217":"暂未安装表情包插件,如需使用表情相关功能,请根据本文档安装:https://cloud.tencent.com/document/product/269/70746","k_14j17nz":"暂无表情包","k_0fvjexh":"正在下载中","k_1cdagzz":"已加入待下载队列,其他文件下载中","k_0g4vojc":"开始下载","k_1g32es3":"[调皮]@2x.png","k_1g8qorz":"[爱你]@2x.png","k_1g4hmx6":"[爱情]@2x.png","k_1g6b558":"[爱心]@2x.png","k_1g3m4su":"[傲慢]@2x.png","k_1g2jym7":"[白眼]@2x.png","k_0cgkxuw":"[棒棒糖]@2x.png","k_1g48br2":"[抱抱]@2x.png","k_1g49ol8":"[抱拳]@2x.png","k_1g0ras3":"[爆筋]@2x.png","k_1ghy881":"[鄙视]@2x.png","k_1g86bmv":"[闭嘴]@2x.png","k_1g1xs1p":"[鞭炮]@2x.png","k_1g8i6ri":"[便便]@2x.png","k_1g2u5kf":"[擦汗]@2x.png","k_1g60uwh":"[彩带]@2x.png","k_1g1o0d0":"[彩球]@2x.png","k_1g6a6yq":"[菜刀]@2x.png","k_1g6vqo2":"[差劲]@2x.png","k_1g0kvjc":"[钞票]@2x.png","k_1g65x7e":"[车厢]@2x.png","k_0e1tjol":"[打哈欠]@2x.png","k_1g65n58":"[大兵]@2x.png","k_1g7se7o":"[大哭]@2x.png","k_1g03868":"[蛋糕]@2x.png","k_1h8nm66":"[刀]@2x.png","k_1g3dlpi":"[得意]@2x.png","k_1g3u434":"[灯泡]@2x.png","k_1giuqs7":"[凋谢]@2x.png","k_1g8r0r9":"[多云]@2x.png","k_1g7k6i1":"[发呆]@2x.png","k_1g44zsp":"[发抖]@2x.png","k_1g5l96i":"[飞机]@2x.png","k_1g7wsqj":"[飞吻]@2x.png","k_1g49luq":"[奋斗]@2x.png","k_1gixbsm":"[风车]@2x.png","k_1g6cqbq":"[尴尬]@2x.png","k_1g6jbw5":"[勾引]@2x.png","k_1g3lwo1":"[鼓掌]@2x.png","k_1g13nkj":"[害羞]@2x.png","k_1g0mt47":"[憨笑]@2x.png","k_0bxujkf":"[红灯笼]@2x.png","k_0hhaeh8":"[红双喜]@2x.png","k_1g0jnts":"[坏笑]@2x.png","k_1g46g9c":"[挥手]@2x.png","k_1g4vi9g":"[回头]@2x.png","k_1gf7hes":"[饥饿]@2x.png","k_1g6mvsm":"[激动]@2x.png","k_1gku5mf":"[街舞]@2x.png","k_1g4hidg":"[惊恐]@2x.png","k_1gjbrtu":"[惊讶]@2x.png","k_1g6sand":"[咖啡]@2x.png","k_1g4s8rj":"[磕头]@2x.png","k_1g1wn34":"[可爱]@2x.png","k_1g3l0wd":"[可怜]@2x.png","k_1ggaon9":"[抠鼻]@2x.png","k_1ggvcb0":"[骷髅]@2x.png","k_1h8yqjt":"[酷]@2x.png","k_0jac97i":"[快哭了]@2x.png","k_1h8oiby":"[困]@2x.png","k_1g0s5hg":"[蜡烛]@2x.png","k_1g1iuer":"[篮球]@2x.png","k_1g2xjfi":"[冷汗]@2x.png","k_0s5oyqw":"[礼品袋]@2x.png","k_1g1qqvf":"[礼物]@2x.png","k_1g2slew":"[流汗]@2x.png","k_1g3z9xx":"[流泪]@2x.png","k_1g6pabn":"[麻将]@2x.png","k_0pkaxul":"[麦克风]@2x.png","k_1g7m0zj":"[猫咪]@2x.png","k_0ibvtpo":"[么么哒]@2x.png","k_1g1hoh1":"[玫瑰]@2x.png","k_1gfzeow":"[米饭]@2x.png","k_1g5l15p":"[面条]@2x.png","k_1g2hfa6":"[奶瓶]@2x.png","k_1gix9pj":"[难过]@2x.png","k_1giqn6g":"[闹钟]@2x.png","k_1h8kd64":"[怒]@2x.png","k_1g0vui9":"[怄火]@2x.png","k_1g1jsj7":"[皮球]@2x.png","k_1ghdluw":"[啤酒]@2x.png","k_1gl6ec7":"[瓢虫]@2x.png","k_1g7gg5p":"[撇嘴]@2x.png","k_1g8psin":"[乒乓]@2x.png","k_1gjzu3p":"[汽车]@2x.png","k_1h8mr0k":"[强]@2x.png","k_1g45y2n":"[敲打]@2x.png","k_1gkaxsl":"[青蛙]@2x.png","k_0jcfnoo":"[糗大了]@2x.png","k_1g4njy1":"[拳头]@2x.png","k_1h8mqr3":"[弱]@2x.png","k_1h926fg":"[色]@2x.png","k_1g6rtbq":"[沙发]@2x.png","k_1giirh6":"[删除]@2x.png","k_1g14ny9":"[闪电]@2x.png","k_1g6bmsr":"[胜利]@2x.png","k_1g1rytx":"[示爱]@2x.png","k_1g52fbz":"[手枪]@2x.png","k_1h90dam":"[衰]@2x.png","k_1gigiae":"[睡觉]@2x.png","k_1gijchz":"[太阳]@2x.png","k_1g1sgji":"[跳绳]@2x.png","k_1gjwuri":"[跳跳]@2x.png","k_1g0juhk":"[偷笑]@2x.png","k_1h8nzla":"[吐]@2x.png","k_1g6cv0i":"[委屈]@2x.png","k_1g46l5g":"[握手]@2x.png","k_1g2pgkd":"[西瓜]@2x.png","k_1ging9p":"[下雨]@2x.png","k_1h8nzil":"[吓]@2x.png","k_1g7q7wr":"[献吻]@2x.png","k_1gl6uum":"[香蕉]@2x.png","k_1g23fys":"[象棋]@2x.png","k_0j75rdh":"[心碎了]@2x.png","k_1g6ajj2":"[信封]@2x.png","k_1g21prz":"[熊猫]@2x.png","k_1h8octi":"[嘘]@2x.png","k_1h91zox":"[药]@2x.png","k_1ghttfl":"[疑问]@2x.png","k_1ghk7sz":"[阴险]@2x.png","k_0gl37zz":"[右车头]@2x.png","k_0ifkj1p":"[右哼哼]@2x.png","k_0g1yh2e":"[右太极]@2x.png","k_1g9dkfc":"[雨伞]@2x.png","k_1g8jl88":"[月亮]@2x.png","k_1h8lhqj":"[晕]@2x.png","k_1gi9x2q":"[再见]@2x.png","k_1g6dwwv":"[炸弹]@2x.png","k_1fzmkfi":"[折磨]@2x.png","k_1g6jbiw":"[纸巾]@2x.png","k_1ggjnwu":"[咒骂]@2x.png","k_1g4qlq8":"[猪头]@2x.png","k_1g1lqzz":"[抓狂]@2x.png","k_1g80j3u":"[转圈]@2x.png","k_1g0z55s":"[龇牙]@2x.png","k_1g3ju6v":"[钻戒]@2x.png","k_0gl51l6":"[左车头]@2x.png","k_0iflllk":"[左哼哼]@2x.png","k_0g1y3ir":"[左太极]@2x.png","k_026hiq5":"消息列表加载中","k_003tu8k":"爱你","k_003myvp":"傲慢","k_003kddw":"白眼","k_039yfhv":"棒棒糖","k_003nu3p":"抱抱","k_003nijr":"抱拳","k_003mg88":"爆筋","k_002v17e":"鄙视","k_003qhy4":"闭嘴","k_003l5fq":"鞭炮","k_003uacl":"便便","k_003oq1g":"擦汗","k_003qvey":"彩带","k_003jci7":"彩球","k_003pyu1":"菜刀","k_003q97d":"差劲","k_003po5d":"车厢","k_03eadb2":"打哈欠","k_003pnuf":"大兵","k_003kg57":"蛋糕","k_003mxkt":"得意","k_003onu3":"灯泡","k_002uv8s":"凋谢","k_003kqy0":"调皮","k_003tyum":"多云","k_003pv9u":"发呆","k_036o6mu":"发抖t","k_003nogx":"飞机","k_003q7wg":"飞吻","k_003m0jd":"奋斗","k_002ult9":"风车","k_003r8gt":"尴尬","k_003qy4u":"勾引","k_003mnoa":"鼓掌","k_003lmw8":"害羞","k_003mb30":"憨笑","k_03bj41g":"红灯笼","k_03dxw2f":"红双喜","k_003mk57":"坏笑","k_003nmvf":"挥手","k_003r2i7":"回头","k_002s6f3":"饥饿","k_003qd0t":"激动","k_002vgi4":"街舞","k_003nz33":"惊恐","k_002wh4p":"惊讶","k_003ozpu":"咖啡","k_003qvs4":"磕头","k_003l3wb":"可爱","k_003nuwm":"可怜","k_002rw1q":"抠鼻","k_002tujb":"骷髅","k_00030eq":"酷","k_03i8ath":"快哭了","k_000421h":"困","k_003l5i7":"蜡烛","k_003j72g":"篮球","k_003ofwl":"冷汗","k_02mw65v":"礼品袋","k_003ku40":"礼物","k_003ookz":"流汗","k_003on72":"流泪","k_003rjy0":"麻将","k_003q2f8":"猫咪","k_03et393":"么么哒","k_003j7j2":"玫瑰","k_002sr0b":"米饭","k_003nnza":"面条","k_003jef9":"奶瓶","k_002umn0":"难过","k_002rjib":"闹钟","k_0003zcn":"怒","k_003jzwq":"怄火","k_003j4js":"皮球","k_002r5ir":"啤酒","k_002ubu4":"瓢虫","k_003ppo6":"撇嘴","k_003ty3o":"乒乓","k_002vxwe":"汽车","k_00043hb":"强","k_003nmbo":"敲打","k_002tfhq":"青蛙","k_03i7lrn":"糗大了","k_003r03m":"拳头","k_00043h0":"弱","k_000345z":"色","k_003qmp9":"沙发","k_003it8a":"闪电","k_003pxow":"胜利","k_003kw8e":"示爱","k_003n99g":"手枪","k_00035cl":"衰","k_002vl3h":"睡觉","k_002rgqk":"太阳","k_003m9d1":"跳绳","k_002vobp":"跳跳","k_003mkoz":"偷笑","k_00041px":"吐","k_003rjh5":"委屈","k_003j36u":"西瓜","k_002re92":"下雨","k_00041py":"吓","k_003q06o":"献吻","k_002ubjp":"香蕉","k_003o2tr":"象棋","k_03ie6pa":"心碎了","k_003rao5":"信封","k_003l3us":"熊猫","k_000424d":"嘘","k_00033yi":"药","k_002qtyy":"疑问","k_002qe0o":"阴险","k_03gu7us":"右车头","k_03ere8m":"右哼哼","k_003uqk3":"雨伞","k_003tzdv":"月亮","k_0003z00":"晕","k_002vdrd":"再见","k_003ra1w":"炸弹","k_003lcad":"折磨","k_003q7sz":"纸巾","k_002thn9":"咒骂","k_003qx7f":"猪头","k_003l044":"抓狂","k_003qg4h":"转圈","k_003kb97":"龇牙","k_03gu53l":"左车头","k_03erd1f":"左哼哼","k_003nyvl":"爱情","k_003r85z":"爱心","k_003mk8j":"钞票","k_003pwfj":"大哭","k_00042w5":"刀","k_003nmtr":"握手","k_03c529p":"右太极","k_003n4mk":"钻戒","k_03c5488":"左太极","k_1llp7tu":"该用户不存在","k_0tbyqyb":"加载中…","k_0td1p3f":"保存中…","k_1klqdh1":"仅限汉字、英文、数字和下划线","k_03el5lp":"未填写","k_1ui0gai":"搜索指定内容","k_003nvk2":"消息","k_03agld7":"群提示","k_002wkr3":"翻译","k_13g4hxv":"翻译完成","k_1qqgjra":"$option3条未读消息","k_0uubyjr":"以下为未读消息"}
\ No newline at end of file
+{"k_1fdhj9g":"This version does not support the message","k_06pujtm":"Accept all friend requests","k_0gyhkp5":"Require approval for friend requests","k_121ruco":"Reject all friend requests","k_05nspni":"Custom field","k_03fchyy":"Group profile photo","k_03i9mfe":"Group introduction","k_03agq58":"Group name","k_039xqny":"Group notification","k_003tr0a":"Group owner","k_002wddw":"Mute","k_0got6f7":"Unmute","k_1uaqed6":"[Custom]","k_0z2z7rx":"[Voice]","k_0y39ngu":"[Emoji]","k_0y1a2my":"[Image]","k_0z4fib8":"[Video]","k_0y24mcg":"[Location]","k_0pewpd1":"[Chat history]","k_13s8d9p":"Unknown message","k_003qkx2":"Calendar","k_003n2pz":"Camera","k_03idjo0":"Contact","k_003ltgm":"Location","k_02k3k86":"Mic","k_003pm7l":"Album","k_15ao57x":"Album write","k_164m3jd":"Local storage","k_03r6qyx":"We need your approval to get information.","k_02noktt":"Reject","k_00043x4":"Agree","k_003qzac":"Yesterday","k_003r39d":"2 days ago","k_03fqp9o":"Sun","k_03ibg5h":"Mon","k_03i7hu1":"Tue","k_03iaiks":"Wed","k_03el9pa":"Thu","k_03i7ok1":"Fri","k_03efxyg":"Sat","k_003q7ba":"Afternoon","k_003q7bb":"Morning","k_003pu3h":"Now","k_002rflt":"Delete","k_1don84v":"Failed to locate the original message","k_003q5fi":"Copy","k_003prq0":"Forward","k_002r1h2":"Multiple-choice","k_003j708":"Reference","k_003pqpr":"Recall","k_03ezhho":"Copied","k_11ctfsz":"Not implemented","k_1hbjg5g":"[Group system message]","k_03tvswb":"[Unknown message]","k_155cj23":"You've recalled a message.","k_0gapun3":"Edit it again","k_0003z7x":"You","k_002wfe4":"Read","k_002wjlg":"Unread","k_003nevv":"Cancel","k_001nmhu":"Open with another app","k_105682d":"Failed to load the image","k_0pytyeu":"Image saved successfully","k_0akceel":"Failed to save the image","k_003rk1s":"Save","k_04a0awq":"[Voice message]","k_105c3y3":"Failed to load the video","k_176rzr7":"Chat history","k_002r305":"Send","k_003n8b0":"Shoot","k_003tnp0":"File","k_0ylosxn":"Custom message","k_0jhdhtp":"Sending failed. The video cannot exceed 100 MB.","k_0am7r68":"Slide up to cancel","k_13dsw4l":"Release to cancel","k_15jl6qw":"Too short","k_0gx7vl6":"Press and hold to talk","k_15dlafd":"One-by-one forward","k_15dryxy":"Combine and forward","k_1eyhieh":"Are you sure you want to delete the selected message?","k_118prbn":"Search globally","k_003kv3v":"Search","k_17fmlyf":"Clear chat","k_0dhesoz":"Unpin from top","k_002sk7x":"Pin to top","k_003ll77":"Draft","k_003kfai":"Unknown","k_13dq4an":"Automatic approval","k_0l13cde":"Admin approval","k_11y8c6a":"Disallow group joining","k_1kvyskd":"Modification failed due to network disconnection","k_16payqf":"Group joining mode","k_0vzvn8r":"Modify group name","k_003rzap":"OK","k_038lh6u":"Group management","k_0k5wyiy":"Set admin","k_0goiuwk":"Mute all","k_1g889xx":"If you mute all, only the group owner and admin can speak.","k_0wlrefq":"Add group members to mute","k_0goox5g":"Mute","k_08daijh":"Admin role canceled successfully","k_0k5u935":"Add admin","k_003ngex":"Complete","k_03enyx5":"Group member","k_03erpei":"Admin","k_0qi9tno":"Group owner and admin","k_0uj7208":"Failed to view the group members due to network disconnection","k_0ef2a12":"Modify my nickname in group","k_1aajych":"2–20 characters, including digits, letters, and underscores","k_137pab5":"My nickname in group","k_0ivim6d":"No group notice","k_03eq6cn":"Group notice","k_002vxya":"Modify","k_03gu05e":"Chat room","k_03b4f3p":"Meeting group","k_03avj1p":"Public group","k_03asq2g":"Work group","k_03b3hbi":"Unknown group","k_03es1ox":"Group type","k_003mz1i":"Agree","k_003lpre":"Reject","k_003qk66":"Profile photo","k_003lhvk":"Nickname","k_003ps50":"Account","k_15lx52z":"Status","k_003qgkp":"Gender","k_003m6hr":"Date of birth","k_0003v6a":"Male","k_00043x2":"Female","k_03bcjkv":"Not set","k_11s0gdz":"Modify nickname","k_0p3j4sd":"Allows only letters, digits, and underscores","k_15lyvdt":"Modify status","k_0vylzjp":"None","k_1hs7ese":"Modify it later","k_03exjk7":"Remarks","k_0s3skfd":"Add to blocklist","k_17fpl3y":"Pin chat to top","k_0p3b31s":"Modify remarks","k_0003y9x":"None","k_11zgnfs":"Profile","k_1tez2xl":"No status","k_0vjj2kp":"Group chat history","k_003n2rp":"Select","k_1m9exwh":"Recent contacts","k_119nwqr":"The input cannot be empty","k_0pzwbmg":"Video saved successfully","k_0aktupv":"Failed to save the video","k_1yemzyd":"Received a message","k_13sajrj":"Emoji message","k_13sjeb7":"File message","k_0yd2ft8":"Group notification","k_13s7mxn":"Image message","k_13satlt":"Location message","k_00bbtsx":"Combined message","k_13sqwu4":"Voice message","k_13sqjjp":"Video message","k_03iqsh4":" $s to ","k_191t5n4":"$opUserNickName changed ","k_1pg6aoj":"$opUserNickName quit group chat","k_1f6zt3v":"Invite $invitedMemberString to the group","k_0y7zd07":"Remove $invitedMemberString from the group","k_1d5mshh":"User $joinedMemberString joined the group","k_0yenqf0":"$userName was","k_0spotql":"Set $adminMember as admin","k_0pg5zzj":"System message: $operationType","k_1c7z88n":"[File] $fileName","k_1c3us5n":"The current group does not support @all","k_11k579v":"Invalid statements detected","k_0qba4ns":" attempted to access your $yoursItem","k_0oozw9x":"$diffMinutes minutes ago","k_13hzn00":"$yesterday, yesterday","k_0n9pyxz":"The user does not exist","k_1bjwemh":"Search by user ID","k_02owlq8":"My user ID: $userID","k_1wu8h4x":"Me: $showName","k_16758qw":"Add friend","k_1shx4d9":"Status: $selfSignature","k_0i553x0":"Enter verification information","k_031ocwx":"Enter remarks and list","k_003ojje":"Remarks","k_003lsav":"List","k_167bdvq":"My friends","k_156b4ut":"Friend request sent","k_1loix7s":"Group type: $groupType","k_1lqbsib":"The group chat does not exist","k_03h153m":"Search by group ID","k_0oxak3r":"Group request sent","k_1uh417q":"$displayName recalled a message","k_1aszp2k":"Are you sure you want to send the message again?","k_0h1ygf8":"Call initiated","k_0h169j0":"Call canceled","k_0h13jjk":"Call accepted","k_0h19hfx":"Call rejected","k_0obi9lh":"No answer","k_0ohzb9l":"Call duration: $callTime","k_0y9u662":"$appName currently does not support this file type. You can use another app to open and preview the file.","k_1ht1b80":"Receiving","k_0d5z4m5":"Select reminder receiver","k_1665ltg":"Initiate call","k_003kthh":"Photo","k_119ucng":"The image cannot be empty","k_0w9x8gw":"Selected successfully: $successPath","k_1np495n":"$messageString[Someone@me]","k_1m797yi":"$messageString[@all]","k_1uaov41":"Search for chat content","k_0bxm97s":"Admin ($adminNum/10)","k_0jayw3z":"Group members ($groupMemberNum members)","k_0h1svv1":"Delete group member","k_0h1g636":"Add group member","k_01yfa4o":"$memberCount members","k_0hpukyx":"View more group members","k_0qtsar0":"Mute notifications","k_03xd79d":"Status: $signature","k_1m9dftc":"All contacts","k_0em4gyz":"All group chats","k_002twmj":"Group chat","k_09kga0d":"More chat history","k_1ui5lzi":"$count messages are found","k_09khmso":"Related chat records","k_1kevf4k":"Chat history with $receiver","k_03ignw6":"All","k_03icaxo":"Custom","k_1969986":"[Voice Call]:$callingLastMsgShow","k_1960dlr":"[Video Call]:$callingLastMsgShow","k_1qbg9xc":"$option8 to ","k_1wq5ubm":"$option7 changed ","k_0y5pu80":"$option6 quit group chat","k_0nl7cmd":"Invite $option5 to the group","k_1ju5iqw":"Remove $option4 from the group","k_1ovt677":"User $option3 joined the group","k_0k05b8b":"$option2 was ","k_0wm4xeb":"System message: $option2","k_0nbq9v3":"Call duration: $option2","k_0i1kf53":"[File] $option2","k_1gnnby6":" attempted to access your $option2","k_1wh4atg":"$option2 minutes ago","k_07sh7g1":"$option2, yesterday","k_1pj8xzh":"My user ID: $option2","k_0py1evo":"Status: $option2","k_1kvj4i2":"$option2 recalled a message","k_1v0lbpp":"$option2 currently does not support this file type. You can use another app to open and preview the file.","k_0torwfz":"Selected successfully: $option2","k_0i1bjah":"$option1 recalled a message","k_1qzxh9q":"Call duration: $option3","k_0wrgmom":"[Voice Call]:$option1","k_06ix2f0":"[Video Call]:$option2","k_08o3z5w":"[File] $option1","k_0ezbepg":"$option2[Someone@me]","k_1ccnht1":"$option2[@all]","k_1k3arsw":"Admin ($option2/10)","k_1d4golg":"Group members ($option1 members)","k_1bg69nt":"$option1 members","k_00gjqxj":"Status: $option1","k_0c29cxr":"$option1 messages are found","k_1twk5rz":"Chat history with $option1","k_18o68ro":"Allow ","k_1onpf8u":" to access your camera to take photos, record videos, and make video calls.","k_17irga5":" to access your microphone to send voice messages, record videos, and make voice/video calls.","k_0572kc4":" to access your photos to send images and videos.","k_0slykws":" to access your album to save images and videos.","k_119pkcd":" to access your files to view, select and send files in a chat.","k_03c49qt":"Authorize now","k_0nt2uyg":"Back to the bottom","k_04l16at":"$option1 new messages","k_13p3w93":"Someone @ me","k_18w5uk6":"@ all","k_0jmujgh":"You are receiving other files","k_12s5ept":"Message details","k_0mxa4f4":"$option1 read","k_061tue3":"$option2 unread","k_1vn4xq1":"remove $adminMember from admin","k_0e35hsw":"Please allow us to use your camera to capture photos and videos sending to your friends and make video calls.","k_0dj6yr7":"Please allow us to use your microphone for sending voice message, make video/audio calls.","k_003qnsl":"Save","k_0s3rtpw":"Please allow us to access the media and files on your devices, in order to select and send to your friend, or save from them.","k_0tezv85":" Would like to access $option2","k_002rety":" permission. ","k_0gqewd3":"Later","k_03eq4s1":"Authorize Now","k_18qjstb":"Transfer Group","k_0on1aj2":"$option2 messages @ me","k_09j4izl":"[Someone @ me] ","k_1oqtjw0":"[@ all] ","k_1x5a9vb":"This is: $option1","k_14n31e7":"Add Group","k_08nc5j1":"Group type: $option1","k_1josu12":"$option1 group joining request(s)","k_0n2x5s0":"Verification message: $option2","k_03c1nx0":"Agreed","k_03aw9w8":"Rejected","k_038ryos":"Handle now","k_0gw8pum":"Add Group","k_1gcvfrj":"Please fill in the remarks","k_002v9zj":"确认","k_10oqrki":"轻触拍照","k_0f8b3ws":"加载失败","k_11cm5lm":"手动聚焦","k_002uzrd":"预览","k_003qkn3":"录像","k_003k6a7":"拍照","k_0bqpqco":"拍照按钮","k_1626ozl":"停止录像","k_003lvmu":"前置","k_003lued":"后置","k_003lwzh":"外置","k_002qzi3":"关闭","k_003pufb":"自动","k_0apm0ze":"拍照时闪光","k_157zog5":"始终闪光","k_0cfyqhy":"$option1 画面预览","k_0phctlz":"闪光模式: $option2","k_02vfqe0":"切换至 $option3 摄像头","k_0f0y9ex":"说话时间太短","k_0ln70tk":"无法打开URL","k_11a3jdv":"轻触拍照,长按摄像","k_1k18miv":"请传入离开群组生命周期函数,提供返回首页或其他页面的导航方法。","k_1fu9ahv":"全员禁言状态","k_0gmwbnd":"全员禁言中","k_0got2zr":"您被禁言","k_0y9jck8":"你必须自定义search bar,并处理点击跳转","k_0yum3tv":"如使用自定义区域,请在profileWidgetBuilder传入对应组件","k_09kalj0":"清空聊天记录","k_14j5iul":"删除并退出","k_125ru1w":"解散该群","k_0jtutmw":"退出后不会接收到此群聊消息","k_0jtzmqa":"解散后不会接收到此群聊消息","k_0r8fi93":"好友添加成功","k_02qw14e":"好友申请已发出","k_0n3md5x":"当前用户在黑名单","k_094phq4":"好友添加失败","k_129scag":"好友删除成功","k_129uzfn":"好友删除失败","k_1666isy":"清除好友","k_1679vrd":"加为好友","k_1ualc52":"看看对方带来的数据是啥","k_0szluvp":"设置对方在线状态","k_0f4rnf8":"该用户已是好友","k_1tdkom4":"您已是群成员","k_1p2lyuz":"对方正在输入中...","k_1g8wfpy":"...共$option1人","k_12rv9vw":"回应详情","k_0havgi0":"[查看详情 >>](${linkMessage.link})","k_0n9p7g8":"群组不存在","k_1tdh5vn":"您不是群成员","k_0h1q57v":"暂无群成员","k_0y5drq1":"[查看详情 >>]($option1)","k_03pjp61":"[表情消息]","k_1jpvzul":"[自定义消息]","k_03u3bh1":"[文件消息]","k_1odsnsw":"[群消息]","k_03sel4t":"[图片消息]","k_03sfw3r":"[位置消息]","k_03xpuwq":"[合并消息]","k_07ycxwo":"[没有元素]","k_03rc9vz":"[文本消息]","k_046uopf":"[视频消息]","k_0ehmsun":"设备存储空间不足,建议清理,以获得更好使用体验","k_003kmos":"图片","k_002s86q":"视频","k_06bk5ei":"视频消息仅限 mp4 格式","k_13opfxf":"Web网页端不支持搜索","k_1i0o0y2":"暂时仅限 Android/iOS 端","k_045dtzl":"$option1的聊天记录","k_0t0131u":"群资料信息","k_18ok8xz":"消息接收方式","k_03ax3ks":"群资料","k_0sqvoqo":"将 $option1 设置为管理员","k_1gbg1v8":"将 $option1 取消管理员","k_17k64g4":"群聊创建成功!","k_05mn217":"暂未安装表情包插件,如需使用表情相关功能,请根据本文档安装:https://cloud.tencent.com/document/product/269/70746","k_14j17nz":"暂无表情包","k_0fvjexh":"正在下载中","k_1cdagzz":"已加入待下载队列,其他文件下载中","k_0g4vojc":"开始下载","k_1g32es3":"[调皮]@2x.png","k_1g8qorz":"[爱你]@2x.png","k_1g4hmx6":"[爱情]@2x.png","k_1g6b558":"[爱心]@2x.png","k_1g3m4su":"[傲慢]@2x.png","k_1g2jym7":"[白眼]@2x.png","k_0cgkxuw":"[棒棒糖]@2x.png","k_1g48br2":"[抱抱]@2x.png","k_1g49ol8":"[抱拳]@2x.png","k_1g0ras3":"[爆筋]@2x.png","k_1ghy881":"[鄙视]@2x.png","k_1g86bmv":"[闭嘴]@2x.png","k_1g1xs1p":"[鞭炮]@2x.png","k_1g8i6ri":"[便便]@2x.png","k_1g2u5kf":"[擦汗]@2x.png","k_1g60uwh":"[彩带]@2x.png","k_1g1o0d0":"[彩球]@2x.png","k_1g6a6yq":"[菜刀]@2x.png","k_1g6vqo2":"[差劲]@2x.png","k_1g0kvjc":"[钞票]@2x.png","k_1g65x7e":"[车厢]@2x.png","k_0e1tjol":"[打哈欠]@2x.png","k_1g65n58":"[大兵]@2x.png","k_1g7se7o":"[大哭]@2x.png","k_1g03868":"[蛋糕]@2x.png","k_1h8nm66":"[刀]@2x.png","k_1g3dlpi":"[得意]@2x.png","k_1g3u434":"[灯泡]@2x.png","k_1giuqs7":"[凋谢]@2x.png","k_1g8r0r9":"[多云]@2x.png","k_1g7k6i1":"[发呆]@2x.png","k_1g44zsp":"[发抖]@2x.png","k_1g5l96i":"[飞机]@2x.png","k_1g7wsqj":"[飞吻]@2x.png","k_1g49luq":"[奋斗]@2x.png","k_1gixbsm":"[风车]@2x.png","k_1g6cqbq":"[尴尬]@2x.png","k_1g6jbw5":"[勾引]@2x.png","k_1g3lwo1":"[鼓掌]@2x.png","k_1g13nkj":"[害羞]@2x.png","k_1g0mt47":"[憨笑]@2x.png","k_0bxujkf":"[红灯笼]@2x.png","k_0hhaeh8":"[红双喜]@2x.png","k_1g0jnts":"[坏笑]@2x.png","k_1g46g9c":"[挥手]@2x.png","k_1g4vi9g":"[回头]@2x.png","k_1gf7hes":"[饥饿]@2x.png","k_1g6mvsm":"[激动]@2x.png","k_1gku5mf":"[街舞]@2x.png","k_1g4hidg":"[惊恐]@2x.png","k_1gjbrtu":"[惊讶]@2x.png","k_1g6sand":"[咖啡]@2x.png","k_1g4s8rj":"[磕头]@2x.png","k_1g1wn34":"[可爱]@2x.png","k_1g3l0wd":"[可怜]@2x.png","k_1ggaon9":"[抠鼻]@2x.png","k_1ggvcb0":"[骷髅]@2x.png","k_1h8yqjt":"[酷]@2x.png","k_0jac97i":"[快哭了]@2x.png","k_1h8oiby":"[困]@2x.png","k_1g0s5hg":"[蜡烛]@2x.png","k_1g1iuer":"[篮球]@2x.png","k_1g2xjfi":"[冷汗]@2x.png","k_0s5oyqw":"[礼品袋]@2x.png","k_1g1qqvf":"[礼物]@2x.png","k_1g2slew":"[流汗]@2x.png","k_1g3z9xx":"[流泪]@2x.png","k_1g6pabn":"[麻将]@2x.png","k_0pkaxul":"[麦克风]@2x.png","k_1g7m0zj":"[猫咪]@2x.png","k_0ibvtpo":"[么么哒]@2x.png","k_1g1hoh1":"[玫瑰]@2x.png","k_1gfzeow":"[米饭]@2x.png","k_1g5l15p":"[面条]@2x.png","k_1g2hfa6":"[奶瓶]@2x.png","k_1gix9pj":"[难过]@2x.png","k_1giqn6g":"[闹钟]@2x.png","k_1h8kd64":"[怒]@2x.png","k_1g0vui9":"[怄火]@2x.png","k_1g1jsj7":"[皮球]@2x.png","k_1ghdluw":"[啤酒]@2x.png","k_1gl6ec7":"[瓢虫]@2x.png","k_1g7gg5p":"[撇嘴]@2x.png","k_1g8psin":"[乒乓]@2x.png","k_1gjzu3p":"[汽车]@2x.png","k_1h8mr0k":"[强]@2x.png","k_1g45y2n":"[敲打]@2x.png","k_1gkaxsl":"[青蛙]@2x.png","k_0jcfnoo":"[糗大了]@2x.png","k_1g4njy1":"[拳头]@2x.png","k_1h8mqr3":"[弱]@2x.png","k_1h926fg":"[色]@2x.png","k_1g6rtbq":"[沙发]@2x.png","k_1giirh6":"[删除]@2x.png","k_1g14ny9":"[闪电]@2x.png","k_1g6bmsr":"[胜利]@2x.png","k_1g1rytx":"[示爱]@2x.png","k_1g52fbz":"[手枪]@2x.png","k_1h90dam":"[衰]@2x.png","k_1gigiae":"[睡觉]@2x.png","k_1gijchz":"[太阳]@2x.png","k_1g1sgji":"[跳绳]@2x.png","k_1gjwuri":"[跳跳]@2x.png","k_1g0juhk":"[偷笑]@2x.png","k_1h8nzla":"[吐]@2x.png","k_1g6cv0i":"[委屈]@2x.png","k_1g46l5g":"[握手]@2x.png","k_1g2pgkd":"[西瓜]@2x.png","k_1ging9p":"[下雨]@2x.png","k_1h8nzil":"[吓]@2x.png","k_1g7q7wr":"[献吻]@2x.png","k_1gl6uum":"[香蕉]@2x.png","k_1g23fys":"[象棋]@2x.png","k_0j75rdh":"[心碎了]@2x.png","k_1g6ajj2":"[信封]@2x.png","k_1g21prz":"[熊猫]@2x.png","k_1h8octi":"[嘘]@2x.png","k_1h91zox":"[药]@2x.png","k_1ghttfl":"[疑问]@2x.png","k_1ghk7sz":"[阴险]@2x.png","k_0gl37zz":"[右车头]@2x.png","k_0ifkj1p":"[右哼哼]@2x.png","k_0g1yh2e":"[右太极]@2x.png","k_1g9dkfc":"[雨伞]@2x.png","k_1g8jl88":"[月亮]@2x.png","k_1h8lhqj":"[晕]@2x.png","k_1gi9x2q":"[再见]@2x.png","k_1g6dwwv":"[炸弹]@2x.png","k_1fzmkfi":"[折磨]@2x.png","k_1g6jbiw":"[纸巾]@2x.png","k_1ggjnwu":"[咒骂]@2x.png","k_1g4qlq8":"[猪头]@2x.png","k_1g1lqzz":"[抓狂]@2x.png","k_1g80j3u":"[转圈]@2x.png","k_1g0z55s":"[龇牙]@2x.png","k_1g3ju6v":"[钻戒]@2x.png","k_0gl51l6":"[左车头]@2x.png","k_0iflllk":"[左哼哼]@2x.png","k_0g1y3ir":"[左太极]@2x.png","k_026hiq5":"消息列表加载中","k_003tu8k":"爱你","k_003myvp":"傲慢","k_003kddw":"白眼","k_039yfhv":"棒棒糖","k_003nu3p":"抱抱","k_003nijr":"抱拳","k_003mg88":"爆筋","k_002v17e":"鄙视","k_003qhy4":"闭嘴","k_003l5fq":"鞭炮","k_003uacl":"便便","k_003oq1g":"擦汗","k_003qvey":"彩带","k_003jci7":"彩球","k_003pyu1":"菜刀","k_003q97d":"差劲","k_003po5d":"车厢","k_03eadb2":"打哈欠","k_003pnuf":"大兵","k_003kg57":"蛋糕","k_003mxkt":"得意","k_003onu3":"灯泡","k_002uv8s":"凋谢","k_003kqy0":"调皮","k_003tyum":"多云","k_003pv9u":"发呆","k_036o6mu":"发抖t","k_003nogx":"飞机","k_003q7wg":"飞吻","k_003m0jd":"奋斗","k_002ult9":"风车","k_003r8gt":"尴尬","k_003qy4u":"勾引","k_003mnoa":"鼓掌","k_003lmw8":"害羞","k_003mb30":"憨笑","k_03bj41g":"红灯笼","k_03dxw2f":"红双喜","k_003mk57":"坏笑","k_003nmvf":"挥手","k_003r2i7":"回头","k_002s6f3":"饥饿","k_003qd0t":"激动","k_002vgi4":"街舞","k_003nz33":"惊恐","k_002wh4p":"惊讶","k_003ozpu":"咖啡","k_003qvs4":"磕头","k_003l3wb":"可爱","k_003nuwm":"可怜","k_002rw1q":"抠鼻","k_002tujb":"骷髅","k_00030eq":"酷","k_03i8ath":"快哭了","k_000421h":"困","k_003l5i7":"蜡烛","k_003j72g":"篮球","k_003ofwl":"冷汗","k_02mw65v":"礼品袋","k_003ku40":"礼物","k_003ookz":"流汗","k_003on72":"流泪","k_003rjy0":"麻将","k_003q2f8":"猫咪","k_03et393":"么么哒","k_003j7j2":"玫瑰","k_002sr0b":"米饭","k_003nnza":"面条","k_003jef9":"奶瓶","k_002umn0":"难过","k_002rjib":"闹钟","k_0003zcn":"怒","k_003jzwq":"怄火","k_003j4js":"皮球","k_002r5ir":"啤酒","k_002ubu4":"瓢虫","k_003ppo6":"撇嘴","k_003ty3o":"乒乓","k_002vxwe":"汽车","k_00043hb":"强","k_003nmbo":"敲打","k_002tfhq":"青蛙","k_03i7lrn":"糗大了","k_003r03m":"拳头","k_00043h0":"弱","k_000345z":"色","k_003qmp9":"沙发","k_003it8a":"闪电","k_003pxow":"胜利","k_003kw8e":"示爱","k_003n99g":"手枪","k_00035cl":"衰","k_002vl3h":"睡觉","k_002rgqk":"太阳","k_003m9d1":"跳绳","k_002vobp":"跳跳","k_003mkoz":"偷笑","k_00041px":"吐","k_003rjh5":"委屈","k_003j36u":"西瓜","k_002re92":"下雨","k_00041py":"吓","k_003q06o":"献吻","k_002ubjp":"香蕉","k_003o2tr":"象棋","k_03ie6pa":"心碎了","k_003rao5":"信封","k_003l3us":"熊猫","k_000424d":"嘘","k_00033yi":"药","k_002qtyy":"疑问","k_002qe0o":"阴险","k_03gu7us":"右车头","k_03ere8m":"右哼哼","k_003uqk3":"雨伞","k_003tzdv":"月亮","k_0003z00":"晕","k_002vdrd":"再见","k_003ra1w":"炸弹","k_003lcad":"折磨","k_003q7sz":"纸巾","k_002thn9":"咒骂","k_003qx7f":"猪头","k_003l044":"抓狂","k_003qg4h":"转圈","k_003kb97":"龇牙","k_03gu53l":"左车头","k_03erd1f":"左哼哼","k_003nyvl":"爱情","k_003r85z":"爱心","k_003mk8j":"钞票","k_003pwfj":"大哭","k_00042w5":"刀","k_003nmtr":"握手","k_03c529p":"右太极","k_003n4mk":"钻戒","k_03c5488":"左太极","k_1llp7tu":"该用户不存在","k_0tbyqyb":"加载中…","k_0td1p3f":"保存中…","k_1klqdh1":"仅限汉字、英文、数字和下划线","k_03el5lp":"未填写","k_1ui0gai":"搜索指定内容","k_003nvk2":"消息","k_03agld7":"群提示","k_0elt0kw":"添加群聊","k_0s3sgel":"移出黑名单","k_1qqgjra":"$option3条未读消息","k_0uubyjr":"以下为未读消息","k_16as7eq":"表情回应","k_003s12u":"回复","k_003s38r":"更多","k_002wkr3":"翻译","k_13g4hxv":"翻译完成","k_003molk":"表情","k_165bbw6":"消息历史","k_13sqc0z":"清除消息","k_0glns86":"删除会话","k_13s99rx":"清空消息","k_11vsa3j":"退出群组","k_11vvszp":"解散群组","k_15i9w72":"群管理员","k_0p3espj":"设置备注名","k_118sw9v":"立即搜索","k_0h20hg5":"视频通话","k_0h22snw":"语音通话","k_003lz6t":"对方","k_1xf4yre":"发送给$option1","k_003por5":"截图","k_1rw7s82":" 访问相册中视频权限,以正常使用发送视频等功能。","k_003rcwm":"打开","k_1698c42":"在访达中打开","k_066fxsz":"查看文件夹","k_0k432y2":"无法发送,包含文件夹","k_002wb4y":"会话"}
\ No newline at end of file
diff --git a/lib/i18n/strings_zh-Hans.i18n.json b/lib/i18n/strings_zh-Hans.i18n.json
index 72463e1..5a79486 100644
--- a/lib/i18n/strings_zh-Hans.i18n.json
+++ b/lib/i18n/strings_zh-Hans.i18n.json
@@ -659,8 +659,34 @@
"k_1ui0gai": "搜索指定内容",
"k_003nvk2": "消息",
"k_03agld7": "群提示",
+ "k_0elt0kw": "添加群聊",
+ "k_0s3sgel": "移出黑名单",
+ "k_1qqgjra": "$option3条未读消息",
+ "k_0uubyjr": "以下为未读消息",
+ "k_16as7eq": "表情回应",
+ "k_003s12u": "回复",
+ "k_003s38r": "更多",
"k_002wkr3": "翻译",
"k_13g4hxv": "翻译完成",
- "k_1qqgjra": "$option3条未读消息",
- "k_0uubyjr": "以下为未读消息"
+ "k_003molk": "表情",
+ "k_165bbw6": "消息历史",
+ "k_13sqc0z": "清除消息",
+ "k_0glns86": "删除会话",
+ "k_13s99rx": "清空消息",
+ "k_11vsa3j": "退出群组",
+ "k_11vvszp": "解散群组",
+ "k_15i9w72": "群管理员",
+ "k_0p3espj": "设置备注名",
+ "k_118sw9v": "立即搜索",
+ "k_0h20hg5": "视频通话",
+ "k_0h22snw": "语音通话",
+ "k_003lz6t": "对方",
+ "k_1xf4yre": "发送给$option1",
+ "k_003por5": "截图",
+ "k_1rw7s82": " 访问相册中视频权限,以正常使用发送视频等功能。",
+ "k_003rcwm": "打开",
+ "k_1698c42": "在访达中打开",
+ "k_066fxsz": "查看文件夹",
+ "k_0k432y2": "无法发送,包含文件夹",
+ "k_002wb4y": "会话"
}
\ No newline at end of file
diff --git a/lib/i18n/strings_zh-Hant.i18n.json b/lib/i18n/strings_zh-Hant.i18n.json
index d36d77a..0c8ea1c 100644
--- a/lib/i18n/strings_zh-Hant.i18n.json
+++ b/lib/i18n/strings_zh-Hant.i18n.json
@@ -1 +1 @@
-{"k_1yemzyd":"收到一條訊息","k_0ylosxn":"自定義訊息","k_13sajrj":"貼圖訊息","k_13sjeb7":"檔案訊息","k_0yd2ft8":"群提示訊息","k_13s7mxn":"圖片訊息","k_13satlt":"位置訊息","k_00bbtsx":"合並轉發訊息","k_13sqwu4":"語音訊息","k_13sqjjp":"影片","k_1fdhj9g":"該版本不支持此訊息","k_06pujtm":"同意任何用戶添加好友","k_0gyhkp5":"需要驗證","k_121ruco":"拒絕任何人加好友","k_05nspni":"自定義字段","k_03fchyy":"群頭像","k_03i9mfe":"群簡介","k_03agq58":"群名稱","k_039xqny":"群通知","k_003tr0a":"群主","k_03iqsh4":"$s為 ","k_191t5n4":"$opUserNickName修改","k_1pg6aoj":"$opUserNickName退出群組","k_1f6zt3v":"邀請$invitedMemberString加入群組","k_0y7zd07":"將$invitedMemberString踢出群組","k_03c49qt":"去授權","k_1d5mshh":"用戶$joinedMemberString加入了群組","k_002wddw":"禁言","k_0got6f7":"解除禁言","k_0yenqf0":"$userName 被","k_0spotql":"將 $adminMember 設置為管理員","k_0pg5zzj":"系統訊息 $operationType","k_0ohzb9l":"通話時間:$callTime","k_1uaqed6":"[自定義]","k_0z2z7rx":"[語音]","k_0y39ngu":"[貼圖]","k_1c7z88n":"[檔案] $fileName","k_0y1a2my":"[圖片]","k_0z4fib8":"[影片]","k_0y24mcg":"[位置]","k_0pewpd1":"[聊天記錄]","k_13s8d9p":"未知訊息","k_1c3us5n":"當前群組不支持@全體成員","k_11k579v":"發言中有非法語句","k_003qkx2":"日歷","k_003n2pz":"相機","k_03idjo0":"聯絡人","k_003ltgm":"位置","k_02k3k86":"咪高風","k_003pm7l":"相冊","k_15ao57x":"相冊寫入","k_164m3jd":"本地存儲","k_0qba4ns":"想訪問您的$yoursItem","k_03r6qyx":"我們需要您的同意才能獲取信息","k_02noktt":"不允許","k_00043x4":"好","k_003qzac":"昨天","k_003r39d":"前天","k_03fqp9o":"星期天","k_03ibg5h":"星期一","k_03i7hu1":"星期二","k_03iaiks":"星期三","k_03el9pa":"星期四","k_03i7ok1":"星期五","k_03efxyg":"星期六","k_0oozw9x":"$diffMinutes 分鐘前","k_003q7ba":"下午","k_003q7bb":"上午","k_003pu3h":"現在","k_13hzn00":"昨天 $yesterday","k_0n9pyxz":"用戶不存在","k_1bjwemh":"搜尋用戶 ID","k_003kv3v":"搜尋","k_02owlq8":"我的用戶ID: $userID","k_1wu8h4x":"我是: $showName","k_16758qw":"添加好友","k_1shx4d9":"個性簽名: $selfSignature","k_0i553x0":"填寫驗證信息","k_031ocwx":"請填寫備註和分組","k_003ojje":"備註","k_003lsav":"分組","k_167bdvq":"我的好友","k_156b4ut":"好友申請已發送","k_002r305":"發送","k_03gu05e":"聊天室","k_03b4f3p":"會議群","k_03avj1p":"公開群","k_03asq2g":"工作群","k_03b3hbi":"未知群","k_1loix7s":"群類型: $groupType","k_1lqbsib":"該群組不存在","k_03h153m":"搜尋群ID","k_0oxak3r":"群申請已發送","k_002rflt":"刪除","k_1don84v":"無法定位到原訊息","k_003q5fi":"復製","k_003prq0":"轉發","k_002r1h2":"多選","k_003j708":"引用","k_003pqpr":"回收","k_03ezhho":"已復製","k_11ctfsz":"暫未實現","k_1hbjg5g":"[群系統訊息]","k_03tvswb":"[未知訊息]","k_155cj23":"您回收了一條訊息,","k_0gapun3":"重新編輯","k_1uh417q":"$displayName回收了一條訊息","k_1aszp2k":"您確定要重發這條訊息麽?","k_003rzap":"確定","k_003nevv":"取消","k_0003z7x":"您","k_002wfe4":"已讀","k_002wjlg":"未讀","k_0h1ygf8":"發起通話","k_0h169j0":"取消通話","k_0h13jjk":"接受通話","k_0h19hfx":"拒絕通話","k_0obi9lh":"超時未接聽","k_0y9u662":"「$appName」暫不可以開啟此類檔案,你可以使用其他應用開啟並預覽","k_001nmhu":"用其他應用開啟","k_1ht1b80":"正在接收中","k_105682d":"圖片載入失敗","k_0pytyeu":"圖片保存成功","k_0akceel":"圖片保存失敗","k_003rk1s":"保存","k_04a0awq":"[語音訊息]","k_105c3y3":"影片載入失敗","k_176rzr7":"聊天記錄","k_0d5z4m5":"選擇提醒人","k_003ngex":"完成","k_1665ltg":"發起呼叫","k_003n8b0":"拍攝","k_003kthh":"照片","k_003tnp0":"檔案","k_0jhdhtp":"發送失敗,影片不能大於100MB","k_119ucng":"圖片不能為空","k_0w9x8gw":"選擇成功$successPath","k_13dsw4l":"松開取消","k_0am7r68":"手指上滑,取消發送","k_15jl6qw":"說話時間太短!","k_0gx7vl6":"按住說話","k_15dlafd":"逐條轉發","k_15dryxy":"合並轉發","k_1eyhieh":"確定刪除已選訊息","k_17fmlyf":"清除聊天","k_0dhesoz":"取消置頂","k_002sk7x":"置頂","k_003ll77":"草稿","k_03icaxo":"自定義","k_1969986":"[語音通話]:$callingLastMsgShow","k_1960dlr":"[視訊通話]:$callingLastMsgShow","k_1np495n":"$messageString[有人@我]","k_1m797yi":"$messageString[@所有人]","k_1uaov41":"查找聊天內容","k_003kfai":"未知","k_13dq4an":"自動審批","k_0l13cde":"管理員審批","k_11y8c6a":"禁止加群","k_1kvyskd":"無網絡連接,無法修改","k_16payqf":"加群方式","k_0vzvn8r":"修改群名稱","k_038lh6u":"群管理","k_0k5wyiy":"設置管理員","k_0goiuwk":"全員禁言","k_1g889xx":"全員禁言開啟後,只允許群主和管理員發言。","k_0wlrefq":"添加需要禁言的群成員","k_0goox5g":"設置禁言","k_08daijh":"成功取消管理員身份","k_0bxm97s":"管理員 ($adminNum/10)","k_0k5u935":"添加管理員","k_03enyx5":"群成員","k_0jayw3z":"群成員($groupMemberNum人)","k_0h1svv1":"刪除群成員","k_0h1g636":"添加群成員","k_0uj7208":"無網絡連接,無法查看群成員","k_01yfa4o":"$memberCount人","k_0hpukyx":"查看更多群成員","k_0qtsar0":"訊息免打擾","k_0ef2a12":"修改我的群昵稱","k_1aajych":"僅限中文、字母、數字和下劃線,2-20個字","k_137pab5":"我的群昵稱","k_0ivim6d":"暫無群公告","k_03eq6cn":"群公告","k_002vxya":"編輯","k_17fpl3y":"置頂聊天","k_03es1ox":"群類型","k_003mz1i":"同意","k_003lpre":"拒絕","k_003qk66":"頭像","k_003lhvk":"昵稱","k_003ps50":"賬號","k_15lx52z":"個性簽名","k_003qgkp":"性別","k_003m6hr":"生日","k_0003v6a":"男","k_00043x2":"女","k_03bcjkv":"未設置","k_11s0gdz":"修改昵稱","k_0p3j4sd":"僅限中字、字母、數字和下劃線","k_15lyvdt":"修改簽名","k_0vylzjp":"這個人很懶,什麽也沒寫","k_1hs7ese":"等上線再改這個","k_03exjk7":"備註名","k_0s3skfd":"加入黑名單","k_0p3b31s":"修改備註名","k_0003y9x":"無","k_11zgnfs":"個人資料","k_03xd79d":"個性簽名: $signature","k_1tez2xl":"暫無個性簽名","k_118prbn":"全局搜尋","k_1m9dftc":"全部聯絡人","k_0em4gyz":"全部群組","k_002twmj":"群組","k_09kga0d":"更多聊天記錄","k_1ui5lzi":"$count條相關聊天記錄","k_09khmso":"相關聊天記錄","k_1kevf4k":"與$receiver的聊天記錄","k_0vjj2kp":"群組的聊天記錄","k_003n2rp":"選擇","k_03ignw6":"所有人","k_03erpei":"管理員","k_0qi9tno":"群主、管理員","k_1m9exwh":"最近聯絡人","k_119nwqr":"輸入不能為空","k_0pzwbmg":"影片保存成功","k_0aktupv":"影片保存失敗","k_1qbg9xc":"$option8為 ","k_1wq5ubm":"$option7修改","k_0y5pu80":"$option6退出群組","k_0nl7cmd":"邀請$option5加入群組","k_1ju5iqw":"將$option4踢出群組","k_1ovt677":"用戶$option3加入了群組","k_0k05b8b":"$option2 被","k_0wm4xeb":"系統訊息 $option2","k_0nbq9v3":"通話時間:$option2","k_0i1kf53":"[檔案] $option2","k_1gnnby6":"想訪問您的$option2","k_1wh4atg":"$option2 分鐘前","k_07sh7g1":"昨天 $option2","k_1pj8xzh":"我的用戶ID: $option2","k_0py1evo":"個性簽名: $option2","k_1kvj4i2":"$option2回收了一條訊息","k_1v0lbpp":"「$option2」暫不可以開啟此類檔案,你可以使用其他應用開啟並預覽","k_0torwfz":"選擇成功$option2","k_0i1bjah":"$option1回收了一條訊息","k_1qzxh9q":"通話時間:$option3","k_0wrgmom":"[語音通話]:$option1","k_06ix2f0":"[視訊通話]:$option2","k_08o3z5w":"[檔案] $option1","k_0ezbepg":"$option2[有人@我]","k_1ccnht1":"$option2[@所有人]","k_1k3arsw":"管理員 ($option2/10)","k_1d4golg":"群成員($option1人)","k_1bg69nt":"$option1人","k_00gjqxj":"個性簽名: $option1","k_0c29cxr":"$option1條相關聊天記錄","k_1twk5rz":"與$option1的聊天記錄","k_1vn4xq1":"將 $adminMember 取消管理員","k_0e35hsw":"為方便您將所拍攝的照片或影片發送給朋友,以及進行視訊通話,請允許我們訪問攝像頭進行拍攝照片和影片。","k_0dj6yr7":"為方便您發送語音訊息、拍攝影片以及音視訊通話,請允許我們使用咪高風進行錄音。","k_003qnsl":"存儲","k_0s3rtpw":"為方便您查看和選擇相冊裏的圖片影片發送給朋友,以及保存內容到設備,請允許我們訪問您設備上的照片、媒體內容。","k_0tezv85":" 申請獲取$option2","k_002rety":"權限","k_18o68ro":"需要授予","k_1onpf8u":" 相機權限,以正常使用拍攝圖片/影片、視訊通話等功能。","k_17irga5":" 咪高風權限,以正常使用發送語音訊息、拍攝影片、音視訊通話等功能。","k_0572kc4":" 訪問照片權限,以正常使用發送圖片、影片等功能。","k_0slykws":" 訪問相冊寫入權限,以正常使用存儲圖片、影片等功能。","k_119pkcd":" 檔案讀寫權限,以正常使用在聊天功能中的圖片查看、選擇能力和發送檔案的能力。","k_0gqewd3":"以後再說","k_03eq4s1":"去開啟","k_0nt2uyg":"回到最新位置","k_04l16at":"$option1條新訊息","k_13p3w93":"有人@我","k_18w5uk6":"@所有人","k_0jmujgh":"其他檔案正在接收中","k_12s5ept":"訊息詳情","k_0mxa4f4":"$option1人已讀","k_061tue3":"$option2人未讀","k_18qjstb":"轉讓群主","k_0on1aj2":"有$option2條@我訊息","k_09j4izl":"[有人@我] ","k_1oqtjw0":"[@所有人] ","k_1x5a9vb":"我是: $option1","k_14n31e7":"進群請求","k_08nc5j1":"群類型: $option1","k_1josu12":"$option1 條入群請求","k_0n2x5s0":"驗證消息: $option2","k_03c1nx0":"已同意","k_03aw9w8":"已拒絕","k_038ryos":"去處理","k_0gw8pum":"進群申請","k_1gcvfrj":"請填寫備註名","k_002v9zj":"确认","k_10oqrki":"轻触拍照","k_0f8b3ws":"加载失败","k_11cm5lm":"手动聚焦","k_002uzrd":"预览","k_003qkn3":"录像","k_003k6a7":"拍照","k_0bqpqco":"拍照按钮","k_1626ozl":"停止录像","k_003lvmu":"前置","k_003lued":"后置","k_003lwzh":"外置","k_002qzi3":"关闭","k_003pufb":"自动","k_0apm0ze":"拍照时闪光","k_157zog5":"始终闪光","k_0cfyqhy":"$option1 画面预览","k_0phctlz":"闪光模式: $option2","k_02vfqe0":"切换至 $option3 摄像头","k_0f0y9ex":"说话时间太短","k_0ln70tk":"无法打开URL","k_11a3jdv":"轻触拍照,长按摄像","k_1k18miv":"请传入离开群组生命周期函数,提供返回首页或其他页面的导航方法。","k_1fu9ahv":"全员禁言状态","k_0gmwbnd":"全员禁言中","k_0got2zr":"您被禁言","k_0y9jck8":"你必须自定义search bar,并处理点击跳转","k_0yum3tv":"如使用自定义区域,请在profileWidgetBuilder传入对应组件","k_09kalj0":"清空聊天记录","k_14j5iul":"删除并退出","k_125ru1w":"解散该群","k_0jtutmw":"退出后不会接收到此群聊消息","k_0jtzmqa":"解散后不会接收到此群聊消息","k_0r8fi93":"好友添加成功","k_02qw14e":"好友申请已发出","k_0n3md5x":"当前用户在黑名单","k_094phq4":"好友添加失败","k_129scag":"好友删除成功","k_129uzfn":"好友删除失败","k_1666isy":"清除好友","k_1679vrd":"加为好友","k_1ualc52":"看看对方带来的数据是啥","k_0szluvp":"设置对方在线状态","k_0f4rnf8":"该用户已是好友","k_1tdkom4":"您已是群成员","k_1p2lyuz":"对方正在输入中...","k_1g8wfpy":"...共$option1人","k_12rv9vw":"回应详情","k_0havgi0":"[查看详情 >>](${linkMessage.link})","k_0n9p7g8":"群组不存在","k_1tdh5vn":"您不是群成员","k_0h1q57v":"暂无群成员","k_0y5drq1":"[查看详情 >>]($option1)","k_03pjp61":"[表情消息]","k_1jpvzul":"[自定义消息]","k_03u3bh1":"[文件消息]","k_1odsnsw":"[群消息]","k_03sel4t":"[图片消息]","k_03sfw3r":"[位置消息]","k_03xpuwq":"[合并消息]","k_07ycxwo":"[没有元素]","k_03rc9vz":"[文本消息]","k_046uopf":"[视频消息]","k_0ehmsun":"设备存储空间不足,建议清理,以获得更好使用体验","k_003kmos":"图片","k_002s86q":"视频","k_06bk5ei":"视频消息仅限 mp4 格式","k_13opfxf":"Web网页端不支持搜索","k_1i0o0y2":"暂时仅限 Android/iOS 端","k_045dtzl":"$option1的聊天记录","k_0t0131u":"群资料信息","k_18ok8xz":"消息接收方式","k_03ax3ks":"群资料","k_0sqvoqo":"将 $option1 设置为管理员","k_1gbg1v8":"将 $option1 取消管理员","k_17k64g4":"群聊创建成功!","k_05mn217":"暂未安装表情包插件,如需使用表情相关功能,请根据本文档安装:https://cloud.tencent.com/document/product/269/70746","k_14j17nz":"暂无表情包","k_0fvjexh":"正在下载中","k_1cdagzz":"已加入待下载队列,其他文件下载中","k_0g4vojc":"开始下载","k_1g32es3":"[调皮]@2x.png","k_1g8qorz":"[爱你]@2x.png","k_1g4hmx6":"[爱情]@2x.png","k_1g6b558":"[爱心]@2x.png","k_1g3m4su":"[傲慢]@2x.png","k_1g2jym7":"[白眼]@2x.png","k_0cgkxuw":"[棒棒糖]@2x.png","k_1g48br2":"[抱抱]@2x.png","k_1g49ol8":"[抱拳]@2x.png","k_1g0ras3":"[爆筋]@2x.png","k_1ghy881":"[鄙视]@2x.png","k_1g86bmv":"[闭嘴]@2x.png","k_1g1xs1p":"[鞭炮]@2x.png","k_1g8i6ri":"[便便]@2x.png","k_1g2u5kf":"[擦汗]@2x.png","k_1g60uwh":"[彩带]@2x.png","k_1g1o0d0":"[彩球]@2x.png","k_1g6a6yq":"[菜刀]@2x.png","k_1g6vqo2":"[差劲]@2x.png","k_1g0kvjc":"[钞票]@2x.png","k_1g65x7e":"[车厢]@2x.png","k_0e1tjol":"[打哈欠]@2x.png","k_1g65n58":"[大兵]@2x.png","k_1g7se7o":"[大哭]@2x.png","k_1g03868":"[蛋糕]@2x.png","k_1h8nm66":"[刀]@2x.png","k_1g3dlpi":"[得意]@2x.png","k_1g3u434":"[灯泡]@2x.png","k_1giuqs7":"[凋谢]@2x.png","k_1g8r0r9":"[多云]@2x.png","k_1g7k6i1":"[发呆]@2x.png","k_1g44zsp":"[发抖]@2x.png","k_1g5l96i":"[飞机]@2x.png","k_1g7wsqj":"[飞吻]@2x.png","k_1g49luq":"[奋斗]@2x.png","k_1gixbsm":"[风车]@2x.png","k_1g6cqbq":"[尴尬]@2x.png","k_1g6jbw5":"[勾引]@2x.png","k_1g3lwo1":"[鼓掌]@2x.png","k_1g13nkj":"[害羞]@2x.png","k_1g0mt47":"[憨笑]@2x.png","k_0bxujkf":"[红灯笼]@2x.png","k_0hhaeh8":"[红双喜]@2x.png","k_1g0jnts":"[坏笑]@2x.png","k_1g46g9c":"[挥手]@2x.png","k_1g4vi9g":"[回头]@2x.png","k_1gf7hes":"[饥饿]@2x.png","k_1g6mvsm":"[激动]@2x.png","k_1gku5mf":"[街舞]@2x.png","k_1g4hidg":"[惊恐]@2x.png","k_1gjbrtu":"[惊讶]@2x.png","k_1g6sand":"[咖啡]@2x.png","k_1g4s8rj":"[磕头]@2x.png","k_1g1wn34":"[可爱]@2x.png","k_1g3l0wd":"[可怜]@2x.png","k_1ggaon9":"[抠鼻]@2x.png","k_1ggvcb0":"[骷髅]@2x.png","k_1h8yqjt":"[酷]@2x.png","k_0jac97i":"[快哭了]@2x.png","k_1h8oiby":"[困]@2x.png","k_1g0s5hg":"[蜡烛]@2x.png","k_1g1iuer":"[篮球]@2x.png","k_1g2xjfi":"[冷汗]@2x.png","k_0s5oyqw":"[礼品袋]@2x.png","k_1g1qqvf":"[礼物]@2x.png","k_1g2slew":"[流汗]@2x.png","k_1g3z9xx":"[流泪]@2x.png","k_1g6pabn":"[麻将]@2x.png","k_0pkaxul":"[麦克风]@2x.png","k_1g7m0zj":"[猫咪]@2x.png","k_0ibvtpo":"[么么哒]@2x.png","k_1g1hoh1":"[玫瑰]@2x.png","k_1gfzeow":"[米饭]@2x.png","k_1g5l15p":"[面条]@2x.png","k_1g2hfa6":"[奶瓶]@2x.png","k_1gix9pj":"[难过]@2x.png","k_1giqn6g":"[闹钟]@2x.png","k_1h8kd64":"[怒]@2x.png","k_1g0vui9":"[怄火]@2x.png","k_1g1jsj7":"[皮球]@2x.png","k_1ghdluw":"[啤酒]@2x.png","k_1gl6ec7":"[瓢虫]@2x.png","k_1g7gg5p":"[撇嘴]@2x.png","k_1g8psin":"[乒乓]@2x.png","k_1gjzu3p":"[汽车]@2x.png","k_1h8mr0k":"[强]@2x.png","k_1g45y2n":"[敲打]@2x.png","k_1gkaxsl":"[青蛙]@2x.png","k_0jcfnoo":"[糗大了]@2x.png","k_1g4njy1":"[拳头]@2x.png","k_1h8mqr3":"[弱]@2x.png","k_1h926fg":"[色]@2x.png","k_1g6rtbq":"[沙发]@2x.png","k_1giirh6":"[删除]@2x.png","k_1g14ny9":"[闪电]@2x.png","k_1g6bmsr":"[胜利]@2x.png","k_1g1rytx":"[示爱]@2x.png","k_1g52fbz":"[手枪]@2x.png","k_1h90dam":"[衰]@2x.png","k_1gigiae":"[睡觉]@2x.png","k_1gijchz":"[太阳]@2x.png","k_1g1sgji":"[跳绳]@2x.png","k_1gjwuri":"[跳跳]@2x.png","k_1g0juhk":"[偷笑]@2x.png","k_1h8nzla":"[吐]@2x.png","k_1g6cv0i":"[委屈]@2x.png","k_1g46l5g":"[握手]@2x.png","k_1g2pgkd":"[西瓜]@2x.png","k_1ging9p":"[下雨]@2x.png","k_1h8nzil":"[吓]@2x.png","k_1g7q7wr":"[献吻]@2x.png","k_1gl6uum":"[香蕉]@2x.png","k_1g23fys":"[象棋]@2x.png","k_0j75rdh":"[心碎了]@2x.png","k_1g6ajj2":"[信封]@2x.png","k_1g21prz":"[熊猫]@2x.png","k_1h8octi":"[嘘]@2x.png","k_1h91zox":"[药]@2x.png","k_1ghttfl":"[疑问]@2x.png","k_1ghk7sz":"[阴险]@2x.png","k_0gl37zz":"[右车头]@2x.png","k_0ifkj1p":"[右哼哼]@2x.png","k_0g1yh2e":"[右太极]@2x.png","k_1g9dkfc":"[雨伞]@2x.png","k_1g8jl88":"[月亮]@2x.png","k_1h8lhqj":"[晕]@2x.png","k_1gi9x2q":"[再见]@2x.png","k_1g6dwwv":"[炸弹]@2x.png","k_1fzmkfi":"[折磨]@2x.png","k_1g6jbiw":"[纸巾]@2x.png","k_1ggjnwu":"[咒骂]@2x.png","k_1g4qlq8":"[猪头]@2x.png","k_1g1lqzz":"[抓狂]@2x.png","k_1g80j3u":"[转圈]@2x.png","k_1g0z55s":"[龇牙]@2x.png","k_1g3ju6v":"[钻戒]@2x.png","k_0gl51l6":"[左车头]@2x.png","k_0iflllk":"[左哼哼]@2x.png","k_0g1y3ir":"[左太极]@2x.png","k_026hiq5":"消息列表加载中","k_003tu8k":"爱你","k_003myvp":"傲慢","k_003kddw":"白眼","k_039yfhv":"棒棒糖","k_003nu3p":"抱抱","k_003nijr":"抱拳","k_003mg88":"爆筋","k_002v17e":"鄙视","k_003qhy4":"闭嘴","k_003l5fq":"鞭炮","k_003uacl":"便便","k_003oq1g":"擦汗","k_003qvey":"彩带","k_003jci7":"彩球","k_003pyu1":"菜刀","k_003q97d":"差劲","k_003po5d":"车厢","k_03eadb2":"打哈欠","k_003pnuf":"大兵","k_003kg57":"蛋糕","k_003mxkt":"得意","k_003onu3":"灯泡","k_002uv8s":"凋谢","k_003kqy0":"调皮","k_003tyum":"多云","k_003pv9u":"发呆","k_036o6mu":"发抖t","k_003nogx":"飞机","k_003q7wg":"飞吻","k_003m0jd":"奋斗","k_002ult9":"风车","k_003r8gt":"尴尬","k_003qy4u":"勾引","k_003mnoa":"鼓掌","k_003lmw8":"害羞","k_003mb30":"憨笑","k_03bj41g":"红灯笼","k_03dxw2f":"红双喜","k_003mk57":"坏笑","k_003nmvf":"挥手","k_003r2i7":"回头","k_002s6f3":"饥饿","k_003qd0t":"激动","k_002vgi4":"街舞","k_003nz33":"惊恐","k_002wh4p":"惊讶","k_003ozpu":"咖啡","k_003qvs4":"磕头","k_003l3wb":"可爱","k_003nuwm":"可怜","k_002rw1q":"抠鼻","k_002tujb":"骷髅","k_00030eq":"酷","k_03i8ath":"快哭了","k_000421h":"困","k_003l5i7":"蜡烛","k_003j72g":"篮球","k_003ofwl":"冷汗","k_02mw65v":"礼品袋","k_003ku40":"礼物","k_003ookz":"流汗","k_003on72":"流泪","k_003rjy0":"麻将","k_003q2f8":"猫咪","k_03et393":"么么哒","k_003j7j2":"玫瑰","k_002sr0b":"米饭","k_003nnza":"面条","k_003jef9":"奶瓶","k_002umn0":"难过","k_002rjib":"闹钟","k_0003zcn":"怒","k_003jzwq":"怄火","k_003j4js":"皮球","k_002r5ir":"啤酒","k_002ubu4":"瓢虫","k_003ppo6":"撇嘴","k_003ty3o":"乒乓","k_002vxwe":"汽车","k_00043hb":"强","k_003nmbo":"敲打","k_002tfhq":"青蛙","k_03i7lrn":"糗大了","k_003r03m":"拳头","k_00043h0":"弱","k_000345z":"色","k_003qmp9":"沙发","k_003it8a":"闪电","k_003pxow":"胜利","k_003kw8e":"示爱","k_003n99g":"手枪","k_00035cl":"衰","k_002vl3h":"睡觉","k_002rgqk":"太阳","k_003m9d1":"跳绳","k_002vobp":"跳跳","k_003mkoz":"偷笑","k_00041px":"吐","k_003rjh5":"委屈","k_003j36u":"西瓜","k_002re92":"下雨","k_00041py":"吓","k_003q06o":"献吻","k_002ubjp":"香蕉","k_003o2tr":"象棋","k_03ie6pa":"心碎了","k_003rao5":"信封","k_003l3us":"熊猫","k_000424d":"嘘","k_00033yi":"药","k_002qtyy":"疑问","k_002qe0o":"阴险","k_03gu7us":"右车头","k_03ere8m":"右哼哼","k_003uqk3":"雨伞","k_003tzdv":"月亮","k_0003z00":"晕","k_002vdrd":"再见","k_003ra1w":"炸弹","k_003lcad":"折磨","k_003q7sz":"纸巾","k_002thn9":"咒骂","k_003qx7f":"猪头","k_003l044":"抓狂","k_003qg4h":"转圈","k_003kb97":"龇牙","k_03gu53l":"左车头","k_03erd1f":"左哼哼","k_003nyvl":"爱情","k_003r85z":"爱心","k_003mk8j":"钞票","k_003pwfj":"大哭","k_00042w5":"刀","k_003nmtr":"握手","k_03c529p":"右太极","k_003n4mk":"钻戒","k_03c5488":"左太极","k_1llp7tu":"该用户不存在","k_0tbyqyb":"加载中…","k_0td1p3f":"保存中…","k_1klqdh1":"仅限汉字、英文、数字和下划线","k_03el5lp":"未填写","k_1ui0gai":"搜索指定内容","k_003nvk2":"消息","k_03agld7":"群提示","k_002wkr3":"翻译","k_13g4hxv":"翻译完成","k_1qqgjra":"$option3条未读消息","k_0uubyjr":"以下为未读消息"}
\ No newline at end of file
+{"k_1yemzyd":"收到一條訊息","k_0ylosxn":"自定義訊息","k_13sajrj":"貼圖訊息","k_13sjeb7":"檔案訊息","k_0yd2ft8":"群提示訊息","k_13s7mxn":"圖片訊息","k_13satlt":"位置訊息","k_00bbtsx":"合並轉發訊息","k_13sqwu4":"語音訊息","k_13sqjjp":"影片","k_1fdhj9g":"該版本不支持此訊息","k_06pujtm":"同意任何用戶添加好友","k_0gyhkp5":"需要驗證","k_121ruco":"拒絕任何人加好友","k_05nspni":"自定義字段","k_03fchyy":"群頭像","k_03i9mfe":"群簡介","k_03agq58":"群名稱","k_039xqny":"群通知","k_003tr0a":"群主","k_03iqsh4":"$s為 ","k_191t5n4":"$opUserNickName修改","k_1pg6aoj":"$opUserNickName退出群組","k_1f6zt3v":"邀請$invitedMemberString加入群組","k_0y7zd07":"將$invitedMemberString踢出群組","k_03c49qt":"去授權","k_1d5mshh":"用戶$joinedMemberString加入了群組","k_002wddw":"禁言","k_0got6f7":"解除禁言","k_0yenqf0":"$userName 被","k_0spotql":"將 $adminMember 設置為管理員","k_0pg5zzj":"系統訊息 $operationType","k_0ohzb9l":"通話時間:$callTime","k_1uaqed6":"[自定義]","k_0z2z7rx":"[語音]","k_0y39ngu":"[貼圖]","k_1c7z88n":"[檔案] $fileName","k_0y1a2my":"[圖片]","k_0z4fib8":"[影片]","k_0y24mcg":"[位置]","k_0pewpd1":"[聊天記錄]","k_13s8d9p":"未知訊息","k_1c3us5n":"當前群組不支持@全體成員","k_11k579v":"發言中有非法語句","k_003qkx2":"日歷","k_003n2pz":"相機","k_03idjo0":"聯絡人","k_003ltgm":"位置","k_02k3k86":"咪高風","k_003pm7l":"相冊","k_15ao57x":"相冊寫入","k_164m3jd":"本地存儲","k_0qba4ns":"想訪問您的$yoursItem","k_03r6qyx":"我們需要您的同意才能獲取信息","k_02noktt":"不允許","k_00043x4":"好","k_003qzac":"昨天","k_003r39d":"前天","k_03fqp9o":"星期天","k_03ibg5h":"星期一","k_03i7hu1":"星期二","k_03iaiks":"星期三","k_03el9pa":"星期四","k_03i7ok1":"星期五","k_03efxyg":"星期六","k_0oozw9x":"$diffMinutes 分鐘前","k_003q7ba":"下午","k_003q7bb":"上午","k_003pu3h":"現在","k_13hzn00":"昨天 $yesterday","k_0n9pyxz":"用戶不存在","k_1bjwemh":"搜尋用戶 ID","k_003kv3v":"搜尋","k_02owlq8":"我的用戶ID: $userID","k_1wu8h4x":"我是: $showName","k_16758qw":"添加好友","k_1shx4d9":"個性簽名: $selfSignature","k_0i553x0":"填寫驗證信息","k_031ocwx":"請填寫備註和分組","k_003ojje":"備註","k_003lsav":"分組","k_167bdvq":"我的好友","k_156b4ut":"好友申請已發送","k_002r305":"發送","k_03gu05e":"聊天室","k_03b4f3p":"會議群","k_03avj1p":"公開群","k_03asq2g":"工作群","k_03b3hbi":"未知群","k_1loix7s":"群類型: $groupType","k_1lqbsib":"該群組不存在","k_03h153m":"搜尋群ID","k_0oxak3r":"群申請已發送","k_002rflt":"刪除","k_1don84v":"無法定位到原訊息","k_003q5fi":"復製","k_003prq0":"轉發","k_002r1h2":"多選","k_003j708":"引用","k_003pqpr":"回收","k_03ezhho":"已復製","k_11ctfsz":"暫未實現","k_1hbjg5g":"[群系統訊息]","k_03tvswb":"[未知訊息]","k_155cj23":"您回收了一條訊息,","k_0gapun3":"重新編輯","k_1uh417q":"$displayName回收了一條訊息","k_1aszp2k":"您確定要重發這條訊息麽?","k_003rzap":"確定","k_003nevv":"取消","k_0003z7x":"您","k_002wfe4":"已讀","k_002wjlg":"未讀","k_0h1ygf8":"發起通話","k_0h169j0":"取消通話","k_0h13jjk":"接受通話","k_0h19hfx":"拒絕通話","k_0obi9lh":"超時未接聽","k_0y9u662":"「$appName」暫不可以開啟此類檔案,你可以使用其他應用開啟並預覽","k_001nmhu":"用其他應用開啟","k_1ht1b80":"正在接收中","k_105682d":"圖片載入失敗","k_0pytyeu":"圖片保存成功","k_0akceel":"圖片保存失敗","k_003rk1s":"保存","k_04a0awq":"[語音訊息]","k_105c3y3":"影片載入失敗","k_176rzr7":"聊天記錄","k_0d5z4m5":"選擇提醒人","k_003ngex":"完成","k_1665ltg":"發起呼叫","k_003n8b0":"拍攝","k_003kthh":"照片","k_003tnp0":"檔案","k_0jhdhtp":"發送失敗,影片不能大於100MB","k_119ucng":"圖片不能為空","k_0w9x8gw":"選擇成功$successPath","k_13dsw4l":"松開取消","k_0am7r68":"手指上滑,取消發送","k_15jl6qw":"說話時間太短!","k_0gx7vl6":"按住說話","k_15dlafd":"逐條轉發","k_15dryxy":"合並轉發","k_1eyhieh":"確定刪除已選訊息","k_17fmlyf":"清除聊天","k_0dhesoz":"取消置頂","k_002sk7x":"置頂","k_003ll77":"草稿","k_03icaxo":"自定義","k_1969986":"[語音通話]:$callingLastMsgShow","k_1960dlr":"[視訊通話]:$callingLastMsgShow","k_1np495n":"$messageString[有人@我]","k_1m797yi":"$messageString[@所有人]","k_1uaov41":"查找聊天內容","k_003kfai":"未知","k_13dq4an":"自動審批","k_0l13cde":"管理員審批","k_11y8c6a":"禁止加群","k_1kvyskd":"無網絡連接,無法修改","k_16payqf":"加群方式","k_0vzvn8r":"修改群名稱","k_038lh6u":"群管理","k_0k5wyiy":"設置管理員","k_0goiuwk":"全員禁言","k_1g889xx":"全員禁言開啟後,只允許群主和管理員發言。","k_0wlrefq":"添加需要禁言的群成員","k_0goox5g":"設置禁言","k_08daijh":"成功取消管理員身份","k_0bxm97s":"管理員 ($adminNum/10)","k_0k5u935":"添加管理員","k_03enyx5":"群成員","k_0jayw3z":"群成員($groupMemberNum人)","k_0h1svv1":"刪除群成員","k_0h1g636":"添加群成員","k_0uj7208":"無網絡連接,無法查看群成員","k_01yfa4o":"$memberCount人","k_0hpukyx":"查看更多群成員","k_0qtsar0":"訊息免打擾","k_0ef2a12":"修改我的群昵稱","k_1aajych":"僅限中文、字母、數字和下劃線,2-20個字","k_137pab5":"我的群昵稱","k_0ivim6d":"暫無群公告","k_03eq6cn":"群公告","k_002vxya":"編輯","k_17fpl3y":"置頂聊天","k_03es1ox":"群類型","k_003mz1i":"同意","k_003lpre":"拒絕","k_003qk66":"頭像","k_003lhvk":"昵稱","k_003ps50":"賬號","k_15lx52z":"個性簽名","k_003qgkp":"性別","k_003m6hr":"生日","k_0003v6a":"男","k_00043x2":"女","k_03bcjkv":"未設置","k_11s0gdz":"修改昵稱","k_0p3j4sd":"僅限中字、字母、數字和下劃線","k_15lyvdt":"修改簽名","k_0vylzjp":"這個人很懶,什麽也沒寫","k_1hs7ese":"等上線再改這個","k_03exjk7":"備註名","k_0s3skfd":"加入黑名單","k_0p3b31s":"修改備註名","k_0003y9x":"無","k_11zgnfs":"個人資料","k_03xd79d":"個性簽名: $signature","k_1tez2xl":"暫無個性簽名","k_118prbn":"全局搜尋","k_1m9dftc":"全部聯絡人","k_0em4gyz":"全部群組","k_002twmj":"群組","k_09kga0d":"更多聊天記錄","k_1ui5lzi":"$count條相關聊天記錄","k_09khmso":"相關聊天記錄","k_1kevf4k":"與$receiver的聊天記錄","k_0vjj2kp":"群組的聊天記錄","k_003n2rp":"選擇","k_03ignw6":"所有人","k_03erpei":"管理員","k_0qi9tno":"群主、管理員","k_1m9exwh":"最近聯絡人","k_119nwqr":"輸入不能為空","k_0pzwbmg":"影片保存成功","k_0aktupv":"影片保存失敗","k_1qbg9xc":"$option8為 ","k_1wq5ubm":"$option7修改","k_0y5pu80":"$option6退出群組","k_0nl7cmd":"邀請$option5加入群組","k_1ju5iqw":"將$option4踢出群組","k_1ovt677":"用戶$option3加入了群組","k_0k05b8b":"$option2 被","k_0wm4xeb":"系統訊息 $option2","k_0nbq9v3":"通話時間:$option2","k_0i1kf53":"[檔案] $option2","k_1gnnby6":"想訪問您的$option2","k_1wh4atg":"$option2 分鐘前","k_07sh7g1":"昨天 $option2","k_1pj8xzh":"我的用戶ID: $option2","k_0py1evo":"個性簽名: $option2","k_1kvj4i2":"$option2回收了一條訊息","k_1v0lbpp":"「$option2」暫不可以開啟此類檔案,你可以使用其他應用開啟並預覽","k_0torwfz":"選擇成功$option2","k_0i1bjah":"$option1回收了一條訊息","k_1qzxh9q":"通話時間:$option3","k_0wrgmom":"[語音通話]:$option1","k_06ix2f0":"[視訊通話]:$option2","k_08o3z5w":"[檔案] $option1","k_0ezbepg":"$option2[有人@我]","k_1ccnht1":"$option2[@所有人]","k_1k3arsw":"管理員 ($option2/10)","k_1d4golg":"群成員($option1人)","k_1bg69nt":"$option1人","k_00gjqxj":"個性簽名: $option1","k_0c29cxr":"$option1條相關聊天記錄","k_1twk5rz":"與$option1的聊天記錄","k_1vn4xq1":"將 $adminMember 取消管理員","k_0e35hsw":"為方便您將所拍攝的照片或影片發送給朋友,以及進行視訊通話,請允許我們訪問攝像頭進行拍攝照片和影片。","k_0dj6yr7":"為方便您發送語音訊息、拍攝影片以及音視訊通話,請允許我們使用咪高風進行錄音。","k_003qnsl":"存儲","k_0s3rtpw":"為方便您查看和選擇相冊裏的圖片影片發送給朋友,以及保存內容到設備,請允許我們訪問您設備上的照片、媒體內容。","k_0tezv85":" 申請獲取$option2","k_002rety":"權限","k_18o68ro":"需要授予","k_1onpf8u":" 相機權限,以正常使用拍攝圖片/影片、視訊通話等功能。","k_17irga5":" 咪高風權限,以正常使用發送語音訊息、拍攝影片、音視訊通話等功能。","k_0572kc4":" 訪問照片權限,以正常使用發送圖片、影片等功能。","k_0slykws":" 訪問相冊寫入權限,以正常使用存儲圖片、影片等功能。","k_119pkcd":" 檔案讀寫權限,以正常使用在聊天功能中的圖片查看、選擇能力和發送檔案的能力。","k_0gqewd3":"以後再說","k_03eq4s1":"去開啟","k_0nt2uyg":"回到最新位置","k_04l16at":"$option1條新訊息","k_13p3w93":"有人@我","k_18w5uk6":"@所有人","k_0jmujgh":"其他檔案正在接收中","k_12s5ept":"訊息詳情","k_0mxa4f4":"$option1人已讀","k_061tue3":"$option2人未讀","k_18qjstb":"轉讓群主","k_0on1aj2":"有$option2條@我訊息","k_09j4izl":"[有人@我] ","k_1oqtjw0":"[@所有人] ","k_1x5a9vb":"我是: $option1","k_14n31e7":"進群請求","k_08nc5j1":"群類型: $option1","k_1josu12":"$option1 條入群請求","k_0n2x5s0":"驗證消息: $option2","k_03c1nx0":"已同意","k_03aw9w8":"已拒絕","k_038ryos":"去處理","k_0gw8pum":"進群申請","k_1gcvfrj":"請填寫備註名","k_002v9zj":"确认","k_10oqrki":"轻触拍照","k_0f8b3ws":"加载失败","k_11cm5lm":"手动聚焦","k_002uzrd":"预览","k_003qkn3":"录像","k_003k6a7":"拍照","k_0bqpqco":"拍照按钮","k_1626ozl":"停止录像","k_003lvmu":"前置","k_003lued":"后置","k_003lwzh":"外置","k_002qzi3":"关闭","k_003pufb":"自动","k_0apm0ze":"拍照时闪光","k_157zog5":"始终闪光","k_0cfyqhy":"$option1 画面预览","k_0phctlz":"闪光模式: $option2","k_02vfqe0":"切换至 $option3 摄像头","k_0f0y9ex":"说话时间太短","k_0ln70tk":"无法打开URL","k_11a3jdv":"轻触拍照,长按摄像","k_1k18miv":"请传入离开群组生命周期函数,提供返回首页或其他页面的导航方法。","k_1fu9ahv":"全员禁言状态","k_0gmwbnd":"全员禁言中","k_0got2zr":"您被禁言","k_0y9jck8":"你必须自定义search bar,并处理点击跳转","k_0yum3tv":"如使用自定义区域,请在profileWidgetBuilder传入对应组件","k_09kalj0":"清空聊天记录","k_14j5iul":"删除并退出","k_125ru1w":"解散该群","k_0jtutmw":"退出后不会接收到此群聊消息","k_0jtzmqa":"解散后不会接收到此群聊消息","k_0r8fi93":"好友添加成功","k_02qw14e":"好友申请已发出","k_0n3md5x":"当前用户在黑名单","k_094phq4":"好友添加失败","k_129scag":"好友删除成功","k_129uzfn":"好友删除失败","k_1666isy":"清除好友","k_1679vrd":"加为好友","k_1ualc52":"看看对方带来的数据是啥","k_0szluvp":"设置对方在线状态","k_0f4rnf8":"该用户已是好友","k_1tdkom4":"您已是群成员","k_1p2lyuz":"对方正在输入中...","k_1g8wfpy":"...共$option1人","k_12rv9vw":"回应详情","k_0havgi0":"[查看详情 >>](${linkMessage.link})","k_0n9p7g8":"群组不存在","k_1tdh5vn":"您不是群成员","k_0h1q57v":"暂无群成员","k_0y5drq1":"[查看详情 >>]($option1)","k_03pjp61":"[表情消息]","k_1jpvzul":"[自定义消息]","k_03u3bh1":"[文件消息]","k_1odsnsw":"[群消息]","k_03sel4t":"[图片消息]","k_03sfw3r":"[位置消息]","k_03xpuwq":"[合并消息]","k_07ycxwo":"[没有元素]","k_03rc9vz":"[文本消息]","k_046uopf":"[视频消息]","k_0ehmsun":"设备存储空间不足,建议清理,以获得更好使用体验","k_003kmos":"图片","k_002s86q":"视频","k_06bk5ei":"视频消息仅限 mp4 格式","k_13opfxf":"Web网页端不支持搜索","k_1i0o0y2":"暂时仅限 Android/iOS 端","k_045dtzl":"$option1的聊天记录","k_0t0131u":"群资料信息","k_18ok8xz":"消息接收方式","k_03ax3ks":"群资料","k_0sqvoqo":"将 $option1 设置为管理员","k_1gbg1v8":"将 $option1 取消管理员","k_17k64g4":"群聊创建成功!","k_05mn217":"暂未安装表情包插件,如需使用表情相关功能,请根据本文档安装:https://cloud.tencent.com/document/product/269/70746","k_14j17nz":"暂无表情包","k_0fvjexh":"正在下载中","k_1cdagzz":"已加入待下载队列,其他文件下载中","k_0g4vojc":"开始下载","k_1g32es3":"[调皮]@2x.png","k_1g8qorz":"[爱你]@2x.png","k_1g4hmx6":"[爱情]@2x.png","k_1g6b558":"[爱心]@2x.png","k_1g3m4su":"[傲慢]@2x.png","k_1g2jym7":"[白眼]@2x.png","k_0cgkxuw":"[棒棒糖]@2x.png","k_1g48br2":"[抱抱]@2x.png","k_1g49ol8":"[抱拳]@2x.png","k_1g0ras3":"[爆筋]@2x.png","k_1ghy881":"[鄙视]@2x.png","k_1g86bmv":"[闭嘴]@2x.png","k_1g1xs1p":"[鞭炮]@2x.png","k_1g8i6ri":"[便便]@2x.png","k_1g2u5kf":"[擦汗]@2x.png","k_1g60uwh":"[彩带]@2x.png","k_1g1o0d0":"[彩球]@2x.png","k_1g6a6yq":"[菜刀]@2x.png","k_1g6vqo2":"[差劲]@2x.png","k_1g0kvjc":"[钞票]@2x.png","k_1g65x7e":"[车厢]@2x.png","k_0e1tjol":"[打哈欠]@2x.png","k_1g65n58":"[大兵]@2x.png","k_1g7se7o":"[大哭]@2x.png","k_1g03868":"[蛋糕]@2x.png","k_1h8nm66":"[刀]@2x.png","k_1g3dlpi":"[得意]@2x.png","k_1g3u434":"[灯泡]@2x.png","k_1giuqs7":"[凋谢]@2x.png","k_1g8r0r9":"[多云]@2x.png","k_1g7k6i1":"[发呆]@2x.png","k_1g44zsp":"[发抖]@2x.png","k_1g5l96i":"[飞机]@2x.png","k_1g7wsqj":"[飞吻]@2x.png","k_1g49luq":"[奋斗]@2x.png","k_1gixbsm":"[风车]@2x.png","k_1g6cqbq":"[尴尬]@2x.png","k_1g6jbw5":"[勾引]@2x.png","k_1g3lwo1":"[鼓掌]@2x.png","k_1g13nkj":"[害羞]@2x.png","k_1g0mt47":"[憨笑]@2x.png","k_0bxujkf":"[红灯笼]@2x.png","k_0hhaeh8":"[红双喜]@2x.png","k_1g0jnts":"[坏笑]@2x.png","k_1g46g9c":"[挥手]@2x.png","k_1g4vi9g":"[回头]@2x.png","k_1gf7hes":"[饥饿]@2x.png","k_1g6mvsm":"[激动]@2x.png","k_1gku5mf":"[街舞]@2x.png","k_1g4hidg":"[惊恐]@2x.png","k_1gjbrtu":"[惊讶]@2x.png","k_1g6sand":"[咖啡]@2x.png","k_1g4s8rj":"[磕头]@2x.png","k_1g1wn34":"[可爱]@2x.png","k_1g3l0wd":"[可怜]@2x.png","k_1ggaon9":"[抠鼻]@2x.png","k_1ggvcb0":"[骷髅]@2x.png","k_1h8yqjt":"[酷]@2x.png","k_0jac97i":"[快哭了]@2x.png","k_1h8oiby":"[困]@2x.png","k_1g0s5hg":"[蜡烛]@2x.png","k_1g1iuer":"[篮球]@2x.png","k_1g2xjfi":"[冷汗]@2x.png","k_0s5oyqw":"[礼品袋]@2x.png","k_1g1qqvf":"[礼物]@2x.png","k_1g2slew":"[流汗]@2x.png","k_1g3z9xx":"[流泪]@2x.png","k_1g6pabn":"[麻将]@2x.png","k_0pkaxul":"[麦克风]@2x.png","k_1g7m0zj":"[猫咪]@2x.png","k_0ibvtpo":"[么么哒]@2x.png","k_1g1hoh1":"[玫瑰]@2x.png","k_1gfzeow":"[米饭]@2x.png","k_1g5l15p":"[面条]@2x.png","k_1g2hfa6":"[奶瓶]@2x.png","k_1gix9pj":"[难过]@2x.png","k_1giqn6g":"[闹钟]@2x.png","k_1h8kd64":"[怒]@2x.png","k_1g0vui9":"[怄火]@2x.png","k_1g1jsj7":"[皮球]@2x.png","k_1ghdluw":"[啤酒]@2x.png","k_1gl6ec7":"[瓢虫]@2x.png","k_1g7gg5p":"[撇嘴]@2x.png","k_1g8psin":"[乒乓]@2x.png","k_1gjzu3p":"[汽车]@2x.png","k_1h8mr0k":"[强]@2x.png","k_1g45y2n":"[敲打]@2x.png","k_1gkaxsl":"[青蛙]@2x.png","k_0jcfnoo":"[糗大了]@2x.png","k_1g4njy1":"[拳头]@2x.png","k_1h8mqr3":"[弱]@2x.png","k_1h926fg":"[色]@2x.png","k_1g6rtbq":"[沙发]@2x.png","k_1giirh6":"[删除]@2x.png","k_1g14ny9":"[闪电]@2x.png","k_1g6bmsr":"[胜利]@2x.png","k_1g1rytx":"[示爱]@2x.png","k_1g52fbz":"[手枪]@2x.png","k_1h90dam":"[衰]@2x.png","k_1gigiae":"[睡觉]@2x.png","k_1gijchz":"[太阳]@2x.png","k_1g1sgji":"[跳绳]@2x.png","k_1gjwuri":"[跳跳]@2x.png","k_1g0juhk":"[偷笑]@2x.png","k_1h8nzla":"[吐]@2x.png","k_1g6cv0i":"[委屈]@2x.png","k_1g46l5g":"[握手]@2x.png","k_1g2pgkd":"[西瓜]@2x.png","k_1ging9p":"[下雨]@2x.png","k_1h8nzil":"[吓]@2x.png","k_1g7q7wr":"[献吻]@2x.png","k_1gl6uum":"[香蕉]@2x.png","k_1g23fys":"[象棋]@2x.png","k_0j75rdh":"[心碎了]@2x.png","k_1g6ajj2":"[信封]@2x.png","k_1g21prz":"[熊猫]@2x.png","k_1h8octi":"[嘘]@2x.png","k_1h91zox":"[药]@2x.png","k_1ghttfl":"[疑问]@2x.png","k_1ghk7sz":"[阴险]@2x.png","k_0gl37zz":"[右车头]@2x.png","k_0ifkj1p":"[右哼哼]@2x.png","k_0g1yh2e":"[右太极]@2x.png","k_1g9dkfc":"[雨伞]@2x.png","k_1g8jl88":"[月亮]@2x.png","k_1h8lhqj":"[晕]@2x.png","k_1gi9x2q":"[再见]@2x.png","k_1g6dwwv":"[炸弹]@2x.png","k_1fzmkfi":"[折磨]@2x.png","k_1g6jbiw":"[纸巾]@2x.png","k_1ggjnwu":"[咒骂]@2x.png","k_1g4qlq8":"[猪头]@2x.png","k_1g1lqzz":"[抓狂]@2x.png","k_1g80j3u":"[转圈]@2x.png","k_1g0z55s":"[龇牙]@2x.png","k_1g3ju6v":"[钻戒]@2x.png","k_0gl51l6":"[左车头]@2x.png","k_0iflllk":"[左哼哼]@2x.png","k_0g1y3ir":"[左太极]@2x.png","k_026hiq5":"消息列表加载中","k_003tu8k":"爱你","k_003myvp":"傲慢","k_003kddw":"白眼","k_039yfhv":"棒棒糖","k_003nu3p":"抱抱","k_003nijr":"抱拳","k_003mg88":"爆筋","k_002v17e":"鄙视","k_003qhy4":"闭嘴","k_003l5fq":"鞭炮","k_003uacl":"便便","k_003oq1g":"擦汗","k_003qvey":"彩带","k_003jci7":"彩球","k_003pyu1":"菜刀","k_003q97d":"差劲","k_003po5d":"车厢","k_03eadb2":"打哈欠","k_003pnuf":"大兵","k_003kg57":"蛋糕","k_003mxkt":"得意","k_003onu3":"灯泡","k_002uv8s":"凋谢","k_003kqy0":"调皮","k_003tyum":"多云","k_003pv9u":"发呆","k_036o6mu":"发抖t","k_003nogx":"飞机","k_003q7wg":"飞吻","k_003m0jd":"奋斗","k_002ult9":"风车","k_003r8gt":"尴尬","k_003qy4u":"勾引","k_003mnoa":"鼓掌","k_003lmw8":"害羞","k_003mb30":"憨笑","k_03bj41g":"红灯笼","k_03dxw2f":"红双喜","k_003mk57":"坏笑","k_003nmvf":"挥手","k_003r2i7":"回头","k_002s6f3":"饥饿","k_003qd0t":"激动","k_002vgi4":"街舞","k_003nz33":"惊恐","k_002wh4p":"惊讶","k_003ozpu":"咖啡","k_003qvs4":"磕头","k_003l3wb":"可爱","k_003nuwm":"可怜","k_002rw1q":"抠鼻","k_002tujb":"骷髅","k_00030eq":"酷","k_03i8ath":"快哭了","k_000421h":"困","k_003l5i7":"蜡烛","k_003j72g":"篮球","k_003ofwl":"冷汗","k_02mw65v":"礼品袋","k_003ku40":"礼物","k_003ookz":"流汗","k_003on72":"流泪","k_003rjy0":"麻将","k_003q2f8":"猫咪","k_03et393":"么么哒","k_003j7j2":"玫瑰","k_002sr0b":"米饭","k_003nnza":"面条","k_003jef9":"奶瓶","k_002umn0":"难过","k_002rjib":"闹钟","k_0003zcn":"怒","k_003jzwq":"怄火","k_003j4js":"皮球","k_002r5ir":"啤酒","k_002ubu4":"瓢虫","k_003ppo6":"撇嘴","k_003ty3o":"乒乓","k_002vxwe":"汽车","k_00043hb":"强","k_003nmbo":"敲打","k_002tfhq":"青蛙","k_03i7lrn":"糗大了","k_003r03m":"拳头","k_00043h0":"弱","k_000345z":"色","k_003qmp9":"沙发","k_003it8a":"闪电","k_003pxow":"胜利","k_003kw8e":"示爱","k_003n99g":"手枪","k_00035cl":"衰","k_002vl3h":"睡觉","k_002rgqk":"太阳","k_003m9d1":"跳绳","k_002vobp":"跳跳","k_003mkoz":"偷笑","k_00041px":"吐","k_003rjh5":"委屈","k_003j36u":"西瓜","k_002re92":"下雨","k_00041py":"吓","k_003q06o":"献吻","k_002ubjp":"香蕉","k_003o2tr":"象棋","k_03ie6pa":"心碎了","k_003rao5":"信封","k_003l3us":"熊猫","k_000424d":"嘘","k_00033yi":"药","k_002qtyy":"疑问","k_002qe0o":"阴险","k_03gu7us":"右车头","k_03ere8m":"右哼哼","k_003uqk3":"雨伞","k_003tzdv":"月亮","k_0003z00":"晕","k_002vdrd":"再见","k_003ra1w":"炸弹","k_003lcad":"折磨","k_003q7sz":"纸巾","k_002thn9":"咒骂","k_003qx7f":"猪头","k_003l044":"抓狂","k_003qg4h":"转圈","k_003kb97":"龇牙","k_03gu53l":"左车头","k_03erd1f":"左哼哼","k_003nyvl":"爱情","k_003r85z":"爱心","k_003mk8j":"钞票","k_003pwfj":"大哭","k_00042w5":"刀","k_003nmtr":"握手","k_03c529p":"右太极","k_003n4mk":"钻戒","k_03c5488":"左太极","k_1llp7tu":"该用户不存在","k_0tbyqyb":"加载中…","k_0td1p3f":"保存中…","k_1klqdh1":"仅限汉字、英文、数字和下划线","k_03el5lp":"未填写","k_1ui0gai":"搜索指定内容","k_003nvk2":"消息","k_03agld7":"群提示","k_0elt0kw":"添加群聊","k_0s3sgel":"移出黑名单","k_1qqgjra":"$option3条未读消息","k_0uubyjr":"以下为未读消息","k_16as7eq":"表情回应","k_003s12u":"回复","k_003s38r":"更多","k_002wkr3":"翻译","k_13g4hxv":"翻译完成","k_003molk":"表情","k_165bbw6":"消息历史","k_13sqc0z":"清除消息","k_0glns86":"删除会话","k_13s99rx":"清空消息","k_11vsa3j":"退出群组","k_11vvszp":"解散群组","k_15i9w72":"群管理员","k_0p3espj":"设置备注名","k_118sw9v":"立即搜索","k_0h20hg5":"视频通话","k_0h22snw":"语音通话","k_003lz6t":"对方","k_1xf4yre":"发送给$option1","k_003por5":"截图","k_1rw7s82":" 访问相册中视频权限,以正常使用发送视频等功能。","k_003rcwm":"打开","k_1698c42":"在访达中打开","k_066fxsz":"查看文件夹","k_0k432y2":"无法发送,包含文件夹","k_002wb4y":"会话"}
\ No newline at end of file
diff --git a/lib/tencent_cloud_chat_uikit.dart b/lib/tencent_cloud_chat_uikit.dart
index 4c8ec65..90cc3dd 100644
--- a/lib/tencent_cloud_chat_uikit.dart
+++ b/lib/tencent_cloud_chat_uikit.dart
@@ -32,12 +32,12 @@ export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitSearch/tim_uikit_searc
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_controller.dart';
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitAppBar/tim_uikit_appbar.dart';
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list.dart';
-export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_item.dart';
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field.dart';
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroup/tim_uikit_group_application_list.dart';
export 'package:tencent_im_base/tencent_im_base.dart';
export 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/models/link_preview_content.dart';
-export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitProfile/widget/tim_uikit_profile_userinfo_card.dart';
+export 'package:tencent_cloud_chat_uikit/ui/widgets/column_menu.dart';
+export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitProfile/widget/tim_uikit_profile_userinfo_card/tim_uikit_profile_userinfo_card.dart';
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitConversation/tim_ui_kit_conversation_total_unread.dart';
// Enum
@@ -52,6 +52,7 @@ export 'package:permission_handler/permission_handler.dart';
// Utils
export 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
+export 'package:tencent_cloud_uikit_core/tencent_cloud_uikit_core.dart';
class TIMUIKitCore {
static CoreServicesImpl getInstance() {
diff --git a/lib/ui/controller/tim_uikit_chat_controller.dart b/lib/ui/controller/tim_uikit_chat_controller.dart
index 2152058..a2fadb7 100644
--- a/lib/ui/controller/tim_uikit_chat_controller.dart
+++ b/lib/ui/controller/tim_uikit_chat_controller.dart
@@ -17,17 +17,16 @@ class TIMUIKitChatController {
}
}
- Future loadHistoryMessageList({
- HistoryMsgGetTypeEnum getType =
- HistoryMsgGetTypeEnum.V2TIM_GET_CLOUD_OLDER_MSG,
- String? userID,
- String? groupID,
- int lastMsgSeq = -1,
- required int count,
- String? lastMsgID,
- LoadDirection direction = LoadDirection.previous
- }) async {
- return await model?.loadData(
+ Future loadHistoryMessageList(
+ {HistoryMsgGetTypeEnum getType =
+ HistoryMsgGetTypeEnum.V2TIM_GET_CLOUD_OLDER_MSG,
+ String? userID,
+ String? groupID,
+ int lastMsgSeq = -1,
+ required int count,
+ String? lastMsgID,
+ LoadDirection direction = LoadDirection.previous}) async {
+ return await model?.loadChatRecord(
count: count,
getType: getType,
lastMsgID: lastMsgID,
@@ -70,7 +69,6 @@ class TIMUIKitChatController {
/// Please provide `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
Future updateMessage(
{
-
/// The ID of the target conversation
String? convID,
@@ -88,32 +86,47 @@ class TIMUIKitChatController {
return;
}
- /// Send message;
- /// 发送消息
- /// Please provide `convType` and `convID`, if you use `TIMUIKitChatController` without specifying to a `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.
+ /// 您需要提供 `convType` 和 `userID` 或 `groupID`, 只有在如果您使用 `TIMUIKitChat` 而没有指定 `TIMUIKitChatController`,则必须提供这些参数。
Future?>? sendMessage({
required V2TimMessage? messageInfo,
- /// The type of the target conversation
+ /// 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,
- /// The ID of the target conversation
- String? convID,
+ /// The user ID of the target one-to-one conversation. Required if convType is ConvType.c2c.
+ /// 目标一对一对话的用户 ID。如果 convType 是 ConvType.c2c,则必填。
+ String? userID,
- /// The method for updating the input field when message sending failed
+ /// The target group ID. Required if convType is ConvType.group.
+ /// 目标群组的 ID。如果 convType 是 ConvType.group,则必填。
+ String? groupID,
+
+ /// A callback function to update the input field when message sending fails.
+ /// 当消息发送失败时,用于更新输入框的回调函数。
ValueChanged? setInputField,
- /// Offline push info
+ /// Offline push information.
+ /// 离线推送信息。
OfflinePushInfo? offlinePushInfo,
}) {
- if (convID != null && convType != null) {
+ if (convType != null) {
+ /// Sends a message to the specified conversation. 发送消息到指定的对话。
+ assert((groupID == null) != (userID == null));
+ assert(groupID != null || convType != ConvType.group);
+ assert(userID != null || convType != ConvType.c2c);
+
return globalChatModel.sendMessageFromController(
messageInfo: messageInfo,
convType: convType,
- convID: convID,
+ convID: (convType == ConvType.group ? groupID : userID) ?? "",
setInputField: setInputField,
offlinePushInfo: offlinePushInfo);
} else if (model != null) {
+ /// Sends a message to the current conversation specified on `TIMUIKitChat`. 发送到 `TIMUIKitChat` 中指定的当前对话。
return model!.sendMessageFromController(
messageInfo: messageInfo, offlinePushInfo: offlinePushInfo);
}
diff --git a/lib/ui/controller/tim_uikit_conversation_controller.dart b/lib/ui/controller/tim_uikit_conversation_controller.dart
index 9979cb2..37e7b0c 100644
--- a/lib/ui/controller/tim_uikit_conversation_controller.dart
+++ b/lib/ui/controller/tim_uikit_conversation_controller.dart
@@ -9,6 +9,11 @@ class TIMUIKitConversationController {
return model.selectedConversation;
}
+ /// Set the selected conversation currently
+ set selectedConversation(V2TimConversation? conversation) {
+ model.selectedConversation = conversation;
+ }
+
/// Get the conversation list
List get conversationList {
return model.conversationList;
diff --git a/lib/ui/controller/tim_uikit_profile_controller.dart b/lib/ui/controller/tim_uikit_profile_controller.dart
index 9fbf53c..f2fe078 100644
--- a/lib/ui/controller/tim_uikit_profile_controller.dart
+++ b/lib/ui/controller/tim_uikit_profile_controller.dart
@@ -47,15 +47,23 @@ class TIMUIKitProfileController {
}
/// Show the text input bottom sheet
- showTextInputBottomSheet(
- BuildContext context,
- String title,
- String tips,
- void Function(String) onSubmitted,
- TUITheme theme
- ) {
+ showTextInputBottomSheet({
+ required BuildContext context,
+ required String title,
+ required String tips,
+ required Function(String text) onSubmitted,
+ required TUITheme theme,
+ Offset? initOffset,
+ String? initText,
+ }) {
TextInputBottomSheet.showTextInputBottomSheet(
- context, title, tips, onSubmitted, theme);
+ context: context,
+ title: title,
+ tips: tips,
+ onSubmitted: onSubmitted,
+ theme: theme,
+ initOffset: initOffset,
+ initText: initText);
}
/// Load the profile data
diff --git a/lib/ui/utils/common_utils.dart b/lib/ui/utils/common_utils.dart
index 5487769..1da0c53 100644
--- a/lib/ui/utils/common_utils.dart
+++ b/lib/ui/utils/common_utils.dart
@@ -6,4 +6,153 @@ class TencentUtils{
static String? checkString(String? text){
return (text != null && text.isEmpty) ? null : text;
}
+
+ static String? checkStringWithoutSpace(String? text){
+ if (text == null || text.trim().isEmpty || text.contains(' ')) {
+ return null;
+ }
+ return text;
+ }
+
+ static String getFileType(String fileType) {
+ switch (fileType) {
+ case "3gp":
+ return "video/3gpp";
+ case "torrent":
+ return "application/x-bittorrent";
+ case "kml":
+ return "application/vnd.google-earth.kml+xml";
+ case "gpx":
+ return "application/gpx+xml";
+ case "asf":
+ return "video/x-ms-asf";
+ case "avi":
+ return "video/x-msvideo";
+ case "bin":
+ case "class":
+ case "exe":
+ return "application/octet-stream";
+ case "bmp":
+ return "image/bmp";
+ case "c":
+ return "text/plain";
+ case "conf":
+ return "text/plain";
+ case "cpp":
+ return "text/plain";
+ case "doc":
+ return "application/msword";
+ case "docx":
+ return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
+ case "xls":
+ case "csv":
+ return "application/vnd.ms-excel";
+ case "xlsx":
+ return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+ case "gif":
+ return "image/gif";
+ case "gtar":
+ return "application/x-gtar";
+ case "gz":
+ return "application/x-gzip";
+ case "h":
+ return "text/plain";
+ case "htm":
+ return "text/html";
+ case "html":
+ return "text/html";
+ case "jar":
+ return "application/java-archive";
+ case "java":
+ return "text/plain";
+ case "jpeg":
+ return "image/jpeg";
+ case "jpg":
+ return "image/jpeg";
+ case "js":
+ return "application/x-javascript";
+ case "log":
+ return "text/plain";
+ case "m3u":
+ return "audio/x-mpegurl";
+ case "m4a":
+ return "audio/mp4a-latm";
+ case "m4b":
+ return "audio/mp4a-latm";
+ case "m4p":
+ return "audio/mp4a-latm";
+ case "m4u":
+ return "video/vnd.mpegurl";
+ case "m4v":
+ return "video/x-m4v";
+ case "mov":
+ return "video/quicktime";
+ case "mp2":
+ return "audio/x-mpeg";
+ case "mp3":
+ return "audio/x-mpeg";
+ case "mp4":
+ return "video/mp4";
+ case "mpc":
+ return "application/vnd.mpohun.certificate";
+ case "mpe":
+ return "video/mpeg";
+ case "mpeg":
+ return "video/mpeg";
+ case "mpg":
+ return "video/mpeg";
+ case "mpg4":
+ return "video/mp4";
+ case "mpga":
+ return "audio/mpeg";
+ case "msg":
+ return "application/vnd.ms-outlook";
+ case "ogg":
+ return "audio/ogg";
+ case "pdf":
+ return "application/pdf";
+ case "png":
+ return "image/png";
+ case "pps":
+ return "application/vnd.ms-powerpoint";
+ case "ppt":
+ return "application/vnd.ms-powerpoint";
+ case "pptx":
+ return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
+ case "prop":
+ return "text/plain";
+ case "rc":
+ return "text/plain";
+ case "rmvb":
+ return "audio/x-pn-realaudio";
+ case "rtf":
+ return "application/rtf";
+ case "sh":
+ return "text/plain";
+ case "tar":
+ return "application/x-tar";
+ case "tgz":
+ return "application/x-compressed";
+ case "txt":
+ return "text/plain";
+ case "wav":
+ return "audio/x-wav";
+ case "wma":
+ return "audio/x-ms-wma";
+ case "wmv":
+ return "audio/x-ms-wmv";
+ case "wps":
+ return "application/vnd.ms-works";
+ case "xml":
+ return "text/plain";
+ case "z":
+ return "application/x-compress";
+ case "zip":
+ return "application/x-zip-compressed";
+ default:
+ return "*/*";
+ }
+ }
+
+
}
\ No newline at end of file
diff --git a/lib/ui/utils/message.dart b/lib/ui/utils/message.dart
index 0a28527..32575ef 100644
--- a/lib/ui/utils/message.dart
+++ b/lib/ui/utils/message.dart
@@ -1,13 +1,12 @@
// ignore_for_file: unrelated_type_equality_checks, avoid_print
import 'dart:convert';
-
-import 'package:collection/collection.dart';
import 'package:flutter/cupertino.dart';
-import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/ui/constants/history_message_constant.dart';
import 'package:tencent_cloud_chat_uikit/ui/constants/time.dart';
+import 'package:collection/collection.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
class MessageUtils {
// 判断CallingData的方式和Trtc的方法一致
@@ -101,9 +100,12 @@ class MessageUtils {
}
static String? _getOpUserNick(V2TimGroupMemberInfo? opUser) {
- return TencentUtils.checkString(opUser?.friendRemark) ??
- TencentUtils.checkString(opUser?.nickName) ??
- TencentUtils.checkString(opUser?.userID) ?? "";
+ if(opUser == null){
+ return "";
+ }
+ return TencentUtils.checkString(opUser.friendRemark) ??
+ TencentUtils.checkString(opUser.nickName) ??
+ TencentUtils.checkString(opUser.userID);
}
static String? _getMemberNickName(V2TimGroupMemberInfo e) {
@@ -155,21 +157,21 @@ class MessageUtils {
break;
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_INVITE:
final option5 =
- memberList!.map((e) => _getMemberNickName(e!).toString()).join("、");
+ memberList!.map((e) => _getMemberNickName(e!).toString()).join("、");
final inviteUser = _getOpUserNick(operationMember);
displayMessage = '$inviteUser' +
TIM_t_para("邀请{{option5}}加入群组", "邀请$option5加入群组")(option5: option5);
break;
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_KICKED:
final option4 =
- memberList!.map((e) => _getMemberNickName(e!).toString()).join("、");
+ memberList!.map((e) => _getMemberNickName(e!).toString()).join("、");
final kickUser = _getOpUserNick(operationMember);
displayMessage = '$kickUser' +
TIM_t_para("将{{option4}}踢出群组", "将$option4踢出群组")(option4: option4);
break;
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_JOIN:
final option3 =
- memberList!.map((e) => _getMemberNickName(e!).toString()).join("、");
+ memberList!.map((e) => _getMemberNickName(e!).toString()).join("、");
displayMessage = TIM_t_para("用户{{option3}}加入了群聊", "用户$option3加入了群聊")(
option3: option3);
break;
@@ -186,7 +188,7 @@ class MessageUtils {
break;
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_SET_ADMIN:
final adminMember =
- memberList!.map((e) => _getMemberNickName(e!).toString()).join("、");
+ memberList!.map((e) => _getMemberNickName(e!).toString()).join("、");
final opMember = _getOpUserNick(operationMember);
final option1 = adminMember;
displayMessage = '$opMember' +
@@ -195,7 +197,7 @@ class MessageUtils {
break;
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_CANCEL_ADMIN:
final adminMember =
- memberList!.map((e) => _getMemberNickName(e!).toString()).join("、");
+ memberList!.map((e) => _getMemberNickName(e!).toString()).join("、");
final opMember = _getOpUserNick(operationMember);
final option1 = adminMember;
displayMessage = '$opMember' +
@@ -276,46 +278,14 @@ class MessageUtils {
}
}
- static Future getAbstractMessage(V2TimMessage message,
- List groupMemberList) async {
- final msgType = message.elemType;
- switch (msgType) {
- case MessageElemType.V2TIM_ELEM_TYPE_CUSTOM:
- return handleCustomMessageString(message);
- case MessageElemType.V2TIM_ELEM_TYPE_SOUND:
- return TIM_t("[语音]");
- case MessageElemType.V2TIM_ELEM_TYPE_TEXT:
- return message.textElem!.text as String;
- case MessageElemType.V2TIM_ELEM_TYPE_FACE:
- return TIM_t("[表情]");
- case MessageElemType.V2TIM_ELEM_TYPE_FILE:
- final String? option2 = message.fileElem!.fileName ?? "";
- return TIM_t_para("[文件] {{option2}}", "[文件] $option2")(
- option2: option2);
- case MessageElemType.V2TIM_ELEM_TYPE_GROUP_TIPS:
- return await MessageUtils.groupTipsMessageAbstract(
- message.groupTipsElem!, groupMemberList);
- case MessageElemType.V2TIM_ELEM_TYPE_IMAGE:
- return TIM_t("[图片]");
- case MessageElemType.V2TIM_ELEM_TYPE_VIDEO:
- return TIM_t("[视频]");
- case MessageElemType.V2TIM_ELEM_TYPE_LOCATION:
- return TIM_t("[位置]");
- case MessageElemType.V2TIM_ELEM_TYPE_MERGER:
- return TIM_t("[聊天记录]");
- default:
- return TIM_t("未知消息");
- }
- }
-
static V2TimImage? getImageFromImgList(
List? list, List order) {
V2TimImage? img;
try {
for (String type in order) {
img = list?.firstWhere(
- (e) =>
- e?.type == HistoryMessageDartConstant.V2_TIM_IMAGE_TYPES[type],
+ (e) =>
+ e?.type == HistoryMessageDartConstant.V2_TIM_IMAGE_TYPES[type],
orElse: () => null);
}
} catch (e) {
@@ -332,10 +302,10 @@ class MessageUtils {
final displayName = friendRemark.isNotEmpty
? friendRemark
: nameCard.isNotEmpty
- ? nameCard
- : nickName.isNotEmpty
- ? nickName
- : sender;
+ ? nameCard
+ : nickName.isNotEmpty
+ ? nickName
+ : sender;
return displayName.toString();
}
diff --git a/lib/ui/utils/permission.dart b/lib/ui/utils/permission.dart
index 74b1e29..675d05b 100644
--- a/lib/ui/utils/permission.dart
+++ b/lib/ui/utils/permission.dart
@@ -1,6 +1,7 @@
// ignore_for_file: unused_import
import 'dart:io';
+
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
@@ -11,6 +12,7 @@ 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/ui/utils/platform.dart';
+
class PermissionRequestInfo extends StatefulWidget {
final Function removeOverLay;
final int permissionType;
@@ -32,14 +34,14 @@ class _PermissionRequestInfo extends TIMUIKitState
@override
void initState() {
super.initState();
- WidgetsBinding.instance?.addObserver(this);
+ WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
widget.removeOverLay();
super.dispose();
- WidgetsBinding.instance?.removeObserver(this);
+ WidgetsBinding.instance.removeObserver(this);
}
@override
@@ -65,11 +67,21 @@ class _PermissionRequestInfo extends TIMUIKitState
"icon": "images/chat_permission_icon_mic.png",
"text": TIM_t("为方便您发送语音消息、拍摄视频以及音视频通话,请允许我们使用麦克风进行录音。")
},
+ 9: {
+ "name": TIM_t("相册"),
+ "icon": "images/chat_permission_icon_file.png",
+ "text": TIM_t("为方便您查看和选择相册里的图片视频发送给朋友,以及保存内容到设备,请允许我们访问您设备上的照片、媒体内容。")
+ },
15: {
"name": TIM_t("存储"),
"icon": "images/chat_permission_icon_file.png",
"text": TIM_t("为方便您查看和选择相册里的图片视频发送给朋友,以及保存内容到设备,请允许我们访问您设备上的照片、媒体内容。")
},
+ 32: {
+ "name": TIM_t("相册"),
+ "icon": "images/chat_permission_icon_file.png",
+ "text": TIM_t("为方便您查看和选择相册里的图片视频发送给朋友,以及保存内容到设备,请允许我们访问您设备上的照片、媒体内容。")
+ },
}[widget.permissionType];
final option2 = permission?["name"] ?? "";
return Stack(
@@ -128,6 +140,9 @@ class _PermissionRequestInfo extends TIMUIKitState
}
class Permissions {
+
+ static OverlayEntry? _entry;
+
static List _names(BuildContext context) {
return [
TIM_t("日历"),
@@ -161,6 +176,10 @@ class Permissions {
'bluetoothScan',
'bluetoothAdvertise',
'bluetoothConnect',
+ 'nearbyWifiDevices',
+ TIM_t("视频"),
+ 'audio',
+ 'scheduleExactAlarm'
];
}
@@ -199,6 +218,10 @@ class Permissions {
'bluetoothScan',
'bluetoothAdvertise',
'bluetoothConnect',
+ 'nearbyWifiDevices',
+ TIM_t(" 访问相册中视频权限,以正常使用发送视频等功能。"),
+ 'audio',
+ 'scheduleExactAlarm'
];
return _prefix + appName + _postfixList[value];
}
@@ -220,7 +243,10 @@ class Permissions {
isShowPermissionPage,
);
if (shouldRequestPermission != null && shouldRequestPermission) {
- return await Permission.byValue(value).request().isGranted;
+ final isGranted = await Permission.byValue(value).request().isGranted;
+ _entry?.remove();
+ _entry = null;
+ return isGranted;
}
return shouldRequestPermission ?? false;
}
@@ -241,11 +267,13 @@ class Permissions {
static showPermissionRequestInfoDialog(BuildContext context, value) async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String appName = packageInfo.appName;
- OverlayEntry? _entry;
final entry = OverlayEntry(builder: (context) {
return PermissionRequestInfo(
appName: appName,
- removeOverLay: () => _entry?.remove(),
+ removeOverLay: () {
+ _entry?.remove();
+ _entry = null;
+ },
permissionType: value,
);
});
@@ -269,6 +297,7 @@ class Permissions {
String appName = packageInfo.appName;
final option2 = _names(context)[value];
final permissionText = _permissionText(context, appName, value);
+
void closeDialog() {
Navigator.of(context).pop(false);
}
diff --git a/lib/ui/utils/platform.dart b/lib/ui/utils/platform.dart
index 3aa485e..4272f2d 100644
--- a/lib/ui/utils/platform.dart
+++ b/lib/ui/utils/platform.dart
@@ -5,13 +5,23 @@ class PlatformUtils {
PlatformUtils._internal();
static late bool _isAndroid;
static late bool _isIos;
+ static late bool _isMobile;
static late bool _isWeb;
+ static late bool _isWindows;
+ static late bool _isMacOS;
+ static late bool _isLinux;
+ static late bool _isDesktop;
static bool _isInstantiation = false;
factory PlatformUtils() {
if (!_isInstantiation) {
_isAndroid = !kIsWeb && Platform.isAndroid;
_isIos = !kIsWeb && Platform.isIOS;
+ _isMobile = _isAndroid || _isIos;
+ _isWindows = !kIsWeb && Platform.isWindows;
+ _isMacOS = !kIsWeb && Platform.isMacOS;
+ _isLinux = !kIsWeb && Platform.isLinux;
+ _isDesktop = _isMacOS || _isWindows || _isLinux;
_isWeb = kIsWeb;
_isInstantiation = true;
}
@@ -32,4 +42,20 @@ class PlatformUtils {
get isIOS {
return _isIos;
}
+
+ get isWindows {
+ return _isWindows;
+ }
+
+ get isMacOS {
+ return _isMacOS;
+ }
+
+ get isMobile {
+ return _isMobile;
+ }
+
+ bool get isDesktop => _isDesktop;
+
+ bool get isLinux => _isLinux;
}
diff --git a/lib/ui/utils/screen_shot.dart b/lib/ui/utils/screen_shot.dart
new file mode 100644
index 0000000..00e7894
--- /dev/null
+++ b/lib/ui/utils/screen_shot.dart
@@ -0,0 +1,104 @@
+import 'dart:async';
+import 'dart:io';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:package_info_plus/package_info_plus.dart';
+import 'package:path/path.dart' as p;
+import 'package:path_provider/path_provider.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
+import 'package:uuid/uuid.dart';
+
+class ScreenshotHelper {
+ static Future captureScreen() async {
+ await requestScreenRecordingPermission();
+ String directory;
+
+ if(PlatformUtils().isWindows){
+ final String documentsDirectoryPath =
+ "${Platform.environment['USERPROFILE']}";
+ PackageInfo packageInfo = await PackageInfo.fromPlatform();
+ String pkgName = packageInfo.packageName;
+ directory = p.join(documentsDirectoryPath, "Documents", ".TencentCloudChat",
+ pkgName, "screenshots");
+ }else{
+ final dic = await getApplicationSupportDirectory();
+ directory = dic.path;
+ }
+
+ const uuid = Uuid();
+ final fileName = 'screenshot_${uuid.v4()}.png';
+ final filePath = '$directory/$fileName';
+ if (Platform.isMacOS) {
+ // 在macOS平台上使用screencapture工具
+ final result = await Process.run(
+ 'screencapture',
+ ['-i', '-s', '-o', filePath],
+ );
+ if (result.exitCode == 0) {
+ return filePath;
+ } else {
+ return null;
+ }
+ } else if (Platform.isWindows) {
+ // 在Windows平台上使用snippingtool工具
+ final result = await Process.run(
+ 'snippingtool',
+ ['/clip', filePath],
+ );
+ if (result.exitCode == 0) {
+ return filePath;
+ } else {
+ return null;
+ }
+ } else {
+ // 不支持的平台
+ return null;
+ }
+ }
+
+ static Future requestScreenRecordingPermission() async {
+ if (Platform.isMacOS) {
+ final result = await Process.run(
+ 'sh', ['-c', 'echo ${Platform.environment['USER']}']);
+ final username = result.stdout.toString().trim();
+ const script =
+ 'tell application "System Events" to return exists (processes where name is "ControlCenter")';
+ final process = await Process.run('osascript', ['-e', script]);
+ final isControlCenterRunning = process.stdout.toString().trim() == 'true';
+
+ if (!isControlCenterRunning) {
+ await Process.run('open', ['-a', 'ControlCenter']);
+ await Future.delayed(const Duration(seconds: 1));
+ }
+
+ final script2 = 'tell application "ControlCenter" to activate\n'
+ 'tell application "System Events"\n'
+ ' tell process "ControlCenter"\n'
+ ' click menu item "Screen Recording" of menu "File" of menu bar 1\n'
+ ' delay 0.5\n'
+ ' keystroke "$username" & return\n'
+ ' end tell\n'
+ 'end tell\n';
+ await Process.run('osascript', ['-e', script2]);
+
+ await Future.delayed(const Duration(seconds: 1));
+ final isScreenRecordingEnabled = await SystemChannels.platform
+ .invokeMethod('isScreenRecordingEnabled');
+ return isScreenRecordingEnabled ?? false;
+ } else {
+ return true;
+ }
+ }
+
+ static Future getImageSize(String imagePath) async {
+ final bytes = await File(imagePath).readAsBytes();
+ final completer = Completer();
+ final imageStream =
+ Image.memory(bytes).image.resolve(ImageConfiguration.empty);
+ imageStream.addListener(ImageStreamListener((imageInfo, _) {
+ completer.complete(Size(
+ imageInfo.image.width.toDouble(), imageInfo.image.height.toDouble()));
+ }));
+ return completer.future;
+ }
+}
diff --git a/lib/ui/utils/screen_utils.dart b/lib/ui/utils/screen_utils.dart
index 922a9e7..73b839c 100644
--- a/lib/ui/utils/screen_utils.dart
+++ b/lib/ui/utils/screen_utils.dart
@@ -2,20 +2,40 @@
import 'package:flutter/cupertino.dart';
-enum ScreenType { Desktop, Tablet, Handset, Watch }
+enum DeviceType { Desktop, Mobile }
class FormFactor {
static double desktop = 900;
- static double tablet = 600;
static double handset = 300;
}
-class ScreenUtils {
- static ScreenType getFormFactor(BuildContext context) {
- double deviceWidth = MediaQuery.of(context).size.shortestSide;
- if (deviceWidth > FormFactor.desktop) return ScreenType.Desktop;
- if (deviceWidth > FormFactor.tablet) return ScreenType.Tablet;
- if (deviceWidth > FormFactor.handset) return ScreenType.Handset;
- return ScreenType.Watch;
+class TUIKitScreenUtils {
+ static DeviceType? deviceType;
+
+ static DeviceType getFormFactor([BuildContext? context]) {
+ if (deviceType != null) return deviceType!;
+
+ 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({
+ required Widget defaultWidget,
+ Widget? desktopWidget,
+ Widget? mobileWidget,
+ }) {
+ if (deviceType == DeviceType.Desktop) return desktopWidget ?? defaultWidget;
+ return mobileWidget ?? defaultWidget;
}
}
diff --git a/lib/ui/utils/sound_record.dart b/lib/ui/utils/sound_record.dart
index c04402e..78ec249 100644
--- a/lib/ui/utils/sound_record.dart
+++ b/lib/ui/utils/sound_record.dart
@@ -1,5 +1,5 @@
import 'dart:async';
-
+import 'package:audioplayers/audioplayers.dart';
import 'package:flutter_plugin_record_plus/const/play_state.dart';
import 'package:flutter_plugin_record_plus/const/response.dart';
import 'package:flutter_plugin_record_plus/index.dart';
@@ -14,33 +14,37 @@ class SoundPlayer {
static final FlutterPluginRecord _recorder = FlutterPluginRecord();
static SoundInterruptListener? _soundInterruptListener;
static bool isInited = false;
+ static final AudioPlayer _audioPlayer = AudioPlayer();
static initSoundPlayer() {
if (!isInited) {
_recorder.init();
+ AudioPlayer.global.setGlobalAudioContext(const AudioContext());
isInited = true;
}
}
- static play({required String url}) {
- _recorder.stopPlay();
+ static Future play({required String url}) async {
+ _audioPlayer.stop();
if (_soundInterruptListener != null) {
_soundInterruptListener!();
}
- _recorder.playByPath(url, 'url');
+ await _audioPlayer.play(UrlSource(url));
}
static stop() {
- _recorder.stopPlay();
+ _audioPlayer.stop();
}
static dispose() {
+ _audioPlayer.dispose();
_recorder.dispose();
}
- static StreamSubscription playStateListener(
- {required PlayStateListener listener}) =>
- _recorder.responsePlayStateController.listen(listener);
+ static StreamSubscription playStateListener(
+ {required void Function(PlayerState)? listener}) =>
+ _audioPlayer.onPlayerStateChanged.listen(listener);
+
static setSoundInterruptListener(SoundInterruptListener listener) {
_soundInterruptListener = listener;
diff --git a/lib/ui/utils/time_ago.dart b/lib/ui/utils/time_ago.dart
index dbb2ac7..95cdbcc 100644
--- a/lib/ui/utils/time_ago.dart
+++ b/lib/ui/utils/time_ago.dart
@@ -11,18 +11,6 @@ class TimeAgo {
];
}
- List daysMap() {
- return [
- TIM_t("星期天"),
- TIM_t("星期一"),
- TIM_t("星期二"),
- TIM_t("星期三"),
- TIM_t("星期四"),
- TIM_t("星期五"),
- TIM_t("星期六")
- ];
- }
-
List weekdayMap() {
return [
'',
@@ -36,7 +24,7 @@ class TimeAgo {
];
}
- String getYearMounthDate(DateTime dateTime) {
+ String getYearMonthDate(DateTime dateTime) {
String month = dateTime.month.toString();
String date = dateTime.day.toString();
return dateTime.year.toString() +
@@ -48,7 +36,7 @@ class TimeAgo {
date;
}
- String getMounthDate(DateTime dateTime) {
+ String getMonthDate(DateTime dateTime) {
String month = dateTime.month.toString();
String date = dateTime.day.toString();
return (month.length == 1 ? '0' : '') +
@@ -59,39 +47,33 @@ class TimeAgo {
}
String getTimeStringForChat(int timeStamp) {
- final formatedTimeStamp = timeStamp * 1000;
- final DateTime date =
- DateTime.fromMillisecondsSinceEpoch(formatedTimeStamp);
- final currentTimeStamp = DateTime.now().millisecondsSinceEpoch;
+ final DateTime date = DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000);
final Duration duration = DateTime.now().difference(date);
+ final int diffDays = duration.inDays +
+ (duration.inMinutes >
+ DateTime.now()
+ .difference(DateTime(DateTime.now().year,
+ DateTime.now().month, DateTime.now().day))
+ .inMinutes
+ ? 1
+ : 0);
+ final int diffMinutes = duration.inMinutes;
- int diffDays = duration.inDays;
- final diffMinutes = duration.inMinutes;
var res;
// 一个礼拜之内
if (diffDays > 0 && diffDays < 7) {
- final String formatTodayZero =
- DateFormat('yyyyMMdd').format(DateTime.now());
- final todayZero = DateTime.parse(formatTodayZero).millisecondsSinceEpoch;
- final todayDiff = currentTimeStamp - todayZero;
-
- final isTwoDay = todayDiff < (diffMinutes - diffDays * 1440) * 60000;
- if (isTwoDay) {
- diffDays = diffDays + 1;
- }
-
if (diffDays <= 2) {
res = dayMap()[diffDays - 1];
} else {
- res = daysMap()[date.weekday];
+ res = weekdayMap()[date.weekday];
}
} else if (diffDays >= 7) {
//当年内
if (date.year == DateTime.now().year) {
- res = getMounthDate(date);
+ res = getMonthDate(date);
} else {
- res = getYearMounthDate(date);
+ res = getYearMonthDate(date);
}
} else {
if (diffMinutes > 1) {
@@ -99,9 +81,9 @@ class TimeAgo {
final String option2 = diffMinutes.toString();
res = TIM_t_para("{{option2}} 分钟前", "$option2 分钟前")(option2: option2);
} else {
- final prefix = date.hour > 12 ? TIM_t("下午") : TIM_t("上午");
- final timeStr = DateFormat('hh:mm').format(date);
- res = "$prefix $timeStr";
+ res =
+ "${date.hour}:${date.minute < 10 ? date.minute.toString() + "0" : date.minute}";
+ // res = "$prefix $timeStr";
}
} else {
res = TIM_t("现在");
@@ -115,26 +97,27 @@ class TimeAgo {
var nowTime = DateTime.now();
nowTime = DateTime(nowTime.year, nowTime.month, nowTime.day);
var ftime = DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000);
- var preFix = ftime.hour >= 12 ? TIM_t("下午") : TIM_t("上午");
- final timeStr = DateFormat('hh:mm').format(ftime);
- // 一年外 年月日 + 上/下午 + 时间 (12小时制)
+ // var preFix = ftime.hour >= 12 ? TIM_t("下午") : TIM_t("上午");
+ final timeStr =
+ DateFormat('HH:mm').format(ftime); // Use 'HH:mm' for 24-hour format
+ // 一年外 年月日 + 时间 (24小时制)
if (nowTime.year != ftime.year) {
- return '${DateFormat('yyyy-MM-dd').format(ftime)} $preFix $timeStr';
+ return '${DateFormat('yyyy-MM-dd').format(ftime)} $timeStr';
}
- // 一年内一周外 月日 + 上/下午 + 时间 (12小时制)
+ // 一年内一周外 月日 + 时间 (24小时制)
if (ftime.isBefore(nowTime.subtract(const Duration(days: 6)))) {
- return '${DateFormat('MM-dd').format(ftime)} $preFix $timeStr';
+ return '${DateFormat('MM-dd').format(ftime)} $timeStr';
}
- // 一周内一天外 星期 + 上/下午 + 时间 (12小时制)
+ // 一周内一天外 星期 + 时间 (24小时制)
if (ftime.isBefore(nowTime.subtract(const Duration(days: 1)))) {
- return '${weekdayMap()[ftime.weekday]} $preFix $timeStr';
+ return '${weekdayMap()[ftime.weekday]} $timeStr';
}
- // 昨日 昨天 + 上/下午 + 时间 (12小时制)
+ // 昨日 昨天 + 时间 (24小时制)
if (nowTime.day != ftime.day) {
- String option2 = '$preFix $timeStr';
+ String option2 = timeStr;
return TIM_t_para("昨天 {{option2}}", "昨天 $option2")(option2: option2);
}
- // 同年月日 上/下午 + 时间 (12小时制)
- return '$preFix $timeStr';
+ // 同年月日 时间 (24小时制)
+ return timeStr;
}
}
diff --git a/lib/ui/views/TIMUIKitAddFriend/tim_uikit_add_friend.dart b/lib/ui/views/TIMUIKitAddFriend/tim_uikit_add_friend.dart
index 3ba37fc..aef87b5 100644
--- a/lib/ui/views/TIMUIKitAddFriend/tim_uikit_add_friend.dart
+++ b/lib/ui/views/TIMUIKitAddFriend/tim_uikit_add_friend.dart
@@ -1,5 +1,8 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
+import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_wide_modal_operation_key.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
+import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.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_state.dart';
@@ -20,11 +23,15 @@ class TIMUIKitAddFriend extends StatefulWidget {
/// The life cycle hooks for adding friends and contact business logic
final AddFriendLifeCycle? lifeCycle;
+
+ final VoidCallback? closeFunc;
+
const TIMUIKitAddFriend(
{Key? key,
this.isShowDefaultGroup = false,
this.lifeCycle,
- required this.onTapAlreadyFriendsItem})
+ required this.onTapAlreadyFriendsItem,
+ this.closeFunc})
: super(key: key);
@override
@@ -45,6 +52,9 @@ class _TIMUIKitAddFriendState extends TIMUIKitState {
Widget _searchResultItemBuilder(
V2TimUserFullInfo friendInfo, TUITheme theme) {
+ final isDesktopScreen =
+ TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
+
final faceUrl = friendInfo.faceUrl ?? "";
final userID = friendInfo.userID ?? "";
final String showName =
@@ -74,14 +84,32 @@ class _TIMUIKitAddFriendState extends TIMUIKitState {
return;
}
- Navigator.pushReplacement(
- context,
- MaterialPageRoute(
- builder: (context) => SendApplication(
- lifeCycle: widget.lifeCycle,
- isShowDefaultGroup: widget.isShowDefaultGroup ?? false,
- friendInfo: friendInfo,
- model: _selfInfoViewModel)));
+ if (isDesktopScreen) {
+ if (widget.closeFunc != null) {
+ widget.closeFunc!();
+ }
+ TUIKitWidePopup.showPopupWindow(
+ operationKey: TUIKitWideModalOperationKey.addFriend,
+ context: context,
+ width: MediaQuery.of(context).size.width * 0.3,
+ height: MediaQuery.of(context).size.width * 0.4,
+ title: TIM_t("添加好友"),
+ child: (closeFuncSendApplication) => SendApplication(
+ lifeCycle: widget.lifeCycle,
+ isShowDefaultGroup: widget.isShowDefaultGroup ?? false,
+ friendInfo: friendInfo,
+ model: _selfInfoViewModel),
+ );
+ } else {
+ Navigator.pushReplacement(
+ context,
+ MaterialPageRoute(
+ builder: (context) => SendApplication(
+ lifeCycle: widget.lifeCycle,
+ isShowDefaultGroup: widget.isShowDefaultGroup ?? false,
+ friendInfo: friendInfo,
+ model: _selfInfoViewModel)));
+ }
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
@@ -89,9 +117,9 @@ class _TIMUIKitAddFriendState extends TIMUIKitState {
// crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
- width: 48,
- height: 48,
- margin: const EdgeInsets.only(right: 12),
+ width: isDesktopScreen ? 38 : 48,
+ height: isDesktopScreen ? 38 : 48,
+ margin: const EdgeInsets.only(right: 16),
child: Avatar(faceUrl: faceUrl, showName: showName),
),
Column(
@@ -99,7 +127,9 @@ class _TIMUIKitAddFriendState extends TIMUIKitState {
children: [
Text(
showName,
- style: TextStyle(color: theme.darkTextColor, fontSize: 18),
+ style: TextStyle(
+ color: theme.darkTextColor,
+ fontSize: isDesktopScreen ? 16 : 18),
),
const SizedBox(
height: 4,
diff --git a/lib/ui/views/TIMUIKitAddFriend/tim_uikit_send_application.dart b/lib/ui/views/TIMUIKitAddFriend/tim_uikit_send_application.dart
index 883dbc7..6b0515b 100644
--- a/lib/ui/views/TIMUIKitAddFriend/tim_uikit_send_application.dart
+++ b/lib/ui/views/TIMUIKitAddFriend/tim_uikit_send_application.dart
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
-import 'package:tencent_im_base/tencent_im_base.dart';
+import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/add_friend_life_cycle.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
@@ -53,26 +54,9 @@ class _SendApplicationState extends TIMUIKitState {
: userID) ??
"";
final option2 = widget.friendInfo.selfSignature ?? "";
- return Scaffold(
- appBar: AppBar(
- title: Text(
- TIM_t("添加好友"),
- style: TextStyle(color: theme.white, fontSize: 17),
- ),
- shadowColor: theme.white,
- flexibleSpace: Container(
- decoration: BoxDecoration(
- gradient: LinearGradient(colors: [
- theme.lightPrimaryColor ?? CommonColor.lightPrimaryColor,
- theme.primaryColor ?? CommonColor.primaryColor
- ]),
- ),
- ),
- iconTheme: IconThemeData(
- color: theme.white,
- ),
- ),
- body: SingleChildScrollView(
+
+ Widget sendApplicationBody(){
+ return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -95,7 +79,7 @@ class _SendApplicationState extends TIMUIKitState {
Text(
showName,
style:
- TextStyle(color: theme.darkTextColor, fontSize: 18),
+ TextStyle(color: theme.darkTextColor, fontSize: 18),
),
const SizedBox(
height: 4,
@@ -103,16 +87,16 @@ class _SendApplicationState extends TIMUIKitState {
Text(
"ID: $userID",
style:
- TextStyle(fontSize: 13, color: theme.weakTextColor),
+ TextStyle(fontSize: 13, color: theme.weakTextColor),
),
const SizedBox(
height: 4,
),
- Text(
+ if(TencentUtils.checkString(option2) != null)Text(
TIM_t_para("个性签名: {{option2}}", "个性签名: $option2")(
option2: option2),
style:
- TextStyle(fontSize: 13, color: theme.weakTextColor),
+ TextStyle(fontSize: 13, color: theme.weakTextColor),
),
],
)
@@ -184,19 +168,19 @@ class _SendApplicationState extends TIMUIKitState {
Container(
color: theme.white,
padding:
- const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
TIM_t("分组"),
style:
- TextStyle(color: theme.darkTextColor, fontSize: 16),
+ TextStyle(color: theme.darkTextColor, fontSize: 16),
),
Text(
TIM_t("我的好友"),
style:
- TextStyle(color: theme.darkTextColor, fontSize: 16),
+ TextStyle(color: theme.darkTextColor, fontSize: 16),
)
],
),
@@ -213,7 +197,7 @@ class _SendApplicationState extends TIMUIKitState {
if (widget.lifeCycle?.shouldAddFriend != null &&
await widget.lifeCycle!.shouldAddFriend(userID, remark,
- friendGroup, addWording, context) ==
+ friendGroup, addWording, context) ==
false) {
return;
}
@@ -246,7 +230,29 @@ class _SendApplicationState extends TIMUIKitState {
)
],
),
+ );
+ }
+
+ return TUIKitScreenUtils.getDeviceWidget(
+ desktopWidget: Container(
+ padding: const EdgeInsets.only(top: 10),
+ color: theme.weakBackgroundColor,
+ child: sendApplicationBody(),
),
- );
+ defaultWidget: Scaffold(
+ appBar: AppBar(
+ title: Text(
+ TIM_t("添加好友"),
+ style: TextStyle(color: theme.appbarTextColor, fontSize: 17),
+ ),
+ shadowColor: theme.white,
+ backgroundColor: theme.appbarBgColor ??
+ theme.primaryColor,
+ iconTheme: IconThemeData(
+ color: theme.appbarTextColor,
+ ),
+ ),
+ body: sendApplicationBody(),
+ ));
}
}
diff --git a/lib/ui/views/TIMUIKitAddGroup/tim_uikit_add_group.dart b/lib/ui/views/TIMUIKitAddGroup/tim_uikit_add_group.dart
index 4a96136..23d4798 100644
--- a/lib/ui/views/TIMUIKitAddGroup/tim_uikit_add_group.dart
+++ b/lib/ui/views/TIMUIKitAddGroup/tim_uikit_add_group.dart
@@ -1,4 +1,7 @@
import 'package:flutter/material.dart';
+import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_wide_modal_operation_key.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
+import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/add_group_life_cycle.dart';
@@ -19,8 +22,10 @@ class TIMUIKitAddGroup extends StatefulWidget {
final Function(String groupID, V2TimConversation conversation)
onTapExistGroup;
+ final VoidCallback? closeFunc;
+
const TIMUIKitAddGroup(
- {Key? key, this.lifeCycle, required this.onTapExistGroup})
+ {Key? key, this.lifeCycle, required this.onTapExistGroup, this.closeFunc})
: super(key: key);
@override
@@ -67,6 +72,8 @@ class _TIMUIKitAddGroupState extends TIMUIKitState {
final groupID = groupInfo.groupID;
final showName = groupInfo.groupName ?? groupID;
final groupType = _getGroupType(groupInfo.groupType);
+ final isDesktopScreen =
+ TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
return InkWell(
onTap: () async {
final V2TimConversation? groupConversation =
@@ -76,16 +83,38 @@ class _TIMUIKitAddGroupState extends TIMUIKitState {
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("您已是群成员"),
infoCode: 6660202));
+ if (widget.closeFunc != null) {
+ widget.closeFunc!();
+ }
widget.onTapExistGroup(groupID, groupConversation);
return;
}
- Navigator.pushReplacement(
- context,
- MaterialPageRoute(
- builder: (context) => SendJoinGroupApplication(
- lifeCycle: widget.lifeCycle,
- groupInfo: groupInfo,
- )));
+
+ if(isDesktopScreen){
+ if (widget.closeFunc != null) {
+ widget.closeFunc!();
+ }
+ TUIKitWidePopup.showPopupWindow(
+ operationKey: TUIKitWideModalOperationKey.addGroup,
+ context: context,
+ width: MediaQuery.of(context).size.width * 0.3,
+ height: MediaQuery.of(context).size.width * 0.4,
+ title: TIM_t("添加群聊"),
+ child: (closeFuncSendApplication) => SendJoinGroupApplication(
+ lifeCycle: widget.lifeCycle,
+ groupInfo: groupInfo,
+ ),
+ );
+ }else{
+ Navigator.pushReplacement(
+ context,
+ MaterialPageRoute(
+ builder: (context) => SendJoinGroupApplication(
+ lifeCycle: widget.lifeCycle,
+ groupInfo: groupInfo,
+ )));
+ }
+
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
@@ -93,9 +122,9 @@ class _TIMUIKitAddGroupState extends TIMUIKitState {
// crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
- width: 48,
- height: 48,
- margin: const EdgeInsets.only(right: 12),
+ width: isDesktopScreen ? 38 : 48,
+ height: isDesktopScreen ? 38 : 48,
+ margin: const EdgeInsets.only(right: 16),
child: Avatar(faceUrl: faceUrl, showName: showName),
),
Column(
@@ -103,7 +132,7 @@ class _TIMUIKitAddGroupState extends TIMUIKitState {
children: [
Text(
showName,
- style: const TextStyle(fontSize: 18),
+ style: TextStyle(fontSize: isDesktopScreen ? 16 : 18),
),
Text(
"ID: $groupID",
diff --git a/lib/ui/views/TIMUIKitAddGroup/tim_uikit_send_application.dart b/lib/ui/views/TIMUIKitAddGroup/tim_uikit_send_application.dart
index 178c192..1c64605 100644
--- a/lib/ui/views/TIMUIKitAddGroup/tim_uikit_send_application.dart
+++ b/lib/ui/views/TIMUIKitAddGroup/tim_uikit_send_application.dart
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/add_group_life_cycle.dart';
@@ -74,26 +75,9 @@ class _SendJoinGroupApplicationState
final groupID = widget.groupInfo.groupID;
final showName = widget.groupInfo.groupName ?? groupID;
final option1 = _getGroupType(widget.groupInfo.groupType);
- return Scaffold(
- appBar: AppBar(
- title: Text(
- TIM_t("进群申请"),
- style: TextStyle(color: theme.white, fontSize: 17),
- ),
- shadowColor: theme.white,
- flexibleSpace: Container(
- decoration: BoxDecoration(
- gradient: LinearGradient(colors: [
- theme.lightPrimaryColor ?? CommonColor.lightPrimaryColor,
- theme.primaryColor ?? CommonColor.primaryColor
- ]),
- ),
- ),
- iconTheme: IconThemeData(
- color: theme.white,
- ),
- ),
- body: SingleChildScrollView(
+
+ Widget sendGroupApplicationBody(){
+ return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -116,7 +100,7 @@ class _SendJoinGroupApplicationState
Text(
showName,
style:
- TextStyle(color: theme.darkTextColor, fontSize: 18),
+ TextStyle(color: theme.darkTextColor, fontSize: 18),
),
const SizedBox(
height: 4,
@@ -124,7 +108,7 @@ class _SendJoinGroupApplicationState
Text(
"ID: $groupID",
style:
- TextStyle(fontSize: 13, color: theme.weakTextColor),
+ TextStyle(fontSize: 13, color: theme.weakTextColor),
),
const SizedBox(
height: 4,
@@ -133,7 +117,7 @@ class _SendJoinGroupApplicationState
TIM_t_para("群类型: {{option1}}", "群类型: $option1")(
option1: option1),
style:
- TextStyle(fontSize: 12, color: theme.weakTextColor),
+ TextStyle(fontSize: 12, color: theme.weakTextColor),
),
],
)
@@ -181,7 +165,26 @@ class _SendJoinGroupApplicationState
)
],
),
+ );
+ }
+
+
+ return TUIKitScreenUtils.getDeviceWidget(
+ desktopWidget: sendGroupApplicationBody(),
+ defaultWidget: Scaffold(
+ appBar: AppBar(
+ title: Text(
+ TIM_t("进群申请"),
+ style: TextStyle(color: theme.appbarTextColor, fontSize: 17),
+ ),
+ shadowColor: theme.white,
+ backgroundColor: theme.appbarBgColor ??
+ theme.primaryColor,
+ iconTheme: IconThemeData(
+ color: theme.appbarTextColor,
+ ),
),
- );
+ body: sendGroupApplicationBody(),
+ ));
}
}
diff --git a/lib/ui/views/TIMUIKitBlackList/tim_uikit_black_list.dart b/lib/ui/views/TIMUIKitBlackList/tim_uikit_black_list.dart
index bb97ccd..8c21604 100644
--- a/lib/ui/views/TIMUIKitBlackList/tim_uikit_black_list.dart
+++ b/lib/ui/views/TIMUIKitBlackList/tim_uikit_black_list.dart
@@ -7,6 +7,7 @@ import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_friendsh
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
@@ -50,35 +51,33 @@ class _TIMUIKitBlackListState extends TIMUIKitState {
final theme = Provider.of(context).theme;
final showName = _getShowName(friendInfo);
final faceUrl = friendInfo.userProfile?.faceUrl ?? "";
- return Slidable(
- endActionPane: ActionPane(motion: const DrawerMotion(), children: [
- SlidableAction(
- onPressed: (context) async {
- await _friendshipViewModel
- .deleteFromBlockList([friendInfo.userID]);
- },
- backgroundColor: theme.cautionColor ?? CommonColor.cautionColor,
- foregroundColor: theme.white,
- label: TIM_t("删除"),
- autoClose: true,
- )
- ]),
- child: GestureDetector(
+ final isDesktopScreen =
+ TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
+
+ Widget itemWidget() {
+ return Material(
+ color: theme.wideBackgroundColor,
+ child: InkWell(
onTap: () {
if (widget.onTapItem != null) {
widget.onTapItem!(friendInfo);
}
},
child: Container(
- padding: const EdgeInsets.only(top: 10, left: 16),
+ padding: const EdgeInsets.only(top: 10, left: 16, right: 16),
+ decoration: BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ color: theme.weakDividerColor ??
+ CommonColor.weakDividerColor))),
child: Row(
children: [
Container(
padding: const EdgeInsets.only(bottom: 12),
margin: const EdgeInsets.only(right: 12),
child: SizedBox(
- height: 40,
- width: 40,
+ height: isDesktopScreen ? 30 : 40,
+ width: isDesktopScreen ? 30 : 40,
child: Avatar(faceUrl: faceUrl, showName: showName),
),
),
@@ -86,19 +85,45 @@ class _TIMUIKitBlackListState extends TIMUIKitState {
child: Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(top: 10, bottom: 20),
- decoration: BoxDecoration(
- border: Border(
- bottom: BorderSide(
- color: theme.weakDividerColor ??
- CommonColor.weakDividerColor))),
child: Text(
showName,
- style: TextStyle(color: theme.black, fontSize: 18),
+ style: TextStyle(
+ color: theme.black, fontSize: isDesktopScreen ? 14 : 18),
),
- ))
+ )),
+ if (isDesktopScreen)
+ OutlinedButton(
+ onPressed: () {
+ _friendshipViewModel
+ .deleteFromBlockList([friendInfo.userID]);
+ },
+ child: Text(
+ TIM_t("移出黑名单"),
+ style: TextStyle(color: theme.primaryColor),
+ ))
],
),
),
+ ),
+ );
+ }
+
+ return TUIKitScreenUtils.getDeviceWidget(
+ desktopWidget: itemWidget(),
+ defaultWidget: Slidable(
+ endActionPane: ActionPane(motion: const DrawerMotion(), children: [
+ SlidableAction(
+ onPressed: (context) async {
+ await _friendshipViewModel
+ .deleteFromBlockList([friendInfo.userID]);
+ },
+ backgroundColor: theme.cautionColor ?? CommonColor.cautionColor,
+ foregroundColor: theme.white,
+ label: TIM_t("删除"),
+ autoClose: true,
+ )
+ ]),
+ child: itemWidget(),
));
}
@@ -106,10 +131,6 @@ class _TIMUIKitBlackListState extends TIMUIKitState {
return widget.itemBuilder ?? _itemBuilder;
}
- @override
- void initState() {
- super.initState();
- }
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue.dart b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue.dart
index 56babcc..6b876bc 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue.dart
@@ -8,8 +8,8 @@ enum MessageListTongueType {
none,
toLatest,
showUnread,
- showPrevious,
atMe,
+ showPrevious,
atAll,
}
@@ -36,9 +36,9 @@ class TIMUIKitHistoryMessageListTongue extends TIMUIKitStatelessWidget {
TIMUIKitHistoryMessageListTongue({
Key? key,
- required this.previousCount,
required this.valueType,
required this.onClick,
+ required this.previousCount,
required this.unreadCount,
this.tongueItemBuilder,
this.atNum = "",
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue_container.dart b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue_container.dart
index e03eea7..321c8f9 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue_container.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue_container.dart
@@ -1,7 +1,7 @@
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
-import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
import 'package:tencent_im_base/tencent_im_base.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';
@@ -61,20 +61,22 @@ class _TIMUIKitHistoryMessageListTongueContainerState
final screenHeight = MediaQuery.of(context).size.height;
final offset = widget.scrollController.offset;
final conversationUnreadCount = widget.model.getConversationUnreadCount();
- if (offset <= 0.0 &&
- conversationUnreadCount != 0) {
+ if (offset <= 0.0 && conversationUnreadCount != 0) {
widget.model.showLatestUnread();
}
if (widget.scrollController.offset <=
widget.scrollController.position.minScrollExtent &&
- !widget.scrollController.position.outOfRange && !widget.model.haveMoreLatestData) {
+ !widget.scrollController.position.outOfRange &&
+ !widget.model.haveMoreLatestData) {
changePositionState(HistoryMessagePosition.bottom);
} else if (widget.scrollController.offset <= screenHeight * 1.6 &&
widget.scrollController.offset > 0 &&
- !widget.scrollController.position.outOfRange && !widget.model.haveMoreLatestData) {
+ !widget.scrollController.position.outOfRange &&
+ !widget.model.haveMoreLatestData) {
changePositionState(HistoryMessagePosition.inTwoScreen);
} else if (widget.scrollController.offset > screenHeight * 1.6 &&
- !widget.scrollController.position.outOfRange && !widget.model.haveMoreLatestData) {
+ !widget.scrollController.position.outOfRange &&
+ !widget.model.haveMoreLatestData) {
changePositionState(HistoryMessagePosition.awayTwoScreen);
}
}
@@ -85,9 +87,8 @@ class _TIMUIKitHistoryMessageListTongueContainerState
MessageListTongueType _getTongueValueType(
List? groupAtInfoList) {
-
- if(globalModel.getMessageListPosition(widget.model.conversationID) ==
- HistoryMessagePosition.notShowLatest){
+ if (globalModel.getMessageListPosition(widget.model.conversationID) ==
+ HistoryMessagePosition.notShowLatest) {
return MessageListTongueType.none;
}
if (groupAtInfoList != null &&
@@ -152,22 +153,24 @@ class _TIMUIKitHistoryMessageListTongueContainerState
} else {
widget.scrollToIndexBySeq(groupAtInfoList!.removeAt(0)!.seq);
}
- } else if ((widget.conversation.unreadCount ?? 0) > 20 && !isClickShowPrevious) {
- try{
+ } else if ((widget.conversation.unreadCount ?? 0) > 20 &&
+ !isClickShowPrevious) {
+ try {
isClickShowPrevious = true;
final String? lastSeqString =
widget.conversation.lastMessage?.seq;
final int? lastSeq =
- TencentUtils.checkString(lastSeqString) != null
- ? int.parse(lastSeqString!)
- : null;
+ TencentUtils.checkString(lastSeqString) != null
+ ? int.parse(lastSeqString!)
+ : null;
final int? previousCount = widget.conversation.unreadCount;
if (lastSeq != null && previousCount != null) {
final targetSeq = lastSeq - previousCount;
- await widget.model.loadListForSpecificMessage(seq: targetSeq);
+ await widget.model
+ .loadListForSpecificMessage(seq: targetSeq);
// widget.scrollToIndexBySeq((targetSeq + 1).toString());
}
- }catch(e){
+ } catch (e) {
// TODO: 这里后续加个弹窗提示客户,找消息失败了
}
// widget.model.loadListForSpecificMessage(seq: count);
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_tongue_item.dart b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_tongue_item.dart
index 6c83348..5f0b663 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_tongue_item.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_tongue_item.dart
@@ -21,7 +21,7 @@ class TIMUIKitTongueItem extends TIMUIKitStatelessWidget {
final int previousCount;
- TIMUIKitTongueItem( {
+ TIMUIKitTongueItem({
Key? key,
required this.onClick,
required this.valueType,
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list.dart b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list.dart
index f5f0dbb..b9239b5 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list.dart
@@ -8,17 +8,14 @@ import 'package:scroll_to_index/scroll_to_index.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_im_base/tencent_im_base.dart';
-
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.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';
-
// ignore: unused_import
import 'package:tencent_cloud_chat_uikit/ui/utils/optimize_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_config.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/keepalive_wrapper.dart';
-
import 'TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue.dart';
import 'TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue_container.dart';
@@ -76,7 +73,8 @@ class TIMUIKitHistoryMessageList extends StatefulWidget {
final V2TimMessage? initFindingMsg;
/// use for load more message
- final Future Function(String?, LoadDirection direction, [int?]) onLoadMore;
+ final Future Function(String?, LoadDirection direction, [int?])
+ onLoadMore;
/// configuration for list view
final TIMUIKitHistoryMessageListConfig? mainHistoryListConfig;
@@ -99,7 +97,7 @@ class TIMUIKitHistoryMessageList extends StatefulWidget {
this.initFindingMsg,
this.isAllowScroll = true,
this.mainHistoryListConfig,
- required this.conversation})
+ required this.conversation})
: super(key: key);
@override
@@ -272,8 +270,8 @@ class _TIMUIKitHistoryMessageListState
} else {
if (widget.model.haveMoreData) {
findingSeq = targetSeq;
- widget.onLoadMore(
- _getMessageId(widget.messageList.length - 1), LoadDirection.previous, singleLoadAmount);
+ widget.onLoadMore(_getMessageId(widget.messageList.length - 1),
+ LoadDirection.previous, singleLoadAmount);
} else {
showCantFindMsg();
}
@@ -308,15 +306,15 @@ class _TIMUIKitHistoryMessageListState
}
}
- List _getRecivedMessageList(int recivedMessageListCount) {
- if (recivedMessageListCount == 0) {
+ List _getReceivedMessageList(int receivedMessageListCount) {
+ if (receivedMessageListCount == 0) {
return [];
}
final haveTimeStampMessage =
- widget.messageList[recivedMessageListCount]?.elemType == 11;
+ widget.messageList[receivedMessageListCount]?.elemType == 11;
final endPoint = haveTimeStampMessage
- ? recivedMessageListCount + 1
- : recivedMessageListCount;
+ ? receivedMessageListCount + 1
+ : receivedMessageListCount;
return widget.messageList.sublist(0, endPoint).reversed.toList();
}
@@ -334,22 +332,23 @@ class _TIMUIKitHistoryMessageListState
final globalModel = context.read();
final receivedNewMessageList = globalModel.receivedMessageListCount;
final shouldShowUnreadMessage = receivedNewMessageList > 0;
- final unreadMessageList = _getRecivedMessageList(receivedNewMessageList);
+ final unreadMessageList = _getReceivedMessageList(receivedNewMessageList);
final readMessageList = messageList
.sublist(unreadMessageList.length, messageList.length)
.toList();
- final throteFunction = OptimizeUtils.multiThrottle((index, LoadDirection direction) async {
+ final throttleFunction =
+ OptimizeUtils.multiThrottle((index, LoadDirection direction) async {
final msgID =
TIMUIKitChatUtils.getMessageIDWithinIndex(readMessageList, index);
await widget.onLoadMore(msgID, direction);
}, 20);
- final throteFunctionWithMsgID = OptimizeUtils.multiThrottle((msgID, LoadDirection direction) async {
+ final throttleFunctionWithMsgID =
+ OptimizeUtils.multiThrottle((msgID, LoadDirection direction) async {
await widget.onLoadMore(msgID, direction);
}, 200);
-
if (findingMsg != null) {
_onScrollToIndex(findingMsg!);
} else if (findingSeq != "") {
@@ -360,167 +359,187 @@ class _TIMUIKitHistoryMessageListState
return "${message?.msgID} - ${message?.timestamp} - ${message?.seq} -${message?.id}";
}
- return Container(
- color: theme.chatBgColor,
- child: Stack(
- alignment: Alignment.topCenter,
- children: [
- CustomScrollView(
- center: shouldShowUnreadMessage ? centerKey : null,
- key: widget.mainHistoryListConfig?.key,
- primary: widget.mainHistoryListConfig?.primary,
- physics: (widget.isAllowScroll == false)
- ? const NeverScrollableScrollPhysics()
- : widget.mainHistoryListConfig?.physics,
- // padding: widget.mainHistoryListConfig?.padding ?? EdgeInsets.zero,
- // itemExtent: widget.mainHistoryListConfig?.itemExtent,
- // prototypeItem: widget.mainHistoryListConfig?.prototypeItem,
- cacheExtent: widget.mainHistoryListConfig?.cacheExtent ?? 1500,
- semanticChildCount:
- widget.mainHistoryListConfig?.semanticChildCount,
- dragStartBehavior:
- widget.mainHistoryListConfig?.dragStartBehavior ??
- DragStartBehavior.start,
- keyboardDismissBehavior:
- widget.mainHistoryListConfig?.keyboardDismissBehavior ??
- ScrollViewKeyboardDismissBehavior.manual,
- restorationId: widget.mainHistoryListConfig?.restorationId,
- clipBehavior:
- widget.mainHistoryListConfig?.clipBehavior ?? Clip.hardEdge,
- reverse: true,
- shrinkWrap: !shouldShowUnreadMessage,
+ return Stack(
+ alignment: Alignment.topCenter,
+ children: [
+ Scrollbar(
controller: _autoScrollController,
- slivers: [
- SliverPadding(
- padding:
- widget.mainHistoryListConfig?.padding ?? EdgeInsets.zero,
- sliver: SliverList(
- delegate: SliverChildBuilderDelegate(
- (BuildContext context, int index) {
- final messageItem = unreadMessageList[index];
- if(index == unreadMessageList.length - 1 && widget.model.haveMoreLatestData == true){
- throteFunctionWithMsgID(messageItem?.msgID ?? "", LoadDirection.latest);
- }
- return AutoScrollTag(
- controller: _autoScrollController,
- index: -index,
- key: ValueKey(
- getMessageIdentifier(messageItem, index)),
- highlightColor: Colors.black.withOpacity(0.1),
- child: KeepAliveWrapper(
- keepAlive: messageItem?.elemType ==
- MessageElemType.V2TIM_ELEM_TYPE_SOUND,
- child: Container(
- padding: const EdgeInsets.symmetric(
- horizontal: 16),
- child:
- _getMessageItemBuilder(messageItem))),
- );
- },
- childCount: unreadMessageList.length,
- findChildIndexCallback: (Key key) {
- final ValueKey valueKey =
- key as ValueKey;
- final String data = valueKey.value;
- final int index = unreadMessageList.indexWhere(
- (element) =>
- getMessageIdentifier(element, 0) == data);
- return index != -1 ? index : null;
- })),
- ),
- SliverPadding(
- padding: EdgeInsets.zero,
- key: centerKey,
- ),
- SliverPadding(
- padding:
- widget.mainHistoryListConfig?.padding ?? EdgeInsets.zero,
- sliver: SliverList(
- delegate: SliverChildBuilderDelegate(
- (BuildContext context, int index) {
- final messageItem = readMessageList[index];
- if (index == readMessageList.length - 1) {
- if (widget.model.haveMoreData) {
- throteFunction(index, LoadDirection.previous);
- return Column(
- children: [
- LoadingAnimationWidget.staggeredDotsWave(
- color: theme.weakTextColor ?? Colors.grey,
- size: 28,
- ),
- AutoScrollTag(
- controller: _autoScrollController,
- index: -index,
- key: ValueKey(getMessageIdentifier(
- messageItem, index)),
- highlightColor:
- Colors.black.withOpacity(0.1),
- child: KeepAliveWrapper(
+ child: CustomScrollView(
+ center: shouldShowUnreadMessage ? centerKey : null,
+ key: widget.mainHistoryListConfig?.key,
+ primary: widget.mainHistoryListConfig?.primary,
+ physics: (widget.isAllowScroll == false)
+ ? const NeverScrollableScrollPhysics()
+ : widget.mainHistoryListConfig?.physics,
+ // padding: widget.mainHistoryListConfig?.padding ?? EdgeInsets.zero,
+ // itemExtent: widget.mainHistoryListConfig?.itemExtent,
+ // prototypeItem: widget.mainHistoryListConfig?.prototypeItem,
+ cacheExtent: widget.mainHistoryListConfig?.cacheExtent ?? 1500,
+ semanticChildCount:
+ widget.mainHistoryListConfig?.semanticChildCount,
+ dragStartBehavior:
+ widget.mainHistoryListConfig?.dragStartBehavior ??
+ DragStartBehavior.start,
+ keyboardDismissBehavior:
+ widget.mainHistoryListConfig?.keyboardDismissBehavior ??
+ ScrollViewKeyboardDismissBehavior.manual,
+ restorationId: widget.mainHistoryListConfig?.restorationId,
+ clipBehavior:
+ widget.mainHistoryListConfig?.clipBehavior ?? Clip.hardEdge,
+ reverse: true,
+ shrinkWrap: !shouldShowUnreadMessage,
+ controller: _autoScrollController,
+ slivers: [
+ SliverPadding(
+ padding:
+ widget.mainHistoryListConfig?.padding ?? EdgeInsets.zero,
+ sliver: SliverList(
+ delegate: SliverChildBuilderDelegate(
+ (BuildContext context, int index) {
+ final messageItem = unreadMessageList[index];
+ if (index == unreadMessageList.length - 1 &&
+ widget.model.haveMoreLatestData == true) {
+ throttleFunctionWithMsgID(
+ messageItem?.msgID ?? "",
+ LoadDirection.latest);
+ }
+ return AutoScrollTag(
+ controller: _autoScrollController,
+ index: -index,
+ key: ValueKey(
+ getMessageIdentifier(messageItem, index)),
+ highlightColor: Colors.black.withOpacity(0.1),
+ child: KeepAliveWrapper(
+ keepAlive: messageItem?.elemType ==
+ MessageElemType.V2TIM_ELEM_TYPE_SOUND,
+ child: Container(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 16),
+ child:
+ _getMessageItemBuilder(messageItem))),
+ );
+ },
+ childCount: unreadMessageList.length,
+ findChildIndexCallback: (Key key) {
+ final ValueKey valueKey =
+ key as ValueKey;
+ final String data = valueKey.value;
+ final int index = unreadMessageList.indexWhere(
+ (element) =>
+ getMessageIdentifier(element, 0) == data);
+ return index != -1 ? index : null;
+ })),
+ ),
+ SliverPadding(
+ padding: EdgeInsets.zero,
+ key: centerKey,
+ ),
+ SliverPadding(
+ padding:
+ widget.mainHistoryListConfig?.padding ?? EdgeInsets.zero,
+ sliver: Selector(
+ selector: (context, model) {
+ return model.haveMoreData;
+ },
+ shouldRebuild: (previous, next) {
+ return previous != next;
+ },
+ builder: (context, haveMoreData, child) {
+ return SliverList(
+ delegate: SliverChildBuilderDelegate(
+ (BuildContext context, int index) {
+ final messageItem = readMessageList[index];
+ final isSelf = messageItem?.isSelf ?? true;
+ if (index == readMessageList.length - 1) {
+ if (haveMoreData) {
+ throttleFunction(
+ index, LoadDirection.previous);
+ return Column(
+ children: [
+ LoadingAnimationWidget
+ .staggeredDotsWave(
+ color: theme.weakTextColor ??
+ Colors.grey,
+ size: 28,
+ ),
+ AutoScrollTag(
+ controller: _autoScrollController,
+ index: -index,
+ key: ValueKey(getMessageIdentifier(
+ messageItem, index)),
+ highlightColor:
+ Colors.black.withOpacity(0.1),
+ child: KeepAliveWrapper(
+ keepAlive: messageItem
+ ?.elemType ==
+ MessageElemType
+ .V2TIM_ELEM_TYPE_SOUND,
+ child: Container(
+ padding: const EdgeInsets
+ .symmetric(
+ horizontal: 16),
+ child: _getMessageItemBuilder(
+ messageItem))),
+ ),
+ ],
+ );
+ }
+ }
+ if (index == 0 &&
+ widget.model.haveMoreLatestData == true &&
+ globalModel.receivedMessageListCount < 10) {
+ throttleFunction(index, LoadDirection.latest);
+ }
+ return AutoScrollTag(
+ controller: _autoScrollController,
+ index: -index,
+ key: ValueKey(
+ getMessageIdentifier(messageItem, index)),
+ highlightColor: Colors.black.withOpacity(0.1),
+ child: KeepAliveWrapper(
keepAlive: messageItem?.elemType ==
MessageElemType.V2TIM_ELEM_TYPE_SOUND,
child: Container(
- padding: const EdgeInsets.symmetric(
- horizontal: 16),
+ padding: EdgeInsets.only(
+ left: isSelf ? 0 : 16,
+ right: isSelf ? 16 : 0),
child: _getMessageItemBuilder(
- messageItem)),
- ),
- ),
- ],
- );
- }
- }
- if(index == 0 && widget.model.haveMoreLatestData == true && globalModel.receivedMessageListCount < 10){
- throteFunction(index, LoadDirection.latest);
- }
- return AutoScrollTag(
- controller: _autoScrollController,
- index: -index,
- key: ValueKey(
- getMessageIdentifier(messageItem, index)),
- highlightColor: Colors.black.withOpacity(0.1),
- child: KeepAliveWrapper(
- keepAlive: messageItem?.elemType ==
- MessageElemType.V2TIM_ELEM_TYPE_SOUND,
- child: Container(
- padding: const EdgeInsets.symmetric(
- horizontal: 16),
- child:
- _getMessageItemBuilder(messageItem))),
- );
- },
- addAutomaticKeepAlives: true,
- childCount: readMessageList.length,
- findChildIndexCallback: (Key key) {
- final ValueKey valueKey =
- key as ValueKey;
- final String data = valueKey.value;
- final int index = readMessageList.indexWhere(
- (element) =>
- getMessageIdentifier(element, 0) == data);
- return index > -1 ? index : null;
- }
- )),
- ),
- ],
- ),
- TIMUIKitHistoryMessageListTongueContainer(
- conversation: widget.conversation,
- model: widget.model,
- scrollController: _autoScrollController,
- scrollToIndexBySeq: _onScrollToIndexBySeq,
- groupAtInfoList: widget.groupAtInfoList,
- tongueItemBuilder: widget.tongueItemBuilder,
- ),
- if (loadingPlace == LoadingPlace.top)
- Positioned(
- top: 8,
- child: LoadingAnimationWidget.staggeredDotsWave(
- color: theme.weakTextColor ?? Colors.grey,
- size: 28,
- ),
+ messageItem))),
+ );
+ },
+ childCount: readMessageList.length,
+ findChildIndexCallback: (Key key) {
+ final ValueKey valueKey =
+ key as ValueKey;
+ final String data = valueKey.value;
+ final int index = readMessageList.indexWhere(
+ (element) =>
+ getMessageIdentifier(element, 0) ==
+ data);
+ return index > -1 ? index : null;
+ }));
+ },
+ ),
+ ),
+ ],
+ )),
+ TIMUIKitHistoryMessageListTongueContainer(
+ conversation: widget.conversation,
+ model: widget.model,
+ scrollController: _autoScrollController,
+ scrollToIndexBySeq: _onScrollToIndexBySeq,
+ groupAtInfoList: widget.groupAtInfoList,
+ tongueItemBuilder: widget.tongueItemBuilder,
+ ),
+ if (loadingPlace == LoadingPlace.top)
+ Positioned(
+ top: 8,
+ child: LoadingAnimationWidget.staggeredDotsWave(
+ color: theme.weakTextColor ?? Colors.grey,
+ size: 28,
),
- ],
- ),
+ ),
+ ],
);
}
}
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_item.dart b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_item.dart
index d626b6f..6383cab 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_item.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_item.dart
@@ -1,20 +1,22 @@
-// ignore_for_file: non_constant_identifier_names
-
import 'dart:convert';
+import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:provider/provider.dart';
+import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_wide_modal_operation_key.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_text_translate_elem.dart';
+import 'package:tencent_cloud_chat_uikit/ui/widgets/forward_message_screen.dart';
+import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.dart';
import 'package:tencent_super_tooltip/tencent_super_tooltip.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_statelesswidget.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_self_info_view_model.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/tencent_cloud_chat_uikit.dart';
@@ -30,9 +32,7 @@ import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageIt
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_face_elem.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_cloud_custom_data.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart';
-import 'package:tencent_cloud_chat_uikit/ui/widgets/loading.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/radio_button.dart';
-
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import '../TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_select_emoji.dart';
@@ -66,6 +66,15 @@ typedef MessageItemContent = Widget? Function(
VoidCallback clearJump,
);
+class MessageHoverControlItem {
+ String name;
+ Widget icon;
+ ValueChanged onClick;
+
+ MessageHoverControlItem(
+ {required this.name, required this.icon, required this.onClick});
+}
+
class MessageItemBuilder {
/// text message builder
final MessageItemContent? textMessageItemBuilder;
@@ -125,6 +134,19 @@ class MessageItemBuilder {
});
}
+class MessageToolTipItem {
+ final String label;
+ final String id;
+ final String iconImageAsset;
+ final VoidCallback onClick;
+
+ MessageToolTipItem(
+ {required this.label,
+ required this.id,
+ required this.iconImageAsset,
+ required this.onClick});
+}
+
class ToolTipsConfig {
final bool showReplyMessage;
final bool showMultipleChoiceMessage;
@@ -135,6 +157,8 @@ class ToolTipsConfig {
final bool showTranslation;
final Widget? Function(V2TimMessage message, Function() closeTooltip,
[Key? key, BuildContext? context])? additionalItemBuilder;
+ List Function(
+ V2TimMessage message, Function() closeTooltip)? additionalMessageToolTips;
ToolTipsConfig(
{this.showDeleteMessage = true,
@@ -144,7 +168,9 @@ class ToolTipsConfig {
this.showTranslation = true,
this.showCopyMessage = true,
this.showForwardMessage = true,
- this.additionalItemBuilder});
+ 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.")
+ this.additionalItemBuilder});
}
class TIMUIKitHistoryMessageListItem extends StatefulWidget {
@@ -152,7 +178,8 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
final V2TimMessage message;
/// tap remote user avatar callback function
- final void Function(String userID)? onTapForOthersPortrait;
+ final void Function(String userID, TapDownDetails tapDetails)?
+ onTapForOthersPortrait;
/// the function use for reply message, when click replied message can scroll to it.
final Function? onScrollToIndex;
@@ -167,7 +194,7 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
/// message item builder, works for customize all message types and row layout.
final MessageItemBuilder? messageItemBuilder;
- /// controll avatart hide or show
+ /// Control avatar hide or show
final bool showAvatar;
/// message sending status
@@ -185,7 +212,7 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
/// allow avatar can tap
final bool allowAvatarTap;
- /// allow notifi user when send reply message
+ /// Auto mention user when send reply message
final bool allowAtUserWhenReply;
@Deprecated(
@@ -260,7 +287,7 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
this.bottomRowBuilder,
this.isUseDefaultEmoji = false,
this.customEmojiStickerList = const [],
- this.textFieldController})
+ this.textFieldController})
: super(key: key);
@override
@@ -306,13 +333,17 @@ class _TIMUIKItHistoryMessageListItemState
extends TIMUIKitState
with TickerProviderStateMixin {
SuperTooltip? tooltip;
+
// ignore: unused_field
final MessageService _messageService = serviceLocator();
final TUISelfInfoViewModel selfInfoModel =
serviceLocator();
final TUIThemeViewModel themeModel = serviceLocator();
+
// bool isChecked = false;
final GlobalKey _key = GlobalKey();
+ bool isShowWideToolTip = false;
+ TapDownDetails? _tapDetails;
closeTooltip() {
tooltip?.close();
@@ -323,8 +354,11 @@ class _TIMUIKItHistoryMessageListItemState
message.cloudCustomData != null && message.cloudCustomData != "";
if (hasCustomData) {
try {
- final CloudCustomData messageCloudCustomData =
- CloudCustomData.fromJson(json.decode(message.cloudCustomData!));
+ final CloudCustomData messageCloudCustomData = CloudCustomData.fromJson(
+ json.decode(
+ TencentUtils.checkString(message.cloudCustomData) != null
+ ? message.cloudCustomData!
+ : "{}"));
if (messageCloudCustomData.messageReply != null) {
MessageRepliedData.fromJson(messageCloudCustomData.messageReply!);
return true;
@@ -343,7 +377,7 @@ class _TIMUIKItHistoryMessageListItemState
final isShowJump = (model.jumpMsgID == messageItem.msgID) &&
(messageItem.msgID?.isNotEmpty ?? false);
final MessageItemBuilder? messageItemBuilder = widget.messageItemBuilder;
- final isFromSelf = messageItem.isSelf ?? false;
+ final isFromSelf = messageItem.isSelf ?? true;
void clearJump() {
Future.delayed(const Duration(milliseconds: 100), () {
model.jumpMsgID = "";
@@ -382,7 +416,7 @@ class _TIMUIKItHistoryMessageListItemState
message: messageItem,
soundElem: messageItem.soundElem!,
msgID: messageItem.msgID ?? "",
- isFromSelf: messageItem.isSelf ?? false,
+ isFromSelf: messageItem.isSelf ?? true,
clearJump: clearJump,
isShowJump: isShowJump,
localCustomInt: messageItem.localCustomInt,
@@ -410,6 +444,8 @@ class _TIMUIKItHistoryMessageListItemState
fontStyle: widget.themeData?.messageTextStyle,
backgroundColor: widget.themeData?.messageBackgroundColor,
textPadding: widget.textPadding,
+ isUseDefaultEmoji: widget.isUseDefaultEmoji,
+ customEmojiStickerList: widget.customEmojiStickerList,
chatModel: model,
isShowMessageReaction: widget.isUseMessageReaction,
);
@@ -424,7 +460,7 @@ class _TIMUIKItHistoryMessageListItemState
return TIMUIKitTextElem(
chatModel: model,
message: messageItem,
- isFromSelf: messageItem.isSelf ?? false,
+ isFromSelf: messageItem.isSelf ?? true,
clearJump: clearJump,
isShowJump: isShowJump,
borderRadius: widget.themeData?.messageBorderRadius,
@@ -464,7 +500,7 @@ class _TIMUIKItHistoryMessageListItemState
message: messageItem,
messageID: messageItem.msgID,
fileElem: messageItem.fileElem,
- isSelf: messageItem.isSelf ?? false,
+ isSelf: messageItem.isSelf ?? true,
clearJump: clearJump,
isShowJump: isShowJump,
isShowMessageReaction: widget.isUseMessageReaction,
@@ -527,7 +563,7 @@ class _TIMUIKItHistoryMessageListItemState
)!;
}
return TIMUIKitMergerElem(
- messageItemBuilder: messageItemBuilder,
+ messageItemBuilder: messageItemBuilder,
model: model,
isShowJump: isShowJump,
clearJump: clearJump,
@@ -535,7 +571,7 @@ class _TIMUIKItHistoryMessageListItemState
isShowMessageReaction: widget.isUseMessageReaction,
mergerElem: messageItem.mergerElem!,
messageID: messageItem.msgID ?? "",
- isSelf: messageItem.isSelf ?? false);
+ isSelf: messageItem.isSelf ?? true);
default:
return Text(TIM_t("[未知消息]"));
}
@@ -545,8 +581,9 @@ class _TIMUIKItHistoryMessageListItemState
final messageItem = widget.message;
return Container(
padding: const EdgeInsets.only(bottom: 20),
- child:
- TIMUIKitGroupTipsElem(groupTipsElem: messageItem.groupTipsElem!, groupMemberList: model.groupMemberList ?? []));
+ child: TIMUIKitGroupTipsElem(
+ groupTipsElem: messageItem.groupTipsElem!,
+ groupMemberList: model.groupMemberList ?? []));
}
Widget _selfRevokeEditMessageBuilder(theme, model) {
@@ -611,12 +648,11 @@ class _TIMUIKItHistoryMessageListItemState
width: 100,
child: Container(
decoration: BoxDecoration(
- gradient: LinearGradient(colors: [
- const Color(0x00C0E1FF),
- theme.primaryColor ?? CommonColor.lightPrimaryColor
- ]),
- )
- ),
+ gradient: LinearGradient(colors: [
+ const Color(0x00C0E1FF),
+ theme.primaryColor ?? CommonColor.lightPrimaryColor
+ ]),
+ )),
),
),
Text(
@@ -635,12 +671,11 @@ class _TIMUIKItHistoryMessageListItemState
width: 100,
child: Container(
decoration: BoxDecoration(
- gradient: LinearGradient(colors: [
- theme.primaryColor ?? CommonColor.primaryColor,
- const Color(0x00C0E1FF),
- ]),
- )
- ),
+ gradient: LinearGradient(colors: [
+ theme.primaryColor ?? CommonColor.primaryColor,
+ const Color(0x00C0E1FF),
+ ]),
+ )),
),
),
],
@@ -648,15 +683,17 @@ class _TIMUIKItHistoryMessageListItemState
);
}
- bool isRevokable(int timestamp) =>
+ bool isRevocable(int timestamp) =>
(DateTime.now().millisecondsSinceEpoch / 1000).ceil() - timestamp < 120;
- _onLongPress(
+ _onOpenToolTip(
c,
V2TimMessage message,
TUIChatSeparateViewModel model,
TUITheme theme,
TapDownDetails? details,
+ bool? isFromWideTooltip,
+ bool? isShowMoreSticker,
) {
if (tooltip != null && tooltip!.isOpen) {
tooltip!.close();
@@ -665,29 +702,40 @@ class _TIMUIKItHistoryMessageListItemState
tooltip = null;
final screenHeight = MediaQuery.of(context).size.height;
- if (context.size!.height + 180 > screenHeight && !PlatformUtils().isWeb) {
- initTools(
- context: c,
- isLongMessage: true,
- model: model,
- details: details,
- theme: theme,
- );
- if (widget.onScrollToIndexBegin != null) {
- widget.onScrollToIndexBegin!(message);
- }
- Future.delayed(const Duration(milliseconds: 500), () {
- tooltip!.show(c);
- });
- } else {
- initTools(
+ final screenWidth = MediaQuery.of(context).size.width;
+ final isLongMessage =
+ context.size!.height + 350 > screenHeight && PlatformUtils().isMobile;
+ final isDesktopScreen =
+ TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
+ final tapDetails =
+ (isDesktopScreen || isLongMessage) ? (details ?? _tapDetails) : details;
+ final isSelf = message.isSelf ?? true;
+
+ final targetWidth =
+ min(MediaQuery.of(context).size.width * 0.84, 350).toDouble();
+ final double dx = !isSelf
+ ? min(tapDetails?.globalPosition.dx ?? targetWidth,
+ screenWidth - targetWidth)
+ : max(tapDetails?.globalPosition.dx ?? targetWidth, targetWidth)
+ .toDouble();
+ final double dy = min(
+ tapDetails?.globalPosition.dy ?? MediaQuery.of(context).size.height,
+ MediaQuery.of(context).size.height - 320)
+ .toDouble();
+ final finalTapDetail = tapDetails != null
+ ? TapDownDetails(
+ globalPosition: Offset(dx, dy),
+ )
+ : null;
+
+ initTools(
context: c,
model: model,
- details: details,
+ isShowMoreSticker: isShowMoreSticker,
+ details: finalTapDetail,
theme: theme,
- );
- tooltip!.show(c, targetCenter: details?.globalPosition);
- }
+ isFromWideToolTip: isFromWideTooltip);
+ tooltip!.show(c, targetCenter: finalTapDetail?.globalPosition);
}
_clickOnCurrentSticker(int sticker) async {
@@ -711,8 +759,15 @@ class _TIMUIKItHistoryMessageListItemState
bool isLongMessage = false,
required TUIChatSeparateViewModel model,
TUITheme? theme,
- TapDownDetails? details}) {
- final isSelf = widget.message.isSelf ?? false;
+ bool? isShowMoreSticker,
+ TapDownDetails? details,
+ bool? isFromWideToolTip}) {
+ final isUseMessageReaction = widget.message.elemType == 2
+ ? false
+ : model.chatConfig.isUseMessageReaction;
+ final isDesktopScreen =
+ TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
+ final isSelf = widget.message.isSelf ?? true;
double arrowTipDistance = 30;
double arrowBaseWidth = 10;
double arrowLength = 10;
@@ -727,12 +782,12 @@ class _TIMUIKItHistoryMessageListItemState
if (details != null && box != null) {
double screenWidth = MediaQuery.of(context).size.width;
final mousePosition = details.globalPosition;
- hasArrow = false;
+ hasArrow = isDesktopScreen ? false : true;
arrowTipDistance = 0;
arrowBaseWidth = 0;
arrowLength = 0;
popupDirection = TooltipDirection.down;
- if (isSelf) {
+ if (isSelf || (isFromWideToolTip ?? false)) {
right = screenWidth - mousePosition.dx;
} else {
left = mousePosition.dx;
@@ -744,14 +799,16 @@ class _TIMUIKItHistoryMessageListItemState
Offset offset = box.localToGlobal(Offset.zero);
double boxWidth = box.size.width;
if (isSelf) {
- right = screenWidth - offset.dx - boxWidth;
+ right = screenWidth -
+ offset.dx -
+ ((isUseMessageReaction) ? boxWidth : (boxWidth / 1.3));
} else {
left = offset.dx;
}
if (offset.dy < 300 && !isLongMessage && viewInsetsBottom == 0) {
selectEmojiPanelPosition = SelectEmojiPanelPosition.up;
popupDirection = TooltipDirection.down;
- } else if(viewInsetsBottom != 0 && offset.dy < 220){
+ } else if (viewInsetsBottom != 0 && offset.dy < 220) {
selectEmojiPanelPosition = SelectEmojiPanelPosition.up;
popupDirection = TooltipDirection.down;
}
@@ -773,16 +830,15 @@ class _TIMUIKItHistoryMessageListItemState
borderColor: theme?.white ?? Colors.white,
backgroundColor: theme?.white ?? Colors.white,
shadowColor: Colors.black26,
- hasShadow: true,
+ hasShadow: isDesktopScreen ? false : true,
borderWidth: 1.0,
showCloseButton: ShowCloseButton.none,
touchThroughAreaShape: ClipAreaShape.rectangle,
content: TIMUIKitMessageTooltip(
model: model,
+ isShowMoreSticker: isShowMoreSticker ?? false,
toolTipsConfig: widget.toolTipsConfig,
- isUseMessageReaction: widget.message.elemType == 2
- ? false
- : model.chatConfig.isUseMessageReaction,
+ isUseMessageReaction: isUseMessageReaction,
message: widget.message,
allowAtUserWhenReply: widget.allowAtUserWhenReply,
onLongPressForOthersHeadPortrait:
@@ -797,12 +853,6 @@ class _TIMUIKItHistoryMessageListItemState
);
}
- double getMaxWidth(isSelect) {
- final size = MediaQuery.of(context).size;
- final width = size.width;
- return width - (isSelect ? 180 : 150);
- }
-
Widget _getMessageItemBuilder(V2TimMessage message, int? messageStatues,
TUIChatSeparateViewModel model) {
final messageBuilder = _messageItemBuilder;
@@ -811,7 +861,7 @@ class _TIMUIKItHistoryMessageListItemState
}
// 弹出对话框
- Future showResendMsgFailDialg(BuildContext context) {
+ Future showResendMsgFailDialog(BuildContext context) {
return showDialog(
context: context,
builder: (context) {
@@ -837,6 +887,117 @@ class _TIMUIKItHistoryMessageListItemState
);
}
+ @override
+ void dispose() {
+ super.dispose();
+ if (tooltip?.isOpen ?? false) {
+ tooltip?.close();
+ }
+ }
+
+ bool isVoteMessage(V2TimMessage message) {
+ bool isvote = false;
+ V2TimCustomElem? custom = message.customElem;
+
+ if (custom != null) {
+ String? data = custom.data;
+ if (data != null && data.isNotEmpty) {
+ try {
+ Map mapData = json.decode(data);
+ if (mapData["businessID"] == "group_poll") {
+ isvote = true;
+ }
+ } catch (err) {
+ // err
+ }
+ }
+ }
+ return isvote;
+ }
+
+ List getMessageHoverControlBar(
+ TUIChatSeparateViewModel model, TUITheme theme) {
+ return [
+ if (widget.isUseMessageReaction ?? false)
+ MessageHoverControlItem(
+ name: TIM_t("表情回应"),
+ icon: Icon(
+ Icons.emoji_emotions,
+ size: 13,
+ color: hexToColor("8f959e"),
+ ),
+ onClick: (details) {
+ _onOpenToolTip(
+ context, widget.message, model, theme, details, true, true);
+ },
+ ),
+ if (widget.toolTipsConfig?.showReplyMessage ?? true)
+ MessageHoverControlItem(
+ name: TIM_t("回复"),
+ icon: Icon(
+ Icons.message,
+ size: 13,
+ color: hexToColor("8f959e"),
+ ),
+ onClick: (_) {
+ model.repliedMessage = widget.message;
+ if (widget.allowAtUserWhenReply &&
+ widget.onLongPressForOthersHeadPortrait != null &&
+ !(widget.message.isSelf ?? true)) {
+ widget.onLongPressForOthersHeadPortrait!(
+ widget.message.sender, widget.message.nickName);
+ }
+ },
+ ),
+ if ((widget.toolTipsConfig?.showForwardMessage ?? true) &&
+ !isVoteMessage(widget.message))
+ MessageHoverControlItem(
+ name: TIM_t("转发"),
+ icon: Icon(
+ Icons.send,
+ size: 13,
+ color: hexToColor("8f959e"),
+ ),
+ onClick: (_) {
+ model.addToMultiSelectedMessageList(widget.message);
+ TUIKitWidePopup.showPopupWindow(
+ operationKey: TUIKitWideModalOperationKey.forward,
+ context: context,
+ title: TIM_t("转发"),
+ submitWidget: Text(TIM_t("发送")),
+ width: MediaQuery.of(context).size.width * 0.5,
+ height: MediaQuery.of(context).size.height * 0.8,
+ onSubmit: () {
+ forwardMessageScreenKey.currentState?.handleForwardMessage();
+ },
+ child: (onClose) => Container(
+ padding: const EdgeInsets.symmetric(horizontal: 10),
+ child: ForwardMessageScreen(
+ conversationType: ConvType.c2c,
+ key: forwardMessageScreenKey,
+ onClose: onClose,
+ model: model,
+ ),
+ ),
+ theme: theme);
+ },
+ ),
+ MessageHoverControlItem(
+ name: TIM_t("更多"),
+ icon: Icon(
+ Icons.more_horiz,
+ size: 13,
+ color: hexToColor("8f959e"),
+ ),
+ onClick: (details) {
+ _onOpenToolTip(
+ context, widget.message, model, theme, details, true, false);
+ },
+ ),
+ ...?model.chatConfig.additionalDesktopMessageHoverBarItem
+ ];
+ }
+
_onMsgSendFailIconTap(V2TimMessage message, TUIChatSeparateViewModel model) {
final convID = model.conversationID;
final convType = model.conversationType;
@@ -848,12 +1009,99 @@ class _TIMUIKItHistoryMessageListItemState
context);
}
- @override
- void dispose() {
- super.dispose();
- if (tooltip?.isOpen ?? false) {
- tooltip?.close();
- }
+ Widget renderHoverTipAndReadStatus(TUIChatSeparateViewModel model,
+ bool isSelf, V2TimMessage message, bool isPeerRead, TUITheme theme) {
+ final isDesktopScreen =
+ TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
+ final wideHoverTipList = getMessageHoverControlBar(model, theme);
+ final lastItemName = wideHoverTipList.last.name;
+ return Column(
+ mainAxisSize: MainAxisSize.max,
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ if (isDesktopScreen && isShowWideToolTip)
+ Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(4),
+ border: Border.all(color: hexToColor("d9dde0"), width: 1)),
+ margin: const EdgeInsets.symmetric(horizontal: 4),
+ child: Row(
+ children: wideHoverTipList
+ .map((e) => Tooltip(
+ message: e.name,
+ preferBelow: false,
+ textStyle: TextStyle(fontSize: 12, color: theme.white),
+ child: Row(
+ children: [
+ InkWell(
+ onTapDown: e.onClick,
+ child: SizedBox(
+ width: 22,
+ height: 22,
+ child: e.icon,
+ ),
+ ),
+ if (lastItemName != e.name)
+ SizedBox(
+ width: 1,
+ height: 22,
+ child: Container(
+ color: theme.weakDividerColor,
+ ),
+ )
+ ],
+ ),
+ ))
+ .toList(),
+ ),
+ ),
+ if (!isDesktopScreen || !isShowWideToolTip)
+ const SizedBox(
+ height: 24,
+ ),
+ if (isSelf &&
+ message.status == MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL)
+ Container(
+ padding: const EdgeInsets.only(bottom: 3),
+ margin: const EdgeInsets.only(right: 6),
+ child: GestureDetector(
+ onTap: () async {
+ final reSend = await showResendMsgFailDialog(context);
+ if (reSend != null) {
+ _onMsgSendFailIconTap(message, model);
+ }
+ },
+ child: Icon(Icons.error, color: theme.cautionColor, size: 18),
+ )),
+ if (model.chatConfig.isShowReadingStatus &&
+ widget.showMessageReadRecipt &&
+ model.conversationType == ConvType.c2c &&
+ isSelf &&
+ (message.status == MessageStatus.V2TIM_MSG_STATUS_SEND_SUCC ||
+ message.status == MessageStatus.V2TIM_MSG_STATUS_SENDING))
+ Container(
+ padding: const EdgeInsets.only(bottom: 3),
+ margin: const EdgeInsets.only(right: 6),
+ child: Text(
+ isPeerRead ? TIM_t("已读") : TIM_t("未读"),
+ style: TextStyle(
+ color: theme.chatMessageItemUnreadStatusTextColor,
+ fontSize: 12),
+ ),
+ ),
+ if (model.chatConfig.isShowGroupReadingStatus &&
+ model.chatConfig.isShowGroupMessageReadReceipt &&
+ model.conversationType == ConvType.group &&
+ isSelf &&
+ (message.status == MessageStatus.V2TIM_MSG_STATUS_SEND_SUCC ||
+ message.status == MessageStatus.V2TIM_MSG_STATUS_SENDING))
+ TIMUIKitMessageReadReceipt(
+ messageItem: widget.message,
+ onTapAvatar: widget.onTapForOthersPortrait,
+ ),
+ ],
+ );
}
@override
@@ -865,7 +1113,7 @@ class _TIMUIKItHistoryMessageListItemState
final TUITheme theme = value.theme;
final message = widget.message;
final msgType = message.elemType;
- final isSelf = message.isSelf ?? false;
+ final isSelf = message.isSelf ?? true;
final msgStatus = message.status;
final isGroupTipsMsg =
msgType == MessageElemType.V2TIM_ELEM_TYPE_GROUP_TIPS;
@@ -880,10 +1128,12 @@ class _TIMUIKItHistoryMessageListItemState
isGroupMessage && model.chatConfig.isShowSelfNameInGroup;
final isShowNickNameForOthers =
isGroupMessage && model.chatConfig.isShowOthersNameInGroup;
+ final isDesktopScreen =
+ TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
if (isTimeDivider) {
return _timeDividerBuilder(theme, message.timestamp ?? 0, model);
}
- if(isLatestDivider){
+ if (isLatestDivider) {
return _latestDividerBuilder(theme);
}
void clearJump() {
@@ -908,7 +1158,7 @@ class _TIMUIKItHistoryMessageListItemState
if (isRevokedMsg) {
final displayName =
isSelf ? TIM_t("您") : message.nickName ?? message.sender;
- return isSelf && isRevokeEditable && isRevokable(message.timestamp!)
+ return isSelf && isRevokeEditable && isRevocable(message.timestamp!)
? _selfRevokeEditMessageBuilder(theme, model)
: _revokedMessageBuilder(theme, displayName ?? "");
}
@@ -938,7 +1188,7 @@ class _TIMUIKItHistoryMessageListItemState
children: [
if (model.isMultiSelect)
Container(
- margin: const EdgeInsets.only(right: 12, top: 10),
+ margin: EdgeInsets.only(right: 12, top: 10, left: isSelf ? 16 : 0),
child: CheckBoxButton(
isChecked: model.multiSelectedMessageList.contains(message),
onChanged: (value) {
@@ -951,260 +1201,250 @@ class _TIMUIKItHistoryMessageListItemState
),
),
Expanded(
- child: GestureDetector(
- behavior:
- model.isMultiSelect ? HitTestBehavior.translucent : null,
- onTap: () {
- if (model.isMultiSelect) {
- final checked =
- model.multiSelectedMessageList.contains(message);
- if (checked) {
- model.removeFromMultiSelectedMessageList(message);
- } else {
- model.addToMultiSelectedMessageList(message);
- }
+ child: MouseRegion(
+ onEnter: (_) {
+ if (isDesktopScreen) {
+ setState(() {
+ isShowWideToolTip = true;
+ });
}
},
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- mainAxisAlignment:
- isSelf ? MainAxisAlignment.end : MainAxisAlignment.start,
- children: [
- if (!isSelf && widget.showAvatar)
- GestureDetector(
- onTap: () {
- if (widget.onTapForOthersPortrait != null &&
- widget.allowAvatarTap) {
- widget
- .onTapForOthersPortrait!(message.sender ?? "");
- }
- },
- onLongPress: () {
- if (widget.onLongPressForOthersHeadPortrait !=
- null) {}
- widget.onLongPressForOthersHeadPortrait!(
- message.sender, message.nickName);
- },
- child: widget.userAvatarBuilder != null
- ? widget.userAvatarBuilder!(context, message)
- : Container(
- margin: (isSelf && isShowNickNameForSelf) ||
- (!isSelf && isShowNickNameForOthers)
- ? const EdgeInsets.only(top: 2)
- : null,
- child: SizedBox(
- width: 40,
- height: 40,
- child: Avatar(
- faceUrl: message.faceUrl ?? "",
- showName:
- MessageUtils.getDisplayName(message),
- ),
- ),
- ),
- ),
- Container(
- margin: widget.showAvatar
- ? (isSelf
- ? const EdgeInsets.only(right: 13)
- : const EdgeInsets.only(left: 13))
- : null,
- child: Column(
- crossAxisAlignment: isSelf
- ? CrossAxisAlignment.end
- : CrossAxisAlignment.start,
- children: [
- if ((isSelf && isShowNickNameForSelf) ||
- (!isSelf && isShowNickNameForOthers))
- widget.topRowBuilder != null
- ? widget.topRowBuilder!(context, message)
- : Container(
- margin: const EdgeInsets.only(bottom: 4),
- child: ConstrainedBox(
- constraints: BoxConstraints(
- maxWidth: MediaQuery.of(context)
- .size
- .width /
- 1.7),
- child: Text(
- MessageUtils.getDisplayName(message),
- overflow: TextOverflow.ellipsis,
- style: widget
- .themeData?.nickNameTextStyle ??
- TextStyle(
- fontSize: 12,
- color: theme.weakTextColor),
- ),
- )),
- Row(
- crossAxisAlignment: CrossAxisAlignment.end,
- children: [
- if (model.chatConfig.isShowReadingStatus &&
- widget.showMessageReadRecipt &&
- model.conversationType == ConvType.c2c &&
- isSelf &&
- message.status ==
- MessageStatus.V2TIM_MSG_STATUS_SEND_SUCC)
- Container(
- padding: const EdgeInsets.only(bottom: 3),
- margin: const EdgeInsets.only(right: 6),
- child: Text(
- isPeerRead ? TIM_t("已读") : TIM_t("未读"),
- style: TextStyle(
- color: theme
- .chatMessageItemUnreadStatusTextColor,
- fontSize: 12),
- ),
- ),
- if (model.chatConfig.isShowGroupReadingStatus &&
- model.chatConfig
- .isShowGroupMessageReadReceipt &&
- model.conversationType == ConvType.group &&
- isSelf &&
- message.status ==
- MessageStatus.V2TIM_MSG_STATUS_SEND_SUCC)
- TIMUIKitMessageReadReceipt(
- messageItem: widget.message,
- onTapAvatar: widget.onTapForOthersPortrait,
- ),
- if (widget.showMessageSending &&
- isSelf &&
- message.status ==
- MessageStatus.V2TIM_MSG_STATUS_SENDING)
- Container(
- padding: const EdgeInsets.only(bottom: 3),
- margin: const EdgeInsets.only(right: 6),
- child: const Loading(),
- ),
- if (isSelf &&
- message.status ==
- MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL)
- Container(
- padding: const EdgeInsets.only(bottom: 3),
- margin: const EdgeInsets.only(right: 6),
- child: GestureDetector(
- onTap: () async {
- final reSend =
- await showResendMsgFailDialg(
- context);
- if (reSend != null) {
- _onMsgSendFailIconTap(message, model);
- }
- },
- child: Icon(Icons.error,
- color: theme.cautionColor, size: 18),
- )),
- Container(
- constraints: BoxConstraints(
- // maxWidth: getMaxWidth(false),
- // maxWidth: getMaxWidth(false),
- maxWidth: constraints.maxWidth * 0.8,
- ),
- child: Builder(builder: (context) {
- return Column(
- crossAxisAlignment:
- (message.isSelf ?? false)
- ? CrossAxisAlignment.end
- : CrossAxisAlignment.start,
- children: [
- GestureDetector(
- child: IgnorePointer(
- ignoring: model.isMultiSelect,
- child: _getMessageItemBuilder(
- message, message.status, model)),
- onSecondaryTapDown: (details) {
- if (PlatformUtils().isWeb) {
- if (widget.allowLongPress) {
- _onLongPress(
- context,
- message,
- model,
- theme,
- details,
- );
- }
- if (widget.onLongPress != null) {
- widget.onLongPress!(context, message);
- }
- }
- },
- onLongPress: () {
- if (widget.allowLongPress) {
- _onLongPress(
- context,
- message,
- model,
- theme,
- null,
- );
- }
- if (widget.onLongPress != null) {
- widget.onLongPress!(context, message);
- }
- },
- ),
- Container(
- color: theme.white,
- child: TIMUIKitTextTranslationElem(
- message: message,
- isUseDefaultEmoji: widget.isUseDefaultEmoji,
- customEmojiStickerList: widget.customEmojiStickerList,
- isFromSelf: message.isSelf ?? false,
- isShowJump: false,
- clearJump: () {},
- chatModel: model),
- )
- ],
- );
- }),
- ),
- if (!isSelf &&
- message.elemType ==
- MessageElemType.V2TIM_ELEM_TYPE_SOUND &&
- message.localCustomInt != null &&
- message.localCustomInt !=
- HistoryMessageDartConstant.read)
- Padding(
- padding: const EdgeInsets.only(
- left: 5, bottom: 12),
- child: Icon(Icons.circle,
- color: theme.cautionColor, size: 10)),
- ],
- ),
- if (widget.bottomRowBuilder != null)
- widget.bottomRowBuilder!(context, message)
- ],
- ),
- ),
- if (widget.message.elemType == 6 && isDownloadWaiting)
- Container(
- margin: const EdgeInsets.only(top: 24, left: 6),
- child: LoadingAnimationWidget.threeArchedCircle(
- color: theme.weakTextColor ?? Colors.grey,
- size: 20,
- ),
- ),
- if (isSelf && widget.showAvatar)
- widget.userAvatarBuilder != null
- ? widget.userAvatarBuilder!(context, message)
- : SizedBox(
- width: 40,
- height: 40,
- child: GestureDetector(
- onTap: () {
+ onExit: (_) {
+ if (isDesktopScreen) {
+ setState(() {
+ isShowWideToolTip = false;
+ });
+ }
+ },
+ child: GestureDetector(
+ behavior:
+ model.isMultiSelect ? HitTestBehavior.translucent : null,
+ onTap: () {
+ if (model.isMultiSelect) {
+ final checked =
+ model.multiSelectedMessageList.contains(message);
+ if (checked) {
+ model.removeFromMultiSelectedMessageList(message);
+ } else {
+ model.addToMultiSelectedMessageList(message);
+ }
+ }
+ },
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: isSelf
+ ? MainAxisAlignment.end
+ : MainAxisAlignment.start,
+ children: [
+ if (!isSelf && widget.showAvatar)
+ InkWell(
+ onLongPress: () {
+ if (widget.onLongPressForOthersHeadPortrait !=
+ null) {}
+ if (model.chatConfig.isAllowLongPressAvatarToAt) {
+ widget.onLongPressForOthersHeadPortrait!(
+ message.sender, message.nickName);
+ }
+ },
+ onTapDown: isDesktopScreen
+ ? (details) {
if (widget.onTapForOthersPortrait != null &&
widget.allowAvatarTap) {
widget.onTapForOthersPortrait!(
- message.sender ?? "");
+ message.sender ?? "", details);
+ }
+ }
+ : null,
+ onTap: isDesktopScreen
+ ? null
+ : () {
+ if (widget.onTapForOthersPortrait != null &&
+ widget.allowAvatarTap) {
+ widget.onTapForOthersPortrait!(
+ message.sender ?? "", TapDownDetails());
}
},
- child: Avatar(
- faceUrl: message.faceUrl ?? "",
- showName:
- MessageUtils.getDisplayName(message)),
- ),
+ child: widget.userAvatarBuilder != null
+ ? widget.userAvatarBuilder!(context, message)
+ : Container(
+ margin: (isSelf && isShowNickNameForSelf) ||
+ (!isSelf && isShowNickNameForOthers)
+ ? const EdgeInsets.only(top: 2)
+ : null,
+ child: SizedBox(
+ width: 40,
+ height: 40,
+ child: Avatar(
+ faceUrl: message.faceUrl ?? "",
+ showName:
+ MessageUtils.getDisplayName(message),
+ ),
+ ),
+ ),
+ ),
+ Container(
+ margin: widget.showAvatar
+ ? (isSelf
+ ? const EdgeInsets.only(right: 13)
+ : const EdgeInsets.only(left: 13))
+ : null,
+ child: Column(
+ crossAxisAlignment: isSelf
+ ? CrossAxisAlignment.end
+ : CrossAxisAlignment.start,
+ children: [
+ if ((isSelf && isShowNickNameForSelf) ||
+ (!isSelf && isShowNickNameForOthers))
+ widget.topRowBuilder != null
+ ? widget.topRowBuilder!(context, message)
+ : Container(
+ margin: const EdgeInsets.only(bottom: 4),
+ child: ConstrainedBox(
+ constraints: BoxConstraints(
+ maxWidth: MediaQuery.of(context)
+ .size
+ .width /
+ 1.7),
+ child: Text(
+ MessageUtils.getDisplayName(message),
+ overflow: TextOverflow.ellipsis,
+ style: widget.themeData
+ ?.nickNameTextStyle ??
+ TextStyle(
+ fontSize: 12,
+ color: theme.weakTextColor),
+ ),
+ )),
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ if (isSelf)
+ renderHoverTipAndReadStatus(model, isSelf,
+ message, isPeerRead, theme),
+ Container(
+ constraints: BoxConstraints(
+ maxWidth: constraints.maxWidth * 0.77,
+ ),
+ child: Builder(builder: (context) {
+ return Column(
+ crossAxisAlignment:
+ (message.isSelf ?? true)
+ ? CrossAxisAlignment.end
+ : CrossAxisAlignment.start,
+ children: [
+ GestureDetector(
+ child: IgnorePointer(
+ ignoring: model.isMultiSelect,
+ child: _getMessageItemBuilder(
+ message,
+ message.status,
+ model)),
+ onSecondaryTapDown: (details) {
+ if (widget.onLongPress != null) {
+ widget.onLongPress!(
+ context, message);
+ return;
+ }
+ if (!PlatformUtils().isMobile) {
+ if (widget.allowLongPress) {
+ _onOpenToolTip(
+ context,
+ message,
+ model,
+ theme,
+ details,
+ false,
+ false);
+ }
+ }
+ },
+ onLongPress: () {
+ if (widget.onLongPress != null) {
+ widget.onLongPress!(
+ context, message);
+ return;
+ }
+ if (widget.allowLongPress &&
+ PlatformUtils().isMobile) {
+ _onOpenToolTip(
+ context,
+ message,
+ model,
+ theme,
+ null,
+ false,
+ false);
+ }
+ },
+ onTapDown: (details) {
+ _tapDetails = details;
+ },
+ ),
+ TIMUIKitTextTranslationElem(
+ message: message,
+ isUseDefaultEmoji:
+ widget.isUseDefaultEmoji,
+ customEmojiStickerList:
+ widget.customEmojiStickerList,
+ isFromSelf: message.isSelf ?? true,
+ isShowJump: false,
+ clearJump: () {},
+ chatModel: model)
+ ],
+ );
+ }),
+ ),
+ if (!isSelf &&
+ message.elemType ==
+ MessageElemType.V2TIM_ELEM_TYPE_SOUND &&
+ message.localCustomInt != null &&
+ message.localCustomInt !=
+ HistoryMessageDartConstant.read)
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 5, bottom: 12),
+ child: Icon(Icons.circle,
+ color: theme.cautionColor, size: 10)),
+ if (!isSelf)
+ renderHoverTipAndReadStatus(model, isSelf,
+ message, isPeerRead, theme),
+ ],
),
- ],
+ if (widget.bottomRowBuilder != null)
+ widget.bottomRowBuilder!(context, message)
+ ],
+ ),
+ ),
+ if (widget.message.elemType == 6 && isDownloadWaiting)
+ Container(
+ margin: const EdgeInsets.only(top: 24, left: 6),
+ child: LoadingAnimationWidget.threeArchedCircle(
+ color: theme.weakTextColor ?? Colors.grey,
+ size: 20,
+ ),
+ ),
+ if (isSelf && widget.showAvatar)
+ widget.userAvatarBuilder != null
+ ? widget.userAvatarBuilder!(context, message)
+ : SizedBox(
+ width: 40,
+ height: 40,
+ child: InkWell(
+ onTapDown: (details) {
+ if (widget.onTapForOthersPortrait != null &&
+ widget.allowAvatarTap) {
+ widget.onTapForOthersPortrait!(
+ message.sender ?? "", details);
+ }
+ },
+ child: Avatar(
+ faceUrl: message.faceUrl ?? "",
+ showName:
+ MessageUtils.getDisplayName(message)),
+ ),
+ ),
+ ],
+ ),
),
),
),
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_message_tooltip.dart b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_message_tooltip.dart
index 00bc57d..d7f6016 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_message_tooltip.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_message_tooltip.dart
@@ -1,11 +1,15 @@
// ignore_for_file: non_constant_identifier_names, avoid_print
+import 'dart:convert';
+import 'dart:io';
import 'dart:math';
-
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
+import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart';
+import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_select_emoji.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_state.dart';
@@ -13,11 +17,11 @@ import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
-
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_item.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/forward_message_screen.dart';
-
-import '../TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_select_emoji.dart';
+import 'package:tencent_open_file/tencent_open_file.dart';
+import 'package:url_launcher/url_launcher.dart';
+import 'package:path/path.dart' as path;
class TIMUIKitMessageTooltip extends StatefulWidget {
/// tool tips panel configuration, long press message will show tool tips panel
@@ -46,6 +50,8 @@ class TIMUIKitMessageTooltip extends StatefulWidget {
final TUIChatSeparateViewModel model;
+ final bool isShowMoreSticker;
+
const TIMUIKitMessageTooltip(
{Key? key,
this.toolTipsConfig,
@@ -56,7 +62,8 @@ class TIMUIKitMessageTooltip extends StatefulWidget {
this.onLongPressForOthersHeadPortrait,
required this.selectEmojiPanelPosition,
required this.onCloseTooltip,
- required this.onSelectSticker})
+ required this.onSelectSticker,
+ this.isShowMoreSticker = false})
: super(key: key);
@override
@@ -65,7 +72,52 @@ class TIMUIKitMessageTooltip extends StatefulWidget {
class TIMUIKitMessageTooltipState
extends TIMUIKitState {
+ final TUIChatGlobalModel globalModal = serviceLocator();
bool isShowMoreSticker = false;
+ bool isShowOpenFile = false;
+ String filePath = "";
+
+ @override
+ void initState() {
+ super.initState();
+ hasFile();
+ isShowMoreSticker = widget.isShowMoreSticker;
+ }
+
+ hasFile() {
+ if (PlatformUtils().isMobile || widget.message.fileElem == null) {
+ isShowOpenFile = false;
+ return;
+ }
+ if (PlatformUtils().isWeb) {
+ isShowOpenFile = true;
+ return;
+ }
+ if (PlatformUtils().isDesktop) {
+ if (globalModal.getMessageProgress(widget.message.msgID) == 100) {
+ String savePath =
+ 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);
+ if (f.existsSync() && widget.message.msgID != null) {
+ filePath = savePath;
+ globalModal.setMessageProgress(widget.message.msgID!, 100);
+ isShowOpenFile = true;
+ return;
+ }
+ }
+ isShowOpenFile = false;
+ }
bool isRevocable(int timestamp, int upperTimeLimit) =>
(DateTime.now().millisecondsSinceEpoch / 1000).ceil() - timestamp <
@@ -73,10 +125,10 @@ class TIMUIKitMessageTooltipState
Widget ItemInkWell({
Widget? child,
- GestureTapCallback? onTap
+ GestureTapCallback? onTap,
}) {
return SizedBox(
- width: 40,
+ width: 44,
child: InkWell(
onTap: onTap,
splashColor: Colors.white,
@@ -88,69 +140,105 @@ class TIMUIKitMessageTooltipState
);
}
- _buildLongPressTipItem(TUITheme theme, TUIChatSeparateViewModel model) {
+ bool isVoteMessage(V2TimMessage message) {
+ bool isvote = false;
+ V2TimCustomElem? custom = message.customElem;
+
+ if (custom != null) {
+ String? data = custom.data;
+ if (data != null && data.isNotEmpty) {
+ try {
+ Map mapData = json.decode(data);
+ if (mapData["businessID"] == "group_poll") {
+ isvote = true;
+ }
+ } catch (err) {
+ // err
+ }
+ }
+ }
+ return isvote;
+ }
+
+ _buildLongPressTipItem(
+ TUITheme theme, TUIChatSeparateViewModel model, V2TimMessage message) {
+ final isDesktopScreen =
+ TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final isCanRevoke = isRevocable(
widget.message.timestamp!, model.chatConfig.upperRecallTime);
final shouldShowRevokeAction = isCanRevoke &&
- (widget.message.isSelf ?? false) &&
+ (widget.message.isSelf ?? true) &&
widget.message.status != MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL;
final shouldShowReplyAction = !(widget.message.customElem?.data != null &&
MessageUtils.isCallingData(widget.message.customElem!.data!));
final shouldShowForwardAction = !(widget.message.customElem?.data != null &&
MessageUtils.isCallingData(widget.message.customElem!.data!));
final tooltipsConfig = widget.toolTipsConfig;
- final defaultTipsList = [
- {
- "label": TIM_t("复制"),
- "id": "copyMessage",
- "icon": "images/copy_message.png"
- },
- if(shouldShowForwardAction)
- {
- "label": TIM_t("转发"),
- "id": "forwardMessage",
- "icon": "images/forward_message.png"
- },
- {
- "label": TIM_t("多选"),
- "id": "multiSelect",
- "icon": "images/multi_message.png"
- },
+ final List defaultTipsList = [
+ if (isShowOpenFile)
+ MessageToolTipItem(
+ label: TIM_t("打开"),
+ id: "open",
+ iconImageAsset: "images/open_in_new.png",
+ onClick: () => _onTap("open", model)),
+ if (isShowOpenFile && PlatformUtils().isDesktop)
+ MessageToolTipItem(
+ label: PlatformUtils().isMacOS ? TIM_t("在访达中打开") : TIM_t("查看文件夹"),
+ id: "finder",
+ iconImageAsset: "images/folder_open.png",
+ onClick: () => _onTap("finder", model)),
+ MessageToolTipItem(
+ label: TIM_t("复制"),
+ id: "copyMessage",
+ iconImageAsset: "images/copy_message.png",
+ onClick: () => _onTap("copyMessage", model)),
+ if (shouldShowForwardAction && !isVoteMessage(widget.message))
+ MessageToolTipItem(
+ label: TIM_t("转发"),
+ id: "forwardMessage",
+ iconImageAsset: "images/forward_message.png",
+ onClick: () => _onTap("forwardMessage", model)),
+ MessageToolTipItem(
+ label: TIM_t("多选"),
+ id: "multiSelect",
+ iconImageAsset: "images/multi_message.png",
+ onClick: () => _onTap("multiSelect", model)),
if (shouldShowReplyAction)
- {
- "label": TIM_t("引用"),
- "id": "replyMessage",
- "icon": "images/reply_message.png"
- },
- {
- "label": TIM_t("删除"),
- "id": "delete",
- "icon": "images/delete_message.png"
- },
- {
- "label": TIM_t("翻译"),
- "id": "translate",
- "icon": "images/translate.png"
- },
+ MessageToolTipItem(
+ label: TIM_t(model.chatConfig.isAtWhenReply ? "回复" : "引用"),
+ id: "replyMessage",
+ iconImageAsset: "images/reply_message.png",
+ onClick: () => _onTap("replyMessage", model)),
+ MessageToolTipItem(
+ label: TIM_t("删除"),
+ id: "delete",
+ iconImageAsset: "images/delete_message.png",
+ onClick: () => _onTap("delete", model)),
+ MessageToolTipItem(
+ label: TIM_t("翻译"),
+ id: "translate",
+ iconImageAsset: "images/translate.png",
+ onClick: () => _onTap("translate", model)),
if (shouldShowRevokeAction)
- {
- "label": TIM_t("撤回"),
- "id": "revoke",
- "icon": "images/revoke_message.png"
- }
+ MessageToolTipItem(
+ label: TIM_t("撤回"),
+ id: "revoke",
+ iconImageAsset: "images/revoke_message.png",
+ onClick: () => _onTap("revoke", model)),
];
- List formatedTipsList = defaultTipsList;
+ List defaultFormattedTipsList = defaultTipsList;
if (tooltipsConfig != null) {
- formatedTipsList = defaultTipsList.where((element) {
- final type = element["id"];
+ defaultFormattedTipsList = defaultTipsList.where((element) {
+ final type = element.id;
if (type == "copyMessage") {
- return tooltipsConfig.showCopyMessage && widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_TEXT;
+ return tooltipsConfig.showCopyMessage &&
+ widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_TEXT;
}
if (type == "forwardMessage") {
- return tooltipsConfig.showForwardMessage;
+ return tooltipsConfig.showForwardMessage && !isDesktopScreen;
}
if (type == "replyMessage") {
- return tooltipsConfig.showReplyMessage;
+ return tooltipsConfig.showReplyMessage && !isDesktopScreen;
}
if (type == "delete") {
return (!PlatformUtils().isWeb) && tooltipsConfig.showDeleteMessage;
@@ -158,67 +246,158 @@ class TIMUIKitMessageTooltipState
if (type == "multiSelect") {
return tooltipsConfig.showMultipleChoiceMessage;
}
- if (type == "translate") {
- return tooltipsConfig.showTranslation && widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_TEXT;
- }
+
if (type == "revoke") {
return tooltipsConfig.showRecallMessage;
}
+ if (type == "translate") {
+ return tooltipsConfig.showTranslation &&
+ widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_TEXT;
+ }
return true;
}).toList();
}
- return formatedTipsList
- .map(
- (item) => Material(
- color: Colors.white,
- child: ItemInkWell(
- onTap: () {
- _onTap(item["id"]!, model);
- },
- child: Column(
- children: [
- Image.asset(
- item["icon"]!,
- package: 'tencent_cloud_chat_uikit',
- width: 20,
- height: 20,
+
+ final List? customList =
+ widget.toolTipsConfig?.additionalMessageToolTips != null
+ ? (widget.toolTipsConfig?.additionalMessageToolTips!(
+ message, widget.onCloseTooltip))
+ : [];
+
+ List formattedTipsList = [
+ ...defaultFormattedTipsList,
+ ...?customList,
+ ];
+
+ List widgetList = [];
+ if (isDesktopScreen) {
+ widgetList = formattedTipsList
+ .map(
+ (item) => Material(
+ color: Colors.white,
+ child: InkWell(
+ onTap: () {
+ item.onClick();
+ },
+ child: Container(
+ padding: const EdgeInsets.all(6),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Image.asset(
+ item.iconImageAsset,
+ package: 'tencent_cloud_chat_uikit',
+ width: 20,
+ height: 20,
+ ),
+ const SizedBox(
+ height: 4,
+ width: 8,
+ ),
+ Text(
+ item.label,
+ style: TextStyle(
+ decoration: TextDecoration.none,
+ color: theme.darkTextColor,
+ fontSize: 12,
+ ),
+ )
+ ],
),
- const SizedBox(
- height: 4,
- ),
- Text(
- item["label"]!,
- style: TextStyle(
- decoration: TextDecoration.none,
- color: theme.darkTextColor,
- fontSize: 10,
- ),
- )
- ],
+ ),
),
),
- ),
- )
- .toList();
+ )
+ .toList();
+ } else {
+ widgetList = formattedTipsList
+ .map(
+ (item) => Material(
+ color: Colors.white,
+ child: ItemInkWell(
+ onTap: () {
+ item.onClick();
+ },
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Image.asset(
+ item.iconImageAsset,
+ package: 'tencent_cloud_chat_uikit',
+ width: 20,
+ height: 20,
+ ),
+ const SizedBox(
+ height: 4,
+ width: 60,
+ ),
+ Text(
+ item.label,
+ style: TextStyle(
+ decoration: TextDecoration.none,
+ color: theme.darkTextColor,
+ fontSize: 10,
+ ),
+ )
+ ],
+ ),
+ ),
+ ),
+ )
+ .toList();
+ }
+ if (widgetList.isEmpty && widget.isUseMessageReaction == false) {
+ widget.onCloseTooltip();
+ }
+
+ return widgetList;
}
_onTap(String operation, TUIChatSeparateViewModel model) async {
final messageItem = widget.message;
final msgID = messageItem.msgID as String;
switch (operation) {
+ case "open":
+ if (PlatformUtils().isDesktop) {
+ final String savePath =
+ TencentUtils.checkString(widget.message.fileElem!.localUrl) ??
+ globalModal.getFileMessageLocation(widget.message.msgID);
+ launchUrl(Uri.file(savePath));
+ } else {
+ if (PlatformUtils().isWindows) {
+ OpenFile.open(widget.message.fileElem?.path ?? "");
+ } else {
+ launchUrl(
+ Uri.parse(widget.message.fileElem?.path ?? ""),
+ mode: LaunchMode.externalApplication,
+ );
+ }
+ }
+ break;
+ case "finder":
+ final String savePath =
+ TencentUtils.checkString(widget.message.fileElem!.localUrl) ??
+ globalModal.getFileMessageLocation(widget.message.msgID);
+ final String fileDir = path.dirname(savePath);
+ if (PlatformUtils().isWindows) {
+ OpenFile.open(fileDir);
+ } else {
+ launchUrl(Uri.file(fileDir));
+ }
+ break;
case "delete":
model.deleteMsg(msgID, webMessageInstance: messageItem.messageFromWeb);
break;
case "revoke":
model.revokeMsg(msgID, messageItem.messageFromWeb);
break;
+ case 'translate':
+ model.translateText(widget.message);
+ break;
case "multiSelect":
model.updateMultiSelectStatus(true);
model.addToMultiSelectedMessageList(widget.message);
break;
- case 'translate':
- model.translateText(widget.message);
- break;
case "forwardMessage":
model.addToMultiSelectedMessageList(widget.message);
Navigator.push(
@@ -238,16 +417,15 @@ class TIMUIKitMessageTooltipState
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("已复制"),
infoCode: 6660408));
- } catch (e) {
- print(e);
- }
+ // ignore: empty_catches
+ } catch (e) {}
}
break;
case "replyMessage":
model.repliedMessage = widget.message;
if (widget.allowAtUserWhenReply &&
widget.onLongPressForOthersHeadPortrait != null &&
- !(widget.message.isSelf ?? false)) {
+ !(widget.message.isSelf ?? true)) {
widget.onLongPressForOthersHeadPortrait!(
widget.message.sender, widget.message.nickName);
}
@@ -264,7 +442,8 @@ class TIMUIKitMessageTooltipState
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final TUITheme theme = value.theme;
-
+ final isDesktopScreen =
+ TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: widget.model),
@@ -278,16 +457,38 @@ class TIMUIKitMessageTooltipState
? widget.toolTipsConfig!.additionalItemBuilder!(
widget.message, widget.onCloseTooltip, null, context)
: null;
+ final message = widget.message;
return Container(
- padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
+ decoration: isDesktopScreen
+ ? BoxDecoration(
+ boxShadow: const [
+ BoxShadow(
+ color: Color(0xCCbebebe),
+ offset: Offset(2, 2),
+ blurRadius: 10,
+ spreadRadius: 1,
+ ),
+ ],
+ border: Border.all(
+ width: 1,
+ color: hexToColor("dee0e3"),
+ ),
+ color: Colors.white,
+ borderRadius: const BorderRadius.all(Radius.circular(10)),
+ )
+ : null,
+ color: isDesktopScreen ? null : Colors.white,
+ padding: EdgeInsets.symmetric(
+ horizontal: 8, vertical: isDesktopScreen ? 8 : 4),
child: ConstrainedBox(
constraints: BoxConstraints(
- maxWidth: min(MediaQuery.of(context).size.width * 0.7, 350),
+ maxWidth: min(MediaQuery.of(context).size.width * 0.75, 350),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
- if (widget.isUseMessageReaction &&
+ if ((!isDesktopScreen || widget.isShowMoreSticker) &&
+ widget.isUseMessageReaction &&
widget.selectEmojiPanelPosition ==
SelectEmojiPanelPosition.up)
TIMUIKitMessageReactionEmojiSelectPanel(
@@ -299,7 +500,8 @@ class TIMUIKitMessageTooltipState
});
},
),
- if (widget.isUseMessageReaction &&
+ if (!isDesktopScreen &&
+ widget.isUseMessageReaction &&
widget.selectEmojiPanelPosition ==
SelectEmojiPanelPosition.up &&
isShowMoreSticker == false)
@@ -314,40 +516,58 @@ class TIMUIKitMessageTooltipState
Row(
mainAxisSize: MainAxisSize.min,
children: [
- if (widget.isUseMessageReaction)
+ if (!isDesktopScreen && widget.isUseMessageReaction)
Expanded(
child: Wrap(
direction: Axis.horizontal,
- alignment: ScreenUtils.getFormFactor(context) ==
- ScreenType.Handset
- ? WrapAlignment.spaceBetween
- : WrapAlignment.start,
+ alignment:
+ TUIKitScreenUtils.getFormFactor(context) ==
+ DeviceType.Mobile
+ ? WrapAlignment.spaceBetween
+ : WrapAlignment.start,
spacing: 4,
runSpacing: 8,
children: [
- ..._buildLongPressTipItem(theme, model),
+ ..._buildLongPressTipItem(theme, model, message),
if (extraTipsActionItem != null)
extraTipsActionItem
],
)),
- if (!widget.isUseMessageReaction)
- Wrap(
- direction: Axis.horizontal,
- alignment: ScreenUtils.getFormFactor(context) ==
- ScreenType.Handset
- ? WrapAlignment.spaceAround
- : WrapAlignment.start,
- spacing: 4,
- runSpacing: 16,
- children: [
- ..._buildLongPressTipItem(theme, model),
- if (extraTipsActionItem != null)
- extraTipsActionItem
- ],
- )
+ if (!isDesktopScreen && !widget.isUseMessageReaction)
+ ConstrainedBox(
+ constraints: BoxConstraints(
+ maxWidth: min(
+ MediaQuery.of(context).size.width * 0.75,
+ 350),
+ ),
+ child: Wrap(
+ direction: Axis.horizontal,
+ alignment:
+ TUIKitScreenUtils.getFormFactor(context) ==
+ DeviceType.Mobile
+ ? WrapAlignment.spaceBetween
+ : WrapAlignment.start,
+ spacing: 4,
+ runSpacing: 8,
+ children: [
+ ..._buildLongPressTipItem(
+ theme, model, message),
+ if (extraTipsActionItem != null)
+ extraTipsActionItem
+ ],
+ ),
+ ),
+ if (isDesktopScreen)
+ Table(columnWidths: const {
+ 0: IntrinsicColumnWidth(),
+ }, children: [
+ ..._buildLongPressTipItem(theme, model, message)
+ .map((e) => TableRow(children: [e]))
+ ])
],
),
- if (widget.isUseMessageReaction &&
+ if (!isDesktopScreen &&
+ widget.isUseMessageReaction &&
widget.selectEmojiPanelPosition ==
SelectEmojiPanelPosition.down &&
isShowMoreSticker == false)
@@ -358,7 +578,8 @@ class TIMUIKitMessageTooltipState
indent: 0,
// endIndent: 10,
color: Colors.black12)),
- if (widget.isUseMessageReaction &&
+ if ((!isDesktopScreen || widget.isShowMoreSticker) &&
+ widget.isUseMessageReaction &&
widget.selectEmojiPanelPosition ==
SelectEmojiPanelPosition.down)
TIMUIKitMessageReactionEmojiSelectPanel(
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_history_message_list_container.dart b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_history_message_list_container.dart
index 838b933..6ed275f 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_history_message_list_container.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_history_message_list_container.dart
@@ -1,3 +1,5 @@
+// ignore_for_file: deprecated_member_use_from_same_package
+
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
@@ -33,6 +35,9 @@ class TIMUIKitHistoryMessageListContainer extends StatefulWidget {
/// message item builder, works for customize all message types and row layout.
final MessageItemBuilder? messageItemBuilder;
+ /// The controller for text field.
+ final TIMUIKitInputTextFieldController? textFieldController;
+
/// the builder for avatar
final Widget Function(BuildContext context, V2TimMessage message)?
userAvatarBuilder;
@@ -46,7 +51,7 @@ class TIMUIKitHistoryMessageListContainer extends StatefulWidget {
/// conversation type
final ConvType conversationType;
- final void Function(String userID)? onTapAvatar;
+ final void Function(String userID, TapDownDetails tapDetails)? 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")
@@ -62,9 +67,6 @@ class TIMUIKitHistoryMessageListContainer extends StatefulWidget {
final List customEmojiStickerList;
- /// The controller for text field.
- final TIMUIKitInputTextFieldController? textFieldController;
-
final bool isAllowScroll;
final V2TimConversation conversation;
@@ -105,12 +107,13 @@ class _TIMUIKitHistoryMessageListContainerState
List historyMessageList = [];
- Future requestForData(String? lastMsgID, LoadDirection direction, TUIChatSeparateViewModel model,
+ Future requestForData(String? lastMsgID, LoadDirection direction,
+ TUIChatSeparateViewModel model,
[int? count]) async {
- print("requestForData $lastMsgID $direction");
- if ((direction == LoadDirection.previous && model.haveMoreData) || (direction == LoadDirection.latest && model.haveMoreLatestData)) {
- await model.loadData(
- direction: direction,
+ if ((direction == LoadDirection.previous && model.haveMoreData) ||
+ (direction == LoadDirection.latest && model.haveMoreLatestData)) {
+ await model.loadChatRecord(
+ direction: direction,
count: count ?? (kIsWeb ? 15 : HistoryMessageDartConstant.getCount),
lastMsgID: lastMsgID);
}
@@ -152,6 +155,7 @@ class _TIMUIKitHistoryMessageListContainerState
mainHistoryListConfig: widget.mainHistoryListConfig,
itemBuilder: (context, message) {
return TIMUIKitHistoryMessageListItem(
+ textFieldController: widget.textFieldController,
userAvatarBuilder: widget.userAvatarBuilder,
customEmojiStickerList: widget.customEmojiStickerList,
isUseDefaultEmoji: widget.isUseDefaultEmoji,
@@ -166,7 +170,6 @@ class _TIMUIKitHistoryMessageListContainerState
message: message!,
showAvatar: chatConfig.isShowAvatar,
onTapForOthersPortrait: widget.onTapAvatar,
- textFieldController: widget.textFieldController,
messageItemBuilder: widget.messageItemBuilder,
onLongPressForOthersHeadPortrait:
widget.onLongPressForOthersHeadPortrait,
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_message_read_receipt.dart b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_message_read_receipt.dart
index 5c9a738..2b1c04a 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_message_read_receipt.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_message_read_receipt.dart
@@ -3,13 +3,16 @@ import 'package:provider/provider.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.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/core/tim_uikit_wide_modal_operation_key.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/message_read_receipt.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
+import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.dart';
class TIMUIKitMessageReadReceipt extends TIMUIKitStatelessWidget {
final V2TimMessage messageItem;
- final void Function(String)? onTapAvatar;
+ final void Function(String, TapDownDetails tapDetails)? onTapAvatar;
TIMUIKitMessageReadReceipt(
{Key? key, this.onTapAvatar, required this.messageItem})
@@ -20,31 +23,48 @@ class TIMUIKitMessageReadReceipt extends TIMUIKitStatelessWidget {
final TUITheme theme = value.theme;
final TUIChatSeparateViewModel model =
Provider.of(context, listen: false);
+ final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
return Selector(
builder: (context, value, child) {
- if (value == null || value.unreadCount == 0 && value.readCount == 0) {
- return Container();
- }
+ // if (value == null || value.unreadCount == 0 && value.readCount == 0) {
+ // return Container();
+ // }
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
- if ((value.readCount ?? 0) > 0) {
- Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) => MessageReadReceipt(
- model: model,
- onTapAvatar: onTapAvatar,
- messageItem: messageItem,
- unreadCount: value.unreadCount ?? 0,
- readCount: value.readCount ?? 0)));
+ if ((value?.readCount ?? 0) > 0) {
+ if(isDesktopScreen){
+ TUIKitWidePopup.showPopupWindow(
+ operationKey: TUIKitWideModalOperationKey.messageReadDetails,
+ context: context,
+ width: MediaQuery.of(context).size.width * 0.5,
+ height: MediaQuery.of(context).size.height * 0.8,
+ title: TIM_t("消息详情"),
+ child: (onClose) => MessageReadReceipt(
+ model: model,
+ onTapAvatar: onTapAvatar,
+ messageItem: messageItem,
+ unreadCount: value?.unreadCount ?? 0,
+ readCount: value?.readCount ?? 0)
+ );
+ }else{
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => MessageReadReceipt(
+ model: model,
+ onTapAvatar: onTapAvatar,
+ messageItem: messageItem,
+ unreadCount: value?.unreadCount ?? 0,
+ readCount: value?.readCount ?? 0)));
+ }
}
},
child: Container(
- padding:
- const EdgeInsets.only(bottom: 3, right: 6, left: 6, top: 6),
- child: (value.unreadCount == 0 && (value.readCount ?? 0) > 0)
+ padding: EdgeInsets.only(
+ bottom: 3, right: 6, left: 6, top: isDesktopScreen ? 2 : 6),
+ child: ((value?.unreadCount ?? 0) == 0 && (value?.readCount ?? 0) > 0)
? Icon(
Icons.check_circle_outline,
size: 18,
@@ -58,12 +78,12 @@ class TIMUIKitMessageReadReceipt extends TIMUIKitStatelessWidget {
shape: BoxShape.circle,
border: Border.all(
width: 1.3,
- color: (value.readCount ?? 0) > 0
+ color: (value?.readCount ?? 0) > 0
? theme.primaryColor!
: theme.weakTextColor!)),
- child: (value.readCount ?? 0) > 0
+ child: (value?.readCount ?? 0) > 0
? Text(
- '${value.readCount}',
+ '${value?.readCount ?? 0}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 8, color: theme.primaryColor),
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitAppBar/tim_uikit_appbar.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitAppBar/tim_uikit_appbar.dart
index 6690568..396a60b 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKitAppBar/tim_uikit_appbar.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKitAppBar/tim_uikit_appbar.dart
@@ -9,6 +9,7 @@ import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_ser
import 'package:tencent_cloud_chat_uikit/data_services/group/group_services.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitAppBar/tim_uikit_appbar_title.dart';
import 'package:tuple/tuple.dart';
@@ -30,7 +31,9 @@ class TIMUIKitAppBar extends StatefulWidget implements PreferredSizeWidget {
/// If allow update the conversation show name automatically.
final bool isConversationShowNameFixed;
- final bool showC2cMessageEditStaus;
+ final bool showC2cMessageEditStatus;
+
+ final GestureTapDownCallback? onClickTitle;
const TIMUIKitAppBar({
Key? key,
@@ -39,7 +42,8 @@ class TIMUIKitAppBar extends StatefulWidget implements PreferredSizeWidget {
this.showTotalUnReadCount = true,
this.conversationID = "",
this.conversationShowName = "",
- this.showC2cMessageEditStaus = true,
+ this.showC2cMessageEditStatus = true,
+ this.onClickTitle,
}) : super(key: key);
@override
@@ -76,9 +80,8 @@ class _TIMUIKitAppBarState extends TIMUIKitState {
changedInfo.userProfile?.userID) ??
"";
}
+ // ignore: empty_catches
} catch (e) {
- // ignore: avoid_print
- print(e);
}
},
);
@@ -100,9 +103,8 @@ class _TIMUIKitAppBarState extends TIMUIKitState {
setState(() {});
}
}
+ // ignore: empty_catches
} catch (e) {
- // ignore: avoid_print
- print(e);
}
},
);
@@ -159,9 +161,8 @@ class _TIMUIKitAppBarState extends TIMUIKitState {
"";
});
}
+ // ignore: empty_catches
} catch (e) {
- // ignore: avoid_print
- print(e);
}
}
@@ -172,11 +173,16 @@ class _TIMUIKitAppBarState extends TIMUIKitState {
final setAppbar = widget.config;
final TUIChatSeparateViewModel chatVM =
Provider.of(context);
+ final isDesktopScreen =
+ TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
return AppBar(
- backgroundColor: setAppbar?.backgroundColor ?? theme.chatHeaderBgColor,
+ backgroundColor: setAppbar?.backgroundColor ??
+ theme.chatHeaderBgColor ??
+ theme.appbarBgColor ??
+ theme.primaryColor,
actionsIconTheme: setAppbar?.actionsIconTheme,
foregroundColor: setAppbar?.foregroundColor,
- elevation: setAppbar?.elevation,
+ elevation: setAppbar?.elevation ?? (isDesktopScreen ? 0 : 1),
bottom: setAppbar?.bottom,
bottomOpacity: setAppbar?.bottomOpacity ?? 1.0,
titleSpacing: setAppbar?.titleSpacing,
@@ -188,46 +194,28 @@ class _TIMUIKitAppBarState extends TIMUIKitState {
toolbarOpacity: setAppbar?.toolbarOpacity ?? 1.0,
toolbarTextStyle: setAppbar?.toolbarTextStyle,
textTheme: setAppbar?.textTheme,
- flexibleSpace: setAppbar?.backgroundColor == null
- ? theme.chatHeaderBgColor != null
- ? setAppbar?.flexibleSpace ??
- Container(
- decoration: BoxDecoration(
- gradient: LinearGradient(colors: [
- theme.lightPrimaryColor ??
- CommonColor.lightPrimaryColor,
- theme.primaryColor ?? CommonColor.primaryColor
- ]),
- ),
- )
- : null
- : null,
iconTheme: setAppbar?.iconTheme ??
const IconThemeData(
color: Colors.white,
),
- // title: setAppbar?.title ??
- // Text(
- // _conversationShowName,
- // style: const TextStyle(
- // color: Colors.white,
- // fontSize: 17,
- // ),
- // ),
title: TIMUIKitAppBarTitle(
title: setAppbar?.title,
- textStyle: setAppbar?.textTheme?.titleMedium,
+ onClick: widget.onClickTitle,
+ textStyle: setAppbar?.textTheme?.titleMedium ??
+ TextStyle(
+ color: theme.appbarTextColor ?? hexToColor("010000"),
+ fontSize: 16),
conversationShowName: _conversationShowName,
- showC2cMessageEditStaus: widget.showC2cMessageEditStaus,
+ showC2cMessageEditStatus: widget.showC2cMessageEditStatus,
fromUser: widget.conversationID,
),
- centerTitle: setAppbar?.centerTitle ?? true,
- leadingWidth: setAppbar?.leadingWidth ?? 70,
+ centerTitle: setAppbar?.centerTitle ?? (isDesktopScreen ? false : true),
+ leadingWidth: setAppbar?.leadingWidth ?? (isDesktopScreen ? 8 : 70),
leading: Selector>(
builder: (context, data, _) {
final isMultiSelect = data.item1;
final unReadCount = data.item2;
- return isMultiSelect
+ return (!isDesktopScreen && isMultiSelect)
? TextButton(
onPressed: () {
chatVM.updateMultiSelectStatus(false);
@@ -235,42 +223,49 @@ class _TIMUIKitAppBarState extends TIMUIKitState {
child: Text(
TIM_t('取消'),
style: setAppbar?.textTheme?.titleMedium ??
- const TextStyle(
- color: Colors.white,
+ TextStyle(
+ color:
+ theme.appbarTextColor ?? hexToColor("010000"),
fontSize: 16,
),
),
)
: setAppbar?.leading ??
- Row(
- children: [
- IconButton(
- padding: const EdgeInsets.only(left: 16),
- constraints: const BoxConstraints(),
- icon: Icon(
- Icons.arrow_back_ios,
- color: setAppbar?.textTheme?.titleMedium?.color ??
- Colors.white,
- size: 17,
- ),
- onPressed: () async {
- chatVM.repliedMessage = null;
- Navigator.pop(context);
- },
- ),
- if (widget.showTotalUnReadCount && unReadCount > 0)
- Container(
- width: 22,
- height: 22,
- alignment: Alignment.center,
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- color: theme.cautionColor,
- ),
- child: Text(_getTotalUnReadCount(unReadCount)),
- ),
- ],
- );
+ (isDesktopScreen
+ ? Container()
+ : Row(
+ children: [
+ IconButton(
+ padding: const EdgeInsets.only(left: 16),
+ constraints: const BoxConstraints(),
+ icon: Icon(
+ Icons.arrow_back_ios,
+ color: setAppbar
+ ?.textTheme?.titleMedium?.color ??
+ theme.appbarTextColor ??
+ hexToColor("010000"),
+ size: 17,
+ ),
+ onPressed: () async {
+ chatVM.repliedMessage = null;
+ Navigator.pop(context);
+ },
+ ),
+ if (widget.showTotalUnReadCount &&
+ unReadCount > 0)
+ Container(
+ width: 22,
+ height: 22,
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ color: theme.cautionColor,
+ ),
+ child:
+ Text(_getTotalUnReadCount(unReadCount)),
+ ),
+ ],
+ ));
},
shouldRebuild: (prev, next) =>
prev.item1 != next.item1 || prev.item2 != next.item2,
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitAppBar/tim_uikit_appbar_title.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitAppBar/tim_uikit_appbar_title.dart
index 08b8450..92126d2 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKitAppBar/tim_uikit_appbar_title.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKitAppBar/tim_uikit_appbar_title.dart
@@ -6,8 +6,9 @@ import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_glo
class TIMUIKitAppBarTitle extends StatelessWidget {
final Widget? title;
final String conversationShowName;
- final bool showC2cMessageEditStaus;
+ final bool showC2cMessageEditStatus;
final String fromUser;
+ final GestureTapDownCallback? onClick;
final TextStyle? textStyle;
const TIMUIKitAppBarTitle(
@@ -15,10 +16,24 @@ class TIMUIKitAppBarTitle extends StatelessWidget {
this.title,
this.textStyle,
required this.conversationShowName,
- required this.showC2cMessageEditStaus,
- required this.fromUser})
+ required this.showC2cMessageEditStatus,
+ required this.fromUser, this.onClick})
: super(key: key);
+ Widget titleText(String text){
+ return InkWell(
+ onTapDown: onClick,
+ child: Text(
+ text,
+ style: textStyle ??
+ const TextStyle(
+ color: Colors.white,
+ fontSize: 17,
+ ),
+ ),
+ );
+ }
+
// String conversationShowName;
@override
Widget build(BuildContext context) {
@@ -28,36 +43,19 @@ class TIMUIKitAppBarTitle extends StatelessWidget {
if (title != null) {
return title!;
}
- return Text(
- conversationShowName,
- style: textStyle ??
- const TextStyle(
- color: Colors.white,
- fontSize: 17,
- ),
- );
+ return titleText(conversationShowName,);
} else {
- if (showC2cMessageEditStaus) {
- return Text(
- TIM_t("对方正在输入中..."),
- style: textStyle ??
- const TextStyle(
- color: Colors.white,
- fontSize: 17,
- ),
- );
+ if (showC2cMessageEditStatus) {
+ return titleText(
+ TIM_t("对方正在输入中..."),);
+
} else {
if (title != null) {
return title!;
}
- return Text(
- conversationShowName,
- style: textStyle ??
- const TextStyle(
- color: Colors.white,
- fontSize: 17,
- ),
- );
+ return titleText(
+ conversationShowName,);
+
}
}
}
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_detail.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_detail.dart
index 4b73b59..d69f4b5 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_detail.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_detail.dart
@@ -20,7 +20,7 @@ class TIMUIKitMessageReactionDetail extends StatefulWidget {
/// the sticker list from message reaction
final List stickerList;
- final Function(String userID)? onTapAvatar;
+ final Function(String userID, TapDownDetails tapDetails)? onTapAvatar;
const TIMUIKitMessageReactionDetail(
{required this.currentStickerIndex,
@@ -42,7 +42,7 @@ class TIMUIKitMessageReactionDetailState
serviceLocator();
Widget getUserItem(
- String userID, TUITheme theme, Function(String userID)? onTapAvatar) {
+ String userID, TUITheme theme, Function(String userID, TapDownDetails tapDetails)? onTapAvatar) {
V2TimGroupMemberFullInfo? memberInfo;
String showName = userID;
try {
@@ -66,11 +66,11 @@ class TIMUIKitMessageReactionDetailState
// e
}
- return GestureDetector(
- onTap: () {
+ return InkWell(
+ onTapDown: (tapDetails) {
if (onTapAvatar != null) {
if (userID != selfInfoModel.loginInfo?.userID) {
- onTapAvatar(userID);
+ onTapAvatar(userID, tapDetails);
}
}
},
@@ -137,7 +137,7 @@ class TIMUIKitMessageReactionDetailState
}
Widget getStickerNameList(
- int sticker, TUITheme theme, Function(String userID)? onTapAvatar) {
+ int sticker, TUITheme theme, Function(String userID, TapDownDetails tapDetails)? onTapAvatar) {
final nameList = widget.messageReaction[sticker.toString()];
return SingleChildScrollView(
child: Column(
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_select_emoji.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_select_emoji.dart
index 9adbd8b..323ee05 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_select_emoji.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_select_emoji.dart
@@ -2,6 +2,7 @@ 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_state.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/message_reaction_emoji.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_emoji_panel.dart'
as emoji;
@@ -33,6 +34,7 @@ class TIMUIKitMessageReactionEmojiSelectPanelState
_buildSimplePanel(TUITheme theme) {
final List> emojiData = messageReactionEmojiData;
+ final isDesktopScreen = TUIKitScreenUtils.getFormFactor() == DeviceType.Desktop;
return Material(
color: Colors.white,
child: ExtendedWrap(
@@ -41,7 +43,8 @@ class TIMUIKitMessageReactionEmojiSelectPanelState
crossAxisAlignment: WrapCrossAlignment.center,
runSpacing: 24,
children: [
- GestureDetector(
+ if(!isDesktopScreen)
+ GestureDetector(
onTap: () {
widget.onClickShowMore(!widget.isShowMoreSticker);
},
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_show_item.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_show_item.dart
index 89ec53f..2506e71 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_show_item.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_show_item.dart
@@ -78,7 +78,7 @@ class TIMUIKitMessageReactionShowItem extends TIMUIKitStatelessWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
- GestureDetector(
+ InkWell(
onTap: clickOnCurrentSticker,
child: Container(
margin: EdgeInsets.only(
@@ -87,8 +87,8 @@ class TIMUIKitMessageReactionShowItem extends TIMUIKitStatelessWidget {
child: Text(
String.fromCharCode(sticker),
style: TextStyle(
- fontSize: (!PlatformUtils().isIOS) ? 12 : 16,
- ),
+ fontSize: (!PlatformUtils().isIOS) ? 12 : 16,
+ color: hexToColor("f9453d")),
),
),
),
@@ -145,11 +145,11 @@ class TIMUIKitMessageReactionShowItem extends TIMUIKitStatelessWidget {
// e
}
}
- return GestureDetector(
- onTap: () {
+ return InkWell(
+ onTapDown: (tapDetails) {
if (model.onTapAvatar != null) {
if (e != selfInfoModel.loginInfo?.userID) {
- model.onTapAvatar!(e);
+ model.onTapAvatar!(e, tapDetails);
}
}
},
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_utils.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_utils.dart
index ee82e42..041dc9f 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_utils.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_utils.dart
@@ -1,6 +1,6 @@
import 'dart:convert';
-import 'package:tencent_im_base/tencent_im_base.dart';
+import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
@@ -16,8 +16,10 @@ class MessageReactionUtils {
static CloudCustomData getCloudCustomData(V2TimMessage message) {
CloudCustomData messageCloudCustomData;
try {
- messageCloudCustomData =
- CloudCustomData.fromJson(json.decode(message.cloudCustomData!));
+ messageCloudCustomData = CloudCustomData.fromJson(json.decode(
+ TencentUtils.checkString(message.cloudCustomData) != null
+ ? message.cloudCustomData!
+ : "{}"));
} catch (e) {
messageCloudCustomData = CloudCustomData();
}
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_wrapper.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_wrapper.dart
index da94359..3387b40 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_wrapper.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_wrapper.dart
@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.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_state.dart';
@@ -26,7 +27,7 @@ class TIMUIKitMessageReactionWrapper extends StatefulWidget {
this.clearJump,
required this.isFromSelf,
this.backgroundColor,
- required this.chatModel,
+ required this.chatModel,
required this.message,
this.borderRadius,
required this.child,
@@ -76,8 +77,10 @@ class _TIMUIKitMessageReactionWrapperState
Map messageReaction = {};
CloudCustomData messageCloudCustomData;
try {
- messageCloudCustomData = CloudCustomData.fromJson(
- json.decode(widget.message.cloudCustomData!));
+ messageCloudCustomData = CloudCustomData.fromJson(json.decode(
+ TencentUtils.checkString(widget.message.cloudCustomData) != null
+ ? widget.message.cloudCustomData!
+ : "{}"));
} catch (e) {
messageCloudCustomData = CloudCustomData();
}
@@ -134,7 +137,7 @@ class _TIMUIKitMessageReactionWrapperState
} else {
if ((widget.chatModel.jumpMsgID == widget.message.msgID) &&
(widget.message.msgID?.isNotEmpty ?? false)) {
- if (widget.clearJump != null) {
+ if(widget.clearJump != null){
widget.clearJump!();
}
}
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_face_elem.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_face_elem.dart
index fed5487..fd10412 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_face_elem.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_face_elem.dart
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.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_state.dart';
@@ -28,10 +29,6 @@ class TIMUIKitFaceElem extends StatefulWidget {
}
class _TIMUIKitTextElemState extends TIMUIKitState {
- @override
- void initState() {
- super.initState();
- }
bool isFromNetwork() {
return widget.path.startsWith('http');
@@ -51,8 +48,9 @@ class _TIMUIKitTextElemState extends TIMUIKitState {
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
+ final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
return TIMUIKitMessageReactionWrapper(
- chatModel: widget.model,
+ chatModel: widget.model,
isShowJump: widget.isShowJump,
isFromSelf: widget.message.isSelf ?? true,
clearJump: widget.clearJump,
@@ -61,7 +59,7 @@ class _TIMUIKitTextElemState extends TIMUIKitState {
child: Container(
padding: const EdgeInsets.all(10),
constraints:
- BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.3),
+ BoxConstraints(maxWidth: MediaQuery.of(context).size.width * (isDesktopScreen ? 0.1 : 0.3)),
child: isFromNetwork()
? Image.network(widget.path)
: Image.asset(createPathFromNative(widget.path)),
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_file_elem.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_file_elem.dart
index 19b8474..f121465 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_file_elem.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_file_elem.dart
@@ -3,17 +3,18 @@
import 'dart:io';
import 'dart:math';
+import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.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_open_file/tencent_open_file.dart';
-import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.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_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/view_models/tui_chat_global_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.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/tim_uikit_chat_file_icon.dart';
@@ -75,23 +76,20 @@ class _TIMUIKitFileElemState extends TIMUIKitState {
}
if (model.getMessageProgress(widget.messageID) == 100) {
- String savePath = widget.message.fileElem!.localUrl ??
+ String savePath = TencentUtils.checkString(widget.message.fileElem!.localUrl) ??
model.getFileMessageLocation(widget.messageID);
File f = File(savePath);
if (f.existsSync() && widget.messageID != null) {
filePath = savePath;
- // model.setFileMessageLocation(widget.messageID!, filePath);
return true;
}
return false;
}
- // String savePath = await getSavePath();
String savePath = widget.message.fileElem!.localUrl ?? '';
File f = File(savePath);
if (f.existsSync() && widget.messageID != null) {
filePath = savePath;
model.setMessageProgress(widget.messageID!, 100);
- // model.setFileMessageLocation(widget.messageID!, filePath);
return true;
}
return false;
@@ -110,8 +108,10 @@ class _TIMUIKitFileElemState extends TIMUIKitState {
}
addUrlToWaitingPath() async {
- model.addWaitingList(widget.messageID!);
- print("add path success");
+ if(widget.messageID !=null ){
+ model.addWaitingList(widget.messageID!);
+ print("add path success");
+ }
}
checkIsWaiting() {
@@ -127,22 +127,59 @@ class _TIMUIKitFileElemState extends TIMUIKitState {
}
downloadFile(TUITheme theme) async {
- if (!await Permissions.checkPermission(
- context, Permission.storage.value, theme)) {
- return;
+ if(PlatformUtils().isMobile){
+ if (PlatformUtils().isIOS) {
+ if (!await Permissions.checkPermission(
+ context, Permission.photosAddOnly.value, theme, false)) {
+ return;
+ }
+ } else {
+ final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
+ AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
+ if ((androidInfo.version.sdkInt ?? 0) >= 33) {
+ } else {
+ var storage = await Permissions.checkPermission(
+ context, Permission.storage.value,
+ );
+ if(!storage){
+ return;
+ }
+ }
+ }
}
await model.downloadFile();
}
tryOpenFile(context, theme) async {
- if (!await Permissions.checkPermission(
- context, Permission.storage.value, theme)) {
- return;
+ if(PlatformUtils().isMobile){
+ if (PlatformUtils().isIOS) {
+ if (!await Permissions.checkPermission(
+ context, Permission.photosAddOnly.value, theme!, false)) {
+ return;
+ }
+ } else {
+ final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
+ AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
+ if ((androidInfo.version.sdkInt ?? 0) >= 33) {
+ } else {
+ var storage = await Permissions.checkPermission(
+ context, Permission.storage.value,
+ );
+ if(!storage){
+ return;
+ }
+ }
+ }
}
+
try {
- OpenFile.open(filePath);
+ if(PlatformUtils().isDesktop && !PlatformUtils().isWindows){
+ launchUrl(Uri.file(filePath));
+ }else{
+ OpenFile.open(filePath);
+ }
+ // ignore: empty_catches
} catch (e) {
- print(e);
}
}
@@ -181,7 +218,7 @@ class _TIMUIKitFileElemState extends TIMUIKitState {
fileFormat =
fileName.split(".")[max(fileName.split(".").length - 1, 0)];
}
- return GestureDetector(
+ return InkWell(
onTap: () async {
if (PlatformUtils().isWeb) {
launchUrl(
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_file_icon.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_file_icon.dart
index ebf586a..e2853cb 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_file_icon.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_file_icon.dart
@@ -7,8 +7,9 @@ import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget
class TIMUIKitFileIcon extends TIMUIKitStatelessWidget {
final String? fileFormat;
+ final double? size;
- TIMUIKitFileIcon({this.fileFormat, Key? key}) : super(key: key);
+ TIMUIKitFileIcon( {this.size, this.fileFormat, Key? key}) : super(key: key);
Map fileMap = {
"doc": "images/word.png",
@@ -34,6 +35,11 @@ class TIMUIKitFileIcon extends TIMUIKitStatelessWidget {
"tif": "images/image_icon.png",
"wmf": "images/image_icon.png",
"dib": "images/image_icon.png",
+ "mp4": "images/video_icon.png",
+ "avi": "images/video_icon.png",
+ "mov": "images/video_icon.png",
+ "wmv": "images/video_icon.png",
+ "flv": "images/video_icon.png",
};
Widget _getFileIcon() {
@@ -46,8 +52,8 @@ class TIMUIKitFileIcon extends TIMUIKitStatelessWidget {
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
return SizedBox(
- height: 50,
- width: 50,
+ height: size ?? 50,
+ width: size ?? 50,
child: Container(padding: const EdgeInsets.all(4), child: _getFileIcon()),
);
}
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_image_elem.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_image_elem.dart
index 0b44e04..2b1eed1 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_image_elem.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_image_elem.dart
@@ -3,24 +3,24 @@
import 'dart:async';
import 'dart:convert';
import 'package:crypto/crypto.dart';
+import 'package:device_info_plus/device_info_plus.dart';
import 'package:http/http.dart' as http;
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
+import 'package:tencent_open_file/tencent_open_file.dart';
import 'package:universal_html/html.dart' as html;
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:cached_network_image/cached_network_image.dart';
-
import 'package:flutter/material.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:path_provider/path_provider.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_state.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/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/ui/constants/history_message_constant.dart';
@@ -31,6 +31,7 @@ import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageIt
import 'package:tencent_cloud_chat_uikit/ui/widgets/image_screen.dart';
import 'package:transparent_image/transparent_image.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
+import 'package:url_launcher/url_launcher.dart';
class TIMUIKitImageElem extends StatefulWidget {
final V2TimMessage message;
@@ -79,10 +80,11 @@ class _TIMUIKitImageElem extends TIMUIKitState {
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(5)),
border: Border.all(
- width: 1,
- color: theme?.black ?? Colors.white,
+ width: 2,
+ color: theme?.weakDividerColor ?? Colors.grey,
)),
- height: 100,
+ height: 170,
+ width: 170,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
@@ -135,9 +137,27 @@ class _TIMUIKitImageElem extends TIMUIKitState {
return;
}
} else {
- if (!await Permissions.checkPermission(
- context, Permission.storage.value, theme!)) {
- return;
+ final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
+ AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
+ if (PlatformUtils().isMobile) {
+ if ((androidInfo.version.sdkInt ?? 0) >= 33) {
+ final photos = await Permissions.checkPermission(
+ context,
+ Permission.photos.value,
+ theme,
+ );
+ if (!photos) {
+ return;
+ }
+ } else {
+ final storage = await Permissions.checkPermission(
+ context,
+ Permission.storage.value,
+ );
+ if (!storage) {
+ return;
+ }
+ }
}
}
@@ -233,7 +253,8 @@ class _TIMUIKitImageElem extends TIMUIKitState {
if (path != null && PlatformUtils().isWeb
? true
: File(path!).existsSync()) {
- return await _saveImageToLocal(context, path, isAsset: true, theme: theme);
+ return await _saveImageToLocal(context, path,
+ isAsset: true, theme: theme);
} else {
String imgUrl = getBigPicUrl();
if (widget.message.imageElem!.imageList![0]!.localUrl != '' &&
@@ -288,18 +309,22 @@ class _TIMUIKitImageElem extends TIMUIKitState {
child: errorDisplay(context, theme),
));
- Widget _renderLocalImage(
- String imgPath, dynamic heroTag, double positionRadio, TUITheme? theme) {
- double? currentPositionRadio;
+ Widget _renderLocalImage(String smallImage, dynamic heroTag,
+ double? positionRadio, TUITheme? theme, String? originImage) {
+ double? currentPositionRadio = positionRadio;
+ File imgF = File(smallImage);
- File imgF = File(imgPath);
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) {
@@ -311,30 +336,42 @@ class _TIMUIKitImageElem extends TIMUIKitState {
final preloadImage = model.preloadImageMap[
message.seq! + message.timestamp.toString() + (message.msgID ?? "")];
+ final isDesktopScreen =
+ TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
+
return Stack(
alignment: AlignmentDirectional.topStart,
children: [
- AspectRatio(
- aspectRatio: currentPositionRadio ?? positionRadio,
- child: Container(
- decoration: const BoxDecoration(color: Colors.transparent),
+ if (!isDesktopScreen && currentPositionRadio != null)
+ AspectRatio(
+ aspectRatio: currentPositionRadio!,
+ child: Container(
+ decoration: const BoxDecoration(color: Colors.transparent),
+ ),
),
- ),
getImage(
- GestureDetector(
+ InkWell(
onTap: () {
- Navigator.of(context).push(
- PageRouteBuilder(
- opaque: false, // set to false
- pageBuilder: (_, __, ___) => ImageScreen(
- imageProvider: FileImage(File(imgPath)),
- heroTag: heroTag,
- messageID: widget.message.msgID,
- downloadFn: () async {
- return await _saveImg(theme!);
- }),
- ),
- );
+ 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:
@@ -344,13 +381,11 @@ class _TIMUIKitImageElem extends TIMUIKitState {
child: preloadImage != null
? RawImage(
image: preloadImage,
- fit: BoxFit.fitWidth,
- width: double.infinity,
+ fit: BoxFit.contain,
)
: Image.file(
- File(imgPath),
- fit: BoxFit.fitWidth,
- width: double.infinity,
+ File(smallImage),
+ fit: BoxFit.contain,
),
),
)),
@@ -406,35 +441,45 @@ class _TIMUIKitImageElem extends TIMUIKitState {
}
Widget _renderNetworkImage(
- dynamic heroTag, double positionRadio, TUITheme? theme,
+ dynamic heroTag, double? positionRadio, TUITheme? theme,
{String? path, V2TimImage? originalImg, V2TimImage? smallImg}) {
try {
+ final isDesktopScreen =
+ TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
String bigImgUrl = originalImg?.url ?? getBigPicUrl();
if (bigImgUrl.isEmpty && smallImg?.url != null) {
bigImgUrl = smallImg!.url!;
}
return Stack(
- alignment: widget.message.isSelf ?? false
+ alignment: widget.message.isSelf ?? true
? AlignmentDirectional.topEnd
: AlignmentDirectional.topStart,
children: [
getImage(
GestureDetector(
onTap: () {
- Navigator.of(context).push(
- PageRouteBuilder(
- opaque: false, // set to false
- pageBuilder: (_, __, ___) => ImageScreen(
- imageProvider: CachedNetworkImageProvider(
- path ?? bigImgUrl,
- cacheKey: widget.message.msgID,
- ),
- heroTag: heroTag,
- messageID: widget.message.msgID,
- downloadFn: () async {
- return await _saveImg(theme!);
- })),
- );
+ if (isDesktopScreen) {
+ onTIMCallback(TIMCallback(
+ infoCode: 6660414,
+ infoRecommendText: TIM_t("正在下载中"),
+ type: TIMCallbackType.INFO
+ ));
+ } else {
+ Navigator.of(context).push(
+ PageRouteBuilder(
+ opaque: false, // set to false
+ pageBuilder: (_, __, ___) => ImageScreen(
+ imageProvider: CachedNetworkImageProvider(
+ path ?? bigImgUrl,
+ cacheKey: widget.message.msgID,
+ ),
+ heroTag: heroTag,
+ messageID: widget.message.msgID,
+ downloadFn: () async {
+ return await _saveImg(theme!);
+ })),
+ );
+ }
},
child: Container(
constraints:
@@ -474,13 +519,13 @@ class _TIMUIKitImageElem extends TIMUIKitState {
bool isNeedShowLocalPath() {
final current = (DateTime.now().millisecondsSinceEpoch / 1000).ceil();
final timeStamp = widget.message.timestamp ?? current;
- return (widget.message.isSelf ?? false) &&
+ return (widget.message.isSelf ?? true) &&
(isSent || current - timeStamp < 300);
}
Widget? _renderImage(dynamic heroTag, TUITheme? theme,
{V2TimImage? originalImg, V2TimImage? smallImg}) {
- double positionRadio = 1.0;
+ double? positionRadio;
if (smallImg?.width != null &&
smallImg?.height != null &&
smallImg?.width != 0 &&
@@ -501,8 +546,12 @@ class _TIMUIKitImageElem extends TIMUIKitState {
widget.message.imageElem!.path != null &&
widget.message.imageElem!.path!.isNotEmpty &&
File(widget.message.imageElem!.path!).existsSync())) {
- return _renderLocalImage(widget.message.imageElem!.path!, heroTag,
- networkImagePositionRadio ?? positionRadio, theme);
+ return _renderLocalImage(
+ widget.message.imageElem!.path!,
+ heroTag,
+ networkImagePositionRadio ?? positionRadio,
+ theme,
+ widget.message.imageElem!.path!);
}
} catch (e) {
// ignore: avoid_print
@@ -513,15 +562,18 @@ class _TIMUIKitImageElem extends TIMUIKitState {
if (smallImg?.localUrl != null &&
smallImg?.localUrl != "" &&
File((smallImg?.localUrl!)!).existsSync()) {
- return _renderLocalImage(smallImg!.localUrl!, heroTag,
- networkImagePositionRadio ?? positionRadio, theme);
+ return _renderLocalImage(smallImg!.localUrl!, heroTag, positionRadio,
+ theme, originalImg?.localUrl);
}
} catch (e) {
// ignore: avoid_print
print(e);
+ return _renderNetworkImage(heroTag, positionRadio, theme,
+ smallImg: smallImg, originalImg: originalImg);
}
- if ((smallImg?.url ?? originalImg?.url) != null &&
+ if (
+ (smallImg?.url ?? originalImg?.url) != null &&
(smallImg?.url ?? originalImg?.url)!.isNotEmpty) {
return _renderNetworkImage(heroTag, positionRadio, theme,
smallImg: smallImg, originalImg: originalImg);
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_reply_elem.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_reply_elem.dart
index 4b3336b..fb8c83a 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_reply_elem.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_reply_elem.dart
@@ -2,22 +2,20 @@
import 'dart:async';
import 'dart:convert';
-
import 'package:flutter/material.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/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart';
+import 'package:tencent_extended_text/extended_text.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_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/view_models/tui_chat_global_model.dart';
-
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
-
-
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_show_panel.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/main.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_face_elem.dart';
-
-
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_chat_config.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_cloud_custom_data.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/link_preview_entry.dart';
@@ -35,6 +33,8 @@ class TIMUIKitReplyElem extends StatefulWidget {
final EdgeInsetsGeometry? textPadding;
final TUIChatSeparateViewModel chatModel;
final bool? isShowMessageReaction;
+ final bool isUseDefaultEmoji;
+ final List customEmojiStickerList;
const TIMUIKitReplyElem({
Key? key,
@@ -47,6 +47,8 @@ class TIMUIKitReplyElem extends StatefulWidget {
this.isShowMessageReaction,
this.backgroundColor,
this.textPadding,
+ this.isUseDefaultEmoji = false,
+ this.customEmojiStickerList = const [],
required this.chatModel,
}) : super(key: key);
@@ -63,7 +65,10 @@ class _TIMUIKitReplyElemState extends TIMUIKitState {
MessageRepliedData? _getRepliedMessage() {
try {
final CloudCustomData messageCloudCustomData = CloudCustomData.fromJson(
- json.decode(widget.message.cloudCustomData!));
+ json.decode(
+ TencentUtils.checkString(widget.message.cloudCustomData) != null
+ ? widget.message.cloudCustomData!
+ : "{}"));
if (messageCloudCustomData.messageReply != null) {
final MessageRepliedData repliedMessage =
MessageRepliedData.fromJson(messageCloudCustomData.messageReply!);
@@ -81,14 +86,14 @@ class _TIMUIKitReplyElemState extends TIMUIKitState {
final messageID = cloudCustomData.messageID;
final message = await widget.chatModel.findMessage(messageID);
if (message != null) {
- if(mounted){
+ if (mounted) {
setState(() {
rawMessage = message;
});
}
}
}
- if(mounted){
+ if (mounted) {
setState(() {
repliedMessage = cloudCustomData;
});
@@ -104,17 +109,17 @@ class _TIMUIKitReplyElemState extends TIMUIKitState {
}
_rawMessageBuilder(V2TimMessage? message, TUITheme? theme) {
- if(repliedMessage == null){
+ if (repliedMessage == null) {
return const SizedBox(width: 0, height: 12);
}
if (message == null) {
- if(repliedMessage?.messageAbstract != null){
+ if (repliedMessage?.messageAbstract != null) {
return _defaultRawMessageText(repliedMessage!.messageAbstract, theme);
}
return const SizedBox(width: 0, height: 12);
}
final messageType = message.elemType;
- final isSelf = message.isSelf ?? false;
+ final isSelf = message.isSelf ?? true;
if (widget.chatModel.abstractMessageBuilder != null) {
return _defaultRawMessageText(
widget.chatModel.abstractMessageBuilder!(message),
@@ -148,11 +153,14 @@ class _TIMUIKitReplyElemState extends TIMUIKitState {
case MessageElemType.V2TIM_ELEM_TYPE_IMAGE:
return TIMUIKitImageElem(
chatModel: widget.chatModel,
- message: message, isFrom: "reply", isShowMessageReaction: false);
+ message: message,
+ isFrom: "reply",
+ isShowMessageReaction: false);
case MessageElemType.V2TIM_ELEM_TYPE_VIDEO:
return TIMUIKitVideoElem(message,
chatModel: widget.chatModel,
- isFrom: "reply", isShowMessageReaction: false);
+ isFrom: "reply",
+ isShowMessageReaction: false);
case MessageElemType.V2TIM_ELEM_TYPE_LOCATION:
return _defaultRawMessageText(TIM_t("[位置]"), theme);
case MessageElemType.V2TIM_ELEM_TYPE_MERGER:
@@ -177,7 +185,7 @@ class _TIMUIKitReplyElemState extends TIMUIKitState {
@override
void didUpdateWidget(covariant TIMUIKitReplyElem oldWidget) {
- WidgetsBinding.instance?.addPostFrameCallback((mag) {
+ WidgetsBinding.instance.addPostFrameCallback((mag) {
super.didUpdateWidget(oldWidget);
_getMessageByMessageID();
});
@@ -219,44 +227,39 @@ class _TIMUIKitReplyElemState extends TIMUIKitState {
}
}
- Widget _renderPreviewWidget() {
+ Widget? _renderPreviewWidget() {
// If the link preview info from [localCustomData] is available, use it to render the preview card.
// Otherwise, it will returns null.
if (widget.message.localCustomData != null &&
widget.message.localCustomData!.isNotEmpty) {
- final String localJSON = widget.message.localCustomData!;
- final LocalCustomDataModel? localPreviewInfo =
- LocalCustomDataModel.fromMap(json.decode(localJSON));
- if (localPreviewInfo != null && !localPreviewInfo.isLinkPreviewEmpty()) {
- return Container(
- margin: const EdgeInsets.only(top: 8),
- child:
- // You can use this default widget [LinkPreviewWidget] to render preview card, or you can use custom widget.
- LinkPreviewWidget(linkPreview: localPreviewInfo),
- );
- } else {
- return Text(widget.message.textElem?.text ?? "",
- softWrap: true,
- style: widget.fontStyle ??
- TextStyle(
- fontSize: 16,
- height: widget.chatModel.chatConfig.textHight,
- ));
+ try {
+ final String localJSON = widget.message.localCustomData!;
+ final LocalCustomDataModel? localPreviewInfo =
+ LocalCustomDataModel.fromMap(json.decode(localJSON));
+ if (localPreviewInfo != null &&
+ !localPreviewInfo.isLinkPreviewEmpty()) {
+ return Container(
+ margin: const EdgeInsets.only(top: 8),
+ child:
+ // You can use this default widget [LinkPreviewWidget] to render preview card, or you can use custom widget.
+ LinkPreviewWidget(linkPreview: localPreviewInfo),
+ );
+ } else {
+ return null;
+ }
+ } catch (e) {
+ return null;
}
} else {
- return Text(widget.message.textElem?.text ?? "",
- softWrap: true,
- style: widget.fontStyle ??
- TextStyle(
- fontSize: 16,
- height: widget.chatModel.chatConfig.textHight,
- ));
+ return null;
}
}
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final theme = value.theme;
+ final isDesktopScreen =
+ TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
if (widget.isShowJump) {
if (!isShining) {
Future.delayed(Duration.zero, () {
@@ -269,13 +272,17 @@ class _TIMUIKitReplyElemState extends TIMUIKitState {
}
}
}
- final defaultStyle = (widget.message.isSelf ?? false)
- ? theme.lightPrimaryMaterialColor.shade50
- : theme.weakBackgroundColor;
+
+ final isFromSelf = widget.message.isSelf ?? true;
+
+ final defaultStyle = isFromSelf
+ ? (theme.chatMessageItemFromSelfBgColor ??
+ theme.lightPrimaryMaterialColor.shade50)
+ : (theme.chatMessageItemFromOthersBgColor);
+
final backgroundColor = isShowJumpState
? const Color.fromRGBO(245, 166, 35, 1)
- : (widget.backgroundColor ?? defaultStyle);
- final isFromSelf = widget.message.isSelf ?? false;
+ : (defaultStyle ?? widget.backgroundColor);
final borderRadius = isFromSelf
? const BorderRadius.only(
@@ -291,9 +298,13 @@ class _TIMUIKitReplyElemState extends TIMUIKitState {
final textWithLink = LinkPreviewEntry.getHyperlinksText(
widget.message.textElem?.text ?? "",
widget.chatModel.chatConfig.isSupportMarkdownForTextMessage,
- widget.chatModel.chatConfig.onTapLink);
+ onLinkTap: widget.chatModel.chatConfig.onTapLink,
+ isUseDefaultEmoji: widget.isUseDefaultEmoji,
+ customEmojiStickerList: widget.customEmojiStickerList,
+ isEnableTextSelection:
+ widget.chatModel.chatConfig.isEnableTextSelection);
return Container(
- padding: widget.textPadding ?? const EdgeInsets.all(10),
+ padding: widget.textPadding ?? EdgeInsets.all(isDesktopScreen ? 12 : 10),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: widget.borderRadius ?? borderRadius,
@@ -318,7 +329,9 @@ class _TIMUIKitReplyElemState extends TIMUIKitState