V5 (#1222)
* chore(flutter): change BoxFit of VLC player * chore(flutter): remove unused imports * chore(flutter sample): tweak breakpoints, ignore some lints * chore(flutter): minor code readability improvements * chore(flutter): extract out platform_widget_builder to its own "package" * fix(flutter): fix a theme bug with PlatformDialog * chore(persistence): test plugin on macOS * Generated the macos runner for the example * Re-generated the android runner because it was using the v1 embedding * chore(persistence): test plugin on web * chore(persistence): add Windows runner to example * chore(persistence): add linux runner to example * feat(flutter): branded context menus * chore(flutter): fix bug where lines would be shown in between menu items * chore(flutter): import/export fixes * chore(flutter): move file * chore(flutter): /src/channel Also refactors several /channel widgets to use class widgets instead of methods. Some methods, however, have been left as those cases do actually produce cleaner code. chore(flutter): fix bad directory placement chore(flutter): fix bad directory placement * chore(flutter): more directory organization * chore(flutter): navigator improvements * chore(flutter): void async improvements * chore(flutter): fix imports/exports * docs(flutter): documentation updates for /attachment * docs(flutter): doc updates For: * /avatars * /bottom_sheets * /channel * feat(flutter): selectable message text on web & desktop * Squashed commit of the following: commit 50b249edb6b2ee2d744e4e00e97f81979026aab4 Author: GroovinChip <[email protected]> Date: Wed Mar 16 14:55:05 2022 -0400 chore(flutter): wrapping up docs commit 7d289ae45e20c0109bcdb4496f7b693e2adc4ef9 Author: GroovinChip <[email protected]> Date: Wed Mar 16 14:42:09 2022 -0400 chore(flutter): more docs & typedefs commit 6b36f6f3864f15b52e441d58ee7e62577ff8f843 Author: GroovinChip <[email protected]> Date: Wed Mar 16 14:22:34 2022 -0400 chore(flutter): more doc fixes commit 1a9a6de3c836e9ee5d6373033f1d9a7da830bddc Merge: 59188f5d 88abf99f Author: GroovinChip <[email protected]> Date: Wed Mar 16 13:17:52 2022 -0400 Merge branch 'desk_web_dev' into utils commit 59188f5d9c1dd68edd531086cdfd9d42faf93fc8 Author: GroovinChip <[email protected]> Date: Wed Mar 16 12:48:16 2022 -0400 chore(flutter): add todo commit 9b4a986ca5757f284677eb5335637d11ccb1d642 Author: GroovinChip <[email protected]> Date: Wed Mar 16 12:43:12 2022 -0400 chore(flutter): more docs and typedefs updates commit 8786d2dd82b94f673b36371fb2b33a1ebcc9840a Author: GroovinChip <[email protected]> Date: Wed Mar 16 12:14:14 2022 -0400 chore(flutter): more typedef and doc template updates commit 9e906beb0e7a054c13a32c8c8c5b0e2a9e27bb21 Author: GroovinChip <[email protected]> Date: Wed Mar 16 10:46:04 2022 -0400 chore(flutter): create /uitils and move files Fixes subsequent imports/exports * chore(flutter): update file_selector plugin * chore(flutter): include utils/typedefs.dart in library export * Squashed commit of the following: commit 501228afa614b0d740858dec1ebedd3f671e2c96 Author: GroovinChip <[email protected]> Date: Thu Mar 17 16:18:12 2022 -0400 feat(flutter): ensure fullscreen media feature works on ALL platforms commit cd0aab92bc7d90af03258829a5aae66125578af5 Author: GroovinChip <[email protected]> Date: Thu Mar 17 14:26:48 2022 -0400 feat(flutter): working conditional import for initializing VLC * fix(flutter): remove stray import causing web to break * chore(flutter): ensure context menus can show on web in sample * feat(flutter) FullScreenMedia context menus for Web * feat(flutter): branded context menus * chore(flutter): no success dialog on file download (web) * chore(flutter): remove platform UI packages * chore(flutter): properly show/hide delete message menu item Also deletes deprecated PlatformDialog * feat(flutter): 'view info' channel preview context menu * feat(flutter): more menu actions for ChannelPreview Also new ConfirmationDialog and add missing docs * feat(flutter/localizations): translations related to channel mutes * chore(flutter): remove prints and fix super.didChangeDependencies order * chore(flutter): rename some things in ChannelBottomSheet * chore(flutter): more sensible name in TypingIndicator * chore(flutter): remove unused code in MessageWidget * feat(flutter): ChannelInfoDialog * chore(flutter): organize files more Also refactors out some internal MessageListView widgets * fix(flutter): fix render overflow issue * chore(flutter): move MediaListView * chore(flutter): remove unused imports * fix(flutter): fix weird share menu placement on macos * chore(flutter): clean up fullscreenmedia conditional imports * chore(flutter): adjust package exports * chore(flutter): update dart_vlc * chore(flutter): clean up platform checks * chore(flutter): run flutter format on lib * chore(flutter): minor sample cleanup * chore(flutter): ensure vlc doesn't get initialized in non-desktop testing environments * test(flutter): reformat existing tests * fix(flutter): pass onThreadReplyTap down to ThreadReplyButton * First round of new tests commit a90bed427521123fdaa4c5e276744a224fac2b68 Author: GroovinChip <[email protected]> Date: Thu Apr 7 12:34:19 2022 -0400 test(flutter): Username test commit d7bc179c20eae2713c9248191518bf1d955104a9 Author: GroovinChip <[email protected]> Date: Thu Apr 7 12:30:47 2022 -0400 chore(flutter): remove unused imports commit 09c9517de2917dc5ccd7de019b50a7875386991c Author: GroovinChip <[email protected]> Date: Thu Apr 7 12:29:58 2022 -0400 test(flutter): ThreadSeparator test commit 9422d1129211aaaac9a51ca4d29fe94bde06593c Author: GroovinChip <[email protected]> Date: Thu Apr 7 12:19:43 2022 -0400 chore(flutter): rename LoadingIndicator parameter commit 63af943a1a16e78a1e0377d76176420bd7e1fbc7 Author: GroovinChip <[email protected]> Date: Thu Apr 7 11:47:46 2022 -0400 test(flutter): FloatingDateDivider test commit d8e67cf6fca2b76ac091a9047104c0910d5fdfef Author: GroovinChip <[email protected]> Date: Tue Mar 29 15:19:39 2022 -0400 test(flutter): InputAttachment tests commit d2b7d5dd3304c8be6f74bf5b0281d7a95dd027ec Author: GroovinChip <[email protected]> Date: Tue Mar 29 14:41:40 2022 -0400 test(flutter): DmCheckbox tests commit 01567f235a2f00880bb530b232489e3790159b25 Author: GroovinChip <[email protected]> Date: Tue Mar 29 14:16:08 2022 -0400 chore(flutter): remove unused imports commit c9625858e840df6082506ae9374f1d2710053cbd Author: GroovinChip <[email protected]> Date: Tue Mar 29 14:15:44 2022 -0400 test(flutter): CountdownButton tests commit d770474bb11ee8b36a3b0e18db31695fd812a6b3 Author: GroovinChip <[email protected]> Date: Tue Mar 29 14:11:37 2022 -0400 test(flutter): CommandButton tests commit 95e63706d1dd971373d39d471894a38bb2a634f2 Author: GroovinChip <[email protected]> Date: Tue Mar 29 14:07:43 2022 -0400 test(flutter): ClearInputItem tests commit 4156586945c91408cb7943b6c57c2ae560a2857c Author: GroovinChip <[email protected]> Date: Tue Mar 29 14:03:59 2022 -0400 test(flutter): AttachmentButton tests commit bba042806326999aabf338636c6e33900de86160 Author: GroovinChip <[email protected]> Date: Tue Mar 29 13:00:42 2022 -0400 test(flutter): golden test for SendButton commit e2cd0f00b9eeb8d81399f6316edb298234de12dc Author: GroovinChip <[email protected]> Date: Tue Mar 29 12:56:52 2022 -0400 chore(flutter): deal with lints commit b4e045f9cbd54c4d42c3f30acfe72d5f8ccf9d82 Author: GroovinChip <[email protected]> Date: Tue Mar 29 12:56:00 2022 -0400 test(flutter): SendButton test commit 4e3d5db0bddb97e508fcdda2e7272fe31e70808f Author: GroovinChip <[email protected]> Date: Tue Mar 29 12:38:21 2022 -0400 test(flutter): KeyboardShortcutRunner tests commit 0dfd7a0bd6fd6bc76902e87f62ac29da0b01bac6 Author: GroovinChip <[email protected]> Date: Tue Mar 29 12:17:30 2022 -0400 test(flutter): UploadProgressIndicator commit ec50c8aea716bdc19e4308e367c8331211c61ea3 Author: GroovinChip <[email protected]> Date: Tue Mar 29 11:47:53 2022 -0400 test(flutter): SendingIndicator tests commit e87bd1450fb5bfb713a867c7e831968f76428848 Author: GroovinChip <[email protected]> Date: Tue Mar 29 11:32:35 2022 -0400 test(flutter): more gallery tests commit 93d46ca649277fe6d289a70f0bdd23584fd0ab94 Author: GroovinChip <[email protected]> Date: Tue Mar 29 11:17:29 2022 -0400 test(flutter): remove bad mock commit c7b03a39f0f3ca7a334dfb908f3473b5e4deab38 Author: GroovinChip <[email protected]> Date: Tue Mar 29 10:45:45 2022 -0400 chore(flutter): minor refactor commit 33aeb7b5ed398287cb930f3b4702f47a82fce2c6 Author: GroovinChip <[email protected]> Date: Tue Mar 29 09:49:07 2022 -0400 test(flutter): MessageDialog tests commit 220b3ae591e135d9606254666e04d4771945d0ae Author: GroovinChip <[email protected]> Date: Tue Mar 29 09:42:51 2022 -0400 test(flutter): DeleteMessageDialog tests commit db508478bb96749971f563de87b6c2a49e1e4e57 Author: GroovinChip <[email protected]> Date: Mon Mar 28 14:46:41 2022 -0400 test(flutter): ConfirmationDialog tests commit e8172dad6bfe18f40b65554ef2c6c16d7437cb56 Author: GroovinChip <[email protected]> Date: Mon Mar 28 14:35:50 2022 -0400 test(flutter): ChannelInfoDialog test commit 04c21458a601af3c3f4a5fc5ce9e8b61493c208c Author: GroovinChip <[email protected]> Date: Mon Mar 28 13:57:02 2022 -0400 test(flutter): DownloadMenuItem tests commit afee1dace543e2fef8543f9b035f684b2ee00f3b Author: GroovinChip <[email protected]> Date: Mon Mar 28 13:53:21 2022 -0400 test(flutter): StreamChatContextMenuItem tests commit 4616b1a973d0cf5224b5a71914c9908339c8462a Author: GroovinChip <[email protected]> Date: Mon Mar 28 13:02:07 2022 -0400 test(flutter): golden test for ErrorAlertSheet commit 22fb8241ae333f57ade8e8550f87695929836b2b Author: GroovinChip <[email protected]> Date: Mon Mar 28 12:59:01 2022 -0400 test(flutter): golden test for EditMessageSheet commit 0be79964827f344ba66e99cb9e5bd3d627aed01d Author: GroovinChip <[email protected]> Date: Mon Mar 28 12:56:08 2022 -0400 test(flutter): golden test for AttachmentModalSheet commit 0ae842a98143478885bc211c788353c8bca57b40 Author: GroovinChip <[email protected]> Date: Mon Mar 28 12:53:26 2022 -0400 test(flutter): GroupAvatar tests commit 697d294c6c7e1bba1d9089a53d5c60d8609ea64e Author: GroovinChip <[email protected]> Date: Mon Mar 28 12:30:25 2022 -0400 test(flutter): UserAvatar tests commit c03ba4124068c83d3a49c4db00ca18a659801c23 Author: GroovinChip <[email protected]> Date: Mon Mar 28 11:04:55 2022 -0400 test(flutter): ErrorAlertSheet test commit 3414f28530b0e22614b83af20361f21dd4967fae Author: GroovinChip <[email protected]> Date: Mon Mar 28 10:45:37 2022 -0400 test(flutter): EditMessageSheet test commit b2962b641dfbf14c11d3da7f80a4be27f9980d90 Author: GroovinChip <[email protected]> Date: Mon Mar 28 10:37:18 2022 -0400 test(flutter): AttachmentModalSheet test test(flutter): AttachmentModalSheet test chore(flutter): update exports commit 7d7e278692c65dea84e7ef3b2d3c1311ebb28408 Author: GroovinChip <[email protected]> Date: Mon Mar 28 09:52:29 2022 -0400 test(flutter): more organization commit 4479b649835acc62d8a0553f9505a0af8077abfc Author: GroovinChip <[email protected]> Date: Mon Mar 28 09:33:06 2022 -0400 test(flutter): tweak AttachmentHandler tests commit b4a41106567e3c08105ce8dc272b1ebf40b3b0bb Author: GroovinChip <[email protected]> Date: Mon Mar 28 09:24:00 2022 -0400 chore(flutter): streamline attachment handling * MobileAttachmentHandler for streamlining related code for mobile * Break out DesktopAttachmentHandler into its own file * Update minSdk version in Android runner commit bfb06d31b79cd941d8b611c7e89e2d7728a394cb Merge: a44b3b39 15c83d03 Author: GroovinChip <[email protected]> Date: Fri Mar 25 13:00:27 2022 -0400 Merge branch 'desk_web_dev' into tests commit a44b3b390adc25a00d1597bb1460973e8555fa9e Author: GroovinChip <[email protected]> Date: Fri Mar 25 12:59:14 2022 -0400 chore(flutter): start cleaning up test dir commit de104a3491d942d12f72dd66152b62c6e8b10a5b Author: GroovinChip <[email protected]> Date: Fri Mar 25 12:56:48 2022 -0400 test(flutter): VideoAttachment test commit 4748a1c081a3272f7fdbdeb69c501344e221e01e Author: GroovinChip <[email protected]> Date: Fri Mar 25 12:52:30 2022 -0400 test(flutter): UrlAttachment test commit 54408ef70f07c60adf8350553fdf3173e5edd7c7 Author: GroovinChip <[email protected]> Date: Fri Mar 25 12:48:24 2022 -0400 test(flutter): ImageGroup test commit 811cff069e490c6bd03be0c0ac2f5ec86fb990e9 Author: GroovinChip <[email protected]> Date: Fri Mar 25 12:46:04 2022 -0400 test(flutter): ImageAttachment test commit a72677976c6524e76793432b0520e99cac4e7a62 Author: GroovinChip <[email protected]> Date: Fri Mar 25 12:34:06 2022 -0400 test(flutter): GiphyAttachment test commit 32ddac022e2c149726b4b6607f41850d556055b8 Author: GroovinChip <[email protected]> Date: Fri Mar 25 12:28:01 2022 -0400 test(flutter): FileAttachment test commit 6534958cab775be1fb9bda56e67caa4746aa1502 Author: GroovinChip <[email protected]> Date: Fri Mar 25 12:14:25 2022 -0400 test(flutter): AttachmentError test commit 718a83c10c4e883ce01c9c002ced24de671d30b9 Author: GroovinChip <[email protected]> Date: Fri Mar 25 12:06:43 2022 -0400 test(flutter): AttachmentUploadStateBuilder test commit a05108c460e36b2e425e4ec06f8bf408b0c20e97 Author: GroovinChip <[email protected]> Date: Fri Mar 25 11:44:34 2022 -0400 chore(flutter): minor cleanup commit 6e73038ac3714a2bd96f8c587af7f743bf2b2023 Author: GroovinChip <[email protected]> Date: Fri Mar 25 11:43:23 2022 -0400 test(flutter): AttachmentTitle test commit c6ee52ec951877feef31dc1427eec3bfc2bd8fcb Author: GroovinChip <[email protected]> Date: Fri Mar 25 11:43:11 2022 -0400 test(flutter): minor cleanup commit 09845cd845aa8c546043f4ae04805a8e6f53f61c Author: GroovinChip <[email protected]> Date: Fri Mar 25 11:42:52 2022 -0400 chore(flutter): improve device segmentation & vlc init check commit 172e6b25a064b5147b7641f29ebff3c920156f70 Author: GroovinChip <[email protected]> Date: Fri Mar 25 10:37:43 2022 -0400 test(flutter): attachment_handler tests commit 5f5a91c70cd7c069f39518f1f50bb74ab484c7e9 Author: GroovinChip <[email protected]> Date: Wed Mar 23 13:55:27 2022 -0400 test(flutter): test PlatformWidgetBuilder & DesktopWidgetBuilder commit 5a457b7557cb4127b74ee022389a24e5e9150601 Author: GroovinChip <[email protected]> Date: Wed Mar 23 13:55:05 2022 -0400 chore(flutter): make base platform widgets testable Also fix environment check in stream_chat.dart commit 88fb16018103c581236aeebd978e705b223b2243 Author: GroovinChip <[email protected]> Date: Wed Mar 23 11:59:19 2022 -0400 chore(flutter): add readme to conditional_parent_widget library commit e9307f8a6a5639d59358c245f033f9d9b209fe20 Author: GroovinChip <[email protected]> Date: Wed Mar 23 11:54:54 2022 -0400 test(flutter): ConditionalParentBuilder tests * chore(flutter): add thumblr dependency * chore(flutter): fix typos * chore(flutter): properly order super calls * feat(flutter) video thumbnails on desktop * chore(flutter): fix incorrect file name * chore(flutter): fix docs for generateVideoThumbnail * chore(flutter): remove incorrect comment * chore(flutter): run flutter format * chore(flutter): ignore lint * chore(flutter): flutter format * chore(flutter): fix tests * fix(flutter): fix MessageListView bug caused by rebase * chore(flutter): migrate desktop stuff to v4/stream_message_input.dart * chore(flutter): migrate over desktop changes for message replies * chore(flutter): fix message replies on desktop * chore(flutter): address lints * chore(flutter): update dart_vlc * chore(flutter): melos clean & update photo_view * fix(ui): port #1118 * fix(ui): use attachmentButtonBuilder * fix(ui): fix file attachment * format * fix(ui): fix message bottom row * chore(flutter sample): use modern window look * chore(flutter): pass userAvatarBuilder through to MessageWidgetContent Also pass onUserAvatarTap & userAvatarBuilder through to UserAvatarTransform * chore: fix MessageWidget menu actions * fix(flutter): fix "enter" sending empty messages in StreamMessageInput * fix(flutter): fix Giphys not loading in quoted messages * chore(flutter): update thumblr * feat(flutter): keyboard navigation for gallery items * fix(ui): fix double date divider * fix(ui): add scroll to bottom back * fix(ui): use mouse region * fix(ui): remove backbutton in desktop example * fix(ui): rename full screen media widget * feat(flutter): elevation prop for gallery header * chore(flutter): update dart_vlc * fix(flutter): fix "show in chat" showing when it shouldn't * fix(flutter): fix "show in chat" for mobile * docs(flutter): minor doc update * chore(flutter): add onMessageSwiped in example app * chore(flutter): pass usernameBuilder through to BottomRow * fix(flutter): hacky fix for chonky link previews * fix(flutter): fix chonky text messages * feat(flutter): press "esc" to close gallery Also refactors KeyboardShortcutRunner API * chore(flutter): fix innaccurate name * fix(ui): fix attachment + long text sizing * add key to channelpage * fix(ui): better example * fix format * fix example * chore(flutter): revert modern window look * improvement(flutter): improve `GalleryNavigationItem` * Make buttons more visible * Spacing from the sides of the screen * fix video thumbnail * fix split view * fix(ui): fix giphy in thread * feat(flutter): desktop message reactions commit 4b880ecb6d4dcbf315a7d41d863174817a9c530a Author: Reuben Turner <[email protected]> Date: Thu Jun 9 12:25:21 2022 -0400 fix(flutter): fixes for desktop reactions * Fix reactions for reverse messages * Ensure reactions near the edges of MessageListView show properly commit f511513f8dbb272397e81d08bad33ee60108d738 Author: Salvatore Giordano <[email protected]> Date: Thu Jun 9 16:43:03 2022 +0200 fix(ui): fix DesktopReactionBuilder and Portal commit c4885648330552f5212c5ba6b771c0f07023f6ae Merge: 2b710a2b 72b34c21 Author: Reuben Turner <[email protected]> Date: Wed Jun 8 14:12:44 2022 -0400 Merge branch 'desk_web_dev' into desktop_reactions commit 2b710a2bb610326123d5b7f75c26caa705114c9b Author: Reuben Turner <[email protected]> Date: Mon Jun 6 12:19:12 2022 -0400 chore(flutter): add debug props commit e24dabc1de12da8dce697b6d4e1a3617e9835549 Author: Reuben Turner <[email protected]> Date: Mon Jun 6 12:00:03 2022 -0400 feat(flutter): first pass at desktop reactions popup on mouse hover * fix(flutter): potential fix for #1199 * fix(flutter): fix non-reversed reactions placement * refactor(flutter): extract bottom reaction, stacked reaction with debug props * fix(flutter): fixes for desktop reactions * Fix bottom row being rendered above reaction buttons * Fix padding issues related to avatars * chore(flutter): add back lost changes from 2b1fb6a Also removed commented code * chore(flutter): stacked reactions update * Show "You" if userId matches the reaction username (per design) * Ensure long names don't throw off alignment * chore(flutter): format * fix(ui): fix mime type generation * chore(flutter): tweak desktop video * chore(flutter): add some missing things from develop * merge develop * fix merge * fix merge * format * fix tests * increate test action timeout * remove test * refactor(ui,core): remove deprecated code and cleanup * run builder * update coverage threasholds * refactor(ui): remove arrow function from countdown button * Update packages/stream_chat_flutter/lib/src/message_input/countdown_button.dart * Update packages/stream_chat_flutter/lib/src/message_input/countdown_button.dart * update comment * fix(ui): fix attachment + link solution * fix(ui): fix attachment + link solution * chore(flutter) update changelog and version * chore(localizations): update changelog and versions * chore(llc,core,ui,persistence,localizations): update changelog and pubspec * chore(llc,core,ui,persistence,localizations): update changelog and pubspec * fix example * fix deps * fix(ui): fix enter sending the message while in slowmode * fix(ui): fix reaction list * fix web * fix(ui): fix image download when using assetUrl * fix(ui): fix typo in changelog * fix(ui): fix gallery navigation * fix(ui): fix giphy sending size * fix(ui): add keyboard shorcuts to gallery view * fix(ui): fix gallery footer * fix(core): allow scroll notification bubbling in lazy load scroll view * fix(ui): fix lazy load scroll view * fix(ui): fix loading to unread position * fix(ui): fix last message preview * chore(ui): update changelog * fix tests * fix(llc): fix connecting while connecting and disconneting (#1237) * fix(llc): fix connecting while connecting and disconneting * chore(llc): update changelog * chore(llc): add test * chore(llc): improve test * fix(llc): _resetRequestFlags regardless of the connection status * feat(llc): add support for AttachmentFileUploaderProvider. (#1246) * feat(llc): add support for `AttachmentFileUploaderProvider`. Signed-off-by: xsahil03x <[email protected]> * chore(llc): update CHANGELOG.md Signed-off-by: xsahil03x <[email protected]> * refactor(llc): Remove deprecated attachmentFileUploader field. Signed-off-by: xsahil03x <[email protected]> * port no localization * fix(ui): fix jiffy locale initialization (#1255) * fix(ui): fix jiffy locale initialization * chore(ui): update changelog * fix(ui): cleanup * chore(llc,core,ui,persistence,localizations): align changelog * chore(llc,core,ui,persistence,localizations): add links to 4.x changelogs * fix(ui): fix date divider not showing (#1259) * fix(ui): fix date divider not showing * chore(ui): update changelog * chore(ui): minor changes Signed-off-by: xsahil03x <[email protected]> * fix(ui): fix separator Signed-off-by: xsahil03x <[email protected]> Co-authored-by: Sahil Kumar <[email protected]> * chore(ui): add contextmenu requirements for web (#1261) * fix unread separator * feat(ui): `StreamChatConfiguration` class (#1128) * feat: StreamChatConfiguration class See #1125 for more details * chore(flutter): update changelog * fix analysis * cleanup * feat(ui): add enforceUniqueReactions to `StreamChatConfiguration` widget * add tests * refactor(ui): rename StreamChatConfigurationProvider and StreamChatConfiguration * refactor(ui): rename StreamChat.config to streamChatConfigData * refactor(ui): make enforceUniqueReactions non nullable * chore(ui): fix analysis errors * refactor(ui): make config fields non nullable * chore(ui): format Co-authored-by: Salvatore Giordano <[email protected]> * feat(ui, core): add StreamAutocomplete (#1263) * feat(ui, core): add stream_autocomplete.dart Signed-off-by: xsahil03x <[email protected]> * refactor(core, ui): refactor message input controller Signed-off-by: xsahil03x <[email protected]> * fix(ui): minor fixes Signed-off-by: xsahil03x <[email protected]> * feat(ui): add StreamAutocompleteOptions widget Signed-off-by: xsahil03x <[email protected]> * Update packages/stream_chat_flutter/lib/src/autocomplete/stream_autocomplete.dart * fix(ui): cleanup * chore(core, ui): cleanup Signed-off-by: xsahil03x <[email protected]> * chore(ui): ignore no empty block * chore(ui): more cleanup Signed-off-by: xsahil03x <[email protected]> * fix(ui): fix command button for bigger screens, other fixes. Signed-off-by: xsahil03x <[email protected]> Co-authored-by: Salvatore Giordano <[email protected]> * fix(ui): increase default file limit to 100MB (#1279) * fix(ui): increase default file limit to 100MB * Update packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart * fix(ui): export kDefaultMaxAttachmentSize and use it in `StreamAttachmentPicker` default * feat(ui): show dialog after clicking on the camera button and permission is denied (#1262) * feat(ui): show dialog after clicking on the camera button and permission is denied * chore(ui): update changelog * Remove permission_handler plugin and refactor * cleanup * cleanup * add tests * fix(ui): reset the permission state on resume * cleanup * fix concurrent calls * fix(ui): cleanup * fix(ui): add try catch * fix(llc, ui): copy file to tempdir before uploading a file and do not serialize bytes (#1285) * fix(llc, ui): copy file to tempdir before uploading a file and do not serialize bytes * fix(llc): do not save failed messages with attachments on web * fix(llc): replace every with any * chore(llc): add test * fix analysis * fix analysis * chore(llc,core,ui,persistence): align changelog with v4 * fix(ui): do not linkify system message mentions * chore(llc,core,ui,persistence,localizations): update changelog and pubspec (#1289) * chore(ui): update deps * chore(llc,core,ui,persistence,localizations): update changelog and pubspec * chore(ui): publish beta.2 * feat(core,ui): add StreamMemberListView, StreamMemberGridView, and StreamMemberListController (#1291) * feat(core,ui): add StreamMemberListView, StreamMemberGridView, and StreamMemberListController * chore(core,ui): update changelog * fix(ui): fix exports * chore(ui): add tests * chore(ui): add StreamMemberGridTile typedef * chore(ui): minor changes Signed-off-by: xsahil03x <[email protected]> Co-authored-by: Sahil Kumar <[email protected]> * fix(ui): do not send empty message * refactor(ui,localization): removed emoji picker and connected (#1292) * refactor(ui,localization): removed emoji picker and connected * chore(ui,localizations): update changelog * chore(repo): update ui coverage threashold * doc(ui): add autocomplete_triggers guide * chore(ui): add tests for isOnlyEmoji * chore(ui): add tests for isOnlyEmoji * feat(ui): add `StreamAutocompleteTrigger.triggerOnlyAfterSpace` * fix(llc): Fix `Channel.removeMessage` not able to remove thread message. (#1294) Signed-off-by: xsahil03x <[email protected]> * chore(ui): flutter format Signed-off-by: xsahil03x <[email protected]> * fix(ui): fix `stopTyping` unhandled exceptions when network is off or spotty. (#1296) * feat(llc, ui): Introduce `keyStrokeHandler` to properly handle keyStrokes. Signed-off-by: xsahil03x <[email protected]> * chore(ui): update CHANGELOG.md Signed-off-by: xsahil03x <[email protected]> * test(llc): add key_stroke_handler_test.dart Signed-off-by: xsahil03x <[email protected]> * chore(ui): update CHANGELOG.md Signed-off-by: xsahil03x <[email protected]> * fix(core): only handle the channel list event if the state is success. Signed-off-by: xsahil03x <[email protected]> * feat(llc): add `hide_history` flag in `channel.addMembers`. Signed-off-by: xsahil03x <[email protected]> * refactor(llc): Convert positional argument to optional arguments Signed-off-by: xsahil03x <[email protected]> * test(llc): update tests Signed-off-by: xsahil03x <[email protected]> * perf: improve image resizing * fix(core): use copyWith while setting controller items. Signed-off-by: xsahil03x <[email protected]> * feat(ui): Added `thumbnailSize`, `thumbnailResizeType`, and `thumbnailCropType` params to `StreamMessageWidget` and `StreamAttachmentPicker` to customize the appearance of image thumbnails. Signed-off-by: xsahil03x <[email protected]> * fix analysis * Release v4.5.0 (#1308) * chore(llc, ui, core): bump version to v4.5.0 Signed-off-by: xsahil03x <[email protected]> * chore(ui, core): update CHANGELOG.md Signed-off-by: xsahil03x <[email protected]> Signed-off-by: xsahil03x <[email protected]> * third party video integration * fix llc * refactor: clean up and add docs * chore(llc): update changelog * chore(llc): add tests * chore(llc): add more tests * fix analysis * chore(llc): add more tests * chore(llc): add more tests * fix(llc): only start listening to event once connected. Signed-off-by: xsahil03x <[email protected]> * chore(llc): update CHANGELOG.md Signed-off-by: xsahil03x <[email protected]> * chore(llc): move files to correct folders. Signed-off-by: xsahil03x <[email protected]> * fix(ui): Fix message text hiding because of a flutter bug (#1327) * fix(ui): remove clip * chore(ui): update changelog * cleanup * fix(ui): hitting "enter" on the android keyboard sends the message instead of going to a new line. Signed-off-by: xsahil03x <[email protected]> * feat(ui): add support for additional text field params in message input. Signed-off-by: xsahil03x <[email protected]> * fix analysis * remove print * chore(llc,core,ui): update pubspec and changelogs (#1333) * fix(ui): show placeholder for web video thumbs * fix file download on web * fix reply and show buttons * remove tests * treat svg as files * fix(ui): change default image resize to clip * solve some todos * polish v5 fixing analysis issues * feat(ui): stream attachment picker bottom sheet (#1336) * refactor(ui): improve stream_attachment_handler.dart, add stream_attachment_picker_bottom_sheet.dart Signed-off-by: xsahil03x <[email protected]> * feat(ui): add copyWith in command_button.dart Signed-off-by: xsahil03x <[email protected]> * chore(ui): remove permission_manager.dart Signed-off-by: xsahil03x <[email protected]> * fix(ui): some fixes Signed-off-by: xsahil03x <[email protected]> * fix(ui): more fixes Signed-off-by: xsahil03x <[email protected]> * chore(ui): fix lints, add doc comments Signed-off-by: xsahil03x <[email protected]> * chore(ci): update flutter version Signed-off-by: xsahil03x <[email protected]> * chore(ui): more fixes Signed-off-by: xsahil03x <[email protected]> * chore(ui): more fixes Signed-off-by: xsahil03x <[email protected]> * chore(ui): more fixes Signed-off-by: xsahil03x <[email protected]> * chore(ci): update melos and flutter version Signed-off-by: xsahil03x <[email protected]> Signed-off-by: xsahil03x <[email protected]> Co-authored-by: Salvatore Giordano <[email protected]> * remove todo * update changelog * update changelogs * revert * update coverage threasholds Signed-off-by: xsahil03x <[email protected]> Co-authored-by: GroovinChip <[email protected]> Co-authored-by: Sahil Kumar <[email protected]> Co-authored-by: Gordon Hayes <[email protected]> Co-authored-by: Sacha Arbonel <[email protected]>
This commit is contained in:
co-authored by
Sahil Kumar
GroovinChip
Gordon Hayes
Sacha Arbonel
parent
3e1d6e91c7
commit
4f2542291c
@@ -0,0 +1,28 @@
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
/// Returns true if the app is running on web.
|
||||
bool get isWeb => CurrentPlatform.isWeb;
|
||||
|
||||
/// Returns true if the app is running in a mobile device.
|
||||
bool get isMobileDevice => CurrentPlatform.isIos || CurrentPlatform.isAndroid;
|
||||
|
||||
/// Returns true if the app is running in a desktop device.
|
||||
bool get isDesktopDevice =>
|
||||
CurrentPlatform.isMacOS ||
|
||||
CurrentPlatform.isWindows ||
|
||||
CurrentPlatform.isLinux;
|
||||
|
||||
/// Returns true if the app is running on windows or linux platform.
|
||||
bool get isDesktopVideoPlayerSupported =>
|
||||
// Dart VLC is not supported on MacOS.
|
||||
!CurrentPlatform.isMacOS &&
|
||||
(CurrentPlatform.isWindows || CurrentPlatform.isLinux);
|
||||
|
||||
/// Returns true if the app is running in a mobile or web.
|
||||
bool get isMobileDeviceOrWeb => isWeb || isMobileDevice;
|
||||
|
||||
/// Returns true if the app is running in a desktop or web.
|
||||
bool get isDesktopDeviceOrWeb => isWeb || isDesktopDevice;
|
||||
|
||||
/// Returns true if the app is running in a flutter test environment.
|
||||
bool get isTestEnvironment => CurrentPlatform.isFlutterTest;
|
||||
@@ -0,0 +1,438 @@
|
||||
import 'package:diacritic/diacritic.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:stream_chat_flutter/src/localization/translations.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
/// String extension
|
||||
extension StringExtension on String {
|
||||
/// Returns the capitalized string
|
||||
String capitalize() =>
|
||||
isNotEmpty ? '${this[0].toUpperCase()}${substring(1).toLowerCase()}' : '';
|
||||
|
||||
/// Returns whether the string contains only emoji's or not.
|
||||
///
|
||||
/// Emojis guidelines
|
||||
/// 1 to 3 emojis: big size with no text bubble.
|
||||
/// 4+ emojis or emojis+text: standard size with text bubble.
|
||||
bool get isOnlyEmoji {
|
||||
final trimmedString = trim();
|
||||
if (trimmedString.isEmpty) return false;
|
||||
if (trimmedString.characters.length > 3) return false;
|
||||
final emojiRegex = RegExp(
|
||||
r'^(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])+$',
|
||||
multiLine: true,
|
||||
caseSensitive: false,
|
||||
);
|
||||
return emojiRegex.hasMatch(trimmedString);
|
||||
}
|
||||
|
||||
/// Removes accents and diacritics from the given String.
|
||||
String get diacriticsInsensitive => removeDiacritics(this);
|
||||
|
||||
/// Levenshtein distance between this and [t].
|
||||
int levenshteinDistance(String t) => levenshtein(this, t);
|
||||
|
||||
/// Returns a resized imageUrl with the given [width], [height], [resize]
|
||||
/// and [crop] if it is from Stream CDN or Dashboard.
|
||||
///
|
||||
/// Read more at https://getstream.io/chat/docs/flutter-dart/file_uploads/?language=dart#image-resizing
|
||||
String getResizedImageUrl({
|
||||
// TODO: Are these sizes optimal? Consider web/desktop
|
||||
double width = 400,
|
||||
double height = 400,
|
||||
String /*clip|crop|scale|fill*/ resize = 'clip',
|
||||
String /*center|top|bottom|left|right*/ crop = 'center',
|
||||
}) {
|
||||
final uri = Uri.parse(this);
|
||||
final host = uri.host;
|
||||
|
||||
final fromStreamCDN = host.endsWith('stream-io-cdn.com');
|
||||
final fromStreamDashboard = host.endsWith('stream-cloud-uploads.imgix.net');
|
||||
|
||||
if (!fromStreamCDN && !fromStreamDashboard) return this;
|
||||
|
||||
final queryParameters = {...uri.queryParameters};
|
||||
|
||||
if (fromStreamCDN) {
|
||||
if (queryParameters['h'].isNullOrMatches('*') &&
|
||||
queryParameters['w'].isNullOrMatches('*') &&
|
||||
queryParameters['crop'].isNullOrMatches('*') &&
|
||||
queryParameters['resize'].isNullOrMatches('*')) {
|
||||
queryParameters['h'] = height.floor().toString();
|
||||
queryParameters['w'] = width.floor().toString();
|
||||
queryParameters['crop'] = crop;
|
||||
queryParameters['resize'] = resize;
|
||||
}
|
||||
} else if (fromStreamDashboard) {
|
||||
queryParameters['height'] = height.floor().toString();
|
||||
queryParameters['width'] = width.floor().toString();
|
||||
queryParameters['fit'] = crop;
|
||||
}
|
||||
|
||||
return uri.replace(queryParameters: queryParameters).toString();
|
||||
}
|
||||
}
|
||||
|
||||
/// List extension
|
||||
extension IterableX<T> on Iterable<T> {
|
||||
/// Insert any item<T> inBetween the list items
|
||||
List<T> insertBetween(T item) => expand((e) sync* {
|
||||
yield item;
|
||||
yield e;
|
||||
}).skip(1).toList(growable: false);
|
||||
}
|
||||
|
||||
/// Useful extension for [PlatformFile]
|
||||
extension PlatformFileX on PlatformFile {
|
||||
/// Converts the [PlatformFile] into [AttachmentFile]
|
||||
AttachmentFile get toAttachmentFile {
|
||||
return AttachmentFile(
|
||||
path: kIsWeb ? null : path,
|
||||
name: name,
|
||||
bytes: bytes,
|
||||
size: size,
|
||||
);
|
||||
}
|
||||
|
||||
/// Converts the [PlatformFile] to a [Attachment].
|
||||
Attachment toAttachment({required String type}) {
|
||||
final file = toAttachmentFile;
|
||||
final extraDataMap = <String, Object>{};
|
||||
|
||||
final mimeType = file.mimeType?.mimeType;
|
||||
|
||||
if (mimeType != null) {
|
||||
extraDataMap['mime_type'] = mimeType;
|
||||
}
|
||||
|
||||
extraDataMap['file_size'] = file.size!;
|
||||
|
||||
final attachment = Attachment(
|
||||
file: file,
|
||||
type: type,
|
||||
extraData: extraDataMap,
|
||||
);
|
||||
|
||||
return attachment;
|
||||
}
|
||||
}
|
||||
|
||||
/// Useful extension for [XFile]
|
||||
extension XFileX on XFile {
|
||||
/// Converts the [PlatformFile] into [AttachmentFile]
|
||||
Future<AttachmentFile> get toAttachmentFile async {
|
||||
final bytes = await readAsBytes();
|
||||
return AttachmentFile(
|
||||
name: name,
|
||||
size: bytes.length,
|
||||
path: path,
|
||||
bytes: bytes,
|
||||
);
|
||||
}
|
||||
|
||||
/// Converts the [XFile] to a [Attachment].
|
||||
Future<Attachment> toAttachment({required String type}) async {
|
||||
final file = await toAttachmentFile;
|
||||
|
||||
final extraDataMap = <String, Object>{};
|
||||
|
||||
final mimeType = this.mimeType ?? file.mimeType?.mimeType;
|
||||
|
||||
if (mimeType != null) {
|
||||
extraDataMap['mime_type'] = mimeType;
|
||||
}
|
||||
|
||||
extraDataMap['file_size'] = file.size!;
|
||||
|
||||
final attachment = Attachment(
|
||||
file: file,
|
||||
type: type,
|
||||
extraData: extraDataMap,
|
||||
);
|
||||
|
||||
return attachment;
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension on [InputDecoration]
|
||||
extension InputDecorationX on InputDecoration {
|
||||
/// Merges this [InputDecoration] with the [other]
|
||||
InputDecoration merge(InputDecoration? other) {
|
||||
if (other == null) return this;
|
||||
return copyWith(
|
||||
icon: other.icon,
|
||||
labelText: other.labelText,
|
||||
labelStyle: labelStyle?.merge(other.labelStyle) ?? other.labelStyle,
|
||||
helperText: other.helperText,
|
||||
helperStyle: helperStyle?.merge(other.helperStyle) ?? other.helperStyle,
|
||||
helperMaxLines: other.helperMaxLines,
|
||||
hintText: other.hintText,
|
||||
hintStyle: hintStyle?.merge(other.hintStyle) ?? other.hintStyle,
|
||||
hintTextDirection: other.hintTextDirection,
|
||||
hintMaxLines: other.hintMaxLines,
|
||||
errorText: other.errorText,
|
||||
errorStyle: errorStyle?.merge(other.errorStyle) ?? other.errorStyle,
|
||||
errorMaxLines: other.errorMaxLines,
|
||||
floatingLabelBehavior: other.floatingLabelBehavior,
|
||||
isCollapsed: other.isCollapsed,
|
||||
isDense: other.isDense,
|
||||
contentPadding: other.contentPadding,
|
||||
prefixIcon: other.prefixIcon,
|
||||
prefix: other.prefix,
|
||||
prefixText: other.prefixText,
|
||||
prefixIconConstraints: other.prefixIconConstraints,
|
||||
prefixStyle: prefixStyle?.merge(other.prefixStyle) ?? other.prefixStyle,
|
||||
suffixIcon: other.suffixIcon,
|
||||
suffix: other.suffix,
|
||||
suffixText: other.suffixText,
|
||||
suffixStyle: suffixStyle?.merge(other.suffixStyle) ?? other.suffixStyle,
|
||||
suffixIconConstraints: other.suffixIconConstraints,
|
||||
counter: other.counter,
|
||||
counterText: other.counterText,
|
||||
counterStyle:
|
||||
counterStyle?.merge(other.counterStyle) ?? other.counterStyle,
|
||||
filled: other.filled,
|
||||
fillColor: other.fillColor,
|
||||
focusColor: other.focusColor,
|
||||
hoverColor: other.hoverColor,
|
||||
errorBorder: other.errorBorder,
|
||||
focusedBorder: other.focusedBorder,
|
||||
focusedErrorBorder: other.focusedErrorBorder,
|
||||
disabledBorder: other.disabledBorder,
|
||||
enabledBorder: other.enabledBorder,
|
||||
border: other.border,
|
||||
enabled: other.enabled,
|
||||
semanticCounterText: other.semanticCounterText,
|
||||
alignLabelWithHint: other.alignLabelWithHint,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets text scale factor through context
|
||||
extension BuildContextX on BuildContext {
|
||||
// ignore: public_member_api_docs
|
||||
double get textScaleFactor =>
|
||||
MediaQuery.maybeOf(this)?.textScaleFactor ?? 1.0;
|
||||
|
||||
/// Retrieves current translations according to locale
|
||||
/// Defaults to [DefaultTranslations]
|
||||
Translations get translations =>
|
||||
StreamChatLocalizations.of(this) ?? DefaultTranslations.instance;
|
||||
}
|
||||
|
||||
/// Extension on [BorderRadius]
|
||||
extension FlipBorder on BorderRadius {
|
||||
/// Flips borders (Y)
|
||||
BorderRadius mirrorBorderIfReversed({bool reverse = true}) => reverse
|
||||
? BorderRadius.only(
|
||||
topLeft: topRight,
|
||||
topRight: topLeft,
|
||||
bottomLeft: bottomRight,
|
||||
bottomRight: bottomLeft,
|
||||
)
|
||||
: this;
|
||||
}
|
||||
|
||||
/// Extension on [IconButton]
|
||||
extension IconButtonX on IconButton {
|
||||
/// Creates a copy of [IconButton] with specified attributes overridden.
|
||||
IconButton copyWith({
|
||||
double? iconSize,
|
||||
VisualDensity? visualDensity,
|
||||
EdgeInsetsGeometry? padding,
|
||||
AlignmentGeometry? alignment,
|
||||
double? splashRadius,
|
||||
Color? color,
|
||||
Color? focusColor,
|
||||
Color? hoverColor,
|
||||
Color? highlightColor,
|
||||
Color? splashColor,
|
||||
Color? disabledColor,
|
||||
void Function()? onPressed,
|
||||
MouseCursor? mouseCursor,
|
||||
FocusNode? focusNode,
|
||||
bool? autofocus,
|
||||
String? tooltip,
|
||||
bool? enableFeedback,
|
||||
BoxConstraints? constraints,
|
||||
Widget? icon,
|
||||
}) {
|
||||
return IconButton(
|
||||
iconSize: iconSize ?? this.iconSize,
|
||||
visualDensity: visualDensity ?? this.visualDensity,
|
||||
padding: padding ?? this.padding,
|
||||
alignment: alignment ?? this.alignment,
|
||||
splashRadius: splashRadius ?? this.splashRadius,
|
||||
color: color ?? this.color,
|
||||
focusColor: focusColor ?? this.focusColor,
|
||||
hoverColor: hoverColor ?? this.hoverColor,
|
||||
highlightColor: highlightColor ?? this.highlightColor,
|
||||
splashColor: splashColor ?? this.splashColor,
|
||||
disabledColor: disabledColor ?? this.disabledColor,
|
||||
onPressed: onPressed ?? this.onPressed,
|
||||
mouseCursor: mouseCursor ?? this.mouseCursor,
|
||||
focusNode: focusNode ?? this.focusNode,
|
||||
autofocus: autofocus ?? this.autofocus,
|
||||
tooltip: tooltip ?? this.tooltip,
|
||||
enableFeedback: enableFeedback ?? this.enableFeedback,
|
||||
constraints: constraints ?? this.constraints,
|
||||
icon: icon ?? this.icon,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Extensions on List<User>
|
||||
extension UserListX on List<User> {
|
||||
/// It does an search on a list of [User] and returns users with
|
||||
/// `id` or `name` containing the [query].
|
||||
///
|
||||
/// Results are returned sorted by their edit distance from the
|
||||
/// searched string, distance is calculated using the [levenshtein] algorithm.
|
||||
List<User> search(String query) {
|
||||
String normalize(String input) => input.toLowerCase().diacriticsInsensitive;
|
||||
|
||||
final normalizedQuery = normalize(query);
|
||||
|
||||
final matchingUsers = <User, int>{}; // User:lDistance
|
||||
|
||||
for (final user in this) {
|
||||
final normalizedId = normalize(user.id);
|
||||
final normalizedUserName = normalize(user.name);
|
||||
final lDistance = normalizedUserName.levenshteinDistance(normalizedQuery);
|
||||
final containsId = normalizedId.contains(normalizedQuery);
|
||||
final containsName = normalizedUserName.contains(normalizedQuery);
|
||||
if (lDistance < 3 || containsId || containsName) {
|
||||
matchingUsers[user] = lDistance;
|
||||
}
|
||||
}
|
||||
|
||||
final entries = matchingUsers.entries.toList(growable: false)
|
||||
..sort((prev, curr) {
|
||||
bool containsQuery(User user) =>
|
||||
normalize(user.id).contains(normalizedQuery) ||
|
||||
normalize(user.name).contains(normalizedQuery);
|
||||
|
||||
final containsInPrev = containsQuery(prev.key);
|
||||
final containsInCurr = containsQuery(curr.key);
|
||||
|
||||
if (containsInPrev && !containsInCurr) {
|
||||
return -1;
|
||||
} else if (!containsInPrev && containsInCurr) {
|
||||
return 1;
|
||||
}
|
||||
return prev.value.compareTo(curr.value);
|
||||
});
|
||||
|
||||
return entries.map((e) => e.key).toList(growable: false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Extensions on Message
|
||||
extension MessageX on Message {
|
||||
/// It replaces the user mentions with the actual user names.
|
||||
Message replaceMentions({bool linkify = true}) {
|
||||
var messageTextToRender = text;
|
||||
for (final user in mentionedUsers.toSet()) {
|
||||
final userId = user.id;
|
||||
final userName = user.name;
|
||||
if (linkify) {
|
||||
messageTextToRender = messageTextToRender?.replaceAll(
|
||||
'@$userId',
|
||||
'[@$userName](@${userName.replaceAll(' ', '')})',
|
||||
);
|
||||
} else {
|
||||
messageTextToRender = messageTextToRender?.replaceAll(
|
||||
'@$userId',
|
||||
'@$userName',
|
||||
);
|
||||
}
|
||||
}
|
||||
return copyWith(text: messageTextToRender);
|
||||
}
|
||||
|
||||
/// It returns the message with the translated text if available locally
|
||||
Message translate(String language) =>
|
||||
copyWith(text: i18n?['${language}_text'] ?? text);
|
||||
|
||||
/// It returns the message replacing the mentioned user names with
|
||||
/// the respective user ids
|
||||
Message replaceMentionsWithId() {
|
||||
if (mentionedUsers.isEmpty) return this;
|
||||
|
||||
var messageTextToSend = text;
|
||||
if (messageTextToSend == null) return this;
|
||||
|
||||
for (final user in mentionedUsers.toSet()) {
|
||||
final userName = user.name;
|
||||
messageTextToSend = messageTextToSend!.replaceAll(
|
||||
'@$userName',
|
||||
'@${user.id}',
|
||||
);
|
||||
}
|
||||
|
||||
return copyWith(text: messageTextToSend);
|
||||
}
|
||||
}
|
||||
|
||||
/// Extensions on [Uri]
|
||||
extension UriX on Uri {
|
||||
/// Return the URI adding the http scheme if it is missing
|
||||
Uri get withScheme {
|
||||
if (hasScheme) return this;
|
||||
return Uri.parse('http://${toString()}');
|
||||
}
|
||||
}
|
||||
|
||||
/// Extensions on generic type [T]
|
||||
extension TypeX<T> on T? {
|
||||
/// Returns true if the value is null or matches the given [value]
|
||||
/// otherwise returns false.
|
||||
bool isNullOrMatches(T value) => this == null || this == value;
|
||||
}
|
||||
|
||||
/// Useful extensions on [FileType]
|
||||
extension FileTypeX on FileType {
|
||||
/// Converts the [FileType] to a [String].
|
||||
String toAttachmentType() {
|
||||
switch (this) {
|
||||
case FileType.image:
|
||||
return 'image';
|
||||
case FileType.video:
|
||||
return 'video';
|
||||
case FileType.audio:
|
||||
return 'audio';
|
||||
case FileType.any:
|
||||
case FileType.media:
|
||||
case FileType.custom:
|
||||
return 'file';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Useful extensions on [AttachmentPickerType]
|
||||
extension AttachmentPickerTypeX on AttachmentPickerType {
|
||||
/// Converts the [AttachmentPickerType] to a [FileType].
|
||||
FileType get fileType {
|
||||
switch (this) {
|
||||
case AttachmentPickerType.images:
|
||||
return FileType.image;
|
||||
case AttachmentPickerType.videos:
|
||||
return FileType.video;
|
||||
case AttachmentPickerType.files:
|
||||
return FileType.any;
|
||||
case AttachmentPickerType.audios:
|
||||
return FileType.audio;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Useful extensions on [StreamSvgIcon].
|
||||
extension StreamSvgIconX on StreamSvgIcon {
|
||||
/// Converts the [StreamSvgIcon] to a [StreamIconThemeSvgIcon].
|
||||
StreamIconThemeSvgIcon toIconThemeSvgIcon() {
|
||||
return StreamIconThemeSvgIcon.fromStreamSvgIcon(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,532 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_portal/flutter_portal.dart';
|
||||
import 'package:stream_chat_flutter/src/utils/extensions.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:synchronized/synchronized.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
final _permissionRequestLock = Lock();
|
||||
|
||||
/// Executes [computation] when [_permissionRequestLock] is available.
|
||||
///
|
||||
/// Only one asynchronous block can run while the [_permissionRequestLock]
|
||||
/// is retained.
|
||||
Future<T> runInPermissionRequestLock<T>(
|
||||
FutureOr<T> Function() computation, {
|
||||
Duration? timeout,
|
||||
}) {
|
||||
return _permissionRequestLock.synchronized(
|
||||
computation,
|
||||
timeout: timeout,
|
||||
);
|
||||
}
|
||||
|
||||
/// Launch URL
|
||||
Future<void> launchURL(BuildContext context, String url) async {
|
||||
try {
|
||||
await launchUrl(
|
||||
Uri.parse(url).withScheme,
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.translations.launchUrlError)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get centerTitle considering a default and platform specific behaviour
|
||||
bool getEffectiveCenterTitle(
|
||||
ThemeData theme, {
|
||||
bool? centerTitle,
|
||||
List<Widget>? actions,
|
||||
}) {
|
||||
if (centerTitle != null) return centerTitle;
|
||||
if (theme.appBarTheme.centerTitle != null) {
|
||||
return theme.appBarTheme.centerTitle!;
|
||||
}
|
||||
switch (theme.platform) {
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.windows:
|
||||
return false;
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.macOS:
|
||||
return actions == null || actions.length < 2;
|
||||
}
|
||||
}
|
||||
|
||||
/// Shows confirmation dialog
|
||||
@Deprecated(
|
||||
'''
|
||||
showConfirmationDialog is deprecated.
|
||||
Use showConfirmationBottomSheet instead.''',
|
||||
)
|
||||
Future<bool?> showConfirmationDialog(
|
||||
BuildContext context, {
|
||||
required String title,
|
||||
required String okText,
|
||||
Widget? icon,
|
||||
String? question,
|
||||
String? cancelText,
|
||||
}) =>
|
||||
showConfirmationBottomSheet(
|
||||
context,
|
||||
title: title,
|
||||
okText: okText,
|
||||
icon: icon,
|
||||
question: question,
|
||||
cancelText: cancelText,
|
||||
);
|
||||
|
||||
/// Shows confirmation bottom sheet
|
||||
Future<bool?> showConfirmationBottomSheet(
|
||||
BuildContext context, {
|
||||
required String title,
|
||||
required String okText,
|
||||
Widget? icon,
|
||||
String? question,
|
||||
String? cancelText,
|
||||
}) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return showModalBottomSheet(
|
||||
useRootNavigator: false,
|
||||
backgroundColor: chatThemeData.colorTheme.barsBg,
|
||||
context: context,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
topRight: Radius.circular(16),
|
||||
),
|
||||
),
|
||||
builder: (context) {
|
||||
final effect = chatThemeData.colorTheme.borderTop;
|
||||
return SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 26),
|
||||
if (icon != null) icon,
|
||||
const SizedBox(height: 26),
|
||||
Text(
|
||||
title,
|
||||
style: chatThemeData.textTheme.headlineBold,
|
||||
),
|
||||
const SizedBox(height: 7),
|
||||
if (question != null)
|
||||
Text(
|
||||
question,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 36),
|
||||
Container(
|
||||
color: effect.color!.withOpacity(effect.alpha ?? 1),
|
||||
height: 1,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
if (cancelText != null)
|
||||
Flexible(
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
child: Text(
|
||||
cancelText,
|
||||
style: chatThemeData.textTheme.bodyBold.copyWith(
|
||||
color: chatThemeData.colorTheme.textHighEmphasis
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
child: TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
child: Text(
|
||||
okText,
|
||||
style: chatThemeData.textTheme.bodyBold.copyWith(
|
||||
color: chatThemeData.colorTheme.accentError,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Shows info dialog
|
||||
@Deprecated(
|
||||
'''
|
||||
showInfoDialog is deprecated.
|
||||
Use showInfoBottomSheet instead.''',
|
||||
)
|
||||
Future<bool?> showInfoDialog(
|
||||
BuildContext context, {
|
||||
required String title,
|
||||
required String okText,
|
||||
Widget? icon,
|
||||
String? details,
|
||||
StreamChatThemeData? theme,
|
||||
}) =>
|
||||
showInfoBottomSheet(
|
||||
context,
|
||||
title: title,
|
||||
okText: okText,
|
||||
icon: icon,
|
||||
details: details,
|
||||
theme: theme,
|
||||
);
|
||||
|
||||
/// Shows info bottom sheet
|
||||
Future<bool?> showInfoBottomSheet(
|
||||
BuildContext context, {
|
||||
required String title,
|
||||
required String okText,
|
||||
Widget? icon,
|
||||
String? details,
|
||||
StreamChatThemeData? theme,
|
||||
}) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return showModalBottomSheet(
|
||||
useRootNavigator: false,
|
||||
backgroundColor:
|
||||
theme?.colorTheme.barsBg ?? chatThemeData.colorTheme.barsBg,
|
||||
context: context,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
topRight: Radius.circular(16),
|
||||
),
|
||||
),
|
||||
builder: (context) => SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 26,
|
||||
),
|
||||
if (icon != null) icon,
|
||||
const SizedBox(
|
||||
height: 26,
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
style: theme?.textTheme.headlineBold ??
|
||||
chatThemeData.textTheme.headlineBold,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 7,
|
||||
),
|
||||
if (details != null) Text(details),
|
||||
const SizedBox(
|
||||
height: 36,
|
||||
),
|
||||
Container(
|
||||
color: theme?.colorTheme.textHighEmphasis.withOpacity(0.08) ??
|
||||
chatThemeData.colorTheme.textHighEmphasis.withOpacity(0.08),
|
||||
height: 1,
|
||||
),
|
||||
Center(
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(
|
||||
okText,
|
||||
style: TextStyle(
|
||||
color: theme?.colorTheme.textHighEmphasis.withOpacity(0.5) ??
|
||||
chatThemeData.colorTheme.accentPrimary,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Get random png with initials
|
||||
String getRandomPicUrl(User user) =>
|
||||
'https://getstream.io/random_png/?id=${user.id}&name=${user.name}';
|
||||
|
||||
/// Get websiteName from [hostName]
|
||||
String? getWebsiteName(String hostName) {
|
||||
switch (hostName) {
|
||||
case 'reddit':
|
||||
return 'Reddit';
|
||||
case 'youtube':
|
||||
return 'Youtube';
|
||||
case 'wikipedia':
|
||||
return 'Wikipedia';
|
||||
case 'twitter':
|
||||
return 'Twitter';
|
||||
case 'facebook':
|
||||
return 'Facebook';
|
||||
case 'amazon':
|
||||
return 'Amazon';
|
||||
case 'yelp':
|
||||
return 'Yelp';
|
||||
case 'imdb':
|
||||
return 'IMDB';
|
||||
case 'pinterest':
|
||||
return 'Pinterest';
|
||||
case 'tripadvisor':
|
||||
return 'TripAdvisor';
|
||||
case 'instagram':
|
||||
return 'Instagram';
|
||||
case 'walmart':
|
||||
return 'Walmart';
|
||||
case 'craigslist':
|
||||
return 'Craigslist';
|
||||
case 'ebay':
|
||||
return 'eBay';
|
||||
case 'linkedin':
|
||||
return 'LinkedIn';
|
||||
case 'google':
|
||||
return 'Google';
|
||||
case 'apple':
|
||||
return 'Apple';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// A method returns a human readable string representing a file _size
|
||||
String fileSize(dynamic size, [int round = 2]) {
|
||||
if (size == null) return 'Size N/A';
|
||||
|
||||
/**
|
||||
* [size] can be passed as number or as string
|
||||
*
|
||||
* the optional parameter [round] specifies the number
|
||||
* of digits after comma/point (default is 2)
|
||||
*/
|
||||
const divider = 1024;
|
||||
int _size;
|
||||
try {
|
||||
_size = int.parse(size.toString());
|
||||
} catch (e) {
|
||||
throw ArgumentError('Can not parse the size parameter: $e');
|
||||
}
|
||||
|
||||
if (_size < divider) {
|
||||
return '$_size B';
|
||||
}
|
||||
|
||||
if (_size < divider * divider && _size % divider == 0) {
|
||||
return '${(_size / divider).toStringAsFixed(0)} KB';
|
||||
}
|
||||
|
||||
if (_size < divider * divider) {
|
||||
return '${(_size / divider).toStringAsFixed(round)} KB';
|
||||
}
|
||||
|
||||
if (_size < divider * divider * divider && _size % divider == 0) {
|
||||
return '${(_size / (divider * divider)).toStringAsFixed(0)} MB';
|
||||
}
|
||||
|
||||
if (_size < divider * divider * divider) {
|
||||
return '${(_size / divider / divider).toStringAsFixed(round)} MB';
|
||||
}
|
||||
|
||||
if (_size < divider * divider * divider * divider && _size % divider == 0) {
|
||||
return '${(_size / (divider * divider * divider)).toStringAsFixed(0)} GB';
|
||||
}
|
||||
|
||||
if (_size < divider * divider * divider * divider) {
|
||||
return '${(_size / divider / divider / divider).toStringAsFixed(round)} GB';
|
||||
}
|
||||
|
||||
if (_size < divider * divider * divider * divider * divider &&
|
||||
_size % divider == 0) {
|
||||
final num r = _size / divider / divider / divider / divider;
|
||||
return '${r.toStringAsFixed(0)} TB';
|
||||
}
|
||||
|
||||
if (_size < divider * divider * divider * divider * divider) {
|
||||
final num r = _size / divider / divider / divider / divider;
|
||||
return '${r.toStringAsFixed(round)} TB';
|
||||
}
|
||||
|
||||
if (_size < divider * divider * divider * divider * divider * divider &&
|
||||
_size % divider == 0) {
|
||||
final num r = _size / divider / divider / divider / divider / divider;
|
||||
return '${r.toStringAsFixed(0)} PB';
|
||||
} else {
|
||||
final num r = _size / divider / divider / divider / divider / divider;
|
||||
return '${r.toStringAsFixed(round)} PB';
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
StreamSvgIcon getFileTypeImage(String? mimeType) {
|
||||
final subtype = mimeType?.split('/').last;
|
||||
switch (subtype) {
|
||||
case '7z':
|
||||
return StreamSvgIcon.filetype7z();
|
||||
case 'csv':
|
||||
return StreamSvgIcon.filetypeCsv();
|
||||
case 'doc':
|
||||
return StreamSvgIcon.filetypeDoc();
|
||||
case 'docx':
|
||||
return StreamSvgIcon.filetypeDocx();
|
||||
case 'html':
|
||||
return StreamSvgIcon.filetypeHtml();
|
||||
case 'md':
|
||||
return StreamSvgIcon.filetypeMd();
|
||||
case 'odt':
|
||||
return StreamSvgIcon.filetypeOdt();
|
||||
case 'pdf':
|
||||
return StreamSvgIcon.filetypePdf();
|
||||
case 'ppt':
|
||||
return StreamSvgIcon.filetypePpt();
|
||||
case 'pptx':
|
||||
return StreamSvgIcon.filetypePptx();
|
||||
case 'rar':
|
||||
return StreamSvgIcon.filetypeRar();
|
||||
case 'rtf':
|
||||
return StreamSvgIcon.filetypeRtf();
|
||||
case 'tar':
|
||||
return StreamSvgIcon.filetypeTar();
|
||||
case 'txt':
|
||||
return StreamSvgIcon.filetypeTxt();
|
||||
case 'xls':
|
||||
return StreamSvgIcon.filetypeXls();
|
||||
case 'xlsx':
|
||||
return StreamSvgIcon.filetypeXlsx();
|
||||
case 'zip':
|
||||
return StreamSvgIcon.filetypeZip();
|
||||
default:
|
||||
return StreamSvgIcon.filetypeGeneric();
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps attachment widget with custom shape
|
||||
@Deprecated(
|
||||
'''
|
||||
wrapAttachmentWidget is deprecated.
|
||||
Use WrapAttachmentWidget instead
|
||||
''',
|
||||
)
|
||||
Widget wrapAttachmentWidget(
|
||||
BuildContext context,
|
||||
Widget attachmentWidget,
|
||||
ShapeBorder attachmentShape,
|
||||
// ignore: avoid_positional_boolean_parameters
|
||||
bool reverse,
|
||||
) =>
|
||||
WrapAttachmentWidget(
|
||||
attachmentWidget: attachmentWidget,
|
||||
attachmentShape: attachmentShape,
|
||||
reverse: reverse,
|
||||
);
|
||||
|
||||
/// Wraps attachment widget with custom shape
|
||||
class WrapAttachmentWidget extends StatelessWidget {
|
||||
/// Builds a [WrapAttachmentWidget].
|
||||
const WrapAttachmentWidget({
|
||||
super.key,
|
||||
required this.attachmentWidget,
|
||||
required this.attachmentShape,
|
||||
required this.reverse,
|
||||
});
|
||||
|
||||
/// The widget to wrap
|
||||
final Widget attachmentWidget;
|
||||
|
||||
/// The shape of the wrapper
|
||||
final ShapeBorder attachmentShape;
|
||||
|
||||
/// Whether to reverse the wrapper shape
|
||||
final bool reverse;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
shape: attachmentShape,
|
||||
type: MaterialType.transparency,
|
||||
child: attachmentWidget,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Levenshtein algorithm implementation based on:
|
||||
/// http://en.wikipedia.org/wiki/Levenshtein_distance#Iterative_with_two_matrix_rows
|
||||
int levenshtein(String s, String t, {bool caseSensitive = true}) {
|
||||
if (!caseSensitive) {
|
||||
// ignore: parameter_assignments
|
||||
s = s.toLowerCase();
|
||||
// ignore: parameter_assignments
|
||||
t = t.toLowerCase();
|
||||
}
|
||||
if (s == t) return 0;
|
||||
if (s.isEmpty) return t.length;
|
||||
if (t.isEmpty) return s.length;
|
||||
|
||||
final v0 = List<int>.filled(t.length + 1, 0);
|
||||
final v1 = List<int>.filled(t.length + 1, 0);
|
||||
|
||||
for (var i = 0; i < t.length + 1; i < i++) {
|
||||
v0[i] = i;
|
||||
}
|
||||
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
v1[0] = i + 1;
|
||||
|
||||
for (var j = 0; j < t.length; j++) {
|
||||
final cost = (s[i] == t[j]) ? 0 : 1;
|
||||
v1[j + 1] = math.min(v1[j] + 1, math.min(v0[j + 1] + 1, v0[j] + cost));
|
||||
}
|
||||
|
||||
for (var j = 0; j < t.length + 1; j++) {
|
||||
v0[j] = v1[j];
|
||||
}
|
||||
}
|
||||
|
||||
return v1[t.length];
|
||||
}
|
||||
|
||||
/// An easy way to handle attachment related operations on a message
|
||||
extension AttachmentPackagesX on Message {
|
||||
/// This extension will return a List of type [StreamAttachmentPackage] from
|
||||
/// the existing attachments of the message
|
||||
List<StreamAttachmentPackage> getAttachmentPackageList() {
|
||||
final _attachmentPackages = List<StreamAttachmentPackage>.generate(
|
||||
attachments.length,
|
||||
(index) => StreamAttachmentPackage(
|
||||
attachment: attachments[index],
|
||||
message: this,
|
||||
),
|
||||
);
|
||||
return _attachmentPackages;
|
||||
}
|
||||
}
|
||||
|
||||
/// PortalLabel that refers to [StreamMessageListView]
|
||||
const kPortalMessageListViewLabel = _PortalMessageListViewLabel();
|
||||
|
||||
class _PortalMessageListViewLabel extends PortalLabel<void> {
|
||||
const _PortalMessageListViewLabel() : super(null);
|
||||
|
||||
@override
|
||||
String toString() => 'PortalLabel.MessageWidget';
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/src/message_input/attachment_button.dart';
|
||||
import 'package:stream_chat_flutter/src/message_input/command_button.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
/// {@template inProgressBuilder}
|
||||
/// A widget builder for representing in-progress attachment uploads.
|
||||
/// {@endtemplate}
|
||||
typedef InProgressBuilder = Widget Function(BuildContext, int, int);
|
||||
|
||||
/// {@template failedBuilder}
|
||||
/// A widget builder for representing failed attachment uploads.
|
||||
/// {@endtemplate}
|
||||
typedef FailedBuilder = Widget Function(BuildContext, String);
|
||||
|
||||
/// {@template successBuilder}
|
||||
/// A widget builder for representing successful attachment uploads.
|
||||
/// {@endtemplate}
|
||||
typedef SuccessBuilder = WidgetBuilder;
|
||||
|
||||
/// {@template preparingBuilder}
|
||||
/// A widget builder for representing pre-upload attachment state.
|
||||
/// {@endtemplate}
|
||||
typedef PreparingBuilder = WidgetBuilder;
|
||||
|
||||
/// {@template onAttachmentTap}
|
||||
/// The action to perform when the attachment is tapped or clicked.
|
||||
/// {@endtemplate}
|
||||
typedef OnAttachmentTap = VoidCallback;
|
||||
|
||||
/// {@template showMessageCallback}
|
||||
/// The action to perform when "show message" is tapped
|
||||
/// {@endtemplate}
|
||||
typedef ShowMessageCallback = void Function(Message message, Channel channel);
|
||||
|
||||
/// {@template showMessageCallback}
|
||||
/// The action to perform when "reply message" is tapped
|
||||
/// {@endtemplate}
|
||||
typedef ReplyMessageCallback = void Function(Message message);
|
||||
|
||||
/// {@template onImageGroupAttachmentTap}
|
||||
/// The action to perform when a specific image attachment in an [ImageGroup]
|
||||
/// is tapped or clicked.
|
||||
/// {@endtemplate}
|
||||
typedef OnImageGroupAttachmentTap = void Function(
|
||||
Message message,
|
||||
Attachment attachment,
|
||||
);
|
||||
|
||||
/// {@template onUserAvatarPress}
|
||||
/// The action to perform when a user's avatar is tapped, clicked, or
|
||||
/// long-pressed.
|
||||
/// {@endtemplate}
|
||||
typedef OnUserAvatarPress = void Function(User);
|
||||
|
||||
/// {@template placeholderUserImage}
|
||||
/// A widget builder that will build placeholder user images while loading a
|
||||
/// user image.
|
||||
/// {@endtemplate}
|
||||
typedef PlaceholderUserImage = Widget Function(BuildContext, User);
|
||||
|
||||
/// {@template editMessageInputBuilder}
|
||||
// ignore: deprecated_member_use_from_same_package
|
||||
/// A widget builder for building a pre-populated [MessageInput] for use in
|
||||
/// editing messages.
|
||||
/// {@endtemplate}
|
||||
typedef EditMessageInputBuilder = Widget Function(BuildContext, Message);
|
||||
|
||||
/// {@template channelListHeaderTitleBuilder}
|
||||
// ignore: deprecated_member_use_from_same_package
|
||||
/// A widget builder for custom [ChannelListHeader] title widgets.
|
||||
/// {@endtemplate}
|
||||
typedef ChannelListHeaderTitleBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
ConnectionStatus status,
|
||||
StreamChatClient client,
|
||||
);
|
||||
|
||||
/// {@template channelTapCallback}
|
||||
/// The action to perform when a channel is tapped or clicked.
|
||||
/// {@endtemplate}
|
||||
typedef ChannelTapCallback = void Function(Channel, Widget?);
|
||||
|
||||
/// {@template channelInfoCallback}
|
||||
/// The action to perform when a particular channel slidable option is tapped
|
||||
/// or clicked.
|
||||
/// {@endtemplate}
|
||||
typedef ChannelInfoCallback = void Function(Channel);
|
||||
|
||||
/// {@template channelPreviewBuilder}
|
||||
/// Builder used to create a custom [ChannelPreview] for a [Channel]
|
||||
/// {@endtemplate}
|
||||
typedef ChannelPreviewBuilder = Widget Function(BuildContext, Channel);
|
||||
|
||||
/// {@template viewInfoCallback}
|
||||
/// Callback for when 'View Info' is tapped
|
||||
/// {@endtemplate}
|
||||
typedef ViewInfoCallback = void Function(Channel);
|
||||
|
||||
/// {@template attachmentActionsBuilder}
|
||||
/// A widget builder for representing the actions a user can take on an
|
||||
/// attachment.
|
||||
///
|
||||
/// [defaultActionsModal] is the default [AttachmentActionsModal] configuration.
|
||||
/// Use [defaultActionsModal.copyWith] to easily customize it
|
||||
/// {@endtemplate}
|
||||
typedef AttachmentActionsBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
Attachment attachment,
|
||||
AttachmentActionsModal defaultActionsModal,
|
||||
);
|
||||
|
||||
/// {@template errorListener}
|
||||
/// A callback that can be passed to [StreamMessageInput.onError].
|
||||
///
|
||||
/// This callback should not throw.
|
||||
///
|
||||
/// It exists merely for error reporting, and should not be used otherwise.
|
||||
/// {@endtemplate}
|
||||
typedef ErrorListener = void Function(
|
||||
Object error,
|
||||
StackTrace? stackTrace,
|
||||
);
|
||||
|
||||
/// {@template attachmentLimitExceededListener}
|
||||
/// A callback that can be passed to
|
||||
/// [StreamMessageInput.onAttachmentLimitExceed].
|
||||
///
|
||||
/// This callback should not throw.
|
||||
///
|
||||
/// It exists merely for showing custom error, and should not be used otherwise.
|
||||
/// {@endtemplate}
|
||||
typedef AttachmentLimitExceedListener = void Function(
|
||||
int limit,
|
||||
String error,
|
||||
);
|
||||
|
||||
/// {@template attachmentThumbnailBuilder}
|
||||
/// A widget builder for representing attachment thumbnails.
|
||||
/// {@endtemplate}
|
||||
typedef AttachmentThumbnailBuilder = Widget Function(
|
||||
BuildContext,
|
||||
Attachment,
|
||||
);
|
||||
|
||||
/// {@macro mentionTileBuilder}
|
||||
/// A widget builder for representing a custom mention tile.
|
||||
/// {@endtemplate}
|
||||
typedef MentionTileBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
Member member,
|
||||
);
|
||||
|
||||
/// {@template mentionTileOverlayBuilder}
|
||||
/// A widget builder for representing a custom mention tile within a
|
||||
/// [UserMentionsOverlay].
|
||||
/// {@endtemplate}
|
||||
typedef MentionTileOverlayBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
User user,
|
||||
);
|
||||
|
||||
/// {@template userMentionTileBuilder}
|
||||
/// A builder function for representing a custom user mention tile.
|
||||
///
|
||||
// ignore: deprecated_member_use_from_same_package
|
||||
/// Use [UserMentionTile] for the default implementation.
|
||||
/// {@endtemplate}
|
||||
typedef UserMentionTileBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
User user,
|
||||
);
|
||||
|
||||
/// {@template actionButtonBuilder}
|
||||
/// A widget builder for building a custom command button.
|
||||
///
|
||||
/// [commandButton] is the default [CommandButton] configuration,
|
||||
/// use [commandButton.copyWith] to easily customize it.
|
||||
/// {@endtemplate}
|
||||
typedef CommandButtonBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
CommandButton commandButton,
|
||||
);
|
||||
|
||||
/// {@template actionButtonBuilder}
|
||||
/// A widget builder for building a custom action button.
|
||||
///
|
||||
/// [attachmentButton] is the default [AttachmentButton] configuration,
|
||||
/// use [attachmentButton.copyWith] to easily customize it.
|
||||
/// {@endtemplate}
|
||||
typedef AttachmentButtonBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
AttachmentButton attachmentButton,
|
||||
);
|
||||
|
||||
/// {@template quotedMessageAttachmentThumbnailBuilder}
|
||||
/// A widget builder for building a custom quoted message attachment thumbnail.
|
||||
/// {@endtemplate}
|
||||
typedef QuotedMessageAttachmentThumbnailBuilder = Widget Function(
|
||||
BuildContext,
|
||||
Attachment,
|
||||
);
|
||||
|
||||
/// {@template onMessageWidgetAttachmentTap}
|
||||
/// The action to perform when an attachment in an [StreamMessageWidget]
|
||||
/// is tapped or clicked.
|
||||
/// {@endtemplate}
|
||||
typedef OnMessageWidgetAttachmentTap = void Function(
|
||||
Message message,
|
||||
Attachment attachment,
|
||||
);
|
||||
|
||||
/// {@template attachmentBuilder}
|
||||
/// A widget builder for representing attachments.
|
||||
/// {@endtemplate}
|
||||
typedef AttachmentBuilder = Widget Function(
|
||||
BuildContext,
|
||||
Message,
|
||||
List<Attachment>,
|
||||
);
|
||||
|
||||
/// {@template onQuotedMessageTap}
|
||||
/// The action to perform when a quoted message is tapped.
|
||||
/// {@endtemplate}
|
||||
typedef OnQuotedMessageTap = void Function(String?);
|
||||
|
||||
/// {@template onMessageTap}
|
||||
/// The action to perform when a message is tapped.
|
||||
/// {@endtemplate}
|
||||
typedef OnMessageTap = void Function(Message);
|
||||
|
||||
/// {@template messageSearchItemTapCallback}
|
||||
/// The action to perform when tapping or clicking on a user in a
|
||||
// ignore: deprecated_member_use_from_same_package
|
||||
/// [MessageSearchListView].
|
||||
/// {@endtemplate}
|
||||
typedef MessageSearchItemTapCallback = void Function(GetMessageResponse);
|
||||
|
||||
/// {@template messageSearchItemBuilder}
|
||||
/// A widget builder used to create a custom [ListUserItem] from a [User].
|
||||
/// {@endtemplate}
|
||||
typedef MessageSearchItemBuilder = Widget Function(
|
||||
BuildContext,
|
||||
GetMessageResponse,
|
||||
);
|
||||
|
||||
/// {@template messageBuilder}
|
||||
/// A widget builder for creating custom message UI.
|
||||
///
|
||||
/// [defaultMessageWidget] is the default [StreamMessageWidget] configuration.
|
||||
/// Use [defaultMessageWidget.copyWith] to customize it.
|
||||
/// {@endtemplate}
|
||||
typedef MessageBuilder = Widget Function(
|
||||
BuildContext,
|
||||
MessageDetails,
|
||||
List<Message>,
|
||||
StreamMessageWidget defaultMessageWidget,
|
||||
);
|
||||
|
||||
/// {@template parentMessageBuilder}
|
||||
/// A widget builder for creating custom parent message UI.
|
||||
///
|
||||
/// [defaultMessageWidget] is the default [StreamMessageWidget] configuration.
|
||||
/// Use [defaultMessageWidget.copyWith] to customize it.
|
||||
/// {@endtemplate}
|
||||
typedef ParentMessageBuilder = Widget Function(
|
||||
BuildContext,
|
||||
Message?,
|
||||
StreamMessageWidget defaultMessageWidget,
|
||||
);
|
||||
|
||||
/// {@template systemMessageBuilder}
|
||||
/// A widget builder for creating custom system messages.
|
||||
/// {@endtemplate}
|
||||
typedef SystemMessageBuilder = Widget Function(
|
||||
BuildContext,
|
||||
Message,
|
||||
);
|
||||
|
||||
/// {@template threadBuilder}
|
||||
/// A widget builder for creating custom thread UI.
|
||||
/// {@endtemplate}
|
||||
typedef ThreadBuilder = Widget Function(BuildContext context, Message? parent);
|
||||
|
||||
/// {@template threadTapCallback}
|
||||
/// The action to perform when threads are tapped.
|
||||
/// {@endtemplate}
|
||||
typedef ThreadTapCallback = void Function(Message, Widget?);
|
||||
|
||||
/// {@template onMessageSwiped}
|
||||
/// The action to perform when a message is swiped.
|
||||
/// {@endtemplate}
|
||||
typedef OnMessageSwiped = void Function(Message);
|
||||
|
||||
/// {@template spacingWidgetBuilder}
|
||||
/// A widget builder for creating certain spacing after widgets.
|
||||
///
|
||||
/// This spacing can be in the form of any widgets you like.
|
||||
///
|
||||
/// A List of [SpacingType] is provided to help inform the decision of
|
||||
/// what to build after the message (thread, difference in time between
|
||||
/// current and last message, default spacing, etc).
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// MessageListView(
|
||||
/// spacingWidgetBuilder: (context, list) {
|
||||
/// if(list.contains(SpacingType.defaultSpacing)) {
|
||||
/// return SizedBox(height: 2.0,);
|
||||
/// } else {
|
||||
/// return SizedBox(height: 8.0,);
|
||||
/// }
|
||||
/// },
|
||||
/// ),
|
||||
/// ```dart
|
||||
/// {@endtemplate}
|
||||
typedef SpacingWidgetBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
List<SpacingType> spacingTypes,
|
||||
);
|
||||
|
||||
/// {@template attachmentDownloader}
|
||||
/// A callback for downloading an attachment asset.
|
||||
/// {@endtemplate}
|
||||
/// Callback to download an attachment asset
|
||||
typedef AttachmentDownloader = Future<String> Function(
|
||||
Attachment attachment, {
|
||||
ProgressCallback? onReceiveProgress,
|
||||
Map<String, dynamic>? queryParameters,
|
||||
CancelToken? cancelToken,
|
||||
bool deleteOnError,
|
||||
Options? options,
|
||||
});
|
||||
|
||||
/// Callback to receive the path once the attachment asset is downloaded
|
||||
typedef DownloadedPathCallback = void Function(String? path);
|
||||
|
||||
/// {@template userTapCallback}
|
||||
/// Callback called when tapping on a user
|
||||
/// {@endtemplate}
|
||||
typedef UserTapCallback = void Function(User, Widget?);
|
||||
|
||||
/// {@template userItemBuilder}
|
||||
/// Builder used to create a custom [ListUserItem] from a [User]
|
||||
/// {@endtemplate}
|
||||
typedef UserItemBuilder = Widget Function(BuildContext, User, bool);
|
||||
|
||||
/// The action to perform when the "scroll to bottom" button is pressed
|
||||
/// within a [MessageListView].
|
||||
typedef OnScrollToBottom = Function(int unreadCount);
|
||||
|
||||
/// Widget builder for widgets that may require data from the
|
||||
/// [MessageInputController].
|
||||
typedef MessageRelatedBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
StreamMessageInputController messageInputController,
|
||||
);
|
||||
|
||||
/// A function that returns true if the message is valid and can be sent.
|
||||
typedef MessageValidator = bool Function(Message message);
|
||||
@@ -0,0 +1,4 @@
|
||||
export 'device_segmentation.dart';
|
||||
export 'extensions.dart';
|
||||
export 'helpers.dart';
|
||||
export 'typedefs.dart';
|
||||
Reference in New Issue
Block a user