@amityco/social-plus-vise 0.14.28 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +6 -11
  3. package/dist/tools/ast.js +153 -28
  4. package/dist/tools/docs.js +48 -0
  5. package/dist/tools/sdkFacts.js +45 -1
  6. package/docs-cache/README.md +34 -0
  7. package/docs-cache/social-plus-sdk/core-concepts/foundation/logging.mdx +236 -0
  8. package/docs-cache/social-plus-sdk/core-concepts/realtime-communication/live-objects-collections/android.mdx +262 -0
  9. package/docs-cache/social-plus-sdk/core-concepts/realtime-communication/live-objects-collections/flutter.mdx +195 -0
  10. package/docs-cache/social-plus-sdk/core-concepts/realtime-communication/live-objects-collections/ios.mdx +452 -0
  11. package/docs-cache/social-plus-sdk/core-concepts/realtime-communication/live-objects-collections/overview.mdx +133 -0
  12. package/docs-cache/social-plus-sdk/core-concepts/realtime-communication/live-objects-collections/typescript.mdx +264 -0
  13. package/docs-cache/social-plus-sdk/core-concepts/realtime-communication/push-notifications/register-and-unregister-push-notifications-on-a-device.mdx +191 -0
  14. package/docs-cache/social-plus-sdk/core-concepts/realtime-communication/push-notifications/settings/overview.mdx +43 -0
  15. package/docs-cache/social-plus-sdk/core-concepts/realtime-communication/push-notifications/setup/android-setup.mdx +360 -0
  16. package/docs-cache/social-plus-sdk/core-concepts/realtime-communication/push-notifications/setup/flutter-setup.mdx +457 -0
  17. package/docs-cache/social-plus-sdk/core-concepts/realtime-communication/push-notifications/setup/ios-setup.mdx +423 -0
  18. package/docs-cache/social-plus-sdk/core-concepts/realtime-communication/push-notifications/setup/react-native-setup.mdx +384 -0
  19. package/docs-cache/social-plus-sdk/core-concepts/realtime-communication/realtime-events/overview.mdx +94 -0
  20. package/docs-cache/social-plus-sdk/getting-started/authentication.mdx +808 -0
  21. package/docs-cache/social-plus-sdk/getting-started/platform-setup/mobile/android-quick-start.mdx +304 -0
  22. package/docs-cache/social-plus-sdk/getting-started/platform-setup/mobile/flutter-quick-start.mdx +121 -0
  23. package/docs-cache/social-plus-sdk/getting-started/platform-setup/mobile/ios-quick-start.mdx +225 -0
  24. package/docs-cache/social-plus-sdk/getting-started/platform-setup/web/web-quick-start.mdx +99 -0
  25. package/docs-cache/social-plus-sdk/social/communities-spaces/organization/community-invitation.mdx +459 -0
  26. package/docs-cache/social-plus-sdk/social/communities-spaces/organization/join-leave-community.mdx +449 -0
  27. package/docs-cache/social-plus-sdk/social/communities-spaces/organization/query-community-members.mdx +376 -0
  28. package/docs-cache/social-plus-sdk/social/content-management/posts/creation/text-post.mdx +318 -0
  29. package/docs-cache/social-plus-sdk/social/discovery-engagement/notifications/notification-tray-status.mdx +399 -0
  30. package/docs-cache/social-plus-sdk/social/user-relationship/blocking/block-unblock-user.mdx +166 -0
  31. package/docs-cache/social-plus-sdk/social/user-relationship/following/get-follower-following-list.mdx +339 -0
  32. package/package.json +10 -3
  33. package/scripts/dart-model-extractor/bin/extract_models.dart +169 -0
  34. package/scripts/dart-model-extractor/pubspec.lock +149 -0
  35. package/scripts/dart-model-extractor/pubspec.yaml +16 -0
  36. package/scripts/extract-sdk-models.mjs +353 -12
  37. package/scripts/import-sdk-surface.mjs +10 -19
  38. package/sdk-surface/manifest.json +15 -15
  39. package/sdk-surface/models.android.json +1 -1
  40. package/sdk-surface/models.flutter.json +465 -465
  41. package/sdk-surface/models.ios.json +188 -188
  42. package/sdk-surface/models.typescript.json +1 -1
  43. package/skills/social-plus-vise/SKILL.md +14 -0
@@ -0,0 +1,236 @@
1
+ # Logging & Errors
2
+
3
+ > Monitor network activity and handle errors in the social.plus SDK using observeNetworkActivities().
4
+
5
+ # Logging
6
+
7
+ ## Network Activity Monitoring
8
+
9
+ Use `observeNetworkActivities()` to inspect all HTTP requests and responses in your social.plus SDK integration during development.
10
+
11
+ **Best Practice**: Enable network logging during development to troubleshoot issues, but disable it in production to avoid performance overhead.
12
+
13
+
14
+ The `observeNetworkActivities()` function provides real-time insights into all HTTP requests and responses within the SDK, enabling effective debugging and performance analysis.
15
+
16
+ ### Key Features
17
+
18
+ - **Request/Response Logging**: Monitor all API calls with detailed information
19
+ - **Performance Metrics**: Track request timing and response sizes
20
+ - **Error Debugging**: Identify network-related issues quickly
21
+ - **Development Tool**: Essential for troubleshooting SDK integration
22
+
23
+ ```swift iOS
24
+ /// Observe ongoing network activities in sdk
25
+ client.observeNetworkActivities { request, response, data in
26
+
27
+ // request: URLRequest // Request
28
+ // response: HTTPURLResponse // HTTP Protocol response
29
+ // data: Data // Payload returned from server
30
+
31
+ }
32
+ ```
33
+
34
+ ```kotlin Android
35
+ /// Observe ongoing network activities in sdk
36
+ AmityCoreClient.observeNetworkActivities()
37
+ .doOnNext { activity ->
38
+ (activity as? AmityNetworkActivity.HTTP)?.run {
39
+ val response: Response = activity.response
40
+ val request = response.request
41
+ val processedText =
42
+ "Request: ${request.method} ${request.url}\n" +
43
+ "(${DateTime(response.sentRequestAtMillis)})\n" +
44
+ "Response: ${response.code} ${response.message}\n" +
45
+ "(${DateTime(response.receivedResponseAtMillis)})\n"
46
+ print(processedText)
47
+ }
48
+ }
49
+ .doOnError {
50
+ // Exception
51
+ }
52
+ .subscribe()
53
+ ```
54
+
55
+ ```typescript TypeScript
56
+ import { Client } from '@amityco/ts-sdk';
57
+
58
+ /// Observe ongoing network activities in sdk
59
+ Client.onNetworkActivities((request, response) => {
60
+ console.log(request);
61
+ console.log(response);
62
+ });
63
+ ```
64
+
65
+ ## Troubleshooting
66
+
67
+ ### Common Issues
68
+
69
+ Network logging stores request/response data in memory. Disable logging in production or implement log rotation for long-running applications.
70
+
71
+ Logging adds overhead to network operations. Use conditional compilation flags to disable in release builds.
72
+
73
+ social.plus apps can generate many API calls. Implement filtering to log only specific endpoints or error conditions.
74
+
75
+
76
+ # Error Handling
77
+
78
+ Effective error handling is crucial for building reliable social applications. social.plus SDK provides structured error information to help you handle different scenarios gracefully.
79
+
80
+ ## Error Types
81
+
82
+ social.plus errors are categorized into two main types:
83
+
84
+ Issues on the social.plus backend (400xxx, 500xxx codes)
85
+
86
+ Issues in the SDK or client environment (800xxx codes)
87
+
88
+ ## Common Server Errors
89
+
90
+ | Code | Error | Description |
91
+ |------|-------|-------------|
92
+ | 400100 | `UnauthorizedError` | User not authenticated or invalid token |
93
+ | 400101 | `TokenExpired` | Authentication token has expired |
94
+
95
+ | Code | Error | Description |
96
+ |------|-------|-------------|
97
+ | 400301 | `PermissionDenied` | User lacks permission for the action |
98
+ | 400302 | `UserIsMuted` | Muted user attempting to send message |
99
+ | 400304 | `UserIsBanned` | Banned user accessing restricted content |
100
+ | 400312 | `GlobalBanError` | Globally banned user performing any action |
101
+
102
+ | Code | Error | Description |
103
+ |------|-------|-------------|
104
+ | 400307 | `MaxRepetitionExceed` | Too many messages with blocked content |
105
+ | 400308 | `BanWordFound` | Message contains blocked words |
106
+ | 400309 | `LinkNotAllowed` | Message contains non-whitelisted links |
107
+ | 400314 | `UnsafeContent` | Content flagged by AI moderation |
108
+
109
+ | Code | Error | Description |
110
+ |------|-------|-------------|
111
+ | 400000 | `BadRequestError` | Invalid request parameters |
112
+ | 400315 | `DuplicateEntryError` | Duplicate display name or identifier |
113
+ | 400400 | `ItemNotFound` | Requested resource not found |
114
+ | 400900 | `Conflict` | Conflicting data operation |
115
+
116
+ | Code | Error | Description |
117
+ |------|-------|-------------|
118
+ | 500000 | `BusinessError` | Internal system error |
119
+
120
+ ## Client Errors
121
+
122
+ | Code | Error | Description |
123
+ |------|-------|-------------|
124
+ | 800000 | `Unknown` | Uncategorized client-side error |
125
+ | 800110 | `InvalidParameter` | Invalid parameter data type |
126
+ | 800210 | `ConnectionError` | Network connectivity issues |
127
+
128
+ ## Error Handling Implementation
129
+
130
+ ### Basic Error Parsing
131
+ ```swift iOS
132
+ func parseError(_ error: Error) {
133
+ let sdkError = error as NSError
134
+ guard let amityError = AmityErrorCode(rawValue: sdkError.code) else {
135
+ // Unknown error occurred. Please report this error code to Amity
136
+ return
137
+ }
138
+
139
+ // ...
140
+ // Process AmityError here..
141
+ }
142
+ ```
143
+
144
+ ```kotlin Android
145
+ fun parseAmityError(exception: Exception) {
146
+ if (AmityError.from(exception) == AmityError.CHANNEL_IS_MUTED) {
147
+ // Couldn't send a message to this channel because this channel is muted.
148
+ }
149
+ }
150
+ ```
151
+
152
+
153
+ ### Global Ban Handling
154
+
155
+ A global ban error means that the user is banned from the system resulting in the inability to have a connection with the system. If the user already has a session, the session will be revoked, and will be unable to create a new session.
156
+
157
+ ```swift iOS
158
+ var client: AmityClient?
159
+ client.clientErrorDelegate = self // set yourself as the delegate
160
+
161
+ ...
162
+
163
+ // Implement this delegate method which gets called when error occurs
164
+ func didReceiveAsyncError(_ error: Error) {
165
+ let error = error as NSError
166
+ guard let amityError = AmityErrorCode(rawValue: error.code) else {
167
+ assertionFailure("unknown error \(error.code), please report this code to Amity")
168
+ return
169
+ }
170
+
171
+ if amityError == .globalBan {
172
+ // Handle global ban event here
173
+ }
174
+ }
175
+ }
176
+ ```
177
+
178
+ ```kotlin Android
179
+ //error handling upon login
180
+ fun login() {
181
+ AmityCoreClient.login(userId = "userId")
182
+ .build()
183
+ .submit()
184
+ .doOnError {
185
+ if (AmityError.from(it) == AmityError.USER_IS_GLOBAL_BANNED) {
186
+ // handle the case the user is globally banned
187
+ }
188
+ }
189
+ .subscribe()
190
+ }
191
+
192
+ //error handling thru real-time events
193
+ fun subscribeGlobalBan() {
194
+ // Subscription can be done at Application lifecycle
195
+ // and lives through the remaining Application lifecycle
196
+ AmityCoreClient.getGlobalBanEvents()
197
+ .doOnNext {
198
+ // handle the case the user is globally banned
199
+ }
200
+ .subscribe()
201
+ }
202
+ ```
203
+
204
+ ```typescript TypeScript
205
+ import { Client } from '@amityco/ts-sdk';
206
+
207
+ Client.onConnectionError(error => {
208
+ if (error.code === Amity.ServerError.GLOBAL_BAN) {
209
+ console.log('user has been globally banned!');
210
+ }
211
+ })
212
+ ```
213
+
214
+
215
+ ## Best Practices
216
+
217
+ ### Do's ✅
218
+
219
+ - **Provide specific error messages** based on error codes
220
+ - **Log errors** for debugging and monitoring
221
+ - **Implement retry logic** for network errors
222
+ - **Handle global bans** with appropriate user communication
223
+ - **Use error boundaries** in React applications
224
+ - **Show loading states** during error recovery
225
+
226
+ ### Don'ts ❌
227
+
228
+ - **Don't ignore errors** - always handle them appropriately
229
+ - **Don't show technical error codes** to end users
230
+ - **Don't retry indefinitely** - implement maximum retry limits
231
+ - **Don't expose sensitive information** in error messages
232
+
233
+
234
+ **Pro Tip**: Use network logging to identify performance bottlenecks, monitor error rates, and validate that your app is making expected API calls during development.
235
+
236
+ ---
@@ -0,0 +1,262 @@
1
+ # Android Live Objects/Collections
2
+
3
+ > Live Objects are supported in the Android SDK with RxJava Data Streaming
4
+
5
+ The RxJava3 library is being used in Android development to achieve Live Object and Live Collection behavior.
6
+
7
+ It is a Java VM implementation of ReactiveX, a library for composing asynchronous and event-based programs by using observable sequences. The building blocks of RxJava are Observables and Subscribers. Observable is used for emitting items and Subscriber is used for consuming those items.
8
+
9
+ You can visit [ReactiveX](https://reactivex.io/) for more information.
10
+
11
+ ## How it Works
12
+
13
+ SDK handles lots of data received from various sources. Data can be present in local cache. It might also be queried from the server or received from some real-time events. What this means is that same data is constantly updating. The data that you are accessing at the moment can get updated by other sources and becomes out of sync.
14
+
15
+ Rx3 Data Stream helps in syncing the data so you will always get the most recent one. Whenever the data updates, you will be notified through **Flowable Objects** and **Flowable Collection**.
16
+
17
+ New data gets automatically collected everytime when there is an updation and user need not refresh to get the recent data.
18
+
19
+ ### Data Sources
20
+
21
+ Data present in local storage
22
+ Data queried from the server
23
+ Data received from real-time events
24
+
25
+ ## How to Retrieve Data from Rx3 Data Stream
26
+
27
+ To retrieve data from the RxStream, we need to subscribe to the Stream(Flowable/Single/Completable) by defining subscribing and observing threads.
28
+
29
+ ```kotlin
30
+ fun subscribeToFlowable() {
31
+ //Flowable
32
+ val flowableStream: Flowable<Any> =
33
+ Flowable.just("one", "two", "three") // Flowable initialization
34
+ flowableStream
35
+ .subscribeOn(Schedulers.io()) // subscribing an operation on io thread (Background thread)
36
+ .observeOn(AndroidSchedulers.mainThread()) // observing results on main thread (UI thread)
37
+ .doOnNext {
38
+ // data is available here
39
+ }.doOnError {
40
+ // handle error here
41
+ }.subscribe()
42
+ }
43
+ ```
44
+
45
+ ## Events a Data Stream can Emit
46
+
47
+ In the RxJava3 framework we have these different types of objects that can be observed:
48
+
49
+ ### Flowable
50
+ Emits a stream of elements
51
+
52
+ - **doOnNext**
53
+ - **doOnError**
54
+
55
+ ### Single
56
+ Emits exactly one element
57
+
58
+ - **doOnSuccess**
59
+ - **doOnError**
60
+
61
+ ### Completable
62
+ Emits a "complete" event, without emitting any data type, just a success/failure
63
+
64
+ - **doOnComplete**
65
+ - **doOnError**
66
+
67
+ ```kotlin
68
+ val flowableStream: Flowable<List<AmityPost>> = postRepository.getPosts()
69
+
70
+ flowableStream
71
+ .subscribeOn(Schedulers.io())
72
+ .observeOn(AndroidSchedulers.mainThread())
73
+ .doOnNext { posts ->
74
+ // Handle stream of posts
75
+ updateUI(posts)
76
+ }
77
+ .doOnError { error ->
78
+ // Handle error
79
+ showErrorMessage(error.message)
80
+ }
81
+ .subscribe()
82
+ ```
83
+
84
+ ```kotlin
85
+ val singlePost: Single<AmityPost> = postRepository.getPost(postId)
86
+
87
+ singlePost
88
+ .subscribeOn(Schedulers.io())
89
+ .observeOn(AndroidSchedulers.mainThread())
90
+ .doOnSuccess { post ->
91
+ // Handle single post
92
+ displayPost(post)
93
+ }
94
+ .doOnError { error ->
95
+ // Handle error
96
+ showErrorMessage(error.message)
97
+ }
98
+ .subscribe()
99
+ ```
100
+
101
+ ```kotlin
102
+ val deleteOperation: Completable = postRepository.deletePost(postId)
103
+
104
+ deleteOperation
105
+ .subscribeOn(Schedulers.io())
106
+ .observeOn(AndroidSchedulers.mainThread())
107
+ .doOnComplete {
108
+ // Handle successful completion
109
+ showSuccessMessage("Operation completed")
110
+ }
111
+ .doOnError { error ->
112
+ // Handle error
113
+ showErrorMessage(error.message)
114
+ }
115
+ .subscribe()
116
+ ```
117
+
118
+ ## Flow functions
119
+
120
+ By using the `.asFlow()` method, it enables the conversion of Flowable&lt;T&gt; functions of the Amity Android SDK into Flow functions.
121
+
122
+ ```kotlin
123
+ fun getAllUsers(): Flow<PagingData<AmityUser>> {
124
+ return AmityCoreClient.newUserRepository()
125
+ .getUsers()
126
+ .build()
127
+ .query()
128
+ .asFlow()
129
+ }
130
+ ```
131
+
132
+ ## Jetpack Compose compatibility
133
+
134
+ Amity Android SDK seamlessly integrates with Jetpack Compose UI, allowing you to take full advantage of the modern UI toolkit provided by Jetpack Compose. You can effortlessly incorporate our SDK into your Jetpack Compose-based projects to enhance your app's social experience. This compatibility ensures that you can leverage the power of Jetpack Compose while benefiting from the features and capabilities our SDK provides.
135
+
136
+ ### Flow of PagingData in Compose
137
+
138
+ In Jetpack Compose, integrating data from a `Flow<PagingData<T>>` source into your UI is made easy through the `collectAsLazyPagingItems()` function. This function allows you to seamlessly paginate and display items within your Composable functions.
139
+
140
+ To start using it, add compose paging dependency in your project app level build.gradle file.
141
+
142
+ ```gradle
143
+ implementation "androidx.paging:paging-compose:x.y.z"
144
+ ```
145
+
146
+ Then in your Composable functions, you can collect from flow and display data, and also can observe the load state.
147
+
148
+ ```kotlin
149
+ @Composable
150
+ fun UserList(
151
+ modifier: Modifier = Modifier
152
+ ) {
153
+ // collect data from flow using collectAsLazyPagingItems
154
+ val users = getAllUsers().collectAsLazyPagingItems()
155
+
156
+ // display data in LazyColumn or LazyRow
157
+ LazyColumn(
158
+ modifier = modifier.fillMaxSize(),
159
+ horizontalAlignment = Alignment.CenterHorizontally,
160
+ ) {
161
+ items(
162
+ count = users.itemCount,
163
+ key = users.itemKey { it.getUserId() }
164
+ ) { index ->
165
+ // render each item here
166
+ val user = users[index]
167
+ Text(text = "UserId: ${user?.getUserId()}")
168
+ }
169
+
170
+ // handle load state on loading first page
171
+ when (val state = users.loadState.refresh) {
172
+ is LoadState.Error -> {
173
+ // handle error
174
+ item {
175
+ Text(text = "Error: ${state.error.message}")
176
+ }
177
+ }
178
+
179
+ is LoadState.Loading -> {
180
+ // loading first page
181
+ item {
182
+ Column(
183
+ modifier = modifier.fillParentMaxSize(),
184
+ horizontalAlignment = Alignment.CenterHorizontally,
185
+ verticalArrangement = Arrangement.Center,
186
+ ) {
187
+ Text(text = "Refresh/First Loading")
188
+ CircularProgressIndicator(color = Color.Black)
189
+ }
190
+ }
191
+ }
192
+
193
+ else -> {}
194
+ }
195
+
196
+ // handle load state on loading next page
197
+ when (val state = users.loadState.append) {
198
+ is LoadState.Error -> {
199
+ // handle error
200
+ item {
201
+ Text(text = "Error: ${state.error.message}")
202
+ }
203
+ }
204
+
205
+ is LoadState.Loading -> {
206
+ // loading next page
207
+ item {
208
+ Column(
209
+ modifier = modifier.fillMaxWidth(),
210
+ horizontalAlignment = Alignment.CenterHorizontally,
211
+ verticalArrangement = Arrangement.Center,
212
+ ) {
213
+ Text(text = "Pagination Loading")
214
+ CircularProgressIndicator(color = Color.Black)
215
+ }
216
+ }
217
+ }
218
+
219
+ else -> {}
220
+ }
221
+ }
222
+ }
223
+ ```
224
+
225
+ ### Flow in Compose
226
+
227
+ By using `collectAsState()` method, it can deliver asynchronous data updates to your Compose UI components.
228
+
229
+ ```kotlin
230
+ fun getPostAsFlow(): Flow<AmityPost> {
231
+ return AmitySocialClient.newPostRepository()
232
+ .getPost(postId = "postId")
233
+ .asFlow()
234
+ }
235
+
236
+ @Composable
237
+ fun SinglePostItem() {
238
+ val post by getPostAsFlow().collectAsState(initial = null)
239
+
240
+ Text(text = "Post ID: ${post?.getPostId()}")
241
+ }
242
+ ```
243
+
244
+
245
+ ## Best Practices
246
+
247
+ **Proper Disposal**: Always dispose of RxJava subscriptions to prevent memory leaks when Activities/Fragments are destroyed.
248
+
249
+ **Proper Scheduling**: Use `subscribeOn(Schedulers.io())` for background operations and `observeOn(AndroidSchedulers.mainThread())` for UI updates.
250
+
251
+ **Graceful Recovery**: Implement robust error handling with `doOnError` for all RxJava streams to prevent crashes.
252
+
253
+ **State Management**: Use `collectAsState()` for single objects and `collectAsLazyPagingItems()` for paginated collections in Compose.
254
+
255
+ ## Related Resources
256
+
257
+ Learn more about reactive programming concepts
258
+ Explore Android's official paging library
259
+ Build modern Android UI with declarative programming
260
+ Access RxJava3 source code and documentation
261
+
262
+ ---
@@ -0,0 +1,195 @@
1
+ # Flutter Live Objects/Collections
2
+
3
+ > Live Objects are supported in the Flutter SDK with Streams and LiveCollection for real-time data synchronization
4
+
5
+ In Flutter SDK, we have a concept of **Live Object** and **Live Collection**.
6
+
7
+ Live Object is represented by `StreamSubscription<Object>` instance and Live Collection is represented by an instance of `LiveCollection`. LiveCollection is a generic class that encapsulates any other object and notifies the observer whenever any property of the encapsulated object changes.
8
+
9
+ Live Object helps to observe changes in a single object whereas Live Collection helps to observe changes in a list of objects.
10
+
11
+ Examples: `StreamSubscription<AmityPost>` for single objects or `LiveCollection<AmityMessage>` for collections.
12
+
13
+ ## How it Works
14
+
15
+ SDK handles lots of data received from various sources. Data can be present in the local cache. It might also be queried from the server or received from some real-time events. What this means is that the same data is constantly updating. The data that you are accessing at the moment can get updated by other sources and become out of sync.
16
+
17
+ Live Object and Live Collection help in syncing the data so you will always get the most recent one. Whenever the data updates, you will be notified through helper methods in the Live Object and Live Collection classes.
18
+
19
+ New data gets automatically collected every time when there is an update and the user need not refresh to get the recent data.
20
+
21
+ ### Data Sources
22
+
23
+ Data present in local storage
24
+ Data queried from the server
25
+ Data received from real-time events
26
+
27
+ ## LiveObject
28
+
29
+ `StreamSubscription<Object>` is a native flutter class that keeps track of a single object. It is a live object. In Flutter AmitySDK, any object which provides Stream is a Live Object.
30
+
31
+ This function helps listen to Live Object. Whenever any property for the observed object changes, the listen callback will be triggered.
32
+
33
+ ### Example Usage
34
+
35
+ ```dart
36
+ void listenPost(String postId) {
37
+ AmitySocialClient.newPostRepository()
38
+ .getPostStream(postId)
39
+ .stream
40
+ .listen((AmityPost post) {
41
+ //handle results
42
+ }).onError((error, stackTrace) {
43
+ //handle error
44
+ });
45
+ }
46
+ ```
47
+
48
+ ## Live Collection
49
+
50
+ `LiveCollection` is a generic class that keeps track of a collection of objects. It is a Live Collection. In Flutter SDK, any object that is encapsulated by LiveCollection class is a live collection.
51
+
52
+
53
+ ### Stream Observer
54
+
55
+ `asStream` method can get triggered multiple times throughout the lifetime of the application as long as its associated `Stream<List<Object>>` is retained in memory.
56
+
57
+ `asStream` method will be called from the main thread so you can perform any UI update-related task within the listen block itself.
58
+
59
+ - If the requested data collection is stored locally on the device, the block will be called immediately with the local version of the data.
60
+ - In parallel, a request is made to the server to fetch the latest version of the data. Once the data is returned, the listen block will be triggered again.
61
+ - Any future changes to the data from any sources can trigger the listen block.
62
+
63
+ ### Pagination
64
+
65
+ AmityCollection in SDK returns a maximum of pageSize items per page. It has `loadNext()` method to fetch more data. It also exposes `hasNext` property to check if the next page or previous page is present.
66
+
67
+ ```dart
68
+ void loadNextPage() async {
69
+ if (liveCollection.hasNextPage()) {
70
+ liveCollection.loadNext();
71
+ }
72
+ }
73
+ ```
74
+
75
+ Once the next page is available, the same listen block gets triggered and you can access the collection as shown above. If you want to shrink the collection to the original first page, you can do so by calling `reset()` method on the same collection.
76
+
77
+ ### ListView Integration
78
+
79
+ One typical usage of LiveCollection is in ListView. Below is an example of fetching a collection and updating its state in a Widget.
80
+
81
+ ```dart
82
+ void initState(String channelId) {
83
+ messageLiveCollection = AmityChatClient.newMessageRepository()
84
+ .getMessages(channelId)
85
+ .stackFromEnd(true)
86
+ .getLiveCollection(pageSize: 20);
87
+
88
+ messageLiveCollection.getStreamController().stream.listen((event) {
89
+ // update latest results here
90
+ // setState(() {
91
+ // amityMessages = event;
92
+ // });
93
+ });
94
+
95
+ //load first page when initiating widget
96
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
97
+ messageLiveCollection.loadNext();
98
+ });
99
+
100
+ scrollcontroller.addListener(paginationListener);
101
+ }
102
+
103
+ void paginationListener() {
104
+ if ((scrollcontroller.position.pixels >=
105
+ (scrollcontroller.position.maxScrollExtent - 100)) &&
106
+ messageLiveCollection.hasNextPage()) {
107
+ messageLiveCollection.loadNext();
108
+ }
109
+ }
110
+ ```
111
+
112
+ ## Best Practices
113
+
114
+ **Always Cancel Subscriptions**: Prevent memory leaks by properly canceling StreamSubscriptions in the dispose method.
115
+
116
+ ```dart
117
+ class MyWidget extends StatefulWidget {
118
+ @override
119
+ _MyWidgetState createState() => _MyWidgetState();
120
+ }
121
+
122
+ class _MyWidgetState extends State<MyWidget> {
123
+ late StreamSubscription<AmityPost> _postSubscription;
124
+ late LiveCollection<AmityMessage> _messageLiveCollection;
125
+
126
+ @override
127
+ void dispose() {
128
+ // Cancel stream subscriptions
129
+ _postSubscription.cancel();
130
+
131
+ // Reset live collections
132
+ _messageLiveCollection.reset();
133
+
134
+ super.dispose();
135
+ }
136
+ }
137
+ ```
138
+
139
+ **Handle Stream Errors**: Always provide error callbacks for streams to prevent crashes.
140
+
141
+ ```dart
142
+ _postSubscription = repository.getPost(postId).listen(
143
+ (post) {
144
+ // Handle success
145
+ setState(() {
146
+ _post = post;
147
+ });
148
+ },
149
+ onError: (error) {
150
+ // Handle error gracefully
151
+ setState(() {
152
+ _error = error.toString();
153
+ });
154
+ },
155
+ );
156
+ ```
157
+
158
+ **Check hasNextPage**: Always verify if more pages are available before loading to avoid unnecessary API calls.
159
+
160
+ ```dart
161
+ void loadMoreData() {
162
+ if (liveCollection.hasNextPage() && !_isLoading) {
163
+ setState(() {
164
+ _isLoading = true;
165
+ });
166
+ liveCollection.loadNext();
167
+ }
168
+ }
169
+ ```
170
+
171
+ **Use StreamBuilder**: Leverage Flutter's StreamBuilder for efficient UI updates without manual setState calls.
172
+
173
+ ```dart
174
+ StreamBuilder<AmityPost>(
175
+ stream: repository.getPost(postId),
176
+ builder: (context, snapshot) {
177
+ if (snapshot.hasError) {
178
+ return ErrorWidget(snapshot.error!);
179
+ }
180
+
181
+ if (!snapshot.hasData) {
182
+ return CircularProgressIndicator();
183
+ }
184
+
185
+ return PostWidget(post: snapshot.data!);
186
+ },
187
+ );
188
+ ```
189
+
190
+ ## Related Resources
191
+
192
+ Understanding Dart Streams and StreamSubscriptions
193
+ Flutter's StreamBuilder for reactive UI updates
194
+
195
+ ---