Merge remote-tracking branch 'origin/develop' into vale-linting
# Conflicts: # docusaurus/docs/Flutter/05-guides/05-push-notifications/adding_push_notifications_v2.mdx # packages/stream_chat_flutter/CHANGELOG.md
This commit is contained in:
@@ -1,9 +1,8 @@
|
|||||||
name: Dart Code Metrics
|
name: Dart Code Metrics
|
||||||
|
|
||||||
env:
|
env:
|
||||||
flutter_version: "3.3.3"
|
flutter_version: "3.7.0"
|
||||||
folders: "lib, test"
|
folders: "lib, test"
|
||||||
melos_version: "2.7.1"
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -12,62 +11,61 @@ on:
|
|||||||
- master
|
- master
|
||||||
- develop
|
- develop
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
name: dart-code-metrics
|
name: dart-code-metrics
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: "Git Checkout"
|
- name: "Git Checkout"
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: "Cache Flutter dependencies"
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: /opt/hostedtoolcache/flutter
|
|
||||||
key: ${{ env.flutter_version }}-flutter
|
|
||||||
|
|
||||||
- name: "Install Flutter"
|
- name: "Install Flutter"
|
||||||
uses: subosito/flutter-action@v1
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
|
cache: true
|
||||||
flutter-version: ${{ env.flutter_version }}
|
flutter-version: ${{ env.flutter_version }}
|
||||||
|
|
||||||
- name: "Install Tools"
|
- name: "Install Tools"
|
||||||
run: flutter pub global activate melos ${{ env.melos_version }}
|
run: flutter pub global activate melos
|
||||||
- name: "Bootstrap Workspace"
|
- name: "Bootstrap Workspace"
|
||||||
run: melos bootstrap
|
run: melos bootstrap
|
||||||
|
|
||||||
- name: "Stream Chat Metrics"
|
- name: "Stream Chat Metrics"
|
||||||
uses: dart-code-checker/dart-code-metrics-action@v2.0.0
|
uses: dart-code-checker/dart-code-metrics-action@v3
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
relative_path: 'packages/stream_chat'
|
relative_path: 'packages/stream_chat'
|
||||||
folders: ${{ env.folders }}
|
folders: ${{ env.folders }}
|
||||||
|
|
||||||
- name: "Stream Chat Flutter Core Metrics"
|
- name: "Stream Chat Flutter Core Metrics"
|
||||||
uses: dart-code-checker/dart-code-metrics-action@v2.0.0
|
uses: dart-code-checker/dart-code-metrics-action@v3
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
relative_path: 'packages/stream_chat_flutter_core'
|
relative_path: 'packages/stream_chat_flutter_core'
|
||||||
folders: ${{ env.folders }}
|
folders: ${{ env.folders }}
|
||||||
|
|
||||||
- name: "Stream Chat Flutter Metrics"
|
- name: "Stream Chat Flutter Metrics"
|
||||||
uses: dart-code-checker/dart-code-metrics-action@v2.0.0
|
uses: dart-code-checker/dart-code-metrics-action@v3
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
relative_path: 'packages/stream_chat_flutter'
|
relative_path: 'packages/stream_chat_flutter'
|
||||||
folders: ${{ env.folders }}
|
folders: ${{ env.folders }}
|
||||||
|
|
||||||
- name: "Stream Chat Localizations Metrics"
|
- name: "Stream Chat Localizations Metrics"
|
||||||
uses: dart-code-checker/dart-code-metrics-action@v2.0.0
|
uses: dart-code-checker/dart-code-metrics-action@v3
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
relative_path: 'packages/stream_chat_localizations'
|
relative_path: 'packages/stream_chat_localizations'
|
||||||
folders: ${{ env.folders }}
|
folders: ${{ env.folders }}
|
||||||
|
|
||||||
- name: "Stream Chat Persistence Metrics"
|
- name: "Stream Chat Persistence Metrics"
|
||||||
uses: dart-code-checker/dart-code-metrics-action@v2.0.0
|
uses: dart-code-checker/dart-code-metrics-action@v3
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
relative_path: 'packages/stream_chat_persistence'
|
relative_path: 'packages/stream_chat_persistence'
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
push_docusaurus:
|
push_docusaurus:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: push
|
- name: push
|
||||||
uses: GetStream/push-stream-chat-docusaurus-action@main
|
uses: GetStream/push-stream-chat-docusaurus-action@main
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -15,11 +15,15 @@ on:
|
|||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stream_chat:
|
stream_chat:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: axel-op/dart-package-analyzer@v3
|
- uses: axel-op/dart-package-analyzer@v3
|
||||||
id: analysis
|
id: analysis
|
||||||
with:
|
with:
|
||||||
@@ -39,7 +43,7 @@ jobs:
|
|||||||
stream_chat_persistence:
|
stream_chat_persistence:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: axel-op/dart-package-analyzer@v3
|
- uses: axel-op/dart-package-analyzer@v3
|
||||||
id: analysis
|
id: analysis
|
||||||
with:
|
with:
|
||||||
@@ -60,7 +64,7 @@ jobs:
|
|||||||
stream_chat_flutter_core:
|
stream_chat_flutter_core:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: axel-op/dart-package-analyzer@v3
|
- uses: axel-op/dart-package-analyzer@v3
|
||||||
id: analysis
|
id: analysis
|
||||||
with:
|
with:
|
||||||
@@ -80,7 +84,7 @@ jobs:
|
|||||||
stream_chat_flutter:
|
stream_chat_flutter:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: axel-op/dart-package-analyzer@v3
|
- uses: axel-op/dart-package-analyzer@v3
|
||||||
id: analysis
|
id: analysis
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- develop
|
- develop
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
conventional_pr_title:
|
conventional_pr_title:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ name: stream_flutter_workflow
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
|
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
|
||||||
flutter_version: "3.3.3"
|
flutter_version: "3.7.0"
|
||||||
melos_version: "2.7.1"
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -13,6 +12,10 @@ on:
|
|||||||
- master
|
- master
|
||||||
- develop
|
- develop
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
@@ -20,21 +23,17 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: "Git Checkout"
|
- name: "Git Checkout"
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Cache Flutter dependencies
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: /opt/hostedtoolcache/flutter
|
|
||||||
key: ${{ env.flutter_version }}-flutter
|
|
||||||
- name: "Install Flutter"
|
- name: "Install Flutter"
|
||||||
uses: subosito/flutter-action@v1
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
|
cache: true
|
||||||
flutter-version: ${{ env.flutter_version }}
|
flutter-version: ${{ env.flutter_version }}
|
||||||
- name: "Install Tools"
|
- name: "Install Tools"
|
||||||
run: |
|
run: |
|
||||||
flutter pub global activate melos ${{ env.melos_version }}
|
flutter pub global activate melos
|
||||||
- name: "Bootstrap Workspace"
|
- name: "Bootstrap Workspace"
|
||||||
run: melos bootstrap --verbose
|
run: melos bootstrap --verbose
|
||||||
- name: "Dart Analyze"
|
- name: "Dart Analyze"
|
||||||
@@ -51,21 +50,17 @@ jobs:
|
|||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
steps:
|
steps:
|
||||||
- name: "Git Checkout"
|
- name: "Git Checkout"
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Cache Flutter dependencies
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: /opt/hostedtoolcache/flutter
|
|
||||||
key: ${{ env.flutter_version }}-flutter
|
|
||||||
- name: "Install Flutter"
|
- name: "Install Flutter"
|
||||||
uses: subosito/flutter-action@v1
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
|
cache: true
|
||||||
flutter-version: ${{ env.flutter_version }}
|
flutter-version: ${{ env.flutter_version }}
|
||||||
- name: "Install Tools"
|
- name: "Install Tools"
|
||||||
run: |
|
run: |
|
||||||
flutter pub global activate melos ${{ env.melos_version }}
|
flutter pub global activate melos
|
||||||
- name: "Bootstrap Workspace"
|
- name: "Bootstrap Workspace"
|
||||||
run: melos bootstrap
|
run: melos bootstrap
|
||||||
- name: "Melos Format"
|
- name: "Melos Format"
|
||||||
@@ -75,26 +70,22 @@ jobs:
|
|||||||
./.github/workflows/scripts/validate-formatting.sh
|
./.github/workflows/scripts/validate-formatting.sh
|
||||||
|
|
||||||
test:
|
test:
|
||||||
runs-on: macos-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event.pull_request.draft == false
|
if: github.event.pull_request.draft == false
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
steps:
|
steps:
|
||||||
- name: "Git Checkout"
|
- name: "Git Checkout"
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Cache Flutter dependencies
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: /Users/runner/hostedtoolcache/flutter
|
|
||||||
key: ${{ env.flutter_version }}-flutter
|
|
||||||
- name: "Install Flutter"
|
- name: "Install Flutter"
|
||||||
uses: subosito/flutter-action@v1
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
|
cache: true
|
||||||
flutter-version: ${{ env.flutter_version }}
|
flutter-version: ${{ env.flutter_version }}
|
||||||
- name: "Install Tools"
|
- name: "Install Tools"
|
||||||
run: |
|
run: |
|
||||||
flutter pub global activate melos ${{ env.melos_version }}
|
flutter pub global activate melos
|
||||||
flutter pub global activate remove_from_coverage
|
flutter pub global activate remove_from_coverage
|
||||||
- name: "Bootstrap Workspace"
|
- name: "Bootstrap Workspace"
|
||||||
run: melos bootstrap
|
run: melos bootstrap
|
||||||
|
|||||||
+99
-46
@@ -8,20 +8,56 @@ Adding Push Notifications (V2) To Your Application
|
|||||||
|
|
||||||
### Introduction
|
### Introduction
|
||||||
|
|
||||||
|
This guide details how to add push notifications to your app.
|
||||||
|
|
||||||
Push notifications are a core part of the experience for a messaging app. Users often need to be notified
|
Push notifications are a core part of the experience for a messaging app. Users often need to be notified
|
||||||
of new messages and old notifications sometimes need to be updated silently.
|
of new messages and old notifications sometimes need to be updated silently.
|
||||||
|
|
||||||
This guide details how to add push notifications to your app.
|
Stream Chat sends push notification to channel members that have at least one registered device.
|
||||||
|
Push notifications are only sent for new messages and not for other events.
|
||||||
|
You can use [Webhooks](https://getstream.io/chat/docs/android/webhooks_overview/) to send push notifications on other types of events.
|
||||||
|
|
||||||
You can read more about Stream’s [push delivery logic](https://getstream.io/chat/docs/flutter-dart/push_introduction/?language=dart#push-delivery-rules).
|
You can read more about Stream’s [push delivery logic](https://getstream.io/chat/docs/flutter-dart/push_introduction/?language=dart#push-delivery-rules).
|
||||||
|
|
||||||
|
To receive push notifications from Stream Chat, you'll need to:
|
||||||
|
|
||||||
|
1. Configure your push notification provider on the Stream Dashboard.
|
||||||
|
2. Add the client-side integration. For Flutter this guide demonstrates using Firebase Cloud Messaging (FCM).
|
||||||
|
|
||||||
|
### Push Delivery Rules
|
||||||
|
|
||||||
|
Push message delivery behaves according to these rules:
|
||||||
|
|
||||||
|
- Push notifications are sent only for new messages.
|
||||||
|
- Only channel members receive push messages.
|
||||||
|
- Members receive push notifications regardless of their online status.
|
||||||
|
- Replies inside a [thread](https://getstream.io/chat/docs/threads/) are only sent to users that are part of that thread:
|
||||||
|
- They posted at least one message
|
||||||
|
- They were mentioned
|
||||||
|
- Messages from muted users are not sent.
|
||||||
|
- Messages from muted channels are not sent.
|
||||||
|
- Messages are sent to all registered devices for a user (up to 25).
|
||||||
|
- The message doesn't contain the flag `skip_push` as true.
|
||||||
|
- `push_notifications` is enabled (default) on the channel type for message is sent.
|
||||||
|
|
||||||
|
:::info
|
||||||
|
|
||||||
|
If you would like get push notifications only when users are offline, please contact support.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
|
||||||
|
Push notifications require membership. Watching a channel isn't enough.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
### Setup FCM
|
### Setup FCM
|
||||||
|
|
||||||
To integrate push notifications in your Flutter app, you need to use the package [`firebase_messaging`](https://pub.dev/packages/firebase_messaging).
|
To integrate push notifications in your Flutter app, you need to use the package [firebase_messaging](https://pub.dev/packages/firebase_messaging).
|
||||||
|
|
||||||
|
|
||||||
Follow the [Firebase documentation](https://firebase.flutter.dev/docs/messaging/overview/) to set up the plugin for Android and iOS.
|
|
||||||
|
|
||||||
|
Follow the [Flutter Firebase documentation](https://firebase.flutter.dev/docs/messaging/overview/) to set up the plugin for Android and iOS.
|
||||||
|
Additional setup and instructions can be found [here](https://firebase.google.com/docs/cloud-messaging/flutter/client). Be sure to read this documentation to understand Firebase messaging functionality.
|
||||||
|
|
||||||
Once that's done, FCM should be able to send push notifications to your devices.
|
Once that's done, FCM should be able to send push notifications to your devices.
|
||||||
|
|
||||||
@@ -29,9 +65,9 @@ Once that's done, FCM should be able to send push notifications to your devices.
|
|||||||
|
|
||||||
#### Step 1 - Get the Firebase Credentials
|
#### Step 1 - Get the Firebase Credentials
|
||||||
|
|
||||||
These credentials are the [private key file](https://firebase.google.com/docs/admin/setup#:~:text=To%20generate%20a%20private%20key%20file%20for%20your%20service%20account%3A) for your service account, in firebase console.
|
These credentials are the [private key file](https://firebase.google.com/docs/admin/setup#:~:text=To%20generate%20a%20private%20key%20file%20for%20your%20service%20account%3A) for your service account, in Firebase console.
|
||||||
|
|
||||||
To generate a private key file for your service account, in the Firebase console:
|
To generate a private key file for your service account in the Firebase console:
|
||||||
|
|
||||||
- Open Settings > Service Accounts.
|
- Open Settings > Service Accounts.
|
||||||
|
|
||||||
@@ -39,7 +75,7 @@ To generate a private key file for your service account, in the Firebase console
|
|||||||
|
|
||||||
- Securely store the JSON file containing the key.
|
- Securely store the JSON file containing the key.
|
||||||
|
|
||||||
This JSON file contains the credentials which needs to be uploaded to Stream’s server as explained in next step.
|
This JSON file contains the credentials that need to be uploaded to Stream’s server, as explained in the next step.
|
||||||
|
|
||||||
#### Step 2 - Upload the Firebase Credentials to Stream
|
#### Step 2 - Upload the Firebase Credentials to Stream
|
||||||
|
|
||||||
@@ -47,21 +83,21 @@ You can upload your Firebase credentials using either the dashboard or the app s
|
|||||||
|
|
||||||
##### Using the Stream Dashboard
|
##### Using the Stream Dashboard
|
||||||
|
|
||||||
1. Go to the **Chat Overview** page on Stream Dashboard
|
1. Go to the **Chat Overview** page on Stream Dashboard.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
2. Enable **Firebase Notification** toggle on **Chat Overview**
|
2. Enable **Firebase Notification** toggle on **Chat Overview**.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
3. Enter your Firebase Credentials and press `"Save"`.
|
3. Enter your Firebase Credentials and press "Save".
|
||||||
|
|
||||||
##### Using the API
|
##### Using the API
|
||||||
|
|
||||||
You can also enable Firebase notifications and upload the Firebase credentials using one of our server SDKs.
|
You can also enable Firebase notifications and upload the Firebase credentials using one of our server SDKs.
|
||||||
|
|
||||||
For example, using the JavaScript SDK:
|
For example, using the Stream JavaScript SDK:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const client = StreamChat.getInstance('api_key', 'api_secret');
|
const client = StreamChat.getInstance('api_key', 'api_secret');
|
||||||
@@ -76,9 +112,10 @@ client.updateAppSettings({
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### Registering a Device With Stream Backend
|
### Registering a Device With Stream Backend
|
||||||
|
|
||||||
Once you configure a Firebase server key and set it up on Stream dashboard then a device that is supposed to receive push notifications needs to be registered on the Stream backend. This is usually done by listening for Firebase device token updates and passing them to the backend as follows:
|
Once you configure a Firebase server key and set it up on the Stream dashboard, a device that is supposed to receive push notifications needs to be registered on the Stream backend. This is usually done by listening for Firebase device token updates and passing them to the backend as follows:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
firebaseMessaging.onTokenRefresh.listen((token) {
|
firebaseMessaging.onTokenRefresh.listen((token) {
|
||||||
@@ -86,7 +123,7 @@ firebaseMessaging.onTokenRefresh.listen((token) {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Push Notifications v2 also supports specifying a name to the push device tokens you register. By setting the optional `pushProviderName` parameter in the `addDevice` call you can support different configurations between the device and the `PushProvider`.
|
Push Notifications v2 also supports specifying a name for the push device tokens you register. By setting the optional `pushProviderName` param in the `addDevice` call, you can support different configurations between the device and the `PushProvider`.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
firebaseMessaging.onTokenRefresh.listen((token) {
|
firebaseMessaging.onTokenRefresh.listen((token) {
|
||||||
@@ -96,35 +133,35 @@ firebaseMessaging.onTokenRefresh.listen((token) {
|
|||||||
|
|
||||||
### Receiving Notifications
|
### Receiving Notifications
|
||||||
|
|
||||||
Push notifications behave a bit differently depending on whether you are using iOS or Android.
|
Push notifications behave differently depending on whether you are using iOS or Android.
|
||||||
See [here](https://firebase.flutter.dev/docs/messaging/usage#message-types) to understand the difference between **notification** and **data** payloads.
|
See [here](https://firebase.flutter.dev/docs/messaging/usage#message-types) to understand the difference between **notification** and **data** payloads.
|
||||||
|
|
||||||
#### iOS
|
#### iOS
|
||||||
|
|
||||||
On iOS we send both a **notification** and a **data** payload.
|
On iOS, we send both a **notification** and a **data** payload.
|
||||||
This means you don't need to do anything special to get the notification to show up. However, you might want to handle the data payload to perform some logic when the user taps on the notification.
|
This means you don't need to do anything special to get the notification to show up. However, you might want to handle the data payload to perform some logic when the user taps on the notification.
|
||||||
|
|
||||||
To update the template, you can use a backend SDK.
|
To update the template, you can use a backend SDK.
|
||||||
For example, using the JavaScript SDK:
|
For example, using the Stream JavaScript SDK:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const client = StreamChat.getInstance(‘api_key’, ‘api_secret’);
|
const client = StreamChat.getInstance(‘api_key’, ‘api_secret’);
|
||||||
const apn_template = `{
|
const apn_template = `{
|
||||||
"aps": {
|
"aps": {
|
||||||
"alert": {
|
"alert": {
|
||||||
"title": "New message from {{ sender.name }}",
|
"title": "New message from {{ sender.name }}",
|
||||||
"body": "{{ truncate message.text 2000 }}"
|
"body": "{{ truncate message.text 2000 }}"
|
||||||
},
|
},
|
||||||
"mutable-content": 1,
|
"mutable-content": 1,
|
||||||
"category": "stream.chat"
|
"category": "stream.chat"
|
||||||
},
|
},
|
||||||
"stream": {
|
"stream": {
|
||||||
"sender": "stream.chat",
|
"sender": "stream.chat",
|
||||||
"type": "message.new",
|
"type": "message.new",
|
||||||
"version": "v2",
|
"version": "v2",
|
||||||
"id": "{{ message.id }}",
|
"id": "{{ message.id }}",
|
||||||
"cid": "{{ channel.cid }}"
|
"cid": "{{ channel.cid }}"
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
client.updateAppSettings({
|
client.updateAppSettings({
|
||||||
@@ -134,13 +171,23 @@ client.updateAppSettings({
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### Android
|
#### Android
|
||||||
On Android we send only a **data** payload. This gives you more flexibility and lets you decide what to do with the notification.
|
|
||||||
|
On Android, we send only a **data** payload. This gives you more flexibility and lets you decide what to do with the notification.
|
||||||
|
|
||||||
For example, you can listen and generate a notification from them.
|
For example, you can listen and generate a notification from them.
|
||||||
|
|
||||||
To generate a notification when a **data-only** message is received and the app is in background:
|
The code below demonstrates how to generate a notification when a **data-only** message is received and the app is in the background.
|
||||||
|
|
||||||
|
There are a few things to keep in mind about your background message handler:
|
||||||
|
|
||||||
|
1. It must not be an anonymous function.
|
||||||
|
2. It must be a top-level function (e.g. not a class method which requires initialization).
|
||||||
|
3. It must be annotated with @pragma('vm:entry-point') right above the function declaration (otherwise it may be removed during tree shaking for release mode).
|
||||||
|
|
||||||
|
For additional information on background messages, please see the [Firebase documentation](https://firebase.google.com/docs/cloud-messaging/flutter/receive#background_messages).
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
|
@pragma('vm:entry-point')
|
||||||
Future<void> onBackgroundMessage(RemoteMessage message) async {
|
Future<void> onBackgroundMessage(RemoteMessage message) async {
|
||||||
final chatClient = StreamChatClient(apiKey);
|
final chatClient = StreamChatClient(apiKey);
|
||||||
|
|
||||||
@@ -181,13 +228,13 @@ void handleNotification(
|
|||||||
FirebaseMessaging.onBackgroundMessage(onBackgroundMessage);
|
FirebaseMessaging.onBackgroundMessage(onBackgroundMessage);
|
||||||
```
|
```
|
||||||
|
|
||||||
In the above example, you get the message details using the `getMessage` method and then you use the [`flutter_local_notifications`](https://pub.dev/packages/flutter_local_notifications) package to show the actual notification.
|
In the above example, you get the message details using the `getMessage` method, and then you use the [flutter_local_notifications](https://pub.dev/packages/flutter_local_notifications) package to show the actual notification.
|
||||||
|
|
||||||
##### Using a Template on Android
|
##### Using a Template on Android
|
||||||
|
|
||||||
It's still possible to add a **notification** payload to Android notifications.
|
Adding a **notification** payload to Android notifications is still possible.
|
||||||
You can do so by adding a template using a backend SDK.
|
You can do so by adding a template using a backend SDK.
|
||||||
For example, using the JavaScript SDK:
|
For example, using the Stream JavaScript SDK:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const client = StreamChat.getInstance(‘api_key’, ‘api_secret’);
|
const client = StreamChat.getInstance(‘api_key’, ‘api_secret’);
|
||||||
@@ -207,32 +254,33 @@ client.updateAppSettings({
|
|||||||
|
|
||||||
### Possible Issues
|
### Possible Issues
|
||||||
|
|
||||||
Make sure to read the [general push notification docs](https://getstream.io/chat/docs/flutter-dart/push_introduction/?language=dart) in order to avoid known gotchas that may make your relationship with notifications difficult 😢.
|
Make sure to read the [general push notification docs](https://getstream.io/chat/docs/flutter-dart/push_introduction/?language=dart) to prevent common issues with notifications 😢.
|
||||||
|
|
||||||
### Testing if Push Notifications are Setup Correctly
|
### Testing if Push Notifications are Setup Correctly
|
||||||
|
|
||||||
If you're not sure whether you've set up push notifications correctly, for example, you don't always receive them, or they don’t work reliably, then you can follow these steps to make sure your configuration is correct and working:
|
If you're not sure whether you've set up push notifications correctly, for example, you don't always receive them, or they don’t work reliably, then you can follow these steps to make sure your config is correct and working:
|
||||||
1. Clone our repository for push testing: `git clone [email protected]:GetStream/chat-push-test.git`
|
|
||||||
|
1. Clone our repo for push testing: `git clone [email protected]:GetStream/chat-push-test.git`
|
||||||
2. `cd flutter`
|
2. `cd flutter`
|
||||||
3. In that folder run `flutter pub get`
|
3. In that folder run `flutter pub get`
|
||||||
4. Input your API key and secret in `lib/main.dart`
|
4. Input your api key and secret in `lib/main.dart`
|
||||||
5. Change the bundle identifier/application ID and development team/user so you can run the app on your physical device.**Do not** run on an iOS simulator, as it will not work. Testing on an Android emulator is fine.
|
5. Change the bundle identifier/application ID and development team/user so you can run the app on your physical device.**Do not** run on an iOS simulator, as it will not work. Testing on an Android emulator is fine.
|
||||||
6. Add your `google-services.json/GoogleService-Info.plist`
|
6. Add your `google-services.json/GoogleService-Info.plist`
|
||||||
7. Run the app
|
7. Run the app
|
||||||
8. Accept push notification permission (iOS only)
|
8. Accept push notification permission (iOS only)
|
||||||
9. Tap on `Device ID` and copy it
|
9. Tap on `Device ID` and copy it
|
||||||
11. After configuring [stream-cli](https://github.com/GetStream/stream-cli), run the following command using your user ID:
|
10. After configuring [stream-cli](https://github.com/GetStream/stream-cli), run the following command using your user ID:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
stream chat:push:test -u <USER-ID>
|
stream chat:push:test -u <USER-ID>
|
||||||
```
|
```
|
||||||
|
|
||||||
You should get a test push notification 🥳
|
You should get a test push notification 🥳
|
||||||
|
|
||||||
|
|
||||||
### Foreground Notifications
|
### Foreground Notifications
|
||||||
|
|
||||||
Sometimes you may want to show a notification when the app is in the foreground.
|
You may want to show a notification when the app is in the foreground.
|
||||||
For example, when you're in a channel and you receive a new message from someone in another channel.
|
For example, when you're in a channel and receive a new message from someone in another channel.
|
||||||
|
|
||||||
For this scenario, you can also use the `flutter_local_notifications` package to show a notification.
|
For this scenario, you can also use the `flutter_local_notifications` package to show a notification.
|
||||||
|
|
||||||
@@ -248,20 +296,26 @@ FirebaseMessaging.onMessage.listen((message) async {
|
|||||||
```
|
```
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
You should also check that the channel of the message is different than the channel in the foreground.
|
You should also check that the message's channel differs from the channel in the foreground.
|
||||||
How you do this depends on your app infrastructure and how you handle navigation.
|
How you do this depends on your app infrastructure and how you handle navigation.
|
||||||
|
|
||||||
Take a look at the [Stream Chat v1 sample app](https://github.com/GetStream/flutter-samples/blob/main/packages/stream_chat_v1/lib/home_page.dart#L11) to see how we're doing it over there.
|
Take a look at the [Stream Chat v1 sample app](https://github.com/GetStream/flutter-samples/blob/main/packages/stream_chat_v1/lib/home_page.dart#L11) to see how we're doing it over there.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Saving Notification Messages to the Offline Storage (Only Android)
|
### Saving Notification Messages to the Offline Storage (Only Android)
|
||||||
|
|
||||||
When the app is closed you may want to save received messages when you receive them via a notification so that later on when you open the app they're already there.
|
When the app is closed, you can save incoming messages when you receive them via a notification so that they're already there later when you open the app.
|
||||||
|
|
||||||
To do this you need to integrate the package [`stream_chat_persistence`](https://pub.dev/packages/stream_chat_persistence) in our app that exports a persistence client, see [here](https://pub.dev/packages/stream_chat_persistence#usage) how to set it up.
|
To do this, you need to integrate the package [stream_chat_persistence](https://pub.dev/packages/stream_chat_persistence) that exports a persistence client. See [here](https://pub.dev/packages/stream_chat_persistence#usage) for information on how to set it up.
|
||||||
|
|
||||||
Then calling `FirebaseMessaging.onBackgroundMessage(...)` you need to use a TOP-LEVEL or STATIC function to handle background messages; here is an example:
|
Then calling `FirebaseMessaging.onBackgroundMessage(...)` you need to use a TOP-LEVEL or STATIC function to handle background messages.
|
||||||
|
|
||||||
|
For additional information on background messages, please see the [Firebase documentation](https://firebase.google.com/docs/cloud-messaging/flutter/receive#background_messages).
|
||||||
|
|
||||||
|
Here is an example:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
|
@pragma('vm:entry-point')
|
||||||
Future<void> onBackgroundMessage(RemoteMessage message) async {
|
Future<void> onBackgroundMessage(RemoteMessage message) async {
|
||||||
final chatClient = StreamChatClient(apiKey);
|
final chatClient = StreamChatClient(apiKey);
|
||||||
final persistenceClient = StreamChatPersistenceClient();
|
final persistenceClient = StreamChatPersistenceClient();
|
||||||
@@ -306,4 +360,3 @@ void handleNotification(
|
|||||||
|
|
||||||
FirebaseMessaging.onBackgroundMessage(onBackgroundMessage);
|
FirebaseMessaging.onBackgroundMessage(onBackgroundMessage);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ Before investigating potential solutions, let’s first take a look at the relev
|
|||||||
|
|
||||||
Most of the Stream Chat Flutter UI widgets rely on having a [StreamChat](../../customization/stream_chat_and_theming/) ancestor in the widget tree.
|
Most of the Stream Chat Flutter UI widgets rely on having a [StreamChat](../../customization/stream_chat_and_theming/) ancestor in the widget tree.
|
||||||
The **StreamChat** widget is an [InheritedWidget](https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html) that exposes the **StreamChatClient** through **BuildContext**.
|
The **StreamChat** widget is an [InheritedWidget](https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html) that exposes the **StreamChatClient** through **BuildContext**.
|
||||||
This widget also initializes the [StreamChatCore](../../stream_chat_flutter_core/stream_chat_core.mdx) widget and the **StreamChatTheme**.
|
This widget also initializes the [StreamChatCore](../04-stream_chat_flutter_core/stream_chat_core.mdx) widget and the **StreamChatTheme**.
|
||||||
|
|
||||||
**StreamChatCore** is a **StatefulWidget** used to react to life cycle changes and system updates.
|
**StreamChatCore** is a **StatefulWidget** used to react to life cycle changes and system updates.
|
||||||
When the app goes into the background, the WebSocket connection is closed.
|
When the app goes into the background, the WebSocket connection is closed.
|
||||||
|
|||||||
+9
-9
@@ -51,11 +51,11 @@ scripts:
|
|||||||
description: Build all generated files for Dart & Flutter packages in this project.
|
description: Build all generated files for Dart & Flutter packages in this project.
|
||||||
|
|
||||||
generate:dart:
|
generate:dart:
|
||||||
run: melos exec -c 1 --depends-on="build_runner" --no-flutter -- "dart run build_runner build --delete-conflicting-outputs --enable-experiment=super-parameters,enhanced-enums"
|
run: melos exec -c 1 --depends-on="build_runner" --no-flutter -- "dart run build_runner build --delete-conflicting-outputs"
|
||||||
description: Build all generated files for Dart packages in this project.
|
description: Build all generated files for Dart packages in this project.
|
||||||
|
|
||||||
generate:flutter:
|
generate:flutter:
|
||||||
run: melos exec -c 1 --depends-on="build_runner" --flutter -- "flutter run build_runner build --delete-conflicting-outputs --enable-experiment=super-parameters,enhanced-enums"
|
run: melos exec -c 1 --depends-on="build_runner" --flutter -- "flutter pub run build_runner build --delete-conflicting-outputs"
|
||||||
description: Build all generated files for Flutter packages in this project.
|
description: Build all generated files for Flutter packages in this project.
|
||||||
|
|
||||||
test:all:
|
test:all:
|
||||||
@@ -65,29 +65,29 @@ scripts:
|
|||||||
test:dart:
|
test:dart:
|
||||||
run: melos exec -c 1 --fail-fast -- "flutter test --coverage"
|
run: melos exec -c 1 --fail-fast -- "flutter test --coverage"
|
||||||
description: Run Dart tests for a specific package in this project.
|
description: Run Dart tests for a specific package in this project.
|
||||||
select-package:
|
packageFilters:
|
||||||
flutter: false
|
flutter: false
|
||||||
dir-exists: test
|
dirExists: test
|
||||||
|
|
||||||
test:flutter:
|
test:flutter:
|
||||||
run: melos exec -c 4 --fail-fast -- "flutter test --coverage"
|
run: melos exec -c 4 --fail-fast -- "flutter test --coverage"
|
||||||
description: Run Flutter tests for a specific package in this project.
|
description: Run Flutter tests for a specific package in this project.
|
||||||
select-package:
|
packageFilters:
|
||||||
flutter: true
|
flutter: true
|
||||||
dir-exists: test
|
dirExists: test
|
||||||
|
|
||||||
clean:flutter:
|
clean:flutter:
|
||||||
run: melos exec -c 4 --fail-fast -- "flutter clean"
|
run: melos exec -c 4 --fail-fast -- "flutter clean"
|
||||||
description: Run Flutter clean for a specific package in this project.
|
description: Run Flutter clean for a specific package in this project.
|
||||||
select-package:
|
packageFilters:
|
||||||
flutter: true
|
flutter: true
|
||||||
|
|
||||||
coverage:ignore-file:
|
coverage:ignore-file:
|
||||||
run: |
|
run: |
|
||||||
melos exec -c 5 --fail-fast -- "\$MELOS_ROOT_PATH/.github/workflows/scripts/remove-from-coverage.sh"
|
melos exec -c 5 --fail-fast -- "\$MELOS_ROOT_PATH/.github/workflows/scripts/remove-from-coverage.sh"
|
||||||
description: Removes all the ignored files from the coverage report.
|
description: Removes all the ignored files from the coverage report.
|
||||||
select-package:
|
packageFilters:
|
||||||
dir-exists: coverage
|
dirExists: coverage
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -1,3 +1,35 @@
|
|||||||
|
## Upcoming
|
||||||
|
|
||||||
|
🐞 Fixed
|
||||||
|
|
||||||
|
- Fixed streamWatchers. Before it was always new, now it is possible to follow the watchers of a channel.
|
||||||
|
- Make `Message.i18n` field read-only.
|
||||||
|
|
||||||
|
🔄 Changed
|
||||||
|
|
||||||
|
- Cancelling a attachment upload now removes the attachment from the message.
|
||||||
|
- Updated `dio` and other dependencies to resolvable versions.
|
||||||
|
|
||||||
|
✅ Added
|
||||||
|
|
||||||
|
- Added `presence` property to `Channel::watch` method.
|
||||||
|
|
||||||
|
## 5.3.0
|
||||||
|
|
||||||
|
🔄 Changed
|
||||||
|
|
||||||
|
- Updated `rate_limiter` dependency to `^1.0.0`
|
||||||
|
|
||||||
|
## 5.2.0
|
||||||
|
|
||||||
|
✅ Added
|
||||||
|
|
||||||
|
- Added `Huawei` and `Xiaomi` PushProviders.
|
||||||
|
|
||||||
|
🐞 Fixed
|
||||||
|
|
||||||
|
- Fixed initializing last synced date.
|
||||||
|
|
||||||
## 5.1.0
|
## 5.1.0
|
||||||
|
|
||||||
✅ Added
|
✅ Added
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
|
|||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 33
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main.java.srcDirs += 'src/main/kotlin'
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
@@ -40,7 +40,7 @@ android {
|
|||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId "com.example.example"
|
applicationId "com.example.example"
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 33
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,16 @@
|
|||||||
additional functionality it is fine to subclass or reimplement
|
additional functionality it is fine to subclass or reimplement
|
||||||
FlutterApplication and put your custom class here. -->
|
FlutterApplication and put your custom class here. -->
|
||||||
<application
|
<application
|
||||||
android:name="io.flutter.app.FlutterApplication"
|
android:name="${applicationName}"
|
||||||
android:label="example"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:label="example">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:exported="true"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
|
||||||
android:hardwareAccelerated="true"
|
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
the Android process has started. This theme is visible to the user
|
the Android process has started. This theme is visible to the user
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.3.50'
|
ext.kotlin_version = '1.7.21'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.5.0'
|
classpath 'com.android.tools.build:gradle:7.3.1'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ version: 1.0.0+1
|
|||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.17.0 <3.0.0'
|
sdk: '>=2.17.0 <3.0.0'
|
||||||
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
cupertino_icons: ^1.0.0
|
cupertino_icons: ^1.0.0
|
||||||
|
|||||||
@@ -463,12 +463,19 @@ class Channel {
|
|||||||
|
|
||||||
client.logger.info('Found ${attachments.length} attachments');
|
client.logger.info('Found ${attachments.length} attachments');
|
||||||
|
|
||||||
void updateAttachment(Attachment attachment) {
|
void updateAttachment(Attachment attachment, {bool remove = false}) {
|
||||||
final index = message!.attachments.indexWhere(
|
final index = message!.attachments.indexWhere(
|
||||||
(it) => it.id == attachment.id,
|
(it) => it.id == attachment.id,
|
||||||
);
|
);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
final newAttachments = [...message!.attachments]..[index] = attachment;
|
// update or remove attachment from message.
|
||||||
|
final List<Attachment> newAttachments;
|
||||||
|
if (remove) {
|
||||||
|
newAttachments = [...message!.attachments]..removeAt(index);
|
||||||
|
} else {
|
||||||
|
newAttachments = [...message!.attachments]..[index] = attachment;
|
||||||
|
}
|
||||||
|
|
||||||
final updatedMessage = message!.copyWith(attachments: newAttachments);
|
final updatedMessage = message!.copyWith(attachments: newAttachments);
|
||||||
state?.updateMessage(updatedMessage);
|
state?.updateMessage(updatedMessage);
|
||||||
// updating original message for next iteration
|
// updating original message for next iteration
|
||||||
@@ -533,6 +540,14 @@ class Channel {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}).catchError((e, stk) {
|
}).catchError((e, stk) {
|
||||||
|
if (e is StreamChatNetworkError && e.isRequestCancelledError) {
|
||||||
|
client.logger.info('Attachment ${it.id} upload cancelled');
|
||||||
|
|
||||||
|
// remove attachment from message if cancelled.
|
||||||
|
updateAttachment(it, remove: true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
client.logger.severe('error uploading the attachment', e, stk);
|
client.logger.severe('error uploading the attachment', e, stk);
|
||||||
updateAttachment(
|
updateAttachment(
|
||||||
it.copyWith(uploadState: UploadState.failed(error: e.toString())),
|
it.copyWith(uploadState: UploadState.failed(error: e.toString())),
|
||||||
@@ -1219,11 +1234,11 @@ class Channel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Loads the initial channel state and watches for changes.
|
/// Loads the initial channel state and watches for changes.
|
||||||
Future<ChannelState> watch() async {
|
Future<ChannelState> watch({bool presence = false}) async {
|
||||||
ChannelState response;
|
ChannelState response;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
response = await query(watch: true);
|
response = await query(watch: true, presence: presence);
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stackTrace) {
|
||||||
if (!_initializedCompleter.isCompleted) {
|
if (!_initializedCompleter.isCompleted) {
|
||||||
_initializedCompleter.completeError(error, stackTrace);
|
_initializedCompleter.completeError(error, stackTrace);
|
||||||
@@ -1613,6 +1628,10 @@ class ChannelClientState {
|
|||||||
|
|
||||||
_listenMemberUnbanned();
|
_listenMemberUnbanned();
|
||||||
|
|
||||||
|
_listenUserStartWatching();
|
||||||
|
|
||||||
|
_listenUserStopWatching();
|
||||||
|
|
||||||
_startCleaningStaleTypingEvents();
|
_startCleaningStaleTypingEvents();
|
||||||
|
|
||||||
_startCleaningStalePinnedMessages();
|
_startCleaningStalePinnedMessages();
|
||||||
@@ -1754,6 +1773,39 @@ class ChannelClientState {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _listenUserStartWatching() {
|
||||||
|
_subscriptions.add(
|
||||||
|
_channel.on(EventType.userWatchingStart).listen((event) {
|
||||||
|
final watcher = event.user;
|
||||||
|
if (watcher != null) {
|
||||||
|
final existingWatchers = channelState.watchers;
|
||||||
|
updateChannelState(channelState.copyWith(
|
||||||
|
watchers: [
|
||||||
|
...?existingWatchers,
|
||||||
|
watcher,
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _listenUserStopWatching() {
|
||||||
|
_subscriptions.add(
|
||||||
|
_channel.on(EventType.userWatchingStop).listen((event) {
|
||||||
|
final watcher = event.user;
|
||||||
|
if (watcher != null) {
|
||||||
|
final existingWatchers = channelState.watchers;
|
||||||
|
updateChannelState(channelState.copyWith(
|
||||||
|
watchers: existingWatchers
|
||||||
|
?.where((user) => user.id != watcher.id)
|
||||||
|
.toList(growable: false),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _listenMemberUnbanned() {
|
void _listenMemberUnbanned() {
|
||||||
_subscriptions.add(_channel
|
_subscriptions.add(_channel
|
||||||
.on(EventType.userUnbanned)
|
.on(EventType.userUnbanned)
|
||||||
@@ -2083,7 +2135,7 @@ class ChannelClientState {
|
|||||||
channelStateStream.map((cs) => cs.watchers),
|
channelStateStream.map((cs) => cs.watchers),
|
||||||
_channel.client.state.usersStream,
|
_channel.client.state.usersStream,
|
||||||
(watchers, users) => watchers!.map((e) => users[e.id] ?? e).toList(),
|
(watchers, users) => watchers!.map((e) => users[e.id] ?? e).toList(),
|
||||||
);
|
).distinct(const ListEquality().equals);
|
||||||
|
|
||||||
/// Channel member for the current user.
|
/// Channel member for the current user.
|
||||||
Member? get currentUserMember => members.firstWhereOrNull(
|
Member? get currentUserMember => members.firstWhereOrNull(
|
||||||
|
|||||||
@@ -453,6 +453,15 @@ class StreamChatClient {
|
|||||||
if (persistenceEnabled) {
|
if (persistenceEnabled) {
|
||||||
await sync(cids: cids, lastSyncAt: _lastSyncedAt);
|
await sync(cids: cids, lastSyncAt: _lastSyncedAt);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// channels are empty, assuming it's a fresh start
|
||||||
|
// and making sure `lastSyncAt` is initialized
|
||||||
|
if (persistenceEnabled) {
|
||||||
|
final lastSyncAt = await _chatPersistenceClient?.getLastSyncAt();
|
||||||
|
if (lastSyncAt == null) {
|
||||||
|
await _chatPersistenceClient?.updateLastSyncAt(DateTime.now());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
handleEvent(Event(
|
handleEvent(Event(
|
||||||
type: EventType.connectionRecovered,
|
type: EventType.connectionRecovered,
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ enum PushProvider {
|
|||||||
/// Send notifications using Google's Firebase Cloud Messaging
|
/// Send notifications using Google's Firebase Cloud Messaging
|
||||||
firebase,
|
firebase,
|
||||||
|
|
||||||
|
/// Send notifications using Huawei's Push Kit
|
||||||
|
huawei,
|
||||||
|
|
||||||
|
/// Send notifications using Xiaomi's Mi Push Service
|
||||||
|
xiaomi,
|
||||||
|
|
||||||
/// Send notifications using Apple's Push Notification service
|
/// Send notifications using Apple's Push Notification service
|
||||||
apn,
|
apn,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class SortOption<T> {
|
|||||||
final int direction;
|
final int direction;
|
||||||
|
|
||||||
/// Sorting field Comparator required for offline sorting
|
/// Sorting field Comparator required for offline sorting
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||||
final Comparator<T>? comparator;
|
final Comparator<T>? comparator;
|
||||||
|
|
||||||
/// Serialize model to json
|
/// Serialize model to json
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ class StreamChatNetworkError extends StreamChatError {
|
|||||||
ChatErrorCode errorCode, {
|
ChatErrorCode errorCode, {
|
||||||
int? statusCode,
|
int? statusCode,
|
||||||
this.data,
|
this.data,
|
||||||
|
this.isRequestCancelledError = false,
|
||||||
}) : code = errorCode.code,
|
}) : code = errorCode.code,
|
||||||
statusCode = statusCode ?? data?.statusCode,
|
statusCode = statusCode ?? data?.statusCode,
|
||||||
super(errorCode.message);
|
super(errorCode.message);
|
||||||
@@ -84,6 +85,7 @@ class StreamChatNetworkError extends StreamChatError {
|
|||||||
required String message,
|
required String message,
|
||||||
this.statusCode,
|
this.statusCode,
|
||||||
this.data,
|
this.data,
|
||||||
|
this.isRequestCancelledError = false,
|
||||||
}) : super(message);
|
}) : super(message);
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -96,10 +98,13 @@ class StreamChatNetworkError extends StreamChatError {
|
|||||||
}
|
}
|
||||||
return StreamChatNetworkError.raw(
|
return StreamChatNetworkError.raw(
|
||||||
code: errorResponse?.code ?? -1,
|
code: errorResponse?.code ?? -1,
|
||||||
message:
|
message: errorResponse?.message ??
|
||||||
errorResponse?.message ?? response?.statusMessage ?? error.message,
|
response?.statusMessage ??
|
||||||
|
error.message ??
|
||||||
|
'',
|
||||||
statusCode: errorResponse?.statusCode ?? response?.statusCode,
|
statusCode: errorResponse?.statusCode ?? response?.statusCode,
|
||||||
data: errorResponse,
|
data: errorResponse,
|
||||||
|
isRequestCancelledError: error.type == DioErrorType.cancel,
|
||||||
)..stackTrace = error.stackTrace;
|
)..stackTrace = error.stackTrace;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +117,9 @@ class StreamChatNetworkError extends StreamChatError {
|
|||||||
/// Response body. please refer to [ErrorResponse].
|
/// Response body. please refer to [ErrorResponse].
|
||||||
final ErrorResponse? data;
|
final ErrorResponse? data;
|
||||||
|
|
||||||
|
/// True, in case the error is due to a cancelled network request.
|
||||||
|
final bool isRequestCancelledError;
|
||||||
|
|
||||||
StackTrace? _stackTrace;
|
StackTrace? _stackTrace;
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ class LoggingInterceptor extends Interceptor {
|
|||||||
requestHeaders['contentType'] = options.contentType?.toString();
|
requestHeaders['contentType'] = options.contentType?.toString();
|
||||||
requestHeaders['responseType'] = options.responseType.toString();
|
requestHeaders['responseType'] = options.responseType.toString();
|
||||||
requestHeaders['followRedirects'] = options.followRedirects;
|
requestHeaders['followRedirects'] = options.followRedirects;
|
||||||
requestHeaders['connectTimeout'] = options.connectTimeout;
|
requestHeaders['connectTimeout'] = options.connectTimeout?.toString();
|
||||||
requestHeaders['receiveTimeout'] = options.receiveTimeout;
|
requestHeaders['receiveTimeout'] = options.receiveTimeout?.toString();
|
||||||
_printMapAsTable(_logPrintRequest, requestHeaders, header: 'Headers');
|
_printMapAsTable(_logPrintRequest, requestHeaders, header: 'Headers');
|
||||||
_printMapAsTable(_logPrintRequest, options.extra, header: 'Extras');
|
_printMapAsTable(_logPrintRequest, options.extra, header: 'Extras');
|
||||||
}
|
}
|
||||||
@@ -101,7 +101,8 @@ class LoggingInterceptor extends Interceptor {
|
|||||||
options.data as Map?,
|
options.data as Map?,
|
||||||
header: 'Body',
|
header: 'Body',
|
||||||
);
|
);
|
||||||
} else if (data is FormData) {
|
}
|
||||||
|
if (data is FormData) {
|
||||||
final formDataMap = <String, dynamic>{}
|
final formDataMap = <String, dynamic>{}
|
||||||
..addEntries(data.fields)
|
..addEntries(data.fields)
|
||||||
..addEntries(data.files);
|
..addEntries(data.files);
|
||||||
@@ -121,7 +122,7 @@ class LoggingInterceptor extends Interceptor {
|
|||||||
@override
|
@override
|
||||||
void onError(DioError err, ErrorInterceptorHandler handler) {
|
void onError(DioError err, ErrorInterceptorHandler handler) {
|
||||||
if (error) {
|
if (error) {
|
||||||
if (err.type == DioErrorType.response) {
|
if (err.type == DioErrorType.badResponse) {
|
||||||
final uri = err.response?.requestOptions.uri;
|
final uri = err.response?.requestOptions.uri;
|
||||||
_printBoxed(
|
_printBoxed(
|
||||||
_logPrintError,
|
_logPrintError,
|
||||||
@@ -162,7 +163,7 @@ class LoggingInterceptor extends Interceptor {
|
|||||||
_logPrintResponse('║');
|
_logPrintResponse('║');
|
||||||
_printResponse(_logPrintResponse, response);
|
_printResponse(_logPrintResponse, response);
|
||||||
_logPrintResponse('║');
|
_logPrintResponse('║');
|
||||||
_printLine(_logPrintResponse, '╚');
|
_logPrintResponse('╚');
|
||||||
}
|
}
|
||||||
super.onResponse(response, handler);
|
super.onResponse(response, handler);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ class StreamHttpClient {
|
|||||||
httpClient = dio ?? Dio() {
|
httpClient = dio ?? Dio() {
|
||||||
httpClient
|
httpClient
|
||||||
..options.baseUrl = _options.baseUrl
|
..options.baseUrl = _options.baseUrl
|
||||||
..options.receiveTimeout = _options.receiveTimeout.inMilliseconds
|
..options.receiveTimeout = _options.receiveTimeout
|
||||||
..options.connectTimeout = _options.connectTimeout.inMilliseconds
|
..options.connectTimeout = _options.connectTimeout
|
||||||
..options.queryParameters = {
|
..options.queryParameters = {
|
||||||
'api_key': apiKey,
|
'api_key': apiKey,
|
||||||
..._options.queryParameters,
|
..._options.queryParameters,
|
||||||
|
|||||||
@@ -135,7 +135,6 @@ class Attachment extends Equatable {
|
|||||||
late final UploadState uploadState;
|
late final UploadState uploadState;
|
||||||
|
|
||||||
/// Map of custom channel extraData
|
/// Map of custom channel extraData
|
||||||
@JsonKey(includeIfNull: false)
|
|
||||||
final Map<String, Object?> extraData;
|
final Map<String, Object?> extraData;
|
||||||
|
|
||||||
/// The attachment ID.
|
/// The attachment ID.
|
||||||
@@ -146,13 +145,13 @@ class Attachment extends Equatable {
|
|||||||
/// Shortcut for file size.
|
/// Shortcut for file size.
|
||||||
///
|
///
|
||||||
/// {@macro fileSize}
|
/// {@macro fileSize}
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||||
int? get fileSize => extraData['file_size'] as int?;
|
int? get fileSize => extraData['file_size'] as int?;
|
||||||
|
|
||||||
/// Shortcut for file mimeType.
|
/// Shortcut for file mimeType.
|
||||||
///
|
///
|
||||||
/// {@macro mimeType}
|
/// {@macro mimeType}
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||||
String? get mimeType => extraData['mime_type'] as String?;
|
String? get mimeType => extraData['mime_type'] as String?;
|
||||||
|
|
||||||
/// Known top level fields.
|
/// Known top level fields.
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class AttachmentFile {
|
|||||||
|
|
||||||
/// Byte data for this file. Particularly useful if you want to manipulate
|
/// Byte data for this file. Particularly useful if you want to manipulate
|
||||||
/// its data or easily upload to somewhere else.
|
/// its data or easily upload to somewhere else.
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||||
final Uint8List? bytes;
|
final Uint8List? bytes;
|
||||||
|
|
||||||
/// The file size in bytes.
|
/// The file size in bytes.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
part of 'attachment_file.dart';
|
part of 'attachment_file.dart';
|
||||||
|
|
||||||
@@ -43,10 +43,10 @@ mixin _$UploadState {
|
|||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult Function()? preparing,
|
TResult? Function()? preparing,
|
||||||
TResult Function(int uploaded, int total)? inProgress,
|
TResult? Function(int uploaded, int total)? inProgress,
|
||||||
TResult Function()? success,
|
TResult? Function()? success,
|
||||||
TResult Function(String error)? failed,
|
TResult? Function(String error)? failed,
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
@@ -68,10 +68,10 @@ mixin _$UploadState {
|
|||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult Function(Preparing value)? preparing,
|
TResult? Function(Preparing value)? preparing,
|
||||||
TResult Function(InProgress value)? inProgress,
|
TResult? Function(InProgress value)? inProgress,
|
||||||
TResult Function(Success value)? success,
|
TResult? Function(Success value)? success,
|
||||||
TResult Function(Failed value)? failed,
|
TResult? Function(Failed value)? failed,
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
@@ -90,16 +90,18 @@ mixin _$UploadState {
|
|||||||
abstract class $UploadStateCopyWith<$Res> {
|
abstract class $UploadStateCopyWith<$Res> {
|
||||||
factory $UploadStateCopyWith(
|
factory $UploadStateCopyWith(
|
||||||
UploadState value, $Res Function(UploadState) then) =
|
UploadState value, $Res Function(UploadState) then) =
|
||||||
_$UploadStateCopyWithImpl<$Res>;
|
_$UploadStateCopyWithImpl<$Res, UploadState>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class _$UploadStateCopyWithImpl<$Res> implements $UploadStateCopyWith<$Res> {
|
class _$UploadStateCopyWithImpl<$Res, $Val extends UploadState>
|
||||||
|
implements $UploadStateCopyWith<$Res> {
|
||||||
_$UploadStateCopyWithImpl(this._value, this._then);
|
_$UploadStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
final UploadState _value;
|
|
||||||
// ignore: unused_field
|
// ignore: unused_field
|
||||||
final $Res Function(UploadState) _then;
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -110,14 +112,12 @@ abstract class _$$PreparingCopyWith<$Res> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class __$$PreparingCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
class __$$PreparingCopyWithImpl<$Res>
|
||||||
|
extends _$UploadStateCopyWithImpl<$Res, _$Preparing>
|
||||||
implements _$$PreparingCopyWith<$Res> {
|
implements _$$PreparingCopyWith<$Res> {
|
||||||
__$$PreparingCopyWithImpl(
|
__$$PreparingCopyWithImpl(
|
||||||
_$Preparing _value, $Res Function(_$Preparing) _then)
|
_$Preparing _value, $Res Function(_$Preparing) _then)
|
||||||
: super(_value, (v) => _then(v as _$Preparing));
|
: super(_value, _then);
|
||||||
|
|
||||||
@override
|
|
||||||
_$Preparing get _value => super._value as _$Preparing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -162,10 +162,10 @@ class _$Preparing extends Preparing {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult Function()? preparing,
|
TResult? Function()? preparing,
|
||||||
TResult Function(int uploaded, int total)? inProgress,
|
TResult? Function(int uploaded, int total)? inProgress,
|
||||||
TResult Function()? success,
|
TResult? Function()? success,
|
||||||
TResult Function(String error)? failed,
|
TResult? Function(String error)? failed,
|
||||||
}) {
|
}) {
|
||||||
return preparing?.call();
|
return preparing?.call();
|
||||||
}
|
}
|
||||||
@@ -199,10 +199,10 @@ class _$Preparing extends Preparing {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult Function(Preparing value)? preparing,
|
TResult? Function(Preparing value)? preparing,
|
||||||
TResult Function(InProgress value)? inProgress,
|
TResult? Function(InProgress value)? inProgress,
|
||||||
TResult Function(Success value)? success,
|
TResult? Function(Success value)? success,
|
||||||
TResult Function(Failed value)? failed,
|
TResult? Function(Failed value)? failed,
|
||||||
}) {
|
}) {
|
||||||
return preparing?.call(this);
|
return preparing?.call(this);
|
||||||
}
|
}
|
||||||
@@ -242,30 +242,30 @@ abstract class _$$InProgressCopyWith<$Res> {
|
|||||||
factory _$$InProgressCopyWith(
|
factory _$$InProgressCopyWith(
|
||||||
_$InProgress value, $Res Function(_$InProgress) then) =
|
_$InProgress value, $Res Function(_$InProgress) then) =
|
||||||
__$$InProgressCopyWithImpl<$Res>;
|
__$$InProgressCopyWithImpl<$Res>;
|
||||||
|
@useResult
|
||||||
$Res call({int uploaded, int total});
|
$Res call({int uploaded, int total});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class __$$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
class __$$InProgressCopyWithImpl<$Res>
|
||||||
|
extends _$UploadStateCopyWithImpl<$Res, _$InProgress>
|
||||||
implements _$$InProgressCopyWith<$Res> {
|
implements _$$InProgressCopyWith<$Res> {
|
||||||
__$$InProgressCopyWithImpl(
|
__$$InProgressCopyWithImpl(
|
||||||
_$InProgress _value, $Res Function(_$InProgress) _then)
|
_$InProgress _value, $Res Function(_$InProgress) _then)
|
||||||
: super(_value, (v) => _then(v as _$InProgress));
|
: super(_value, _then);
|
||||||
|
|
||||||
@override
|
|
||||||
_$InProgress get _value => super._value as _$InProgress;
|
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
@override
|
@override
|
||||||
$Res call({
|
$Res call({
|
||||||
Object? uploaded = freezed,
|
Object? uploaded = null,
|
||||||
Object? total = freezed,
|
Object? total = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$InProgress(
|
return _then(_$InProgress(
|
||||||
uploaded: uploaded == freezed
|
uploaded: null == uploaded
|
||||||
? _value.uploaded
|
? _value.uploaded
|
||||||
: uploaded // ignore: cast_nullable_to_non_nullable
|
: uploaded // ignore: cast_nullable_to_non_nullable
|
||||||
as int,
|
as int,
|
||||||
total: total == freezed
|
total: null == total
|
||||||
? _value.total
|
? _value.total
|
||||||
: total // ignore: cast_nullable_to_non_nullable
|
: total // ignore: cast_nullable_to_non_nullable
|
||||||
as int,
|
as int,
|
||||||
@@ -302,19 +302,18 @@ class _$InProgress extends InProgress {
|
|||||||
return identical(this, other) ||
|
return identical(this, other) ||
|
||||||
(other.runtimeType == runtimeType &&
|
(other.runtimeType == runtimeType &&
|
||||||
other is _$InProgress &&
|
other is _$InProgress &&
|
||||||
const DeepCollectionEquality().equals(other.uploaded, uploaded) &&
|
(identical(other.uploaded, uploaded) ||
|
||||||
const DeepCollectionEquality().equals(other.total, total));
|
other.uploaded == uploaded) &&
|
||||||
|
(identical(other.total, total) || other.total == total));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(
|
int get hashCode => Object.hash(runtimeType, uploaded, total);
|
||||||
runtimeType,
|
|
||||||
const DeepCollectionEquality().hash(uploaded),
|
|
||||||
const DeepCollectionEquality().hash(total));
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
@override
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
_$$InProgressCopyWith<_$InProgress> get copyWith =>
|
_$$InProgressCopyWith<_$InProgress> get copyWith =>
|
||||||
__$$InProgressCopyWithImpl<_$InProgress>(this, _$identity);
|
__$$InProgressCopyWithImpl<_$InProgress>(this, _$identity);
|
||||||
|
|
||||||
@@ -332,10 +331,10 @@ class _$InProgress extends InProgress {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult Function()? preparing,
|
TResult? Function()? preparing,
|
||||||
TResult Function(int uploaded, int total)? inProgress,
|
TResult? Function(int uploaded, int total)? inProgress,
|
||||||
TResult Function()? success,
|
TResult? Function()? success,
|
||||||
TResult Function(String error)? failed,
|
TResult? Function(String error)? failed,
|
||||||
}) {
|
}) {
|
||||||
return inProgress?.call(uploaded, total);
|
return inProgress?.call(uploaded, total);
|
||||||
}
|
}
|
||||||
@@ -369,10 +368,10 @@ class _$InProgress extends InProgress {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult Function(Preparing value)? preparing,
|
TResult? Function(Preparing value)? preparing,
|
||||||
TResult Function(InProgress value)? inProgress,
|
TResult? Function(InProgress value)? inProgress,
|
||||||
TResult Function(Success value)? success,
|
TResult? Function(Success value)? success,
|
||||||
TResult Function(Failed value)? failed,
|
TResult? Function(Failed value)? failed,
|
||||||
}) {
|
}) {
|
||||||
return inProgress?.call(this);
|
return inProgress?.call(this);
|
||||||
}
|
}
|
||||||
@@ -422,13 +421,11 @@ abstract class _$$SuccessCopyWith<$Res> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class __$$SuccessCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
class __$$SuccessCopyWithImpl<$Res>
|
||||||
|
extends _$UploadStateCopyWithImpl<$Res, _$Success>
|
||||||
implements _$$SuccessCopyWith<$Res> {
|
implements _$$SuccessCopyWith<$Res> {
|
||||||
__$$SuccessCopyWithImpl(_$Success _value, $Res Function(_$Success) _then)
|
__$$SuccessCopyWithImpl(_$Success _value, $Res Function(_$Success) _then)
|
||||||
: super(_value, (v) => _then(v as _$Success));
|
: super(_value, _then);
|
||||||
|
|
||||||
@override
|
|
||||||
_$Success get _value => super._value as _$Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -473,10 +470,10 @@ class _$Success extends Success {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult Function()? preparing,
|
TResult? Function()? preparing,
|
||||||
TResult Function(int uploaded, int total)? inProgress,
|
TResult? Function(int uploaded, int total)? inProgress,
|
||||||
TResult Function()? success,
|
TResult? Function()? success,
|
||||||
TResult Function(String error)? failed,
|
TResult? Function(String error)? failed,
|
||||||
}) {
|
}) {
|
||||||
return success?.call();
|
return success?.call();
|
||||||
}
|
}
|
||||||
@@ -510,10 +507,10 @@ class _$Success extends Success {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult Function(Preparing value)? preparing,
|
TResult? Function(Preparing value)? preparing,
|
||||||
TResult Function(InProgress value)? inProgress,
|
TResult? Function(InProgress value)? inProgress,
|
||||||
TResult Function(Success value)? success,
|
TResult? Function(Success value)? success,
|
||||||
TResult Function(Failed value)? failed,
|
TResult? Function(Failed value)? failed,
|
||||||
}) {
|
}) {
|
||||||
return success?.call(this);
|
return success?.call(this);
|
||||||
}
|
}
|
||||||
@@ -552,24 +549,24 @@ abstract class Success extends UploadState {
|
|||||||
abstract class _$$FailedCopyWith<$Res> {
|
abstract class _$$FailedCopyWith<$Res> {
|
||||||
factory _$$FailedCopyWith(_$Failed value, $Res Function(_$Failed) then) =
|
factory _$$FailedCopyWith(_$Failed value, $Res Function(_$Failed) then) =
|
||||||
__$$FailedCopyWithImpl<$Res>;
|
__$$FailedCopyWithImpl<$Res>;
|
||||||
|
@useResult
|
||||||
$Res call({String error});
|
$Res call({String error});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class __$$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
|
class __$$FailedCopyWithImpl<$Res>
|
||||||
|
extends _$UploadStateCopyWithImpl<$Res, _$Failed>
|
||||||
implements _$$FailedCopyWith<$Res> {
|
implements _$$FailedCopyWith<$Res> {
|
||||||
__$$FailedCopyWithImpl(_$Failed _value, $Res Function(_$Failed) _then)
|
__$$FailedCopyWithImpl(_$Failed _value, $Res Function(_$Failed) _then)
|
||||||
: super(_value, (v) => _then(v as _$Failed));
|
: super(_value, _then);
|
||||||
|
|
||||||
@override
|
|
||||||
_$Failed get _value => super._value as _$Failed;
|
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
@override
|
@override
|
||||||
$Res call({
|
$Res call({
|
||||||
Object? error = freezed,
|
Object? error = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$Failed(
|
return _then(_$Failed(
|
||||||
error: error == freezed
|
error: null == error
|
||||||
? _value.error
|
? _value.error
|
||||||
: error // ignore: cast_nullable_to_non_nullable
|
: error // ignore: cast_nullable_to_non_nullable
|
||||||
as String,
|
as String,
|
||||||
@@ -603,16 +600,16 @@ class _$Failed extends Failed {
|
|||||||
return identical(this, other) ||
|
return identical(this, other) ||
|
||||||
(other.runtimeType == runtimeType &&
|
(other.runtimeType == runtimeType &&
|
||||||
other is _$Failed &&
|
other is _$Failed &&
|
||||||
const DeepCollectionEquality().equals(other.error, error));
|
(identical(other.error, error) || other.error == error));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode => Object.hash(runtimeType, error);
|
||||||
Object.hash(runtimeType, const DeepCollectionEquality().hash(error));
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
@override
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
_$$FailedCopyWith<_$Failed> get copyWith =>
|
_$$FailedCopyWith<_$Failed> get copyWith =>
|
||||||
__$$FailedCopyWithImpl<_$Failed>(this, _$identity);
|
__$$FailedCopyWithImpl<_$Failed>(this, _$identity);
|
||||||
|
|
||||||
@@ -630,10 +627,10 @@ class _$Failed extends Failed {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult Function()? preparing,
|
TResult? Function()? preparing,
|
||||||
TResult Function(int uploaded, int total)? inProgress,
|
TResult? Function(int uploaded, int total)? inProgress,
|
||||||
TResult Function()? success,
|
TResult? Function()? success,
|
||||||
TResult Function(String error)? failed,
|
TResult? Function(String error)? failed,
|
||||||
}) {
|
}) {
|
||||||
return failed?.call(error);
|
return failed?.call(error);
|
||||||
}
|
}
|
||||||
@@ -667,10 +664,10 @@ class _$Failed extends Failed {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult Function(Preparing value)? preparing,
|
TResult? Function(Preparing value)? preparing,
|
||||||
TResult Function(InProgress value)? inProgress,
|
TResult? Function(InProgress value)? inProgress,
|
||||||
TResult Function(Success value)? success,
|
TResult? Function(Success value)? success,
|
||||||
TResult Function(Failed value)? failed,
|
TResult? Function(Failed value)? failed,
|
||||||
}) {
|
}) {
|
||||||
return failed?.call(this);
|
return failed?.call(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,19 +62,19 @@ class ChannelModel {
|
|||||||
final String type;
|
final String type;
|
||||||
|
|
||||||
/// The cid of this channel
|
/// The cid of this channel
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final String cid;
|
final String cid;
|
||||||
|
|
||||||
/// List of user permissions on this channel
|
/// List of user permissions on this channel
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final List<String>? ownCapabilities;
|
final List<String>? ownCapabilities;
|
||||||
|
|
||||||
/// The channel configuration data
|
/// The channel configuration data
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final ChannelConfig config;
|
final ChannelConfig config;
|
||||||
|
|
||||||
/// The user that created this channel
|
/// The user that created this channel
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final User? createdBy;
|
final User? createdBy;
|
||||||
|
|
||||||
/// True if this channel is frozen
|
/// True if this channel is frozen
|
||||||
@@ -82,23 +82,23 @@ class ChannelModel {
|
|||||||
final bool frozen;
|
final bool frozen;
|
||||||
|
|
||||||
/// The date of the last message
|
/// The date of the last message
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final DateTime? lastMessageAt;
|
final DateTime? lastMessageAt;
|
||||||
|
|
||||||
/// The date of channel creation
|
/// The date of channel creation
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final DateTime createdAt;
|
final DateTime createdAt;
|
||||||
|
|
||||||
/// The date of the last channel update
|
/// The date of the last channel update
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final DateTime updatedAt;
|
final DateTime updatedAt;
|
||||||
|
|
||||||
/// The date of channel deletion
|
/// The date of channel deletion
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final DateTime? deletedAt;
|
final DateTime? deletedAt;
|
||||||
|
|
||||||
/// The count of this channel members
|
/// The count of this channel members
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final int memberCount;
|
final int memberCount;
|
||||||
|
|
||||||
/// The number of seconds in a cooldown
|
/// The number of seconds in a cooldown
|
||||||
@@ -106,15 +106,15 @@ class ChannelModel {
|
|||||||
final int cooldown;
|
final int cooldown;
|
||||||
|
|
||||||
/// True if the channel is disabled
|
/// True if the channel is disabled
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||||
bool? get disabled => extraData['disabled'] as bool?;
|
bool? get disabled => extraData['disabled'] as bool?;
|
||||||
|
|
||||||
/// True if the channel is hidden
|
/// True if the channel is hidden
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||||
bool? get hidden => extraData['hidden'] as bool?;
|
bool? get hidden => extraData['hidden'] as bool?;
|
||||||
|
|
||||||
/// The date of the last time channel got truncated
|
/// The date of the last time channel got truncated
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||||
DateTime? get truncatedAt {
|
DateTime? get truncatedAt {
|
||||||
final truncatedAt = extraData['truncated_at'] as String?;
|
final truncatedAt = extraData['truncated_at'] as String?;
|
||||||
if (truncatedAt == null) return null;
|
if (truncatedAt == null) return null;
|
||||||
@@ -122,11 +122,10 @@ class ChannelModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Map of custom channel extraData
|
/// Map of custom channel extraData
|
||||||
@JsonKey(includeIfNull: false)
|
|
||||||
final Map<String, Object?> extraData;
|
final Map<String, Object?> extraData;
|
||||||
|
|
||||||
/// The team the channel belongs to
|
/// The team the channel belongs to
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final String? team;
|
final String? team;
|
||||||
|
|
||||||
/// Known top level fields.
|
/// Known top level fields.
|
||||||
|
|||||||
@@ -38,30 +38,11 @@ ChannelModel _$ChannelModelFromJson(Map<String, dynamic> json) => ChannelModel(
|
|||||||
cooldown: json['cooldown'] as int? ?? 0,
|
cooldown: json['cooldown'] as int? ?? 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$ChannelModelToJson(ChannelModel instance) {
|
Map<String, dynamic> _$ChannelModelToJson(ChannelModel instance) =>
|
||||||
final val = <String, dynamic>{
|
<String, dynamic>{
|
||||||
'id': instance.id,
|
'id': instance.id,
|
||||||
'type': instance.type,
|
'type': instance.type,
|
||||||
};
|
'frozen': instance.frozen,
|
||||||
|
'cooldown': instance.cooldown,
|
||||||
void writeNotNull(String key, dynamic value) {
|
'extra_data': instance.extraData,
|
||||||
if (value != null) {
|
};
|
||||||
val[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeNotNull('cid', readonly(instance.cid));
|
|
||||||
writeNotNull('own_capabilities', readonly(instance.ownCapabilities));
|
|
||||||
writeNotNull('config', readonly(instance.config));
|
|
||||||
writeNotNull('created_by', readonly(instance.createdBy));
|
|
||||||
val['frozen'] = instance.frozen;
|
|
||||||
writeNotNull('last_message_at', readonly(instance.lastMessageAt));
|
|
||||||
writeNotNull('created_at', readonly(instance.createdAt));
|
|
||||||
writeNotNull('updated_at', readonly(instance.updatedAt));
|
|
||||||
writeNotNull('deleted_at', readonly(instance.deletedAt));
|
|
||||||
writeNotNull('member_count', readonly(instance.memberCount));
|
|
||||||
val['cooldown'] = instance.cooldown;
|
|
||||||
val['extra_data'] = instance.extraData;
|
|
||||||
writeNotNull('team', readonly(instance.team));
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -95,14 +95,11 @@ class Message extends Equatable {
|
|||||||
final String? text;
|
final String? text;
|
||||||
|
|
||||||
/// The status of a sending message.
|
/// The status of a sending message.
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
final MessageSendingStatus status;
|
final MessageSendingStatus status;
|
||||||
|
|
||||||
/// The message type.
|
/// The message type.
|
||||||
@JsonKey(
|
@JsonKey(includeToJson: false)
|
||||||
includeIfNull: false,
|
|
||||||
toJson: Serializer.readOnly,
|
|
||||||
)
|
|
||||||
final String type;
|
final String type;
|
||||||
|
|
||||||
/// The list of attachments, either provided by the user or generated from a
|
/// The list of attachments, either provided by the user or generated from a
|
||||||
@@ -115,26 +112,26 @@ class Message extends Equatable {
|
|||||||
final List<User> mentionedUsers;
|
final List<User> mentionedUsers;
|
||||||
|
|
||||||
/// A map describing the count of number of every reaction.
|
/// A map describing the count of number of every reaction.
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final Map<String, int>? reactionCounts;
|
final Map<String, int>? reactionCounts;
|
||||||
|
|
||||||
/// A map describing the count of score of every reaction.
|
/// A map describing the count of score of every reaction.
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final Map<String, int>? reactionScores;
|
final Map<String, int>? reactionScores;
|
||||||
|
|
||||||
/// The latest reactions to the message created by any user.
|
/// The latest reactions to the message created by any user.
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final List<Reaction>? latestReactions;
|
final List<Reaction>? latestReactions;
|
||||||
|
|
||||||
/// The reactions added to the message by the current user.
|
/// The reactions added to the message by the current user.
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final List<Reaction>? ownReactions;
|
final List<Reaction>? ownReactions;
|
||||||
|
|
||||||
/// The ID of the parent message, if the message is a thread reply.
|
/// The ID of the parent message, if the message is a thread reply.
|
||||||
final String? parentId;
|
final String? parentId;
|
||||||
|
|
||||||
/// A quoted reply message.
|
/// A quoted reply message.
|
||||||
@JsonKey(toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final Message? quotedMessage;
|
final Message? quotedMessage;
|
||||||
|
|
||||||
final String? _quotedMessageId;
|
final String? _quotedMessageId;
|
||||||
@@ -143,11 +140,11 @@ class Message extends Equatable {
|
|||||||
String? get quotedMessageId => _quotedMessageId ?? quotedMessage?.id;
|
String? get quotedMessageId => _quotedMessageId ?? quotedMessage?.id;
|
||||||
|
|
||||||
/// Reserved field indicating the number of replies for this message.
|
/// Reserved field indicating the number of replies for this message.
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final int? replyCount;
|
final int? replyCount;
|
||||||
|
|
||||||
/// Reserved field indicating the thread participants for this message.
|
/// Reserved field indicating the thread participants for this message.
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final List<User>? threadParticipants;
|
final List<User>? threadParticipants;
|
||||||
|
|
||||||
/// Check if this message needs to show in the channel.
|
/// Check if this message needs to show in the channel.
|
||||||
@@ -157,41 +154,38 @@ class Message extends Equatable {
|
|||||||
final bool silent;
|
final bool silent;
|
||||||
|
|
||||||
/// If true the message is shadowed.
|
/// If true the message is shadowed.
|
||||||
@JsonKey(
|
@JsonKey(includeToJson: false)
|
||||||
includeIfNull: false,
|
|
||||||
toJson: Serializer.readOnly,
|
|
||||||
)
|
|
||||||
final bool shadowed;
|
final bool shadowed;
|
||||||
|
|
||||||
/// A used command name.
|
/// A used command name.
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final String? command;
|
final String? command;
|
||||||
|
|
||||||
final DateTime? _createdAt;
|
final DateTime? _createdAt;
|
||||||
|
|
||||||
/// Reserved field indicating when the message was deleted.
|
/// Reserved field indicating when the message was deleted.
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final DateTime? deletedAt;
|
final DateTime? deletedAt;
|
||||||
|
|
||||||
/// Reserved field indicating when the message was created.
|
/// Reserved field indicating when the message was created.
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
DateTime get createdAt => _createdAt ?? DateTime.now();
|
DateTime get createdAt => _createdAt ?? DateTime.now();
|
||||||
|
|
||||||
final DateTime? _updatedAt;
|
final DateTime? _updatedAt;
|
||||||
|
|
||||||
/// Reserved field indicating when the message was updated last time.
|
/// Reserved field indicating when the message was updated last time.
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
DateTime get updatedAt => _updatedAt ?? DateTime.now();
|
DateTime get updatedAt => _updatedAt ?? DateTime.now();
|
||||||
|
|
||||||
/// User who sent the message.
|
/// User who sent the message.
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final User? user;
|
final User? user;
|
||||||
|
|
||||||
/// If true the message is pinned.
|
/// If true the message is pinned.
|
||||||
final bool pinned;
|
final bool pinned;
|
||||||
|
|
||||||
/// Reserved field indicating when the message was pinned.
|
/// Reserved field indicating when the message was pinned.
|
||||||
@JsonKey(toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final DateTime? pinnedAt;
|
final DateTime? pinnedAt;
|
||||||
|
|
||||||
/// Reserved field indicating when the message will expire.
|
/// Reserved field indicating when the message will expire.
|
||||||
@@ -200,11 +194,10 @@ class Message extends Equatable {
|
|||||||
final DateTime? pinExpires;
|
final DateTime? pinExpires;
|
||||||
|
|
||||||
/// Reserved field indicating who pinned the message.
|
/// Reserved field indicating who pinned the message.
|
||||||
@JsonKey(toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final User? pinnedBy;
|
final User? pinnedBy;
|
||||||
|
|
||||||
/// Message custom extraData.
|
/// Message custom extraData.
|
||||||
@JsonKey(includeIfNull: false)
|
|
||||||
final Map<String, Object?> extraData;
|
final Map<String, Object?> extraData;
|
||||||
|
|
||||||
/// True if the message is a system info.
|
/// True if the message is a system info.
|
||||||
@@ -217,7 +210,7 @@ class Message extends Equatable {
|
|||||||
bool get isEphemeral => type == 'ephemeral';
|
bool get isEphemeral => type == 'ephemeral';
|
||||||
|
|
||||||
/// A Map of translations.
|
/// A Map of translations.
|
||||||
@JsonKey(includeIfNull: false)
|
@JsonKey(includeToJson: false)
|
||||||
final Map<String, String>? i18n;
|
final Map<String, String>? i18n;
|
||||||
|
|
||||||
/// Known top level fields.
|
/// Known top level fields.
|
||||||
|
|||||||
@@ -71,43 +71,16 @@ Message _$MessageFromJson(Map<String, dynamic> json) => Message(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$MessageToJson(Message instance) {
|
Map<String, dynamic> _$MessageToJson(Message instance) => <String, dynamic>{
|
||||||
final val = <String, dynamic>{
|
'id': instance.id,
|
||||||
'id': instance.id,
|
'text': instance.text,
|
||||||
'text': instance.text,
|
'attachments': instance.attachments.map((e) => e.toJson()).toList(),
|
||||||
};
|
'mentioned_users': User.toIds(instance.mentionedUsers),
|
||||||
|
'parent_id': instance.parentId,
|
||||||
void writeNotNull(String key, dynamic value) {
|
'quoted_message_id': instance.quotedMessageId,
|
||||||
if (value != null) {
|
'show_in_channel': instance.showInChannel,
|
||||||
val[key] = value;
|
'silent': instance.silent,
|
||||||
}
|
'pinned': instance.pinned,
|
||||||
}
|
'pin_expires': instance.pinExpires?.toIso8601String(),
|
||||||
|
'extra_data': instance.extraData,
|
||||||
writeNotNull('type', readonly(instance.type));
|
};
|
||||||
val['attachments'] = instance.attachments.map((e) => e.toJson()).toList();
|
|
||||||
val['mentioned_users'] = User.toIds(instance.mentionedUsers);
|
|
||||||
writeNotNull('reaction_counts', readonly(instance.reactionCounts));
|
|
||||||
writeNotNull('reaction_scores', readonly(instance.reactionScores));
|
|
||||||
writeNotNull('latest_reactions', readonly(instance.latestReactions));
|
|
||||||
writeNotNull('own_reactions', readonly(instance.ownReactions));
|
|
||||||
val['parent_id'] = instance.parentId;
|
|
||||||
val['quoted_message'] = readonly(instance.quotedMessage);
|
|
||||||
val['quoted_message_id'] = instance.quotedMessageId;
|
|
||||||
writeNotNull('reply_count', readonly(instance.replyCount));
|
|
||||||
writeNotNull('thread_participants', readonly(instance.threadParticipants));
|
|
||||||
val['show_in_channel'] = instance.showInChannel;
|
|
||||||
val['silent'] = instance.silent;
|
|
||||||
writeNotNull('shadowed', readonly(instance.shadowed));
|
|
||||||
writeNotNull('command', readonly(instance.command));
|
|
||||||
writeNotNull('deleted_at', readonly(instance.deletedAt));
|
|
||||||
writeNotNull('created_at', readonly(instance.createdAt));
|
|
||||||
writeNotNull('updated_at', readonly(instance.updatedAt));
|
|
||||||
writeNotNull('user', readonly(instance.user));
|
|
||||||
val['pinned'] = instance.pinned;
|
|
||||||
val['pinned_at'] = readonly(instance.pinnedAt);
|
|
||||||
val['pin_expires'] = instance.pinExpires?.toIso8601String();
|
|
||||||
val['pinned_by'] = readonly(instance.pinnedBy);
|
|
||||||
val['extra_data'] = instance.extraData;
|
|
||||||
writeNotNull('i18n', instance.i18n);
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -33,22 +33,21 @@ class Reaction {
|
|||||||
final String type;
|
final String type;
|
||||||
|
|
||||||
/// The date of the reaction
|
/// The date of the reaction
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final DateTime createdAt;
|
final DateTime createdAt;
|
||||||
|
|
||||||
/// The user that sent the reaction
|
/// The user that sent the reaction
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final User? user;
|
final User? user;
|
||||||
|
|
||||||
/// The score of the reaction (ie. number of reactions sent)
|
/// The score of the reaction (ie. number of reactions sent)
|
||||||
final int score;
|
final int score;
|
||||||
|
|
||||||
/// The userId that sent the reaction
|
/// The userId that sent the reaction
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final String? userId;
|
final String? userId;
|
||||||
|
|
||||||
/// Reaction custom extraData
|
/// Reaction custom extraData
|
||||||
@JsonKey(includeIfNull: false)
|
|
||||||
final Map<String, Object?> extraData;
|
final Map<String, Object?> extraData;
|
||||||
|
|
||||||
/// Map of custom user extraData
|
/// Map of custom user extraData
|
||||||
|
|||||||
@@ -20,22 +20,9 @@ Reaction _$ReactionFromJson(Map<String, dynamic> json) => Reaction(
|
|||||||
extraData: json['extra_data'] as Map<String, dynamic>? ?? const {},
|
extraData: json['extra_data'] as Map<String, dynamic>? ?? const {},
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$ReactionToJson(Reaction instance) {
|
Map<String, dynamic> _$ReactionToJson(Reaction instance) => <String, dynamic>{
|
||||||
final val = <String, dynamic>{
|
'message_id': instance.messageId,
|
||||||
'message_id': instance.messageId,
|
'type': instance.type,
|
||||||
'type': instance.type,
|
'score': instance.score,
|
||||||
};
|
'extra_data': instance.extraData,
|
||||||
|
};
|
||||||
void writeNotNull(String key, dynamic value) {
|
|
||||||
if (value != null) {
|
|
||||||
val[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeNotNull('created_at', readonly(instance.createdAt));
|
|
||||||
writeNotNull('user', readonly(instance.user));
|
|
||||||
val['score'] = instance.score;
|
|
||||||
writeNotNull('user_id', readonly(instance.userId));
|
|
||||||
val['extra_data'] = instance.extraData;
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class User extends Equatable {
|
|||||||
/// Shortcut for user name.
|
/// Shortcut for user name.
|
||||||
///
|
///
|
||||||
/// {@macro name}
|
/// {@macro name}
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||||
String get name {
|
String get name {
|
||||||
if (extraData.containsKey('name') && extraData['name'] != null) {
|
if (extraData.containsKey('name') && extraData['name'] != null) {
|
||||||
final name = extraData['name']! as String;
|
final name = extraData['name']! as String;
|
||||||
@@ -91,48 +91,39 @@ class User extends Equatable {
|
|||||||
/// Shortcut for user image.
|
/// Shortcut for user image.
|
||||||
///
|
///
|
||||||
/// {@macro image}
|
/// {@macro image}
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||||
String? get image => extraData['image'] as String?;
|
String? get image => extraData['image'] as String?;
|
||||||
|
|
||||||
/// User role.
|
/// User role.
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final String? role;
|
final String? role;
|
||||||
|
|
||||||
/// User teams
|
/// User teams
|
||||||
@JsonKey(
|
@JsonKey(includeToJson: false)
|
||||||
includeIfNull: false,
|
|
||||||
toJson: Serializer.readOnly,
|
|
||||||
)
|
|
||||||
final List<String> teams;
|
final List<String> teams;
|
||||||
|
|
||||||
/// Date of user creation.
|
/// Date of user creation.
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final DateTime createdAt;
|
final DateTime createdAt;
|
||||||
|
|
||||||
/// Date of last user update.
|
/// Date of last user update.
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final DateTime updatedAt;
|
final DateTime updatedAt;
|
||||||
|
|
||||||
/// Date of last user connection.
|
/// Date of last user connection.
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final DateTime? lastActive;
|
final DateTime? lastActive;
|
||||||
|
|
||||||
/// True if user is online.
|
/// True if user is online.
|
||||||
@JsonKey(
|
@JsonKey(includeToJson: false)
|
||||||
includeIfNull: false,
|
|
||||||
toJson: Serializer.readOnly,
|
|
||||||
)
|
|
||||||
final bool online;
|
final bool online;
|
||||||
|
|
||||||
/// True if user is banned from the chat.
|
/// True if user is banned from the chat.
|
||||||
@JsonKey(
|
@JsonKey(includeToJson: false)
|
||||||
includeIfNull: false,
|
|
||||||
toJson: Serializer.readOnly,
|
|
||||||
)
|
|
||||||
final bool banned;
|
final bool banned;
|
||||||
|
|
||||||
/// The date at which the ban will expire.
|
/// The date at which the ban will expire.
|
||||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
|
@JsonKey(includeToJson: false)
|
||||||
final DateTime? banExpires;
|
final DateTime? banExpires;
|
||||||
|
|
||||||
/// The language this user prefers.
|
/// The language this user prefers.
|
||||||
@@ -140,7 +131,6 @@ class User extends Equatable {
|
|||||||
final String? language;
|
final String? language;
|
||||||
|
|
||||||
/// Map of custom user extraData.
|
/// Map of custom user extraData.
|
||||||
@JsonKey(includeIfNull: false)
|
|
||||||
final Map<String, Object?> extraData;
|
final Map<String, Object?> extraData;
|
||||||
|
|
||||||
/// List of users to list of userIds.
|
/// List of users to list of userIds.
|
||||||
|
|||||||
@@ -41,14 +41,6 @@ Map<String, dynamic> _$UserToJson(User instance) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writeNotNull('role', readonly(instance.role));
|
|
||||||
writeNotNull('teams', readonly(instance.teams));
|
|
||||||
writeNotNull('created_at', readonly(instance.createdAt));
|
|
||||||
writeNotNull('updated_at', readonly(instance.updatedAt));
|
|
||||||
writeNotNull('last_active', readonly(instance.lastActive));
|
|
||||||
writeNotNull('online', readonly(instance.online));
|
|
||||||
writeNotNull('banned', readonly(instance.banned));
|
|
||||||
writeNotNull('ban_expires', readonly(instance.banExpires));
|
|
||||||
writeNotNull('language', instance.language);
|
writeNotNull('language', instance.language);
|
||||||
val['extra_data'] = instance.extraData;
|
val['extra_data'] = instance.extraData;
|
||||||
return val;
|
return val;
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
/// Used to avoid to serialize properties to json
|
|
||||||
// ignore: prefer_void_to_null
|
|
||||||
Null readonly(_) => null;
|
|
||||||
|
|
||||||
/// Helper class for serialization to and from json
|
/// Helper class for serialization to and from json
|
||||||
class Serializer {
|
class Serializer {
|
||||||
/// Used to avoid to serialize properties to json
|
|
||||||
static const Function readOnly = readonly;
|
|
||||||
|
|
||||||
/// Takes unknown json keys and puts them in the `extra_data` key
|
/// Takes unknown json keys and puts them in the `extra_data` key
|
||||||
static Map<String, dynamic> moveToExtraDataFromRoot(
|
static Map<String, dynamic> moveToExtraDataFromRoot(
|
||||||
Map<String, dynamic> json,
|
Map<String, dynamic> json,
|
||||||
|
|||||||
@@ -36,6 +36,12 @@ class EventType {
|
|||||||
/// Event sent when updating a message
|
/// Event sent when updating a message
|
||||||
static const String messageUpdated = 'message.updated';
|
static const String messageUpdated = 'message.updated';
|
||||||
|
|
||||||
|
/// Event sent when a user starts watching a channel
|
||||||
|
static const String userWatchingStart = 'user.watching.start';
|
||||||
|
|
||||||
|
/// Event sent when a user stops watching a channel
|
||||||
|
static const String userWatchingStop = 'user.watching.stop';
|
||||||
|
|
||||||
/// Event sent when reading a message
|
/// Event sent when reading a message
|
||||||
static const String messageRead = 'message.read';
|
static const String messageRead = 'message.read';
|
||||||
|
|
||||||
|
|||||||
@@ -10,41 +10,13 @@ export 'package:logging/logging.dart' show Logger, Level, LogRecord;
|
|||||||
export 'package:rate_limiter/rate_limiter.dart';
|
export 'package:rate_limiter/rate_limiter.dart';
|
||||||
export 'package:uuid/uuid.dart';
|
export 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
export './src/core/api/attachment_file_uploader.dart';
|
|
||||||
export './src/core/api/requests.dart';
|
|
||||||
export './src/core/api/requests.dart';
|
|
||||||
export './src/core/api/responses.dart';
|
|
||||||
export './src/core/api/stream_chat_api.dart' show PushProvider;
|
|
||||||
export './src/core/error/error.dart';
|
|
||||||
export './src/core/models/action.dart';
|
|
||||||
export './src/core/models/attachment.dart';
|
|
||||||
export './src/core/models/attachment_file.dart';
|
|
||||||
export './src/core/models/channel_config.dart';
|
|
||||||
export './src/core/models/channel_model.dart';
|
|
||||||
export './src/core/models/channel_state.dart';
|
|
||||||
export './src/core/models/command.dart';
|
|
||||||
export './src/core/models/device.dart';
|
|
||||||
export './src/core/models/event.dart';
|
|
||||||
export './src/core/models/filter.dart' show Filter;
|
|
||||||
export './src/core/models/member.dart';
|
|
||||||
export './src/core/models/message.dart';
|
|
||||||
export './src/core/models/mute.dart';
|
|
||||||
export './src/core/models/own_user.dart';
|
|
||||||
export './src/core/models/reaction.dart';
|
|
||||||
export './src/core/models/read.dart';
|
|
||||||
export './src/core/models/user.dart';
|
|
||||||
export './src/core/util/extension.dart';
|
|
||||||
export './src/db/chat_persistence_client.dart';
|
|
||||||
export './src/event_type.dart';
|
|
||||||
export './src/permission_type.dart';
|
|
||||||
export './src/ws/connection_status.dart';
|
|
||||||
export 'src/client/channel.dart';
|
export 'src/client/channel.dart';
|
||||||
export 'src/client/client.dart';
|
export 'src/client/client.dart';
|
||||||
export 'src/client/key_stroke_handler.dart';
|
export 'src/client/key_stroke_handler.dart';
|
||||||
export 'src/core/api/attachment_file_uploader.dart' show AttachmentFileUploader;
|
export 'src/core/api/attachment_file_uploader.dart';
|
||||||
export 'src/core/api/requests.dart';
|
|
||||||
export 'src/core/api/requests.dart';
|
export 'src/core/api/requests.dart';
|
||||||
export 'src/core/api/responses.dart';
|
export 'src/core/api/responses.dart';
|
||||||
|
export 'src/core/api/stream_chat_api.dart' show PushProvider;
|
||||||
export 'src/core/api/stream_chat_api.dart';
|
export 'src/core/api/stream_chat_api.dart';
|
||||||
export 'src/core/error/error.dart';
|
export 'src/core/error/error.dart';
|
||||||
export 'src/core/models/action.dart';
|
export 'src/core/models/action.dart';
|
||||||
@@ -68,4 +40,5 @@ export 'src/core/platform_detector/platform_detector.dart';
|
|||||||
export 'src/core/util/extension.dart';
|
export 'src/core/util/extension.dart';
|
||||||
export 'src/db/chat_persistence_client.dart';
|
export 'src/db/chat_persistence_client.dart';
|
||||||
export 'src/event_type.dart';
|
export 'src/event_type.dart';
|
||||||
|
export 'src/permission_type.dart';
|
||||||
export 'src/ws/connection_status.dart';
|
export 'src/ws/connection_status.dart';
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ import 'package:stream_chat/src/client/client.dart';
|
|||||||
/// Current package version
|
/// Current package version
|
||||||
/// Used in [StreamChatClient] to build the `x-stream-client` header
|
/// Used in [StreamChatClient] to build the `x-stream-client` header
|
||||||
// ignore: constant_identifier_names
|
// ignore: constant_identifier_names
|
||||||
const PACKAGE_VERSION = '5.1.0';
|
const PACKAGE_VERSION = '5.3.0';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name: stream_chat
|
name: stream_chat
|
||||||
homepage: https://getstream.io/
|
homepage: https://getstream.io/
|
||||||
description: The official Dart client for Stream Chat, a service for building chat applications.
|
description: The official Dart client for Stream Chat, a service for building chat applications.
|
||||||
version: 5.1.0
|
version: 5.3.0
|
||||||
repository: https://github.com/GetStream/stream-chat-flutter
|
repository: https://github.com/GetStream/stream-chat-flutter
|
||||||
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
||||||
|
|
||||||
@@ -9,26 +9,26 @@ environment:
|
|||||||
sdk: '>=2.17.0 <3.0.0'
|
sdk: '>=2.17.0 <3.0.0'
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
async: ^2.5.0
|
async: ^2.10.0
|
||||||
collection: ^1.15.0
|
collection: ^1.17.0
|
||||||
dio: ^4.0.0
|
dio: ^5.1.1
|
||||||
equatable: ^2.0.0
|
equatable: ^2.0.5
|
||||||
freezed_annotation: ^2.0.3
|
freezed_annotation: ^2.2.0
|
||||||
http_parser: ^4.0.0
|
http_parser: ^4.0.2
|
||||||
jose: ^0.3.2
|
jose: ^0.3.3
|
||||||
json_annotation: ^4.5.0
|
json_annotation: ^4.8.0
|
||||||
logging: ^1.0.1
|
logging: ^1.1.1
|
||||||
meta: ^1.3.0
|
meta: ^1.8.0
|
||||||
mime: ^1.0.0
|
mime: ^1.0.4
|
||||||
rate_limiter: ^0.1.1
|
rate_limiter: ^1.0.0
|
||||||
rxdart: ^0.27.0
|
rxdart: ^0.27.7
|
||||||
uuid: ^3.0.4
|
uuid: ^3.0.7
|
||||||
web_socket_channel: ^2.0.0
|
web_socket_channel: ^2.3.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.0.1
|
build_runner: ^2.3.3
|
||||||
dart_code_metrics: ^4.4.0
|
dart_code_metrics: ^5.7.0
|
||||||
freezed: ^2.0.3
|
freezed: ^2.3.2
|
||||||
json_serializable: ^6.2.0
|
json_serializable: ^6.6.1
|
||||||
mocktail: ^0.3.0
|
mocktail: ^0.3.0
|
||||||
test: ^1.17.12
|
test: ^1.24.1
|
||||||
|
|||||||
+25
-101
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
"channel": {
|
"channel": {
|
||||||
"id": "dev",
|
"id": "dev",
|
||||||
@@ -18,400 +17,325 @@
|
|||||||
"text": "fasdfa",
|
"text": "fasdfa",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "dry-meadow-0-e8e74482-b4cd-48db-9d1e-30e6c191786f",
|
"id": "dry-meadow-0-e8e74482-b4cd-48db-9d1e-30e6c191786f",
|
||||||
"text": "test message",
|
"text": "test message",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "dry-meadow-0-53e6299f-9b97-4a9c-a27e-7e2dde49b7e0",
|
"id": "dry-meadow-0-53e6299f-9b97-4a9c-a27e-7e2dde49b7e0",
|
||||||
"text": "test message",
|
"text": "test message",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "dry-meadow-0-80925be0-786e-40a5-b225-486518dafd35",
|
"id": "dry-meadow-0-80925be0-786e-40a5-b225-486518dafd35",
|
||||||
"text": "asdfadf",
|
"text": "asdfadf",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "dry-meadow-0-64d7970f-ede8-4b31-9738-1bc1756d2bfe",
|
"id": "dry-meadow-0-64d7970f-ede8-4b31-9738-1bc1756d2bfe",
|
||||||
"text": "test",
|
"text": "test",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "withered-cell-0-84cbd760-cf55-4f7e-9207-c5f66cccc6dc",
|
"id": "withered-cell-0-84cbd760-cf55-4f7e-9207-c5f66cccc6dc",
|
||||||
"text": "hi",
|
"text": "hi",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "dry-meadow-0-e9203588-43c3-40b1-91f7-f217fc42aa53",
|
"id": "dry-meadow-0-e9203588-43c3-40b1-91f7-f217fc42aa53",
|
||||||
"text": "fantastic",
|
"text": "fantastic",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "withered-cell-0-7e3552d7-7a0d-45f2-a856-e91b23a7e240",
|
"id": "withered-cell-0-7e3552d7-7a0d-45f2-a856-e91b23a7e240",
|
||||||
"text": "nice to meet you",
|
"text": "nice to meet you",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "dry-meadow-0-1ffeafd4-e4fc-4c84-9394-9d7cb10fff42",
|
"id": "dry-meadow-0-1ffeafd4-e4fc-4c84-9394-9d7cb10fff42",
|
||||||
"text": "hey",
|
"text": "hey",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "dry-meadow-0-3f147324-12c8-4b41-9fb5-2db88d065efa",
|
"id": "dry-meadow-0-3f147324-12c8-4b41-9fb5-2db88d065efa",
|
||||||
"text": "hello, everyone",
|
"text": "hello, everyone",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "dry-meadow-0-51a348ae-0c0a-44de-a556-eac7891c0cf0",
|
"id": "dry-meadow-0-51a348ae-0c0a-44de-a556-eac7891c0cf0",
|
||||||
"text": "who is there?",
|
"text": "who is there?",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "icy-recipe-7-a29e237b-8d81-4a97-9bc8-d42bca3f1356",
|
"id": "icy-recipe-7-a29e237b-8d81-4a97-9bc8-d42bca3f1356",
|
||||||
"text": "하이",
|
"text": "하이",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "icy-recipe-7-935c396e-ddf8-4a9a-951c-0a12fa5bf055",
|
"id": "icy-recipe-7-935c396e-ddf8-4a9a-951c-0a12fa5bf055",
|
||||||
"text": "what are you doing?",
|
"text": "what are you doing?",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "throbbing-boat-5-1e4d5730-5ff0-4d25-9948-9f34ffda43e4",
|
"id": "throbbing-boat-5-1e4d5730-5ff0-4d25-9948-9f34ffda43e4",
|
||||||
"text": "👍",
|
"text": "👍",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "snowy-credit-3-3e0c1a0d-d22f-42ee-b2a1-f9f49477bf21",
|
"id": "snowy-credit-3-3e0c1a0d-d22f-42ee-b2a1-f9f49477bf21",
|
||||||
"text": "sdasas",
|
"text": "sdasas",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "snowy-credit-3-3319537e-2d0e-4876-8170-a54f046e4b7d",
|
"id": "snowy-credit-3-3319537e-2d0e-4876-8170-a54f046e4b7d",
|
||||||
"text": "cjshsa",
|
"text": "cjshsa",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "snowy-credit-3-cfaf0b46-1daa-49c5-947c-b16d6697487d",
|
"id": "snowy-credit-3-cfaf0b46-1daa-49c5-947c-b16d6697487d",
|
||||||
"text": "nhisagdhsadz",
|
"text": "nhisagdhsadz",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "snowy-credit-3-cebe25a7-a3a3-49fc-9919-91c6725e81f3",
|
"id": "snowy-credit-3-cebe25a7-a3a3-49fc-9919-91c6725e81f3",
|
||||||
"text": "hvadhsahzd",
|
"text": "hvadhsahzd",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "divine-glade-9-0cea9262-5766-48e9-8b22-311870aed3bf",
|
"id": "divine-glade-9-0cea9262-5766-48e9-8b22-311870aed3bf",
|
||||||
"text": "hello",
|
"text": "hello",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "red-firefly-9-c4e9007b-bb7d-4238-ae08-5f8e3cd03d73",
|
"id": "red-firefly-9-c4e9007b-bb7d-4238-ae08-5f8e3cd03d73",
|
||||||
"text": "hello",
|
"text": "hello",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bitter-glade-2-02aee4eb-4093-4736-808b-2de75820e854",
|
"id": "bitter-glade-2-02aee4eb-4093-4736-808b-2de75820e854",
|
||||||
"text": "hello",
|
"text": "hello",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "morning-sea-1-0c700bcb-46dd-4224-b590-e77bdbccc480",
|
"id": "morning-sea-1-0c700bcb-46dd-4224-b590-e77bdbccc480",
|
||||||
"text": "http://jaeger.ui.gtstrm.com/",
|
"text": "http://jaeger.ui.gtstrm.com/",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "ancient-salad-0-53e8b4e6-5b7b-43ad-aeee-8bfb6a9ed0be",
|
"id": "ancient-salad-0-53e8b4e6-5b7b-43ad-aeee-8bfb6a9ed0be",
|
||||||
"text": "hi",
|
"text": "hi",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "ancient-salad-0-8c225075-bd4c-42e2-8024-530aae13cd40",
|
"id": "ancient-salad-0-8c225075-bd4c-42e2-8024-530aae13cd40",
|
||||||
"text": "hi",
|
"text": "hi",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "proud-sea-7-17802096-cbf8-4e3c-addd-4ee31f4c8b5c",
|
"id": "proud-sea-7-17802096-cbf8-4e3c-addd-4ee31f4c8b5c",
|
||||||
"text": "😃",
|
"text": "😃",
|
||||||
"attachments": [],
|
"attachments": [],
|
||||||
"parent_id": null,
|
"parent_id": null,
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"show_in_channel": null,
|
"show_in_channel": null,
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"status": "SENT",
|
"status": "SENT",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
"pin_expires": null
|
||||||
"pin_expires": null,
|
|
||||||
"pinned_by": null
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"pinned_messages": [],
|
"pinned_messages": [],
|
||||||
|
|||||||
@@ -18,12 +18,9 @@
|
|||||||
],
|
],
|
||||||
"mentioned_users": [],
|
"mentioned_users": [],
|
||||||
"parent_id": "parentId",
|
"parent_id": "parentId",
|
||||||
"quoted_message": null,
|
|
||||||
"quoted_message_id": null,
|
"quoted_message_id": null,
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"pinned_at": null,
|
|
||||||
"pin_expires": null,
|
"pin_expires": null,
|
||||||
"pinned_by": null,
|
|
||||||
"show_in_channel": true,
|
"show_in_channel": true,
|
||||||
"hey": "test"
|
"hey": "test"
|
||||||
}
|
}
|
||||||
@@ -516,6 +516,9 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
|
when(() => persistence.updateLastSyncAt(any()))
|
||||||
|
.thenAnswer((_) => Future.value());
|
||||||
|
when(persistence.getLastSyncAt).thenAnswer((_) async => null);
|
||||||
client = StreamChatClient(apiKey, chatApi: api, ws: ws)
|
client = StreamChatClient(apiKey, chatApi: api, ws: ws)
|
||||||
..chatPersistenceClient = persistence;
|
..chatPersistenceClient = persistence;
|
||||||
await client.connectUser(user, token);
|
await client.connectUser(user, token);
|
||||||
@@ -532,9 +535,12 @@ void main() {
|
|||||||
test(
|
test(
|
||||||
'''should update persistence connectionInfo and lastSync when sync succeeds''',
|
'''should update persistence connectionInfo and lastSync when sync succeeds''',
|
||||||
() async {
|
() async {
|
||||||
|
// persistence.updateLastSyncAt might be called
|
||||||
|
// when connecting the user.
|
||||||
|
// Resetting the logs so we start counting invocations correctly.
|
||||||
|
reset(persistence);
|
||||||
const cids = ['test-cid-1', 'test-cid-2', 'test-cid-3'];
|
const cids = ['test-cid-1', 'test-cid-2', 'test-cid-3'];
|
||||||
final lastSyncAt = DateTime.now();
|
final lastSyncAt = DateTime.now();
|
||||||
|
|
||||||
when(() => api.general.sync(cids, lastSyncAt))
|
when(() => api.general.sync(cids, lastSyncAt))
|
||||||
.thenAnswer((_) async => SyncResponse()
|
.thenAnswer((_) async => SyncResponse()
|
||||||
..events = [
|
..events = [
|
||||||
@@ -567,6 +573,10 @@ void main() {
|
|||||||
test(
|
test(
|
||||||
'should work fine if persistence contains sync params',
|
'should work fine if persistence contains sync params',
|
||||||
() async {
|
() async {
|
||||||
|
// persistence.updateLastSyncAt might be called
|
||||||
|
// when connecting the user.
|
||||||
|
// Resetting the logs so we start counting invocations correctly.
|
||||||
|
reset(persistence);
|
||||||
const cids = ['test-cid-1', 'test-cid-2', 'test-cid-3'];
|
const cids = ['test-cid-1', 'test-cid-2', 'test-cid-3'];
|
||||||
final lastSyncAt = DateTime.now();
|
final lastSyncAt = DateTime.now();
|
||||||
|
|
||||||
|
|||||||
@@ -22,26 +22,37 @@ void main() {
|
|||||||
|
|
||||||
test('addDevice should work', () async {
|
test('addDevice should work', () async {
|
||||||
const deviceId = 'test-device-id';
|
const deviceId = 'test-device-id';
|
||||||
const pushProvider = PushProvider.firebase;
|
const pushProvidersMap = {
|
||||||
|
'apn': PushProvider.apn,
|
||||||
|
'firebase': PushProvider.firebase,
|
||||||
|
'huawei': PushProvider.huawei,
|
||||||
|
'xiaomi': PushProvider.xiaomi,
|
||||||
|
};
|
||||||
const path = '/devices';
|
const path = '/devices';
|
||||||
|
|
||||||
when(() => client.post(
|
for (final pushProviderMapEntry in pushProvidersMap.entries) {
|
||||||
path,
|
final data = {
|
||||||
data: {
|
'id': deviceId,
|
||||||
'id': deviceId,
|
'push_provider': pushProviderMapEntry.key,
|
||||||
'push_provider': pushProvider.name,
|
};
|
||||||
},
|
when(() {
|
||||||
))
|
return client.post(
|
||||||
.thenAnswer(
|
path,
|
||||||
(_) async => successResponse(path, data: <String, dynamic>{}));
|
data: data,
|
||||||
|
);
|
||||||
|
}).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
final res = await deviceApi.addDevice(deviceId, pushProvider);
|
final res =
|
||||||
|
await deviceApi.addDevice(deviceId, pushProviderMapEntry.value);
|
||||||
|
|
||||||
expect(res, isNotNull);
|
expect(res, isNotNull);
|
||||||
|
|
||||||
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
verify(() => client.post(path, data: data)).called(1);
|
||||||
|
}
|
||||||
verifyNoMoreInteractions(client);
|
verifyNoMoreInteractions(client);
|
||||||
|
expect(pushProvidersMap.length, PushProvider.values.length,
|
||||||
|
reason: 'All PushProvider should be tested');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('addDevice should work with pushProviderName', () async {
|
test('addDevice should work with pushProviderName', () async {
|
||||||
|
|||||||
@@ -394,7 +394,12 @@ void main() {
|
|||||||
path,
|
path,
|
||||||
data: {'language': language},
|
data: {'language': language},
|
||||||
)).thenAnswer((_) async => successResponse(path, data: {
|
)).thenAnswer((_) async => successResponse(path, data: {
|
||||||
'message': translatedMessage.toJson(),
|
'message': {
|
||||||
|
...translatedMessage.toJson(),
|
||||||
|
'i18n': {
|
||||||
|
language: translatedMessageText,
|
||||||
|
},
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
final res = await messageApi.translateMessage(messageId, language);
|
final res = await messageApi.translateMessage(messageId, language);
|
||||||
|
|||||||
@@ -129,7 +129,11 @@ void main() {
|
|||||||
await client.get('path');
|
await client.get('path');
|
||||||
} on StreamChatNetworkError catch (e) {
|
} on StreamChatNetworkError catch (e) {
|
||||||
expect(e, isA<StreamChatNetworkError>());
|
expect(e, isA<StreamChatNetworkError>());
|
||||||
expect(e.message, "Dio can't establish new connection after closed.");
|
expect(
|
||||||
|
e.message,
|
||||||
|
"The connection errored: Dio can't establish a new connection"
|
||||||
|
' after it was closed.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,53 @@
|
|||||||
## Upcoming
|
## Upcoming
|
||||||
|
|
||||||
|
🐞 Fixed
|
||||||
|
|
||||||
|
- [#1462](https://github.com/GetStream/stream-chat-flutter/issues/1462) Fixed support for iPad in the share button for images.
|
||||||
|
- [#1475](https://github.com/GetStream/stream-chat-flutter/issues/1475) Fixed typo to fix compilation.
|
||||||
|
|
||||||
|
✅ Added
|
||||||
|
|
||||||
|
- Now it is possible to customize the max lines of the title of a url attachment. Before it was always 1 line.
|
||||||
|
- Added `attachmentActionsModalBuilder` parameter to `StreamMessageWidget` that allows to customize `AttachmentActionsModal`.
|
||||||
|
|
||||||
|
🔄 Changed
|
||||||
|
|
||||||
|
- Updated dependencies to resolvable versions.
|
||||||
|
|
||||||
|
## 5.3.0
|
||||||
|
|
||||||
|
🔄 Changed
|
||||||
|
|
||||||
|
- Updated `photo_manager` dependency to `^2.5.2`
|
||||||
|
|
||||||
|
🐞 Fixed
|
||||||
|
- [[#1424]](https://github.com/GetStream/stream-chat-flutter/issues/1424) Fixed a render issue when showing messages starting with 4 whitespaces.
|
||||||
|
- Fixed a bug where the `AttachmentPickerBottomSheet` was not able to identify the mobile browser.
|
||||||
|
- Fixed uploading files on Windows - fixed temp file path.
|
||||||
|
|
||||||
|
✅ Added
|
||||||
|
- New `noPhotoOrVideoLabel` displayed when there is no files to choose.
|
||||||
|
|
||||||
|
## 5.2.0
|
||||||
|
|
||||||
|
✅ Added
|
||||||
|
- Added a new `bottomRowBuilderWithDefaultWidget` parameter to `StreamMessageWidget` which contains a third parameter (default `BottomRow` widget with `copyWith` method available) to allow easier customization.
|
||||||
|
|
||||||
🔄 Changed
|
🔄 Changed
|
||||||
|
|
||||||
- Updated `lottie` dependency to `^2.0.0`
|
- Updated `lottie` dependency to `^2.0.0`
|
||||||
- Updated `desktop_drop` dependency to `^0.4.0`
|
- Updated `desktop_drop` dependency to `^0.4.0`
|
||||||
- Updated `connectivity_plus` dependency to `^3.0.2`
|
- Updated `connectivity_plus` dependency to `^3.0.2`
|
||||||
|
- Updated `dart_vlc` dependency to `^0.4.0`
|
||||||
|
- Updated `file_picker` dependency to `^5.2.4`
|
||||||
|
- Deprecated `StreamMessageWidget.bottomRowBuilder` in favor of `StreamMessageWidget.bottomRowBuilderWithDefaultWidget`.
|
||||||
|
- Deprecated `StreamMessageWidget.deletedBottomRowBuilder` in favor of `StreamMessageWidget.bottomRowBuilderWithDefaultWidget`.
|
||||||
|
- Deprecated `StreamMessageWidget.usernameBuilder` in favor of `StreamMessageWidget.bottomRowBuilderWithDefaultWidget`.
|
||||||
|
|
||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
- [[#1379]](https://github.com/GetStream/stream-chat-flutter/issues/1379) Fixed "Issues with photo attachments on web", where the cached image attachment would not render while uploading.
|
- [[#1379]](https://github.com/GetStream/stream-chat-flutter/issues/1379) Fixed "Issues with photo attachments on web", where the cached image attachment would not render while uploading.
|
||||||
- Fix render overflow issue with `MessageSearchListTileTitle`. It now uses `Text.rich` instead of `Row`. Better default behaviour and allows `TextOverflow`.
|
- Fix render overflow issue with `MessageSearchListTileTitle`. It now uses `Text.rich` instead of `Row`. Better default behaviour and allows `TextOverflow`.
|
||||||
|
- [[1346]](https://github.com/GetStream/stream-chat-flutter/issues/1346) Fixed a render issue while uploading video on web.
|
||||||
- [[#1347]](https://github.com/GetStream/stream-chat-flutter/issues/1347) `onReply` not working in `AttachmentActionsModal` which is used by `StreamImageAttachment` and `StreamImageGroup`.
|
- [[#1347]](https://github.com/GetStream/stream-chat-flutter/issues/1347) `onReply` not working in `AttachmentActionsModal` which is used by `StreamImageAttachment` and `StreamImageGroup`.
|
||||||
|
|
||||||
## 5.1.0
|
## 5.1.0
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ You should then run `flutter packages get`
|
|||||||
|
|
||||||
The package uses [`photo_manager`](https://pub.dev/packages/photo_manager) to access the device's photo library.
|
The package uses [`photo_manager`](https://pub.dev/packages/photo_manager) to access the device's photo library.
|
||||||
Follow [this wiki](https://pub.dev/packages/photo_manager#android-10-q-29) to fulfil the Android requirements.
|
Follow [this wiki](https://pub.dev/packages/photo_manager#android-10-q-29) to fulfil the Android requirements.
|
||||||
|
You need to take additional steps if you are targeting Android 13. Read [this section](https://pub.dev/packages/photo_manager#android-13-api-33-extra-configs) for more information.
|
||||||
|
|
||||||
### iOS
|
### iOS
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
|
|||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 31
|
compileSdkVersion 33
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main.java.srcDirs += 'src/main/kotlin'
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
@@ -41,7 +41,7 @@ android {
|
|||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId "com.example.example"
|
applicationId "com.example.example"
|
||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
targetSdkVersion 31
|
targetSdkVersion 33
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,17 +9,20 @@
|
|||||||
<uses-permission android:name="android.permission.CAMERA"/>
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION"/>
|
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION"/>
|
||||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||||
<application
|
<application
|
||||||
android:name=".Application"
|
android:name=".Application"
|
||||||
android:label="example"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:label="example">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:exported="true"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
|
||||||
android:hardwareAccelerated="true"
|
|
||||||
android:exported="true"
|
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
the Android process has started. This theme is visible to the user
|
the Android process has started. This theme is visible to the user
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.6.0'
|
ext.kotlin_version = '1.7.21'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.2.2'
|
classpath 'com.android.tools.build:gradle:7.2.2'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 51;
|
objectVersion = 54;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
@@ -199,6 +199,7 @@
|
|||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
@@ -235,6 +236,7 @@
|
|||||||
};
|
};
|
||||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -61,5 +61,7 @@
|
|||||||
<string>Explain why your app uses the mic</string>
|
<string>Explain why your app uses the mic</string>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ class _SplitViewState extends State<SplitView> {
|
|||||||
: Center(
|
: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'Pick a channel to show the messages 💬',
|
'Pick a channel to show the messages 💬',
|
||||||
style: Theme.of(context).textTheme.headline5,
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ class _SplitViewState extends State<SplitView> {
|
|||||||
: Center(
|
: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'Pick a channel to show the messages 💬',
|
'Pick a channel to show the messages 💬',
|
||||||
style: Theme.of(context).textTheme.headline5,
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -74,6 +74,9 @@ class MyApp extends StatelessWidget {
|
|||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
ownMessageTheme: const StreamMessageThemeData(
|
||||||
|
urlAttachmentTitleMaxLine: 1,
|
||||||
|
),
|
||||||
otherMessageTheme: StreamMessageThemeData(
|
otherMessageTheme: StreamMessageThemeData(
|
||||||
messageBackgroundColor: colorTheme.textHighEmphasis,
|
messageBackgroundColor: colorTheme.textHighEmphasis,
|
||||||
messageTextStyle: TextStyle(
|
messageTextStyle: TextStyle(
|
||||||
@@ -82,6 +85,7 @@ class MyApp extends StatelessWidget {
|
|||||||
avatarTheme: StreamAvatarThemeData(
|
avatarTheme: StreamAvatarThemeData(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
|
urlAttachmentTitleMaxLine: 1,
|
||||||
),
|
),
|
||||||
).merge(defaultTheme);
|
).merge(defaultTheme);
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,8 @@
|
|||||||
#include <dart_vlc/dart_vlc_plugin.h>
|
#include <dart_vlc/dart_vlc_plugin.h>
|
||||||
#include <desktop_drop/desktop_drop_plugin.h>
|
#include <desktop_drop/desktop_drop_plugin.h>
|
||||||
#include <file_selector_linux/file_selector_plugin.h>
|
#include <file_selector_linux/file_selector_plugin.h>
|
||||||
#include <screen_retriever/screen_retriever_plugin.h>
|
|
||||||
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
|
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
|
||||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
#include <window_manager/window_manager_plugin.h>
|
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) dart_vlc_registrar =
|
g_autoptr(FlPluginRegistrar) dart_vlc_registrar =
|
||||||
@@ -24,16 +22,10 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
|||||||
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||||
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
|
||||||
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
|
||||||
g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar =
|
g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin");
|
||||||
sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar);
|
sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) window_manager_registrar =
|
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin");
|
|
||||||
window_manager_plugin_register_with_registrar(window_manager_registrar);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
dart_vlc
|
dart_vlc
|
||||||
desktop_drop
|
desktop_drop
|
||||||
file_selector_linux
|
file_selector_linux
|
||||||
screen_retriever
|
|
||||||
sqlite3_flutter_libs
|
sqlite3_flutter_libs
|
||||||
url_launcher_linux
|
url_launcher_linux
|
||||||
window_manager
|
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 51;
|
objectVersion = 54;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXAggregateTarget section */
|
/* Begin PBXAggregateTarget section */
|
||||||
@@ -203,7 +203,7 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 0920;
|
LastSwiftUpdateCheck = 0920;
|
||||||
LastUpgradeCheck = 0930;
|
LastUpgradeCheck = 1300;
|
||||||
ORGANIZATIONNAME = "";
|
ORGANIZATIONNAME = "";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
33CC10EC2044A3C60003C045 = {
|
33CC10EC2044A3C60003C045 = {
|
||||||
@@ -256,6 +256,7 @@
|
|||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
3399D490228B24CF009A79C7 /* ShellScript */ = {
|
3399D490228B24CF009A79C7 /* ShellScript */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
@@ -404,7 +405,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
@@ -484,7 +485,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
@@ -531,7 +532,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1000"
|
LastUpgradeVersion = "1300"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ version: 1.0.0+1
|
|||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.17.0 <3.0.0'
|
sdk: '>=2.17.0 <3.0.0'
|
||||||
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
@@ -27,7 +28,7 @@ dependencies:
|
|||||||
cupertino_icons: ^1.0.4
|
cupertino_icons: ^1.0.4
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
responsive_builder: ^0.4.2
|
responsive_builder: ^0.6.4
|
||||||
stream_chat_flutter:
|
stream_chat_flutter:
|
||||||
path: ../
|
path: ../
|
||||||
stream_chat_localizations:
|
stream_chat_localizations:
|
||||||
|
|||||||
+4
-10
@@ -6,16 +6,14 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <connectivity_plus_windows/connectivity_plus_windows_plugin.h>
|
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||||
#include <dart_vlc/dart_vlc_plugin.h>
|
#include <dart_vlc/dart_vlc_plugin.h>
|
||||||
#include <desktop_drop/desktop_drop_plugin.h>
|
#include <desktop_drop/desktop_drop_plugin.h>
|
||||||
#include <file_selector_windows/file_selector_windows.h>
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
#include <flutter_native_view/flutter_native_view_plugin.h>
|
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||||
#include <screen_retriever/screen_retriever_plugin.h>
|
|
||||||
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
|
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
|
||||||
#include <thumblr_windows/thumblr_windows_plugin.h>
|
#include <thumblr_windows/thumblr_windows_plugin.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
#include <window_manager/window_manager_plugin.h>
|
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
||||||
@@ -26,16 +24,12 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||||||
registry->GetRegistrarForPlugin("DesktopDropPlugin"));
|
registry->GetRegistrarForPlugin("DesktopDropPlugin"));
|
||||||
FileSelectorWindowsRegisterWithRegistrar(
|
FileSelectorWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||||
FlutterNativeViewPluginRegisterWithRegistrar(
|
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FlutterNativeViewPlugin"));
|
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
||||||
ScreenRetrieverPluginRegisterWithRegistrar(
|
|
||||||
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
|
||||||
Sqlite3FlutterLibsPluginRegisterWithRegistrar(
|
Sqlite3FlutterLibsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin"));
|
registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin"));
|
||||||
ThumblrWindowsPluginRegisterWithRegistrar(
|
ThumblrWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("ThumblrWindowsPlugin"));
|
registry->GetRegistrarForPlugin("ThumblrWindowsPlugin"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
WindowManagerPluginRegisterWithRegistrar(
|
|
||||||
registry->GetRegistrarForPlugin("WindowManagerPlugin"));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,14 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
connectivity_plus_windows
|
connectivity_plus
|
||||||
dart_vlc
|
dart_vlc
|
||||||
desktop_drop
|
desktop_drop
|
||||||
file_selector_windows
|
file_selector_windows
|
||||||
flutter_native_view
|
share_plus
|
||||||
screen_retriever
|
|
||||||
sqlite3_flutter_libs
|
sqlite3_flutter_libs
|
||||||
thumblr_windows
|
thumblr_windows
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
window_manager
|
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export 'attachment_error.dart';
|
export 'attachment_error.dart';
|
||||||
export 'attachment_error.dart';
|
|
||||||
export 'attachment_upload_state_builder.dart';
|
export 'attachment_upload_state_builder.dart';
|
||||||
export 'attachment_widget.dart' show AttachmentSource;
|
export 'attachment_widget.dart' show AttachmentSource;
|
||||||
export 'file_attachment.dart';
|
export 'file_attachment.dart';
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ class _FileTypeImage extends StatelessWidget {
|
|||||||
shape: _getDefaultShape(context),
|
shape: _getDefaultShape(context),
|
||||||
child: source.when(
|
child: source.when(
|
||||||
local: () => StreamVideoThumbnailImage(
|
local: () => StreamVideoThumbnailImage(
|
||||||
video: attachment.file!.path!,
|
video: attachment.file!.path,
|
||||||
placeholderBuilder: (_) => const Center(
|
placeholderBuilder: (_) => const Center(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 20,
|
width: 20,
|
||||||
@@ -205,7 +205,7 @@ class _FileTypeImage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
network: () => StreamVideoThumbnailImage(
|
network: () => StreamVideoThumbnailImage(
|
||||||
video: attachment.assetUrl!,
|
video: attachment.assetUrl,
|
||||||
placeholderBuilder: (_) => const Center(
|
placeholderBuilder: (_) => const Center(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 20,
|
width: 20,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class StreamGiphyAttachment extends StreamAttachmentWidget {
|
|||||||
this.onShowMessage,
|
this.onShowMessage,
|
||||||
this.onReplyMessage,
|
this.onReplyMessage,
|
||||||
this.onAttachmentTap,
|
this.onAttachmentTap,
|
||||||
|
this.attachmentActionsModalBuilder,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// {@macro showMessageCallback}
|
/// {@macro showMessageCallback}
|
||||||
@@ -28,6 +29,9 @@ class StreamGiphyAttachment extends StreamAttachmentWidget {
|
|||||||
/// {@macro onAttachmentTap}
|
/// {@macro onAttachmentTap}
|
||||||
final OnAttachmentTap? onAttachmentTap;
|
final OnAttachmentTap? onAttachmentTap;
|
||||||
|
|
||||||
|
/// {@macro attachmentActionsBuilder}
|
||||||
|
final AttachmentActionsBuilder? attachmentActionsModalBuilder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final imageUrl =
|
final imageUrl =
|
||||||
@@ -260,6 +264,7 @@ class StreamGiphyAttachment extends StreamAttachmentWidget {
|
|||||||
userName: message.user!.name,
|
userName: message.user!.name,
|
||||||
onShowMessage: onShowMessage,
|
onShowMessage: onShowMessage,
|
||||||
onReplyMessage: onReplyMessage,
|
onReplyMessage: onReplyMessage,
|
||||||
|
attachmentActionsModalBuilder: attachmentActionsModalBuilder,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
+3
-1
@@ -123,7 +123,9 @@ class StreamAttachmentHandler extends StreamAttachmentHandlerBase {
|
|||||||
|
|
||||||
final tempDir = await getTemporaryDirectory();
|
final tempDir = await getTemporaryDirectory();
|
||||||
final tempPath = Uri.file(tempDir.path, windows: CurrentPlatform.isWindows);
|
final tempPath = Uri.file(tempDir.path, windows: CurrentPlatform.isWindows);
|
||||||
final tempFilePath = tempPath.resolve(fileName!).path;
|
final tempFilePath = tempPath
|
||||||
|
.resolve(fileName!)
|
||||||
|
.toFilePath(windows: CurrentPlatform.isWindows);
|
||||||
print(tempFilePath);
|
print(tempFilePath);
|
||||||
|
|
||||||
final attachmentFileBytes = attachmentFile.bytes;
|
final attachmentFileBytes = attachmentFile.bytes;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class StreamImageAttachment extends StreamAttachmentWidget {
|
|||||||
this.imageThumbnailSize = const Size(400, 400),
|
this.imageThumbnailSize = const Size(400, 400),
|
||||||
this.imageThumbnailResizeType = 'clip',
|
this.imageThumbnailResizeType = 'clip',
|
||||||
this.imageThumbnailCropType = 'center',
|
this.imageThumbnailCropType = 'center',
|
||||||
|
this.attachmentActionsModalBuilder,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The [StreamMessageThemeData] to use for the image title
|
/// The [StreamMessageThemeData] to use for the image title
|
||||||
@@ -52,6 +53,9 @@ class StreamImageAttachment extends StreamAttachmentWidget {
|
|||||||
/// Defaults to [center]
|
/// Defaults to [center]
|
||||||
final String /*center|top|bottom|left|right*/ imageThumbnailCropType;
|
final String /*center|top|bottom|left|right*/ imageThumbnailCropType;
|
||||||
|
|
||||||
|
/// {@macro attachmentActionsBuilder}
|
||||||
|
final AttachmentActionsBuilder? attachmentActionsModalBuilder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return source.when(
|
return source.when(
|
||||||
@@ -161,6 +165,8 @@ class StreamImageAttachment extends StreamAttachmentWidget {
|
|||||||
userName: message.user!.name,
|
userName: message.user!.name,
|
||||||
onShowMessage: onShowMessage,
|
onShowMessage: onShowMessage,
|
||||||
onReplyMessage: onReplyMessage,
|
onReplyMessage: onReplyMessage,
|
||||||
|
attachmentActionsModalBuilder:
|
||||||
|
attachmentActionsModalBuilder,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class StreamImageGroup extends StatelessWidget {
|
|||||||
this.imageThumbnailSize = const Size(400, 400),
|
this.imageThumbnailSize = const Size(400, 400),
|
||||||
this.imageThumbnailResizeType = 'clip',
|
this.imageThumbnailResizeType = 'clip',
|
||||||
this.imageThumbnailCropType = 'center',
|
this.imageThumbnailCropType = 'center',
|
||||||
|
this.attachmentActionsModalBuilder,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// List of attachments to show
|
/// List of attachments to show
|
||||||
@@ -54,6 +55,9 @@ class StreamImageGroup extends StatelessWidget {
|
|||||||
/// Defaults to [center]
|
/// Defaults to [center]
|
||||||
final String /*center|top|bottom|left|right*/ imageThumbnailCropType;
|
final String /*center|top|bottom|left|right*/ imageThumbnailCropType;
|
||||||
|
|
||||||
|
/// {@macro attachmentActionsBuilder}
|
||||||
|
final AttachmentActionsBuilder? attachmentActionsModalBuilder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
@@ -154,6 +158,7 @@ class StreamImageGroup extends StatelessWidget {
|
|||||||
userName: message.user!.name,
|
userName: message.user!.name,
|
||||||
onShowMessage: onShowMessage,
|
onShowMessage: onShowMessage,
|
||||||
onReplyMessage: onReplyMessage,
|
onReplyMessage: onReplyMessage,
|
||||||
|
attachmentActionsModalBuilder: attachmentActionsModalBuilder,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -170,6 +175,7 @@ class StreamImageGroup extends StatelessWidget {
|
|||||||
imageThumbnailSize: imageThumbnailSize,
|
imageThumbnailSize: imageThumbnailSize,
|
||||||
imageThumbnailResizeType: imageThumbnailResizeType,
|
imageThumbnailResizeType: imageThumbnailResizeType,
|
||||||
imageThumbnailCropType: imageThumbnailCropType,
|
imageThumbnailCropType: imageThumbnailCropType,
|
||||||
|
attachmentActionsModalBuilder: attachmentActionsModalBuilder,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ class StreamUrlAttachment extends StatelessWidget {
|
|||||||
if (urlAttachment.title != null)
|
if (urlAttachment.title != null)
|
||||||
Text(
|
Text(
|
||||||
urlAttachment.title!.trim(),
|
urlAttachment.title!.trim(),
|
||||||
maxLines: 1,
|
maxLines: messageTheme.urlAttachmentTitleMaxLine ?? 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: chatThemeData.textTheme.body
|
style: chatThemeData.textTheme.body
|
||||||
.copyWith(fontWeight: FontWeight.w700),
|
.copyWith(fontWeight: FontWeight.w700),
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class StreamVideoAttachment extends StreamAttachmentWidget {
|
|||||||
this.onShowMessage,
|
this.onShowMessage,
|
||||||
this.onReplyMessage,
|
this.onReplyMessage,
|
||||||
this.onAttachmentTap,
|
this.onAttachmentTap,
|
||||||
|
this.attachmentActionsModalBuilder,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The [StreamMessageThemeData] to use for the title
|
/// The [StreamMessageThemeData] to use for the title
|
||||||
@@ -31,6 +32,9 @@ class StreamVideoAttachment extends StreamAttachmentWidget {
|
|||||||
/// {@macro onAttachmentTap}
|
/// {@macro onAttachmentTap}
|
||||||
final OnAttachmentTap? onAttachmentTap;
|
final OnAttachmentTap? onAttachmentTap;
|
||||||
|
|
||||||
|
/// {@macro attachmentActionsBuilder}
|
||||||
|
final AttachmentActionsBuilder? attachmentActionsModalBuilder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return source.when(
|
return source.when(
|
||||||
@@ -41,7 +45,7 @@ class StreamVideoAttachment extends StreamAttachmentWidget {
|
|||||||
return _buildVideoAttachment(
|
return _buildVideoAttachment(
|
||||||
context,
|
context,
|
||||||
StreamVideoThumbnailImage(
|
StreamVideoThumbnailImage(
|
||||||
video: attachment.file!.path!,
|
video: attachment.file!.path,
|
||||||
thumbUrl: attachment.thumbUrl,
|
thumbUrl: attachment.thumbUrl,
|
||||||
constraints: constraints,
|
constraints: constraints,
|
||||||
),
|
),
|
||||||
@@ -54,7 +58,7 @@ class StreamVideoAttachment extends StreamAttachmentWidget {
|
|||||||
return _buildVideoAttachment(
|
return _buildVideoAttachment(
|
||||||
context,
|
context,
|
||||||
StreamVideoThumbnailImage(
|
StreamVideoThumbnailImage(
|
||||||
video: attachment.assetUrl!,
|
video: attachment.assetUrl,
|
||||||
thumbUrl: attachment.thumbUrl,
|
thumbUrl: attachment.thumbUrl,
|
||||||
constraints: constraints,
|
constraints: constraints,
|
||||||
),
|
),
|
||||||
@@ -86,6 +90,8 @@ class StreamVideoAttachment extends StreamAttachmentWidget {
|
|||||||
userName: message.user!.name,
|
userName: message.user!.name,
|
||||||
onShowMessage: onShowMessage,
|
onShowMessage: onShowMessage,
|
||||||
onReplyMessage: onReplyMessage,
|
onReplyMessage: onReplyMessage,
|
||||||
|
attachmentActionsModalBuilder:
|
||||||
|
attachmentActionsModalBuilder,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -162,8 +162,8 @@ class StreamChannelHeader extends StatelessWidget
|
|||||||
showMessage: showConnectionStateTile && showStatus,
|
showMessage: showConnectionStateTile && showStatus,
|
||||||
message: statusString,
|
message: statusString,
|
||||||
child: AppBar(
|
child: AppBar(
|
||||||
toolbarTextStyle: theme.textTheme.bodyText2,
|
toolbarTextStyle: theme.textTheme.bodyMedium,
|
||||||
titleTextStyle: theme.textTheme.headline6,
|
titleTextStyle: theme.textTheme.titleLarge,
|
||||||
systemOverlayStyle: theme.brightness == Brightness.dark
|
systemOverlayStyle: theme.brightness == Brightness.dark
|
||||||
? SystemUiOverlayStyle.light
|
? SystemUiOverlayStyle.light
|
||||||
: SystemUiOverlayStyle.dark,
|
: SystemUiOverlayStyle.dark,
|
||||||
|
|||||||
@@ -131,8 +131,8 @@ class StreamChannelListHeader extends StatelessWidget
|
|||||||
showMessage: showConnectionStateTile && showStatus,
|
showMessage: showConnectionStateTile && showStatus,
|
||||||
message: statusString,
|
message: statusString,
|
||||||
child: AppBar(
|
child: AppBar(
|
||||||
toolbarTextStyle: theme.textTheme.bodyText2,
|
toolbarTextStyle: theme.textTheme.bodyMedium,
|
||||||
titleTextStyle: theme.textTheme.headline6,
|
titleTextStyle: theme.textTheme.titleLarge,
|
||||||
systemOverlayStyle: theme.brightness == Brightness.dark
|
systemOverlayStyle: theme.brightness == Brightness.dark
|
||||||
? SystemUiOverlayStyle.light
|
? SystemUiOverlayStyle.light
|
||||||
: SystemUiOverlayStyle.dark,
|
: SystemUiOverlayStyle.dark,
|
||||||
@@ -181,7 +181,10 @@ class StreamChannelListHeader extends StatelessWidget
|
|||||||
package: 'stream_chat_flutter',
|
package: 'stream_chat_flutter',
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
color: color,
|
colorFilter: ColorFilter.mode(
|
||||||
|
color,
|
||||||
|
BlendMode.srcIn,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:file_selector/file_selector.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
@@ -61,6 +60,7 @@ class StreamGalleryFooter extends StatefulWidget
|
|||||||
|
|
||||||
class _StreamGalleryFooterState extends State<StreamGalleryFooter> {
|
class _StreamGalleryFooterState extends State<StreamGalleryFooter> {
|
||||||
final shareButtonKey = GlobalKey();
|
final shareButtonKey = GlobalKey();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
const showShareButton = !kIsWeb;
|
const showShareButton = !kIsWeb;
|
||||||
@@ -108,12 +108,19 @@ class _StreamGalleryFooterState extends State<StreamGalleryFooter> {
|
|||||||
await file.writeAsBytes(bytes);
|
await file.writeAsBytes(bytes);
|
||||||
final box =
|
final box =
|
||||||
shareButtonKey.currentContext?.findRenderObject();
|
shareButtonKey.currentContext?.findRenderObject();
|
||||||
|
final size = shareButtonKey.currentContext?.size;
|
||||||
|
|
||||||
final position =
|
final position =
|
||||||
(box! as RenderBox).localToGlobal(Offset.zero);
|
(box! as RenderBox).localToGlobal(Offset.zero);
|
||||||
|
|
||||||
await Share.shareXFiles(
|
await Share.shareXFiles(
|
||||||
[XFile(filePath)],
|
[XFile(filePath)],
|
||||||
sharePositionOrigin:
|
sharePositionOrigin: Rect.fromLTWH(
|
||||||
Rect.fromLTWH(position.dx, position.dy, 0, 0),
|
position.dx,
|
||||||
|
position.dy,
|
||||||
|
size?.width ?? 50,
|
||||||
|
(size?.height ?? 2) / 2,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -219,8 +226,8 @@ class _StreamGalleryFooterState extends State<StreamGalleryFooter> {
|
|||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: StreamVideoThumbnailImage(
|
child: StreamVideoThumbnailImage(
|
||||||
video: (attachment.file?.path ??
|
video:
|
||||||
attachment.assetUrl)!,
|
attachment.file?.path ?? attachment.assetUrl,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ class StreamGalleryHeader extends StatelessWidget
|
|||||||
final galleryHeaderThemeData = StreamGalleryHeaderTheme.of(context);
|
final galleryHeaderThemeData = StreamGalleryHeaderTheme.of(context);
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
return AppBar(
|
return AppBar(
|
||||||
toolbarTextStyle: theme.textTheme.bodyText2,
|
toolbarTextStyle: theme.textTheme.bodyMedium,
|
||||||
titleTextStyle: theme.textTheme.headline6,
|
titleTextStyle: theme.textTheme.titleLarge,
|
||||||
systemOverlayStyle: theme.brightness == Brightness.dark
|
systemOverlayStyle: theme.brightness == Brightness.dark
|
||||||
? SystemUiOverlayStyle.light
|
? SystemUiOverlayStyle.light
|
||||||
: SystemUiOverlayStyle.dark,
|
: SystemUiOverlayStyle.dark,
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ abstract class Translations {
|
|||||||
/// The label for showing no users
|
/// The label for showing no users
|
||||||
String get noUsersLabel;
|
String get noUsersLabel;
|
||||||
|
|
||||||
|
/// The label for showing no photo or video
|
||||||
|
String get noPhotoOrVideoLabel;
|
||||||
|
|
||||||
/// The text for showing user is online
|
/// The text for showing user is online
|
||||||
String get userOnlineText;
|
String get userOnlineText;
|
||||||
|
|
||||||
@@ -78,7 +81,10 @@ abstract class Translations {
|
|||||||
|
|
||||||
/// The text for showing the unread messages count
|
/// The text for showing the unread messages count
|
||||||
/// in the [StreamMessageListView]
|
/// in the [StreamMessageListView]
|
||||||
String unreadMessagesSeparatorText(int unreadCount);
|
String unreadMessagesSeparatorText(
|
||||||
|
@Deprecated('unreadCount is not used anymore and will be removed ')
|
||||||
|
int unreadCount,
|
||||||
|
);
|
||||||
|
|
||||||
/// The label for "connected" in [StreamConnectionStatusBuilder]
|
/// The label for "connected" in [StreamConnectionStatusBuilder]
|
||||||
String get connectedLabel;
|
String get connectedLabel;
|
||||||
@@ -373,6 +379,9 @@ class DefaultTranslations implements Translations {
|
|||||||
@override
|
@override
|
||||||
String get noUsersLabel => 'There are no users currently';
|
String get noUsersLabel => 'There are no users currently';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noPhotoOrVideoLabel => 'There is no photo or video';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get retryLabel => 'Retry';
|
String get retryLabel => 'Retry';
|
||||||
|
|
||||||
@@ -791,12 +800,7 @@ Attachment limit exceeded: it's not possible to add more than $limit attachments
|
|||||||
String get linkDisabledError => 'Links are disabled';
|
String get linkDisabledError => 'Links are disabled';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String unreadMessagesSeparatorText(int unreadCount) {
|
String unreadMessagesSeparatorText(int unreadCount) => 'New messages';
|
||||||
if (unreadCount == 1) {
|
|
||||||
return '1 unread message';
|
|
||||||
}
|
|
||||||
return '$unreadCount unread messages';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get enableFileAccessMessage => 'Please enable access to files'
|
String get enableFileAccessMessage => 'Please enable access to files'
|
||||||
|
|||||||
+29
-39
@@ -1,7 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart' show kIsWeb, defaultTargetPlatform;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat_flutter/platform_widget_builder/src/platform_widget.dart';
|
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
/// Shows a modal material design bottom sheet.
|
/// Shows a modal material design bottom sheet.
|
||||||
@@ -107,44 +107,34 @@ Future<T?> showStreamAttachmentPickerModalBottomSheet<T>({
|
|||||||
controller: controller,
|
controller: controller,
|
||||||
initialAttachments: initialAttachments,
|
initialAttachments: initialAttachments,
|
||||||
builder: (context, controller, child) {
|
builder: (context, controller, child) {
|
||||||
return PlatformWidget(
|
final currentPlatform = defaultTargetPlatform;
|
||||||
web: (context) {
|
final isWebOrDesktop = kIsWeb ||
|
||||||
return webOrDesktopAttachmentPickerBuilder.call(
|
currentPlatform == TargetPlatform.macOS ||
|
||||||
context: context,
|
currentPlatform == TargetPlatform.linux ||
|
||||||
controller: controller,
|
currentPlatform == TargetPlatform.windows;
|
||||||
customOptions: customOptions?.map(
|
|
||||||
WebOrDesktopAttachmentPickerOption.fromAttachmentPickerOption,
|
if (isWebOrDesktop) {
|
||||||
),
|
return webOrDesktopAttachmentPickerBuilder.call(
|
||||||
attachmentThumbnailSize: attachmentThumbnailSize,
|
context: context,
|
||||||
attachmentThumbnailFormat: attachmentThumbnailFormat,
|
controller: controller,
|
||||||
attachmentThumbnailQuality: attachmentThumbnailQuality,
|
customOptions: customOptions?.map(
|
||||||
attachmentThumbnailScale: attachmentThumbnailScale,
|
WebOrDesktopAttachmentPickerOption.fromAttachmentPickerOption,
|
||||||
);
|
),
|
||||||
},
|
attachmentThumbnailSize: attachmentThumbnailSize,
|
||||||
mobile: (context) {
|
attachmentThumbnailFormat: attachmentThumbnailFormat,
|
||||||
return mobileAttachmentPickerBuilder.call(
|
attachmentThumbnailQuality: attachmentThumbnailQuality,
|
||||||
context: context,
|
attachmentThumbnailScale: attachmentThumbnailScale,
|
||||||
controller: controller,
|
);
|
||||||
customOptions: customOptions,
|
}
|
||||||
attachmentThumbnailSize: attachmentThumbnailSize,
|
|
||||||
attachmentThumbnailFormat: attachmentThumbnailFormat,
|
return mobileAttachmentPickerBuilder.call(
|
||||||
attachmentThumbnailQuality: attachmentThumbnailQuality,
|
context: context,
|
||||||
attachmentThumbnailScale: attachmentThumbnailScale,
|
controller: controller,
|
||||||
);
|
customOptions: customOptions,
|
||||||
},
|
attachmentThumbnailSize: attachmentThumbnailSize,
|
||||||
desktop: (context) {
|
attachmentThumbnailFormat: attachmentThumbnailFormat,
|
||||||
return webOrDesktopAttachmentPickerBuilder.call(
|
attachmentThumbnailQuality: attachmentThumbnailQuality,
|
||||||
context: context,
|
attachmentThumbnailScale: attachmentThumbnailScale,
|
||||||
controller: controller,
|
|
||||||
customOptions: customOptions?.map(
|
|
||||||
WebOrDesktopAttachmentPickerOption.fromAttachmentPickerOption,
|
|
||||||
),
|
|
||||||
attachmentThumbnailSize: attachmentThumbnailSize,
|
|
||||||
attachmentThumbnailFormat: attachmentThumbnailFormat,
|
|
||||||
attachmentThumbnailQuality: attachmentThumbnailQuality,
|
|
||||||
attachmentThumbnailScale: attachmentThumbnailScale,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ class _ParseAttachments extends StatelessWidget {
|
|||||||
'video': (_, attachment) {
|
'video': (_, attachment) {
|
||||||
return StreamVideoThumbnailImage(
|
return StreamVideoThumbnailImage(
|
||||||
key: ValueKey(attachment.assetUrl),
|
key: ValueKey(attachment.assetUrl),
|
||||||
video: attachment.file?.path ?? attachment.assetUrl!,
|
video: attachment.file?.path ?? attachment.assetUrl,
|
||||||
constraints: BoxConstraints.loose(const Size(32, 32)),
|
constraints: BoxConstraints.loose(const Size(32, 32)),
|
||||||
errorBuilder: (_, __) => AttachmentError(
|
errorBuilder: (_, __) => AttachmentError(
|
||||||
constraints: BoxConstraints.loose(const Size(32, 32)),
|
constraints: BoxConstraints.loose(const Size(32, 32)),
|
||||||
|
|||||||
@@ -1194,7 +1194,7 @@ class StreamMessageInputState extends State<StreamMessageInput>
|
|||||||
104,
|
104,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
video: (attachment.file?.path ?? attachment.assetUrl)!,
|
video: attachment.file?.path ?? attachment.assetUrl,
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
left: 8,
|
left: 8,
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ class StreamMessageTextField extends StatefulWidget {
|
|||||||
this.textAlignVertical,
|
this.textAlignVertical,
|
||||||
this.textDirection,
|
this.textDirection,
|
||||||
this.readOnly = false,
|
this.readOnly = false,
|
||||||
ToolbarOptions? toolbarOptions,
|
|
||||||
this.showCursor,
|
this.showCursor,
|
||||||
this.autofocus = false,
|
this.autofocus = false,
|
||||||
this.obscuringCharacter = '•',
|
this.obscuringCharacter = '•',
|
||||||
@@ -155,31 +154,7 @@ class StreamMessageTextField extends StatefulWidget {
|
|||||||
keyboardType = keyboardType ??
|
keyboardType = keyboardType ??
|
||||||
(maxLines == 1 ? TextInputType.text : TextInputType.multiline),
|
(maxLines == 1 ? TextInputType.text : TextInputType.multiline),
|
||||||
enableInteractiveSelection =
|
enableInteractiveSelection =
|
||||||
enableInteractiveSelection ?? (!readOnly || !obscureText),
|
enableInteractiveSelection ?? (!readOnly || !obscureText);
|
||||||
toolbarOptions = toolbarOptions ??
|
|
||||||
(obscureText
|
|
||||||
? (readOnly
|
|
||||||
// No point in even offering "Select All" in a read-only obscured
|
|
||||||
// field.
|
|
||||||
? const ToolbarOptions()
|
|
||||||
// Writable, but obscured.
|
|
||||||
: const ToolbarOptions(
|
|
||||||
selectAll: true,
|
|
||||||
paste: true,
|
|
||||||
))
|
|
||||||
: (readOnly
|
|
||||||
// Read-only, not obscured.
|
|
||||||
? const ToolbarOptions(
|
|
||||||
selectAll: true,
|
|
||||||
copy: true,
|
|
||||||
)
|
|
||||||
// Writable, not obscured.
|
|
||||||
: const ToolbarOptions(
|
|
||||||
copy: true,
|
|
||||||
cut: true,
|
|
||||||
selectAll: true,
|
|
||||||
paste: true,
|
|
||||||
)));
|
|
||||||
|
|
||||||
/// Controls the message being edited.
|
/// Controls the message being edited.
|
||||||
///
|
///
|
||||||
@@ -304,13 +279,6 @@ class StreamMessageTextField extends StatefulWidget {
|
|||||||
/// {@macro flutter.widgets.editableText.readOnly}
|
/// {@macro flutter.widgets.editableText.readOnly}
|
||||||
final bool readOnly;
|
final bool readOnly;
|
||||||
|
|
||||||
/// Configuration of toolbar options.
|
|
||||||
///
|
|
||||||
/// If not set, select all and paste will default to be enabled. Copy and cut
|
|
||||||
/// will be disabled if [obscureText] is true. If [readOnly] is true,
|
|
||||||
/// paste and cut will be disabled regardless.
|
|
||||||
final ToolbarOptions toolbarOptions;
|
|
||||||
|
|
||||||
/// {@macro flutter.widgets.editableText.showCursor}
|
/// {@macro flutter.widgets.editableText.showCursor}
|
||||||
final bool? showCursor;
|
final bool? showCursor;
|
||||||
|
|
||||||
@@ -720,7 +688,6 @@ class _StreamMessageTextFieldState extends State<StreamMessageTextField>
|
|||||||
textAlignVertical: widget.textAlignVertical,
|
textAlignVertical: widget.textAlignVertical,
|
||||||
textDirection: widget.textDirection,
|
textDirection: widget.textDirection,
|
||||||
readOnly: widget.readOnly,
|
readOnly: widget.readOnly,
|
||||||
toolbarOptions: widget.toolbarOptions,
|
|
||||||
showCursor: widget.showCursor,
|
showCursor: widget.showCursor,
|
||||||
autofocus: widget.autofocus,
|
autofocus: widget.autofocus,
|
||||||
obscuringCharacter: widget.obscuringCharacter,
|
obscuringCharacter: widget.obscuringCharacter,
|
||||||
|
|||||||
+1
-3
@@ -24,9 +24,7 @@ class UnreadMessagesSeparator extends StatelessWidget {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: Text(
|
child: Text(
|
||||||
context.translations.unreadMessagesSeparatorText(
|
context.translations.unreadMessagesSeparatorText(unreadCount),
|
||||||
unreadCount,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: StreamChannelHeaderTheme.of(context).subtitleStyle,
|
style: StreamChannelHeaderTheme.of(context).subtitleStyle,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ class BottomRow extends StatelessWidget {
|
|||||||
this.deletedBottomRowBuilder,
|
this.deletedBottomRowBuilder,
|
||||||
this.onThreadTap,
|
this.onThreadTap,
|
||||||
this.usernameBuilder,
|
this.usernameBuilder,
|
||||||
|
this.sendingIndicatorBuilder,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// {@macro messageIsDeleted}
|
/// {@macro messageIsDeleted}
|
||||||
@@ -88,6 +89,61 @@ class BottomRow extends StatelessWidget {
|
|||||||
/// {@macro usernameBuilder}
|
/// {@macro usernameBuilder}
|
||||||
final Widget Function(BuildContext, Message)? usernameBuilder;
|
final Widget Function(BuildContext, Message)? usernameBuilder;
|
||||||
|
|
||||||
|
/// {@macro sendingIndicatorBuilder}
|
||||||
|
final Widget Function(BuildContext, Message)? sendingIndicatorBuilder;
|
||||||
|
|
||||||
|
/// {@template copyWith}
|
||||||
|
/// Creates a copy of [BottomRow] with specified attributes
|
||||||
|
/// overridden.
|
||||||
|
/// {@endtemplate}
|
||||||
|
BottomRow copyWith({
|
||||||
|
Key? key,
|
||||||
|
bool? isDeleted,
|
||||||
|
Message? message,
|
||||||
|
bool? showThreadReplyIndicator,
|
||||||
|
bool? showInChannel,
|
||||||
|
bool? showTimeStamp,
|
||||||
|
bool? showUsername,
|
||||||
|
bool? reverse,
|
||||||
|
bool? showSendingIndicator,
|
||||||
|
bool? hasUrlAttachments,
|
||||||
|
bool? isGiphy,
|
||||||
|
bool? isOnlyEmoji,
|
||||||
|
StreamMessageThemeData? messageTheme,
|
||||||
|
StreamChatThemeData? streamChatTheme,
|
||||||
|
bool? hasNonUrlAttachments,
|
||||||
|
StreamChatState? streamChat,
|
||||||
|
Widget Function(BuildContext, Message)? deletedBottomRowBuilder,
|
||||||
|
void Function(Message)? onThreadTap,
|
||||||
|
Widget Function(BuildContext, Message)? usernameBuilder,
|
||||||
|
Widget Function(BuildContext, Message)? sendingIndicatorBuilder,
|
||||||
|
}) =>
|
||||||
|
BottomRow(
|
||||||
|
key: key ?? this.key,
|
||||||
|
isDeleted: isDeleted ?? this.isDeleted,
|
||||||
|
message: message ?? this.message,
|
||||||
|
showThreadReplyIndicator:
|
||||||
|
showThreadReplyIndicator ?? this.showThreadReplyIndicator,
|
||||||
|
showInChannel: showInChannel ?? this.showInChannel,
|
||||||
|
showTimeStamp: showTimeStamp ?? this.showTimeStamp,
|
||||||
|
showUsername: showUsername ?? this.showUsername,
|
||||||
|
reverse: reverse ?? this.reverse,
|
||||||
|
showSendingIndicator: showSendingIndicator ?? this.showSendingIndicator,
|
||||||
|
hasUrlAttachments: hasUrlAttachments ?? this.hasUrlAttachments,
|
||||||
|
isGiphy: isGiphy ?? this.isGiphy,
|
||||||
|
isOnlyEmoji: isOnlyEmoji ?? this.isOnlyEmoji,
|
||||||
|
messageTheme: messageTheme ?? this.messageTheme,
|
||||||
|
streamChatTheme: streamChatTheme ?? this.streamChatTheme,
|
||||||
|
hasNonUrlAttachments: hasNonUrlAttachments ?? this.hasNonUrlAttachments,
|
||||||
|
streamChat: streamChat ?? this.streamChat,
|
||||||
|
deletedBottomRowBuilder:
|
||||||
|
deletedBottomRowBuilder ?? this.deletedBottomRowBuilder,
|
||||||
|
onThreadTap: onThreadTap ?? this.onThreadTap,
|
||||||
|
usernameBuilder: usernameBuilder ?? this.usernameBuilder,
|
||||||
|
sendingIndicatorBuilder:
|
||||||
|
sendingIndicatorBuilder ?? this.sendingIndicatorBuilder,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (isDeleted) {
|
if (isDeleted) {
|
||||||
@@ -147,13 +203,14 @@ class BottomRow extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
if (showSendingIndicator)
|
if (showSendingIndicator)
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: SendingIndicatorWrapper(
|
child: sendingIndicatorBuilder?.call(context, message) ??
|
||||||
messageTheme: messageTheme,
|
SendingIndicatorWrapper(
|
||||||
message: message,
|
messageTheme: messageTheme,
|
||||||
hasNonUrlAttachments: hasNonUrlAttachments,
|
message: message,
|
||||||
streamChat: streamChat,
|
hasNonUrlAttachments: hasNonUrlAttachments,
|
||||||
streamChatTheme: streamChatTheme,
|
streamChat: streamChat,
|
||||||
),
|
streamChatTheme: streamChatTheme,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ class StreamMessageText extends StatelessWidget {
|
|||||||
.translate(language)
|
.translate(language)
|
||||||
.replaceMentions()
|
.replaceMentions()
|
||||||
.text
|
.text
|
||||||
?.replaceAll('\n', '\n\n');
|
?.replaceAll('\n', '\n\n')
|
||||||
|
.trim();
|
||||||
final themeData = Theme.of(context);
|
final themeData = Theme.of(context);
|
||||||
return MarkdownBody(
|
return MarkdownBody(
|
||||||
data: messageText ?? '',
|
data: messageText ?? '',
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import 'package:stream_chat_flutter/src/context_menu_items/context_menu_reaction
|
|||||||
import 'package:stream_chat_flutter/src/context_menu_items/stream_chat_context_menu_item.dart';
|
import 'package:stream_chat_flutter/src/context_menu_items/stream_chat_context_menu_item.dart';
|
||||||
import 'package:stream_chat_flutter/src/dialogs/dialogs.dart';
|
import 'package:stream_chat_flutter/src/dialogs/dialogs.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_actions_modal/message_actions_modal.dart';
|
import 'package:stream_chat_flutter/src/message_actions_modal/message_actions_modal.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/message_widget/bottom_row.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_widget/message_widget_content.dart';
|
import 'package:stream_chat_flutter/src/message_widget/message_widget_content.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_widget/reactions/message_reactions_modal.dart';
|
import 'package:stream_chat_flutter/src/message_widget/reactions/message_reactions_modal.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
@@ -79,8 +80,15 @@ class StreamMessageWidget extends StatefulWidget {
|
|||||||
this.userAvatarBuilder,
|
this.userAvatarBuilder,
|
||||||
this.editMessageInputBuilder,
|
this.editMessageInputBuilder,
|
||||||
this.textBuilder,
|
this.textBuilder,
|
||||||
this.bottomRowBuilder,
|
@Deprecated('''
|
||||||
this.deletedBottomRowBuilder,
|
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||||
|
Will be removed in the next major version.
|
||||||
|
''') this.bottomRowBuilder,
|
||||||
|
this.bottomRowBuilderWithDefaultWidget,
|
||||||
|
@Deprecated('''
|
||||||
|
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||||
|
Will be removed in the next major version.
|
||||||
|
''') this.deletedBottomRowBuilder,
|
||||||
this.customAttachmentBuilders,
|
this.customAttachmentBuilders,
|
||||||
this.padding,
|
this.padding,
|
||||||
this.textPadding = const EdgeInsets.symmetric(
|
this.textPadding = const EdgeInsets.symmetric(
|
||||||
@@ -92,11 +100,19 @@ class StreamMessageWidget extends StatefulWidget {
|
|||||||
this.onQuotedMessageTap,
|
this.onQuotedMessageTap,
|
||||||
this.customActions = const [],
|
this.customActions = const [],
|
||||||
this.onAttachmentTap,
|
this.onAttachmentTap,
|
||||||
this.usernameBuilder,
|
@Deprecated('''
|
||||||
|
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||||
|
Will be removed in the next major version.
|
||||||
|
''') this.usernameBuilder,
|
||||||
this.imageAttachmentThumbnailSize = const Size(400, 400),
|
this.imageAttachmentThumbnailSize = const Size(400, 400),
|
||||||
this.imageAttachmentThumbnailResizeType = 'clip',
|
this.imageAttachmentThumbnailResizeType = 'clip',
|
||||||
this.imageAttachmentThumbnailCropType = 'center',
|
this.imageAttachmentThumbnailCropType = 'center',
|
||||||
}) : attachmentBuilders = {
|
this.attachmentActionsModalBuilder,
|
||||||
|
}) : assert(
|
||||||
|
bottomRowBuilder == null || bottomRowBuilderWithDefaultWidget == null,
|
||||||
|
'You can only use one of the two bottom row builders',
|
||||||
|
),
|
||||||
|
attachmentBuilders = {
|
||||||
'image': (context, message, attachments) {
|
'image': (context, message, attachments) {
|
||||||
final border = RoundedRectangleBorder(
|
final border = RoundedRectangleBorder(
|
||||||
side: attachmentBorderSide ??
|
side: attachmentBorderSide ??
|
||||||
@@ -129,6 +145,8 @@ class StreamMessageWidget extends StatefulWidget {
|
|||||||
imageThumbnailResizeType:
|
imageThumbnailResizeType:
|
||||||
imageAttachmentThumbnailResizeType,
|
imageAttachmentThumbnailResizeType,
|
||||||
imageThumbnailCropType: imageAttachmentThumbnailCropType,
|
imageThumbnailCropType: imageAttachmentThumbnailCropType,
|
||||||
|
attachmentActionsModalBuilder:
|
||||||
|
attachmentActionsModalBuilder,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
attachmentShape: border,
|
attachmentShape: border,
|
||||||
@@ -156,6 +174,7 @@ class StreamMessageWidget extends StatefulWidget {
|
|||||||
imageThumbnailSize: imageAttachmentThumbnailSize,
|
imageThumbnailSize: imageAttachmentThumbnailSize,
|
||||||
imageThumbnailResizeType: imageAttachmentThumbnailResizeType,
|
imageThumbnailResizeType: imageAttachmentThumbnailResizeType,
|
||||||
imageThumbnailCropType: imageAttachmentThumbnailCropType,
|
imageThumbnailCropType: imageAttachmentThumbnailCropType,
|
||||||
|
attachmentActionsModalBuilder: attachmentActionsModalBuilder,
|
||||||
),
|
),
|
||||||
attachmentShape: border,
|
attachmentShape: border,
|
||||||
);
|
);
|
||||||
@@ -189,6 +208,8 @@ class StreamMessageWidget extends StatefulWidget {
|
|||||||
onAttachmentTap(message, attachment);
|
onAttachmentTap(message, attachment);
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
|
attachmentActionsModalBuilder:
|
||||||
|
attachmentActionsModalBuilder,
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
@@ -306,7 +327,13 @@ class StreamMessageWidget extends StatefulWidget {
|
|||||||
/// {@template bottomRowBuilder}
|
/// {@template bottomRowBuilder}
|
||||||
/// Widget builder for building a bottom row below the message
|
/// Widget builder for building a bottom row below the message
|
||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
final Widget Function(BuildContext, Message)? bottomRowBuilder;
|
final BottomRowBuilder? bottomRowBuilder;
|
||||||
|
|
||||||
|
/// {@template bottomRowBuilderWithDefaultWidget}
|
||||||
|
/// Widget builder for building a bottom row below the message.
|
||||||
|
/// Also contains the default bottom row widget.
|
||||||
|
/// {@endtemplate}
|
||||||
|
final BottomRowBuilderWithDefaultWidget? bottomRowBuilderWithDefaultWidget;
|
||||||
|
|
||||||
/// {@template deletedBottomRowBuilder}
|
/// {@template deletedBottomRowBuilder}
|
||||||
/// Widget builder for building a bottom row below a deleted message
|
/// Widget builder for building a bottom row below a deleted message
|
||||||
@@ -512,6 +539,9 @@ class StreamMessageWidget extends StatefulWidget {
|
|||||||
/// {@macro onMessageWidgetAttachmentTap}
|
/// {@macro onMessageWidgetAttachmentTap}
|
||||||
final OnMessageWidgetAttachmentTap? onAttachmentTap;
|
final OnMessageWidgetAttachmentTap? onAttachmentTap;
|
||||||
|
|
||||||
|
/// {@macro attachmentActionsBuilder}
|
||||||
|
final AttachmentActionsBuilder? attachmentActionsModalBuilder;
|
||||||
|
|
||||||
/// Size of the image attachment thumbnail.
|
/// Size of the image attachment thumbnail.
|
||||||
final Size imageAttachmentThumbnailSize;
|
final Size imageAttachmentThumbnailSize;
|
||||||
|
|
||||||
@@ -537,9 +567,19 @@ class StreamMessageWidget extends StatefulWidget {
|
|||||||
void Function(Message)? onReplyTap,
|
void Function(Message)? onReplyTap,
|
||||||
Widget Function(BuildContext, Message)? editMessageInputBuilder,
|
Widget Function(BuildContext, Message)? editMessageInputBuilder,
|
||||||
Widget Function(BuildContext, Message)? textBuilder,
|
Widget Function(BuildContext, Message)? textBuilder,
|
||||||
Widget Function(BuildContext, Message)? usernameBuilder,
|
@Deprecated('''
|
||||||
Widget Function(BuildContext, Message)? bottomRowBuilder,
|
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||||
Widget Function(BuildContext, Message)? deletedBottomRowBuilder,
|
Will be removed in the next major version.
|
||||||
|
''') Widget Function(BuildContext, Message)? usernameBuilder,
|
||||||
|
@Deprecated('''
|
||||||
|
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||||
|
Will be removed in the next major version.
|
||||||
|
''') BottomRowBuilder? bottomRowBuilder,
|
||||||
|
BottomRowBuilderWithDefaultWidget? bottomRowBuilderWithDefaultWidget,
|
||||||
|
@Deprecated('''
|
||||||
|
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||||
|
Will be removed in the next major version.
|
||||||
|
''') Widget Function(BuildContext, Message)? deletedBottomRowBuilder,
|
||||||
void Function(BuildContext, Message)? onMessageActions,
|
void Function(BuildContext, Message)? onMessageActions,
|
||||||
Message? message,
|
Message? message,
|
||||||
StreamMessageThemeData? messageTheme,
|
StreamMessageThemeData? messageTheme,
|
||||||
@@ -586,7 +626,31 @@ class StreamMessageWidget extends StatefulWidget {
|
|||||||
Size? imageAttachmentThumbnailSize,
|
Size? imageAttachmentThumbnailSize,
|
||||||
String? imageAttachmentThumbnailResizeType,
|
String? imageAttachmentThumbnailResizeType,
|
||||||
String? imageAttachmentThumbnailCropType,
|
String? imageAttachmentThumbnailCropType,
|
||||||
|
AttachmentActionsBuilder? attachmentActionsModalBuilder,
|
||||||
}) {
|
}) {
|
||||||
|
assert(
|
||||||
|
bottomRowBuilder == null || bottomRowBuilderWithDefaultWidget == null,
|
||||||
|
'You can only use one of the two bottom row builders',
|
||||||
|
);
|
||||||
|
|
||||||
|
var _bottomRowBuilderWithDefaultWidget =
|
||||||
|
bottomRowBuilderWithDefaultWidget ??
|
||||||
|
this.bottomRowBuilderWithDefaultWidget;
|
||||||
|
|
||||||
|
_bottomRowBuilderWithDefaultWidget ??= (context, message, defaultWidget) {
|
||||||
|
final _bottomRowBuilder = bottomRowBuilder ?? this.bottomRowBuilder;
|
||||||
|
if (_bottomRowBuilder != null) {
|
||||||
|
return _bottomRowBuilder(context, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultWidget.copyWith(
|
||||||
|
onThreadTap: onThreadTap ?? this.onThreadTap,
|
||||||
|
usernameBuilder: usernameBuilder ?? this.usernameBuilder,
|
||||||
|
deletedBottomRowBuilder:
|
||||||
|
deletedBottomRowBuilder ?? this.deletedBottomRowBuilder,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return StreamMessageWidget(
|
return StreamMessageWidget(
|
||||||
key: key ?? this.key,
|
key: key ?? this.key,
|
||||||
onMentionTap: onMentionTap ?? this.onMentionTap,
|
onMentionTap: onMentionTap ?? this.onMentionTap,
|
||||||
@@ -595,10 +659,7 @@ class StreamMessageWidget extends StatefulWidget {
|
|||||||
editMessageInputBuilder:
|
editMessageInputBuilder:
|
||||||
editMessageInputBuilder ?? this.editMessageInputBuilder,
|
editMessageInputBuilder ?? this.editMessageInputBuilder,
|
||||||
textBuilder: textBuilder ?? this.textBuilder,
|
textBuilder: textBuilder ?? this.textBuilder,
|
||||||
usernameBuilder: usernameBuilder ?? this.usernameBuilder,
|
bottomRowBuilderWithDefaultWidget: _bottomRowBuilderWithDefaultWidget,
|
||||||
bottomRowBuilder: bottomRowBuilder ?? this.bottomRowBuilder,
|
|
||||||
deletedBottomRowBuilder:
|
|
||||||
deletedBottomRowBuilder ?? this.deletedBottomRowBuilder,
|
|
||||||
onMessageActions: onMessageActions ?? this.onMessageActions,
|
onMessageActions: onMessageActions ?? this.onMessageActions,
|
||||||
message: message ?? this.message,
|
message: message ?? this.message,
|
||||||
messageTheme: messageTheme ?? this.messageTheme,
|
messageTheme: messageTheme ?? this.messageTheme,
|
||||||
@@ -652,6 +713,8 @@ class StreamMessageWidget extends StatefulWidget {
|
|||||||
this.imageAttachmentThumbnailResizeType,
|
this.imageAttachmentThumbnailResizeType,
|
||||||
imageAttachmentThumbnailCropType: imageAttachmentThumbnailCropType ??
|
imageAttachmentThumbnailCropType: imageAttachmentThumbnailCropType ??
|
||||||
this.imageAttachmentThumbnailCropType,
|
this.imageAttachmentThumbnailCropType,
|
||||||
|
attachmentActionsModalBuilder:
|
||||||
|
attachmentActionsModalBuilder ?? this.attachmentActionsModalBuilder,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -838,52 +901,69 @@ class _StreamMessageWidgetState extends State<StreamMessageWidget>
|
|||||||
? Alignment.centerRight
|
? Alignment.centerRight
|
||||||
: Alignment.centerLeft,
|
: Alignment.centerLeft,
|
||||||
widthFactor: widget.widthFactor,
|
widthFactor: widget.widthFactor,
|
||||||
child: MessageWidgetContent(
|
child: Builder(builder: (context) {
|
||||||
streamChatTheme: _streamChatTheme,
|
var _bottomRowBuilderWithDefaultWidget =
|
||||||
showUsername: showUsername,
|
widget.bottomRowBuilderWithDefaultWidget;
|
||||||
showTimeStamp: showTimeStamp,
|
|
||||||
showThreadReplyIndicator: showThreadReplyIndicator,
|
_bottomRowBuilderWithDefaultWidget ??=
|
||||||
showSendingIndicator: showSendingIndicator,
|
(context, message, defaultWidget) {
|
||||||
showInChannel: showInChannel,
|
final _bottomRowBuilder = widget.bottomRowBuilder;
|
||||||
isGiphy: isGiphy,
|
if (_bottomRowBuilder != null) {
|
||||||
isOnlyEmoji: isOnlyEmoji,
|
return _bottomRowBuilder(context, message);
|
||||||
hasUrlAttachments: hasUrlAttachments,
|
}
|
||||||
messageTheme: widget.messageTheme,
|
|
||||||
reverse: widget.reverse,
|
return defaultWidget.copyWith(
|
||||||
message: widget.message,
|
onThreadTap: widget.onThreadTap,
|
||||||
hasNonUrlAttachments: hasNonUrlAttachments,
|
usernameBuilder: widget.usernameBuilder,
|
||||||
shouldShowReactions: shouldShowReactions,
|
deletedBottomRowBuilder: widget.deletedBottomRowBuilder,
|
||||||
hasQuotedMessage: hasQuotedMessage,
|
);
|
||||||
textPadding: widget.textPadding,
|
};
|
||||||
attachmentBuilders: widget.attachmentBuilders,
|
|
||||||
attachmentPadding: widget.attachmentPadding,
|
return MessageWidgetContent(
|
||||||
avatarWidth: avatarWidth,
|
streamChatTheme: _streamChatTheme,
|
||||||
bottomRowPadding: bottomRowPadding,
|
showUsername: showUsername,
|
||||||
isFailedState: isFailedState,
|
showTimeStamp: showTimeStamp,
|
||||||
isPinned: isPinned,
|
showThreadReplyIndicator: showThreadReplyIndicator,
|
||||||
messageWidget: widget,
|
showSendingIndicator: showSendingIndicator,
|
||||||
showBottomRow: showBottomRow,
|
showInChannel: showInChannel,
|
||||||
showPinHighlight: widget.showPinHighlight,
|
isGiphy: isGiphy,
|
||||||
showReactionPickerIndicator:
|
isOnlyEmoji: isOnlyEmoji,
|
||||||
widget.showReactionPickerIndicator,
|
hasUrlAttachments: hasUrlAttachments,
|
||||||
showReactions: showReactions,
|
messageTheme: widget.messageTheme,
|
||||||
showUserAvatar: widget.showUserAvatar,
|
reverse: widget.reverse,
|
||||||
streamChat: _streamChat,
|
message: widget.message,
|
||||||
translateUserAvatar: widget.translateUserAvatar,
|
hasNonUrlAttachments: hasNonUrlAttachments,
|
||||||
deletedBottomRowBuilder: widget.deletedBottomRowBuilder,
|
shouldShowReactions: shouldShowReactions,
|
||||||
onThreadTap: widget.onThreadTap,
|
hasQuotedMessage: hasQuotedMessage,
|
||||||
shape: widget.shape,
|
textPadding: widget.textPadding,
|
||||||
borderSide: widget.borderSide,
|
attachmentBuilders: widget.attachmentBuilders,
|
||||||
borderRadiusGeometry: widget.borderRadiusGeometry,
|
attachmentPadding: widget.attachmentPadding,
|
||||||
textBuilder: widget.textBuilder,
|
avatarWidth: avatarWidth,
|
||||||
onLinkTap: widget.onLinkTap,
|
bottomRowPadding: bottomRowPadding,
|
||||||
onMentionTap: widget.onMentionTap,
|
isFailedState: isFailedState,
|
||||||
onQuotedMessageTap: widget.onQuotedMessageTap,
|
isPinned: isPinned,
|
||||||
bottomRowBuilder: widget.bottomRowBuilder,
|
messageWidget: widget,
|
||||||
onUserAvatarTap: widget.onUserAvatarTap,
|
showBottomRow: showBottomRow,
|
||||||
userAvatarBuilder: widget.userAvatarBuilder,
|
showPinHighlight: widget.showPinHighlight,
|
||||||
usernameBuilder: widget.usernameBuilder,
|
showReactionPickerIndicator:
|
||||||
),
|
widget.showReactionPickerIndicator,
|
||||||
|
showReactions: showReactions,
|
||||||
|
showUserAvatar: widget.showUserAvatar,
|
||||||
|
streamChat: _streamChat,
|
||||||
|
translateUserAvatar: widget.translateUserAvatar,
|
||||||
|
shape: widget.shape,
|
||||||
|
borderSide: widget.borderSide,
|
||||||
|
borderRadiusGeometry: widget.borderRadiusGeometry,
|
||||||
|
textBuilder: widget.textBuilder,
|
||||||
|
onLinkTap: widget.onLinkTap,
|
||||||
|
onMentionTap: widget.onMentionTap,
|
||||||
|
onQuotedMessageTap: widget.onQuotedMessageTap,
|
||||||
|
bottomRowBuilderWithDefaultWidget:
|
||||||
|
_bottomRowBuilderWithDefaultWidget,
|
||||||
|
onUserAvatarTap: widget.onUserAvatarTap,
|
||||||
|
userAvatarBuilder: widget.userAvatarBuilder,
|
||||||
|
);
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4,6 +4,18 @@ import 'package:stream_chat_flutter/src/message_widget/message_widget_content_co
|
|||||||
import 'package:stream_chat_flutter/src/message_widget/reactions/desktop_reactions_builder.dart';
|
import 'package:stream_chat_flutter/src/message_widget/reactions/desktop_reactions_builder.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
|
/// Signature for the builder function that will be called when the message
|
||||||
|
/// bottom row is built. Includes the [Message].
|
||||||
|
typedef BottomRowBuilder = Widget Function(BuildContext, Message);
|
||||||
|
|
||||||
|
/// Signature for the builder function that will be called when the message
|
||||||
|
/// bottom row is built. Includes the [Message] and the default [BottomRow].
|
||||||
|
typedef BottomRowBuilderWithDefaultWidget = Widget Function(
|
||||||
|
BuildContext,
|
||||||
|
Message,
|
||||||
|
BottomRow,
|
||||||
|
);
|
||||||
|
|
||||||
/// {@template messageWidgetContent}
|
/// {@template messageWidgetContent}
|
||||||
/// The main content of a [StreamMessageWidget].
|
/// The main content of a [StreamMessageWidget].
|
||||||
///
|
///
|
||||||
@@ -51,12 +63,28 @@ class MessageWidgetContent extends StatelessWidget {
|
|||||||
this.onMentionTap,
|
this.onMentionTap,
|
||||||
this.onLinkTap,
|
this.onLinkTap,
|
||||||
this.textBuilder,
|
this.textBuilder,
|
||||||
this.bottomRowBuilder,
|
@Deprecated('''
|
||||||
this.onThreadTap,
|
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||||
this.deletedBottomRowBuilder,
|
Will be removed in the next major version.
|
||||||
|
''') this.bottomRowBuilder,
|
||||||
|
this.bottomRowBuilderWithDefaultWidget,
|
||||||
|
@Deprecated('''
|
||||||
|
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||||
|
Will be removed in the next major version.
|
||||||
|
''') this.onThreadTap,
|
||||||
|
@Deprecated('''
|
||||||
|
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||||
|
Will be removed in the next major version.
|
||||||
|
''') this.deletedBottomRowBuilder,
|
||||||
this.userAvatarBuilder,
|
this.userAvatarBuilder,
|
||||||
this.usernameBuilder,
|
@Deprecated('''
|
||||||
});
|
Use [bottomRowBuilderWithDefaultWidget] instead.
|
||||||
|
Will be removed in the next major version.
|
||||||
|
''') this.usernameBuilder,
|
||||||
|
}) : assert(
|
||||||
|
bottomRowBuilder == null || bottomRowBuilderWithDefaultWidget == null,
|
||||||
|
'You can only use one of the two bottom row builders',
|
||||||
|
);
|
||||||
|
|
||||||
/// {@macro reverse}
|
/// {@macro reverse}
|
||||||
final bool reverse;
|
final bool reverse;
|
||||||
@@ -152,7 +180,10 @@ class MessageWidgetContent extends StatelessWidget {
|
|||||||
final double bottomRowPadding;
|
final double bottomRowPadding;
|
||||||
|
|
||||||
/// {@macro bottomRowBuilder}
|
/// {@macro bottomRowBuilder}
|
||||||
final Widget Function(BuildContext, Message)? bottomRowBuilder;
|
final BottomRowBuilder? bottomRowBuilder;
|
||||||
|
|
||||||
|
/// {@macro bottomRowBuilderWithDefaultWidget}
|
||||||
|
final BottomRowBuilderWithDefaultWidget? bottomRowBuilderWithDefaultWidget;
|
||||||
|
|
||||||
/// {@macro showInChannelIndicator}
|
/// {@macro showInChannelIndicator}
|
||||||
final bool showInChannel;
|
final bool showInChannel;
|
||||||
@@ -207,30 +238,7 @@ class MessageWidgetContent extends StatelessWidget {
|
|||||||
right: reverse ? bottomRowPadding : 0,
|
right: reverse ? bottomRowPadding : 0,
|
||||||
bottom: isPinned && showPinHighlight ? 6.0 : 0.0,
|
bottom: isPinned && showPinHighlight ? 6.0 : 0.0,
|
||||||
),
|
),
|
||||||
child: bottomRowBuilder?.call(
|
child: _buildBottomRow(context),
|
||||||
context,
|
|
||||||
message,
|
|
||||||
) ??
|
|
||||||
BottomRow(
|
|
||||||
message: message,
|
|
||||||
reverse: reverse,
|
|
||||||
messageTheme: messageTheme,
|
|
||||||
hasUrlAttachments: hasUrlAttachments,
|
|
||||||
isOnlyEmoji: isOnlyEmoji,
|
|
||||||
isDeleted: message.isDeleted,
|
|
||||||
isGiphy: isGiphy,
|
|
||||||
showInChannel: showInChannel,
|
|
||||||
showSendingIndicator: showSendingIndicator,
|
|
||||||
showThreadReplyIndicator: showThreadReplyIndicator,
|
|
||||||
showTimeStamp: showTimeStamp,
|
|
||||||
showUsername: showUsername,
|
|
||||||
streamChatTheme: streamChatTheme,
|
|
||||||
onThreadTap: onThreadTap,
|
|
||||||
deletedBottomRowBuilder: deletedBottomRowBuilder,
|
|
||||||
streamChat: streamChat,
|
|
||||||
hasNonUrlAttachments: hasNonUrlAttachments,
|
|
||||||
usernameBuilder: usernameBuilder,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
@@ -457,4 +465,39 @@ class MessageWidgetContent extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildBottomRow(BuildContext context) {
|
||||||
|
final defaultWidget = BottomRow(
|
||||||
|
message: message,
|
||||||
|
reverse: reverse,
|
||||||
|
messageTheme: messageTheme,
|
||||||
|
hasUrlAttachments: hasUrlAttachments,
|
||||||
|
isOnlyEmoji: isOnlyEmoji,
|
||||||
|
isDeleted: message.isDeleted,
|
||||||
|
isGiphy: isGiphy,
|
||||||
|
showInChannel: showInChannel,
|
||||||
|
showSendingIndicator: showSendingIndicator,
|
||||||
|
showThreadReplyIndicator: showThreadReplyIndicator,
|
||||||
|
showTimeStamp: showTimeStamp,
|
||||||
|
showUsername: showUsername,
|
||||||
|
streamChatTheme: streamChatTheme,
|
||||||
|
onThreadTap: onThreadTap,
|
||||||
|
deletedBottomRowBuilder: deletedBottomRowBuilder,
|
||||||
|
streamChat: streamChat,
|
||||||
|
hasNonUrlAttachments: hasNonUrlAttachments,
|
||||||
|
usernameBuilder: usernameBuilder,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (bottomRowBuilder != null) {
|
||||||
|
return bottomRowBuilder!(context, message);
|
||||||
|
} else if (bottomRowBuilderWithDefaultWidget != null) {
|
||||||
|
return bottomRowBuilderWithDefaultWidget!(
|
||||||
|
context,
|
||||||
|
message,
|
||||||
|
defaultWidget,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultWidget;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1088,7 +1088,12 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
key: key,
|
key: key,
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
color: color,
|
colorFilter: color != null
|
||||||
|
? ColorFilter.mode(
|
||||||
|
color!,
|
||||||
|
BlendMode.srcIn,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,8 +149,8 @@ class StreamThreadHeader extends StatelessWidget
|
|||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
return AppBar(
|
return AppBar(
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
toolbarTextStyle: theme.textTheme.bodyText2,
|
toolbarTextStyle: theme.textTheme.bodyMedium,
|
||||||
titleTextStyle: theme.textTheme.headline6,
|
titleTextStyle: theme.textTheme.titleLarge,
|
||||||
systemOverlayStyle: theme.brightness == Brightness.dark
|
systemOverlayStyle: theme.brightness == Brightness.dark
|
||||||
? SystemUiOverlayStyle.light
|
? SystemUiOverlayStyle.light
|
||||||
: SystemUiOverlayStyle.dark,
|
: SystemUiOverlayStyle.dark,
|
||||||
|
|||||||
+1
-1
@@ -409,7 +409,7 @@ class StreamChannelListErrorWidget extends StatelessWidget {
|
|||||||
TextSpan(text: context.translations.loadingChannelsError),
|
TextSpan(text: context.translations.loadingChannelsError),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
style: Theme.of(context).textTheme.headline6,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
|
|||||||
+3
-3
@@ -366,7 +366,7 @@ class StreamPhotoGallery extends StatelessWidget {
|
|||||||
color: chatThemeData.colorTheme.disabled,
|
color: chatThemeData.colorTheme.disabled,
|
||||||
),
|
),
|
||||||
emptyTitle: Text(
|
emptyTitle: Text(
|
||||||
context.translations.noUsersLabel,
|
context.translations.noPhotoOrVideoLabel,
|
||||||
style: chatThemeData.textTheme.headline,
|
style: chatThemeData.textTheme.headline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -377,7 +377,7 @@ class StreamPhotoGallery extends StatelessWidget {
|
|||||||
return StreamScrollViewLoadMoreError.grid(
|
return StreamScrollViewLoadMoreError.grid(
|
||||||
onTap: controller.retry,
|
onTap: controller.retry,
|
||||||
error: Text(
|
error: Text(
|
||||||
context.translations.loadingUsersError,
|
context.translations.genericErrorText,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -400,7 +400,7 @@ class StreamPhotoGallery extends StatelessWidget {
|
|||||||
return errorBuilder?.call(context, error) ??
|
return errorBuilder?.call(context, error) ??
|
||||||
Center(
|
Center(
|
||||||
child: StreamScrollViewErrorWidget(
|
child: StreamScrollViewErrorWidget(
|
||||||
errorTitle: Text(context.translations.loadingUsersError),
|
errorTitle: Text(context.translations.genericErrorText),
|
||||||
onRetryPressed: controller.refresh,
|
onRetryPressed: controller.refresh,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
this.avatarTheme,
|
this.avatarTheme,
|
||||||
this.createdAtStyle,
|
this.createdAtStyle,
|
||||||
this.linkBackgroundColor,
|
this.linkBackgroundColor,
|
||||||
|
this.urlAttachmentTitleMaxLine,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Text style for message text
|
/// Text style for message text
|
||||||
@@ -59,6 +60,9 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
/// Background color for messages with url attachments.
|
/// Background color for messages with url attachments.
|
||||||
final Color? linkBackgroundColor;
|
final Color? linkBackgroundColor;
|
||||||
|
|
||||||
|
/// Max number of lines in Url link title
|
||||||
|
final int? urlAttachmentTitleMaxLine;
|
||||||
|
|
||||||
/// Copy with a theme
|
/// Copy with a theme
|
||||||
StreamMessageThemeData copyWith({
|
StreamMessageThemeData copyWith({
|
||||||
TextStyle? messageTextStyle,
|
TextStyle? messageTextStyle,
|
||||||
@@ -73,6 +77,7 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
Color? reactionsBorderColor,
|
Color? reactionsBorderColor,
|
||||||
Color? reactionsMaskColor,
|
Color? reactionsMaskColor,
|
||||||
Color? linkBackgroundColor,
|
Color? linkBackgroundColor,
|
||||||
|
int? urlAttachmentTitleMaxLine,
|
||||||
}) {
|
}) {
|
||||||
return StreamMessageThemeData(
|
return StreamMessageThemeData(
|
||||||
messageTextStyle: messageTextStyle ?? this.messageTextStyle,
|
messageTextStyle: messageTextStyle ?? this.messageTextStyle,
|
||||||
@@ -89,6 +94,8 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
reactionsBorderColor: reactionsBorderColor ?? this.reactionsBorderColor,
|
reactionsBorderColor: reactionsBorderColor ?? this.reactionsBorderColor,
|
||||||
reactionsMaskColor: reactionsMaskColor ?? this.reactionsMaskColor,
|
reactionsMaskColor: reactionsMaskColor ?? this.reactionsMaskColor,
|
||||||
linkBackgroundColor: linkBackgroundColor ?? this.linkBackgroundColor,
|
linkBackgroundColor: linkBackgroundColor ?? this.linkBackgroundColor,
|
||||||
|
urlAttachmentTitleMaxLine:
|
||||||
|
urlAttachmentTitleMaxLine ?? this.urlAttachmentTitleMaxLine,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +155,7 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
reactionsBorderColor: other.reactionsBorderColor,
|
reactionsBorderColor: other.reactionsBorderColor,
|
||||||
reactionsMaskColor: other.reactionsMaskColor,
|
reactionsMaskColor: other.reactionsMaskColor,
|
||||||
linkBackgroundColor: other.linkBackgroundColor,
|
linkBackgroundColor: other.linkBackgroundColor,
|
||||||
|
urlAttachmentTitleMaxLine: other.urlAttachmentTitleMaxLine,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +175,8 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
reactionsBorderColor == other.reactionsBorderColor &&
|
reactionsBorderColor == other.reactionsBorderColor &&
|
||||||
reactionsMaskColor == other.reactionsMaskColor &&
|
reactionsMaskColor == other.reactionsMaskColor &&
|
||||||
avatarTheme == other.avatarTheme &&
|
avatarTheme == other.avatarTheme &&
|
||||||
linkBackgroundColor == other.linkBackgroundColor;
|
linkBackgroundColor == other.linkBackgroundColor &&
|
||||||
|
urlAttachmentTitleMaxLine == other.urlAttachmentTitleMaxLine;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
@@ -182,7 +191,8 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
reactionsBorderColor.hashCode ^
|
reactionsBorderColor.hashCode ^
|
||||||
reactionsMaskColor.hashCode ^
|
reactionsMaskColor.hashCode ^
|
||||||
avatarTheme.hashCode ^
|
avatarTheme.hashCode ^
|
||||||
linkBackgroundColor.hashCode;
|
linkBackgroundColor.hashCode ^
|
||||||
|
urlAttachmentTitleMaxLine.hashCode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
@@ -199,6 +209,10 @@ class StreamMessageThemeData with Diagnosticable {
|
|||||||
..add(ColorProperty('reactionsBackgroundColor', reactionsBackgroundColor))
|
..add(ColorProperty('reactionsBackgroundColor', reactionsBackgroundColor))
|
||||||
..add(ColorProperty('reactionsBorderColor', reactionsBorderColor))
|
..add(ColorProperty('reactionsBorderColor', reactionsBorderColor))
|
||||||
..add(ColorProperty('reactionsMaskColor', reactionsMaskColor))
|
..add(ColorProperty('reactionsMaskColor', reactionsMaskColor))
|
||||||
..add(ColorProperty('linkBackgroundColor', linkBackgroundColor));
|
..add(ColorProperty('linkBackgroundColor', linkBackgroundColor))
|
||||||
|
..add(DiagnosticsProperty(
|
||||||
|
'urlAttachmentTitleMaxLine',
|
||||||
|
urlAttachmentTitleMaxLine,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ class _IVideoService {
|
|||||||
///
|
///
|
||||||
/// Thumbnails are not supported on Web at this time.
|
/// Thumbnails are not supported on Web at this time.
|
||||||
///
|
///
|
||||||
|
/// If no [video] path is supplied, or if a thumbnail cannot be generated,
|
||||||
|
/// returns [generatePlaceholderThumbnail]. A stock placeholder image.
|
||||||
|
///
|
||||||
/// For desktop, you can specify the position of the video to generate
|
/// For desktop, you can specify the position of the video to generate
|
||||||
/// the thumbnail.
|
/// the thumbnail.
|
||||||
///
|
///
|
||||||
@@ -29,14 +32,14 @@ class _IVideoService {
|
|||||||
/// creates lower quality of the thumbnail image, but it gets ignored for
|
/// creates lower quality of the thumbnail image, but it gets ignored for
|
||||||
/// PNG format.
|
/// PNG format.
|
||||||
Future<Uint8List?> generateVideoThumbnail({
|
Future<Uint8List?> generateVideoThumbnail({
|
||||||
required String video,
|
String? video,
|
||||||
ImageFormat imageFormat = ImageFormat.PNG,
|
ImageFormat imageFormat = ImageFormat.PNG,
|
||||||
int maxHeight = 0,
|
int maxHeight = 0,
|
||||||
int maxWidth = 0,
|
int maxWidth = 0,
|
||||||
int timeMs = 0,
|
int timeMs = 0,
|
||||||
int quality = 10,
|
int quality = 10,
|
||||||
}) async {
|
}) async {
|
||||||
if (kIsWeb) {
|
if (kIsWeb || video == null) {
|
||||||
final placeholder = await generatePlaceholderThumbnail();
|
final placeholder = await generatePlaceholderThumbnail();
|
||||||
return placeholder;
|
return placeholder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,13 +8,23 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
|||||||
import 'package:video_thumbnail/video_thumbnail.dart';
|
import 'package:video_thumbnail/video_thumbnail.dart';
|
||||||
|
|
||||||
/// {@template streamVideoThumbnailImage}
|
/// {@template streamVideoThumbnailImage}
|
||||||
/// Displays a video thumbnail for video attachments in a message.
|
/// Displays a video thumbnail for video attachments.
|
||||||
|
///
|
||||||
|
/// [thumbUrl] is used if provided.
|
||||||
|
///
|
||||||
|
/// Else [video] (path to local or remote video) is used to generate
|
||||||
|
/// a thumbnail from the video asset.
|
||||||
|
///
|
||||||
|
/// WARNING! a local path does not work on web.
|
||||||
|
///
|
||||||
|
/// If both [thumbUrl] and [video] are null, or if a thumbnail can't be
|
||||||
|
/// generated, a stock default image will be used.
|
||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
class StreamVideoThumbnailImage extends StatefulWidget {
|
class StreamVideoThumbnailImage extends StatefulWidget {
|
||||||
/// {@macro streamVideoThumbnailImage}
|
/// {@macro streamVideoThumbnailImage}
|
||||||
const StreamVideoThumbnailImage({
|
const StreamVideoThumbnailImage({
|
||||||
super.key,
|
super.key,
|
||||||
required this.video,
|
this.video,
|
||||||
this.thumbUrl,
|
this.thumbUrl,
|
||||||
this.constraints,
|
this.constraints,
|
||||||
this.fit = BoxFit.cover,
|
this.fit = BoxFit.cover,
|
||||||
@@ -24,7 +34,7 @@ class StreamVideoThumbnailImage extends StatefulWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/// Video path or url
|
/// Video path or url
|
||||||
final String video;
|
final String? video;
|
||||||
|
|
||||||
/// Video thumbnail url
|
/// Video thumbnail url
|
||||||
final String? thumbUrl;
|
final String? thumbUrl;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:stream_chat_flutter/src/video/vlc/vlc_stub.dart'
|
import 'package:stream_chat_flutter/src/video/vlc/vlc_stub.dart'
|
||||||
if (dart.library.io) 'vlc_manager_desktop.dart'
|
if (dart.library.io) 'vlc_manager_desktop.dart'
|
||||||
if (dar.library.html) 'vlc_manager_web.dart';
|
if (dart.library.html) 'vlc_manager_web.dart';
|
||||||
|
|
||||||
/// {@template vlcManager}
|
/// {@template vlcManager}
|
||||||
/// An abstract class for the purpose of ensuring Flutter applications that
|
/// An abstract class for the purpose of ensuring Flutter applications that
|
||||||
|
|||||||
@@ -83,8 +83,6 @@ export 'src/scroll_view/photo_gallery/stream_photo_gallery_tile.dart';
|
|||||||
export 'src/scroll_view/stream_scroll_view_empty_widget.dart';
|
export 'src/scroll_view/stream_scroll_view_empty_widget.dart';
|
||||||
export 'src/scroll_view/stream_scroll_view_indexed_widget_builder.dart';
|
export 'src/scroll_view/stream_scroll_view_indexed_widget_builder.dart';
|
||||||
export 'src/scroll_view/user_scroll_view/stream_user_grid_tile.dart';
|
export 'src/scroll_view/user_scroll_view/stream_user_grid_tile.dart';
|
||||||
export 'src/scroll_view/user_scroll_view/stream_user_grid_tile.dart';
|
|
||||||
export 'src/scroll_view/user_scroll_view/stream_user_grid_view.dart';
|
|
||||||
export 'src/scroll_view/user_scroll_view/stream_user_grid_view.dart';
|
export 'src/scroll_view/user_scroll_view/stream_user_grid_view.dart';
|
||||||
export 'src/scroll_view/user_scroll_view/stream_user_list_tile.dart';
|
export 'src/scroll_view/user_scroll_view/stream_user_list_tile.dart';
|
||||||
export 'src/scroll_view/user_scroll_view/stream_user_list_view.dart';
|
export 'src/scroll_view/user_scroll_view/stream_user_list_view.dart';
|
||||||
|
|||||||
@@ -1,50 +1,48 @@
|
|||||||
name: stream_chat_flutter
|
name: stream_chat_flutter
|
||||||
homepage: https://github.com/GetStream/stream-chat-flutter
|
homepage: https://github.com/GetStream/stream-chat-flutter
|
||||||
description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter.
|
description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter.
|
||||||
version: 5.1.0
|
version: 5.3.0
|
||||||
repository: https://github.com/GetStream/stream-chat-flutter
|
repository: https://github.com/GetStream/stream-chat-flutter
|
||||||
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.17.0 <3.0.0'
|
sdk: ">=2.17.0 <3.0.0"
|
||||||
flutter: ">=1.17.0"
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
cached_network_image: ^3.0.0
|
cached_network_image: ^3.0.0
|
||||||
chewie: ^1.3.4
|
chewie: ^1.3.4
|
||||||
collection: ^1.15.0
|
collection: ^1.17.0
|
||||||
contextmenu: ^3.0.0
|
contextmenu: ^3.0.0
|
||||||
dart_vlc: ^0.3.0
|
dart_vlc: ^0.4.0
|
||||||
desktop_drop: ^0.4.0
|
desktop_drop: ^0.4.0
|
||||||
diacritic: ^0.1.3
|
diacritic: ^0.1.3
|
||||||
dio: ^4.0.6
|
|
||||||
ezanimation: ^0.6.0
|
ezanimation: ^0.6.0
|
||||||
file_picker: ^4.1.3
|
file_picker: ^5.2.4
|
||||||
file_selector: ^0.9.0
|
file_selector: ^0.9.0
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_markdown: ^0.6.1
|
flutter_markdown: ^0.6.1
|
||||||
flutter_portal: ^1.0.0
|
flutter_portal: ^1.0.0
|
||||||
flutter_svg: ^1.0.1
|
flutter_svg: ^2.0.4
|
||||||
http: ^0.13.4
|
|
||||||
http_parser: ^4.0.0
|
http_parser: ^4.0.0
|
||||||
image_gallery_saver: ^1.7.0
|
image_gallery_saver: ^1.7.1
|
||||||
image_picker: ^0.8.2
|
image_picker: ^0.8.2
|
||||||
jiffy: ^5.0.0
|
jiffy: ^5.0.0
|
||||||
lottie: ^2.0.0
|
lottie: ^2.0.0
|
||||||
meta: ^1.3.0
|
meta: ^1.8.0
|
||||||
path_provider: ^2.0.9
|
path_provider: ^2.0.9
|
||||||
photo_manager: ^2.0.1
|
photo_manager: ^2.5.2
|
||||||
photo_view: ^0.14.0
|
photo_view: ^0.14.0
|
||||||
rxdart: ^0.27.0
|
rxdart: ^0.27.0
|
||||||
share_plus: ^4.5.0
|
share_plus: ^6.3.0
|
||||||
shimmer: ^2.0.0
|
shimmer: ^2.0.0
|
||||||
stream_chat_flutter_core: ^5.1.0
|
stream_chat_flutter_core: ^5.3.0
|
||||||
synchronized: ^3.0.0
|
synchronized: ^3.0.0
|
||||||
thumblr: ^0.0.4
|
thumblr: ^0.0.4
|
||||||
url_launcher: ^6.1.0
|
url_launcher: ^6.1.0
|
||||||
video_player: ^2.4.5
|
video_player: ^2.4.5
|
||||||
video_player_macos: ^1.0.6
|
video_player_macos: ^2.0.1
|
||||||
video_thumbnail: ^0.5.0
|
video_thumbnail: ^0.5.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
@@ -56,9 +54,9 @@ flutter:
|
|||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
dart_code_metrics: ^4.4.0
|
dart_code_metrics: ^5.7.0
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
golden_toolkit: ^0.13.0
|
golden_toolkit: ^0.15.0
|
||||||
mocktail: ^0.3.0
|
mocktail: ^0.3.0
|
||||||
path: ^1.8.0
|
path: ^1.8.2
|
||||||
|
|||||||
+4
-4
@@ -163,7 +163,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'tapping on reply should pop',
|
'tapping on reply should invoke callback',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
final client = MockClient();
|
final client = MockClient();
|
||||||
final clientState = MockClientState();
|
final clientState = MockClientState();
|
||||||
@@ -174,7 +174,7 @@ void main() {
|
|||||||
final themeData = ThemeData();
|
final themeData = ThemeData();
|
||||||
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
final streamTheme = StreamChatThemeData.fromTheme(themeData);
|
||||||
|
|
||||||
final mockObserver = MockNavigatorObserver();
|
final mockCallback = MockVoidCallback();
|
||||||
|
|
||||||
final attachment = Attachment(
|
final attachment = Attachment(
|
||||||
type: 'image',
|
type: 'image',
|
||||||
@@ -192,7 +192,6 @@ void main() {
|
|||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
theme: themeData,
|
theme: themeData,
|
||||||
navigatorObservers: [mockObserver],
|
|
||||||
home: StreamChat(
|
home: StreamChat(
|
||||||
streamChatThemeData: streamTheme,
|
streamChatThemeData: streamTheme,
|
||||||
client: client,
|
client: client,
|
||||||
@@ -200,13 +199,14 @@ void main() {
|
|||||||
child: AttachmentActionsModal(
|
child: AttachmentActionsModal(
|
||||||
message: message,
|
message: message,
|
||||||
attachment: attachment,
|
attachment: attachment,
|
||||||
|
onReply: mockCallback,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
await tester.tap(find.text('Reply'));
|
await tester.tap(find.text('Reply'));
|
||||||
verify(() => mockObserver.didPop(any(), any()));
|
verify(mockCallback.call);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
## Upcomming
|
## Upcoming
|
||||||
|
|
||||||
|
- Updated dependencies to resolvable versions.
|
||||||
|
|
||||||
|
## 5.3.0
|
||||||
|
|
||||||
|
- Updated `stream_chat` dependency to [`5.3.0`](https://pub.dev/packages/stream_chat/changelog).
|
||||||
|
|
||||||
|
## 5.2.0
|
||||||
|
|
||||||
🔄 Changed
|
🔄 Changed
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ apply plugin: 'com.android.application'
|
|||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 33
|
||||||
|
|
||||||
lintOptions {
|
lintOptions {
|
||||||
disable 'InvalidPackage'
|
disable 'InvalidPackage'
|
||||||
@@ -35,7 +35,7 @@ android {
|
|||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId "example.example"
|
applicationId "example.example"
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 33
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,15 @@
|
|||||||
FlutterApplication and put your custom class here. -->
|
FlutterApplication and put your custom class here. -->
|
||||||
<application
|
<application
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:label="example"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:label="example">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:exported="true"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
|
||||||
android:hardwareAccelerated="true"
|
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
the Android process has started. This theme is visible to the user
|
the Android process has started. This theme is visible to the user
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
|
ext.kotlin_version = '1.7.21'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.5.0'
|
classpath 'com.android.tools.build:gradle:7.3.1'
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ version: 1.0.0+1
|
|||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.17.0 <3.0.0'
|
sdk: '>=2.17.0 <3.0.0'
|
||||||
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
@@ -27,6 +28,7 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
stream_chat_flutter_core: ^5.1.0
|
stream_chat_flutter_core: ^5.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user