Invalidate ranges when the filter changes on a list

Some bugs still exist on the client where it gets confused about the
new offsets, but the server code seems to be doing the right thing.
This commit is contained in:
Kegan Dougal 2022-02-18 18:29:25 +00:00
parent 4c10175125
commit 44bfebfab9
5 changed files with 58 additions and 9 deletions

View File

@ -20,6 +20,9 @@
<input id="syncButton" type="button" value="Start Sync" />
<input id="debugButton" type="button" value="DEBUG" style="display: none;" />
<span id="errorMsg"></span>
<div>
<input id="roomfilter" type="text" placeholder="Filter rooms..." />
</div>
</div>
<div class="roomheading">
<img class="roomavatar" decoding="async" id="selectedroomavatar" src="/client/placeholder.svg"/>

View File

@ -2,14 +2,17 @@ let lastError = null;
let activeAbortController = new AbortController();
let activeSessionId;
let activeRoomId = ""; // the room currently being viewed
const DEFAULT_RANGES = [[0, 20]];
let activeLists = [
{
name: "Direct Messages",
listFiltersModified: false,
listFilters: {
is_dm: true,
room_name_like: undefined,
},
activeRanges: [[0, 20]],
activeRanges: DEFAULT_RANGES,
// the constantly changing sliding window ranges. Not an array for performance reasons
// E.g tracking ranges 0-99, 500-599, we don't want to have a 600 element array
roomIndexToRoomId: {},
@ -18,10 +21,12 @@ let activeLists = [
},
{
name: "Group Chats",
listFiltersModified: false,
listFilters: {
is_dm: false,
room_name_like: undefined,
},
activeRanges: [[0, 20]],
activeRanges: DEFAULT_RANGES,
// the constantly changing sliding window ranges. Not an array for performance reasons
// E.g tracking ranges 0-99, 500-599, we don't want to have a 600 element array
roomIndexToRoomId: {},
@ -412,6 +417,14 @@ const doSyncLoop = async (accessToken, sessionId) => {
ranges: al.activeRanges,
filters: al.listFilters,
};
// if we are viewing a window at 100-120 and then we filter down to 5 total rooms,
// we'll end up showing nothing. Therefore, if the filters change (e.g room name filter)
// reset the range back to 0-20.
if (al.listFiltersModified) {
l.ranges = DEFAULT_RANGES;
al.listFiltersModified = false;
al.roomIndexToRoomId = {};
}
// if this is the first request on this session, send sticky request data which never changes
if (!currentPos) {
l.required_state = requiredStateEventsInList;
@ -420,7 +433,6 @@ const doSyncLoop = async (accessToken, sessionId) => {
}
return l;
}),
session_id: sessionId ? sessionId : undefined,
};
// check if we are (un)subscribing to a room and modify request this one time for it
let subscribingToRoom;
@ -752,6 +764,17 @@ window.addEventListener("load", (event) => {
activeSessionId = new Date().getTime() + "";
doSyncLoop(accessToken, activeSessionId);
};
document.getElementById("roomfilter").addEventListener("input", (ev) => {
const roomNameFilter = ev.target.value;
for (let i = 0; i < activeLists.length; i++) {
activeLists[i].listFilters.room_name_like = roomNameFilter;
activeLists[i].listFiltersModified = true;
}
if (activeAbortController) {
// interrupt the sync request to send up new filters
activeAbortController.abort();
}
});
document.getElementById("debugButton").onclick = () => {
const debugBox = document.getElementById("debugCmd");
debugBox.style = "";

View File

@ -42,7 +42,9 @@ type ConnState struct {
muxedReq *sync3.Request
lists *sync3.SortableRoomLists
roomSubscriptions map[string]sync3.RoomSubscription
loadPosition int64
allRooms []sync3.RoomConnMetadata
loadPosition int64
// A channel which the dispatcher uses to send updates to the conn goroutine
// Consumed when the conn is read. There is a limit to how many updates we will store before
@ -104,6 +106,7 @@ func (s *ConnState) load(req *sync3.Request) error {
),
}
}
s.allRooms = rooms
s.loadPosition = initialLoadPosition
for i, l := range req.Lists {
@ -236,9 +239,11 @@ func (s *ConnState) onIncomingListRequest(listIndex int, prevReqList, nextReqLis
var prevRange sync3.SliceRanges
var prevSort []string
var prevFilters *sync3.RequestFilters
if prevReqList != nil {
prevRange = prevReqList.Ranges
prevSort = prevReqList.Sort
prevFilters = prevReqList.Filters
}
if nextReqList.Sort == nil {
nextReqList.Sort = []string{sync3.SortByRecency}
@ -252,10 +257,11 @@ func (s *ConnState) onIncomingListRequest(listIndex int, prevReqList, nextReqLis
} else {
addedRanges = nextReqList.Ranges
}
if !reflect.DeepEqual(prevSort, nextReqList.Sort) {
// the sort operations have changed, invalidate everything (if there were previous syncs), re-sort and re-SYNC
if prevSort != nil {
for _, r := range prevReqList.Ranges {
changedFilters := sync3.ChangedFilters(prevFilters, nextReqList.Filters)
if !reflect.DeepEqual(prevSort, nextReqList.Sort) || changedFilters {
// the sort/filter operations have changed, invalidate everything (if there were previous syncs), re-sort and re-SYNC
if prevSort != nil || changedFilters {
for _, r := range prevRange {
responseOperations = append(responseOperations, &sync3.ResponseOpRange{
List: listIndex,
Operation: sync3.OpInvalidate,
@ -263,6 +269,11 @@ func (s *ConnState) onIncomingListRequest(listIndex int, prevReqList, nextReqLis
})
}
}
if changedFilters {
// we need to re-create the list as the rooms may have completely changed
roomList = sync3.NewFilteredSortableRooms(s.allRooms, nextReqList.Filters)
s.lists.Set(listIndex, roomList)
}
if err := roomList.Sort(nextReqList.Sort); err != nil {
logger.Err(err).Int("index", listIndex).Msg("cannot sort list")
}

View File

@ -115,7 +115,6 @@ func (h *SyncLiveHandler) serve(w http.ResponseWriter, req *http.Request) error
}
}
}
fmt.Println("incoming sync pos=", req.URL.Query().Get("pos"))
conn, err := h.setupConnection(req, &requestBody, req.URL.Query().Get("pos") != "")
if err != nil {
hlog.FromRequest(req).Err(err).Msg("failed to get or create Conn")

View File

@ -194,6 +194,19 @@ func (rf *RequestFilters) Include(r *RoomConnMetadata) bool {
return true
}
func ChangedFilters(prev, next *RequestFilters) bool {
// easier to marshal as JSON rather than do a bazillion nil checks
pb, err := json.Marshal(prev)
if err != nil {
panic(err)
}
nb, err := json.Marshal(next)
if err != nil {
panic(err)
}
return !bytes.Equal(pb, nb)
}
type RoomSubscription struct {
RequiredState [][2]string `json:"required_state"`
TimelineLimit int64 `json:"timeline_limit"`