mirror of
				https://gitlab.com/famedly/fluffychat.git
				synced 2025-11-04 06:17:26 +01:00 
			
		
		
		
	Merge branch 'soru/rate-limit-rerender' into 'main'
feat: Rate limit streams so that large accounts have a smoother UI See merge request famedly/fluffychat!464
This commit is contained in:
		
						commit
						63ae83b852
					
				@ -9,6 +9,7 @@ import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:vrouter/vrouter.dart';
 | 
			
		||||
import '../../widgets/matrix.dart';
 | 
			
		||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
 | 
			
		||||
import '../../utils/stream_extension.dart';
 | 
			
		||||
 | 
			
		||||
class ChatListView extends StatelessWidget {
 | 
			
		||||
  final ChatListController controller;
 | 
			
		||||
@ -161,7 +162,8 @@ class ChatListView extends StatelessWidget {
 | 
			
		||||
                            .client
 | 
			
		||||
                            .onSync
 | 
			
		||||
                            .stream
 | 
			
		||||
                            .where((s) => s.hasRoomUpdate),
 | 
			
		||||
                            .where((s) => s.hasRoomUpdate)
 | 
			
		||||
                            .rateLimit(Duration(seconds: 1)),
 | 
			
		||||
                        builder: (context, snapshot) {
 | 
			
		||||
                          return FutureBuilder<void>(
 | 
			
		||||
                            future: controller.waitForFirstSync(),
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,8 @@ import 'package:scroll_to_index/scroll_to_index.dart';
 | 
			
		||||
import 'package:swipe_to_action/swipe_to_action.dart';
 | 
			
		||||
import 'package:vrouter/vrouter.dart';
 | 
			
		||||
 | 
			
		||||
import '../../utils/stream_extension.dart';
 | 
			
		||||
 | 
			
		||||
class ChatView extends StatelessWidget {
 | 
			
		||||
  final ChatController controller;
 | 
			
		||||
 | 
			
		||||
@ -72,7 +74,8 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
            titleSpacing: 0,
 | 
			
		||||
            title: controller.selectedEvents.isEmpty
 | 
			
		||||
                ? StreamBuilder(
 | 
			
		||||
                    stream: controller.room.onUpdate.stream,
 | 
			
		||||
                    stream: controller.room.onUpdate.stream
 | 
			
		||||
                        .rateLimit(Duration(milliseconds: 250)),
 | 
			
		||||
                    builder: (context, snapshot) => ListTile(
 | 
			
		||||
                          leading: Avatar(controller.room.avatar,
 | 
			
		||||
                              controller.room.displayname),
 | 
			
		||||
@ -105,7 +108,8 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                      .stream
 | 
			
		||||
                                      .where((p) =>
 | 
			
		||||
                                          p.senderId ==
 | 
			
		||||
                                          controller.room.directChatMatrixID),
 | 
			
		||||
                                          controller.room.directChatMatrixID)
 | 
			
		||||
                                      .rateLimit(Duration(seconds: 1)),
 | 
			
		||||
                                  builder: (context, snapshot) => Text(
 | 
			
		||||
                                        controller.room
 | 
			
		||||
                                            .getLocalizedStatus(context),
 | 
			
		||||
@ -283,8 +287,10 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                            : Container()
 | 
			
		||||
                                    : i == 0
 | 
			
		||||
                                        ? StreamBuilder(
 | 
			
		||||
                                            stream:
 | 
			
		||||
                                                controller.room.onUpdate.stream,
 | 
			
		||||
                                            stream: controller
 | 
			
		||||
                                                .room.onUpdate.stream
 | 
			
		||||
                                                .rateLimit(Duration(
 | 
			
		||||
                                                    milliseconds: 250)),
 | 
			
		||||
                                            builder: (_, __) {
 | 
			
		||||
                                              final seenByText = controller.room
 | 
			
		||||
                                                  .getLocalizedSeenByText(
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										46
									
								
								lib/utils/stream_extension.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								lib/utils/stream_extension.dart
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
			
		||||
import 'dart:async';
 | 
			
		||||
 | 
			
		||||
extension StreamExtension on Stream {
 | 
			
		||||
  /// Returns a new Stream which outputs only `true` for every update of the original
 | 
			
		||||
  /// stream, ratelimited by the Duration t
 | 
			
		||||
  Stream<bool> rateLimit(Duration t) {
 | 
			
		||||
    final controller = StreamController<bool>();
 | 
			
		||||
    Timer timer;
 | 
			
		||||
    var gotMessage = false;
 | 
			
		||||
    // as we call our inline-defined function recursively we need to make sure that the
 | 
			
		||||
    // variable exists prior of creating the function. Silly dart.
 | 
			
		||||
    Function _onMessage;
 | 
			
		||||
    // callback to determine if we should send out an update
 | 
			
		||||
    _onMessage = () {
 | 
			
		||||
      // do nothing if it is already closed
 | 
			
		||||
      if (controller.isClosed) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      if (timer == null) {
 | 
			
		||||
        // if we don't have a timer yet, send out the update and start a timer
 | 
			
		||||
        gotMessage = false;
 | 
			
		||||
        controller.add(true);
 | 
			
		||||
        timer = Timer(t, () {
 | 
			
		||||
          // the timer has ended...delete it and, if we got a message, re-run the
 | 
			
		||||
          // method to send out an update!
 | 
			
		||||
          timer = null;
 | 
			
		||||
          if (gotMessage) {
 | 
			
		||||
            _onMessage();
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      } else {
 | 
			
		||||
        // set that we got a message
 | 
			
		||||
        gotMessage = true;
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    final subscription = listen((_) => _onMessage(),
 | 
			
		||||
        onDone: () => controller.close(),
 | 
			
		||||
        onError: (e, s) => controller.addError(e, s));
 | 
			
		||||
    // add proper cleanup to the subscription and the controller, to not memory leak
 | 
			
		||||
    controller.onCancel = () {
 | 
			
		||||
      subscription.cancel();
 | 
			
		||||
      controller.close();
 | 
			
		||||
    };
 | 
			
		||||
    return controller.stream;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -289,7 +289,9 @@ class ChatListItem extends StatelessWidget {
 | 
			
		||||
                curve: Curves.bounceInOut,
 | 
			
		||||
                padding: EdgeInsets.symmetric(horizontal: 7),
 | 
			
		||||
                height: unreadBubbleSize,
 | 
			
		||||
                width: room.notificationCount == 0 && !room.isUnread ? 0 : null,
 | 
			
		||||
                width: room.notificationCount == 0 && !room.isUnread
 | 
			
		||||
                    ? 0
 | 
			
		||||
                    : unreadBubbleSize,
 | 
			
		||||
                decoration: BoxDecoration(
 | 
			
		||||
                  color: room.highlightCount > 0
 | 
			
		||||
                      ? Colors.red
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user