- 
                Notifications
    
You must be signed in to change notification settings  - Fork 349
 
Keep track of the known topics in store #1951
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d6b2242
              342e468
              b515da9
              2e0fcfd
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,14 +1,19 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'dart:async'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'dart:collection'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'package:collection/collection.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'package:flutter/foundation.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import '../api/model/events.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import '../api/model/initial_snapshot.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import '../api/model/model.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import '../api/route/channels.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'realm.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'store.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'user.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final _apiGetChannelTopics = getStreamTopics; // similar to _apiSendMessage in lib/model/message.dart | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// The portion of [PerAccountStore] for channels, topics, and stuff about them. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// This type is useful for expressing the needs of other parts of the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
          
            
          
           | 
    @@ -78,6 +83,27 @@ mixin ChannelStore on UserStore { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Fetch topics in a channel from the server, only if they're not fetched yet. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// The results from the last successful fetch | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// can be retrieved with [getChannelTopics]. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Future<void> fetchTopics(int channelId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Pairs of the known topics and its latest message ID, in the given channel. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
         There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 
        Suggested change
       
    
  | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Returns null if the data has never been fetched yet. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
         There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: 
        Suggested change
       
    
  | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// To fetch it from the server, use [fetchTopics]. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// The result is guaranteed to be sorted by [GetStreamTopicsEntry.maxId] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// descending, and the topics are guaranteed to be distinct. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
         
      Comment on lines
    
      +97
     to 
      +98
    
   
  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit, omit needless words: 
        Suggested change
       
    
  | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// In some cases, the same maxId affected by message moves can be present in | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// multiple [GetStreamTopicsEntry] entries. For this reason, the caller | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// should not rely on [getChannelTopics] to determine which topic the message | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// is in. Instead, refer to [PerAccountStore.messages]. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// See [handleUpdateMessageEvent] on how this could happen. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
         
      Comment on lines
    
      +100
     to 
      +104
    
   
  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems worth highlighting in general that  (Also, isn't message deletion another reason  
        Suggested change
       
    
  | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<GetStreamTopicsEntry>? getChannelTopics(int channelId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// The visibility policy that the self-user has for the given topic. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// This does not incorporate the user's channel-level policy, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
          
            
          
           | 
    @@ -288,6 +314,13 @@ mixin ProxyChannelStore on ChannelStore { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Map<int, ChannelFolder> get channelFolders => channelStore.channelFolders; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Future<void> fetchTopics(int channelId) => channelStore.fetchTopics(channelId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<GetStreamTopicsEntry>? getChannelTopics(int channelId) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channelStore.getChannelTopics(channelId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| UserTopicVisibilityPolicy topicVisibilityPolicy(int streamId, TopicName topic) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channelStore.topicVisibilityPolicy(streamId, topic); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
          
            
          
           | 
    @@ -368,6 +401,37 @@ class ChannelStoreImpl extends HasUserStore with ChannelStore { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final Map<int, ChannelFolder> channelFolders; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Maps indexed by channel IDs, of the known latest message IDs in each topic. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// For example: `_latestMessageIdsByChannelTopic[channel.streamId][topic] = maxId` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// In some cases, the same message IDs, when affected by message moves, can | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// be present for mutliple channel-topic keys. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// See [handleUpdateMessageEvent] on how this could happen. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final Map<int, Map<TopicName, int>> _latestMessageIdsByChannelTopic = {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Future<void> fetchTopics(int channelId) async { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (_latestMessageIdsByChannelTopic[channelId] != null) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final result = await _apiGetChannelTopics(connection, streamId: channelId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allowEmptyTopicName: true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _latestMessageIdsByChannelTopic[channelId] = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (final GetStreamTopicsEntry(:name, :maxId) in result.topics) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: maxId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
         
      Comment on lines
    
      +419
     to 
      +422
    
   
  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should use   | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<GetStreamTopicsEntry>? getChannelTopics(int channelId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final latestMessageIdsByTopic = _latestMessageIdsByChannelTopic[channelId]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (latestMessageIdsByTopic == null) return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (final MapEntry(:key, :value) in latestMessageIdsByTopic.entries) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GetStreamTopicsEntry(maxId: value, name: key), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ].sortedBy((value) => -value.maxId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
         
      Comment on lines
    
      +426
     to 
      +433
    
   
  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might be a case where we want to maintain a sorted list in the data structure, rather than sorting on demand. @gnprice, do you think so?  | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Map<int, TopicKeyedMap<UserTopicVisibilityPolicy>> get debugTopicVisibility => topicVisibility; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
          
            
          
           | 
    @@ -572,6 +636,65 @@ class ChannelStoreImpl extends HasUserStore with ChannelStore { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| forStream[event.topicName] = visibilityPolicy; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Handle a [MessageEvent], returning whether listeners should be notified. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bool handleMessageEvent(MessageEvent event) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (event.message is! StreamMessage) return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final StreamMessage(:streamId, :topic) = event.message as StreamMessage; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
         There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: 
        Suggested change
       
    
  | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final latestMessageIdsByTopic = _latestMessageIdsByChannelTopic[streamId]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If we don't already know about the list of topics of the channel this | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // message belongs to, we don't want to proceed and put one entry about the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // topic of this message, otherwise [fetchTopics] and the callers of | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // [getChannelTopics] would assume that the channel only has this one topic | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // and would never fetch the complete list of topics for that matter. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (latestMessageIdsByTopic == null) return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
         
      Comment on lines
    
      +646
     to 
      +651
    
   
  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about: 
        Suggested change
       
    
  | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If this message is already the latest message in the topic because it was | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // received through fetch in fetch/event race, or it is a message sent even | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // before the latest message of the fetch, we don't do the update. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
         
      Comment on lines
    
      +653
     to 
      +655
    
   
  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm getting confused trying to parse this sentence. Instead, how about, inside the  // The event raced with a message fetch. | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final currentLatestMessageId = latestMessageIdsByTopic[topic]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (currentLatestMessageId != null && currentLatestMessageId >= event.message.id) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| latestMessageIdsByTopic[topic] = event.message.id; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Handle an [UpdateMessageEvent], returning whether listeners should be | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// notified. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bool handleUpdateMessageEvent(UpdateMessageEvent event) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (event.moveData == null) return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final UpdateMessageMoveData( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| :origStreamId, :origTopic, :newStreamId, :newTopic, :propagateMode, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) = event.moveData!; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bool shouldNotify = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final origLatestMessageIdsByTopics = _latestMessageIdsByChannelTopic[origStreamId]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // We only handle the case where all the messages of [origTopic] are | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // moved to [newTopic]; in that case we can remove [origTopic] safely. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // But if only one messsage is moved (`PropagateMode.changeOne`) or a few | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // messages are moved (`PropagateMode.changeLater`), we cannot do anything | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // about [origTopic] here as we cannot determine the new `maxId` for it. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // (This is the case where there could be multiple channel-topic keys with | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // the same `maxId`) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (propagateMode == PropagateMode.changeAll | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && origLatestMessageIdsByTopics != null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| shouldNotify = origLatestMessageIdsByTopics.remove(origTopic) != null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
         
      Comment on lines
    
      +673
     to 
      +684
    
   
  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With a switch/case on  
        Suggested change
       
    
  | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final newLatestMessageIdsByTopics = _latestMessageIdsByChannelTopic[newStreamId]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (newLatestMessageIdsByTopics != null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final movedMaxId = event.messageIds.max; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!newLatestMessageIdsByTopics.containsKey(newTopic) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| || newLatestMessageIdsByTopics[newTopic]! < movedMaxId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| newLatestMessageIdsByTopics[newTopic] = movedMaxId; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| shouldNotify = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
         
      Comment on lines
    
      +686
     to 
      +694
    
   
  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A bit easier to read, I think: 
        Suggested change
       
    
  | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return shouldNotify; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
     | 
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// A [Map] with [TopicName] keys and [V] values. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
          
            
          
           | 
    ||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: too-long line; put comment on line above