diff --git a/src/models/event-timeline-set.js b/src/models/event-timeline-set.js
index 4b46eccf523ac891051aeb9900341990432e14e6..8d96b56e1110a57305dca46cd2211d2dda480900 100644
--- a/src/models/event-timeline-set.js
+++ b/src/models/event-timeline-set.js
@@ -150,10 +150,12 @@ EventTimelineSet.prototype.replaceEventId = function(oldEventId, newEventId) {
  *
  * @param {string=} backPaginationToken   token for back-paginating the new timeline
  * @param {?bool} flush  Whether to flush the non-live timelines too.
+ * @param {?function} onNewLiveTimeline Called with the new unfiltered, live timeline
+ * as soon as it's available. This can be used to set event listeners.
  *
  * @fires module:client~MatrixClient#event:"Room.timelineReset"
  */
-EventTimelineSet.prototype.resetLiveTimeline = function(backPaginationToken, flush) {
+EventTimelineSet.prototype.resetLiveTimeline = function(backPaginationToken, flush, onNewLiveTimeline) {
     // if timeline support is disabled, forget about the old timelines
     const resetAllTimelines = !this._timelineSupport || flush;
 
@@ -166,6 +168,12 @@ EventTimelineSet.prototype.resetLiveTimeline = function(backPaginationToken, flu
         newTimeline = this.addTimeline();
     }
 
+    // Allow event listeners to be set up before we start injecting events
+    // as otherwise events will be missed: most importantly the RoomState.newMember
+    // events which are used to set up reEmit on Member events and only fired when
+    // room members are first created.
+    if (onNewLiveTimeline) onNewLiveTimeline(newTimeline);
+
     // initialise the state in the new timeline from our last known state
     const evMap = this._liveTimeline.getState(EventTimeline.FORWARDS).events;
     const events = [];
diff --git a/src/models/room.js b/src/models/room.js
index a1f88cd7c246cb1448ff1f3e349e644692731124..ce86e142f3f43174630610273e9e5454cbc5f823 100644
--- a/src/models/room.js
+++ b/src/models/room.js
@@ -207,9 +207,16 @@ Room.prototype.getLiveTimeline = function() {
  * @param {string=} backPaginationToken   token for back-paginating the new timeline
  * @param {boolean=} flush True to remove all events in all timelines. If false, only
  * the live timeline is reset.
+ * @param {Object=} onNewLiveTimeline Function called with the new live timeline object
+ * once it has been created, but before it has had any event inserted into it. This can be
+ * used to add any event listeners.
  */
-Room.prototype.resetLiveTimeline = function(backPaginationToken, flush) {
-    for (let i = 0; i < this._timelineSets.length; i++) {
+Room.prototype.resetLiveTimeline = function(backPaginationToken, flush, onNewLiveTimeline) {
+    // The unfiltered timeline set (we pass the onNewLiveTimeline callback here)
+    this._timelineSets[0].resetLiveTimeline(backPaginationToken, flush, onNewLiveTimeline);
+
+    // any other timeline sets
+    for (let i = 1; i < this._timelineSets.length; i++) {
         this._timelineSets[i].resetLiveTimeline(backPaginationToken, flush);
     }
 
diff --git a/src/sync.js b/src/sync.js
index 284b967efb11991b152a01f38d72a1335fe6cbae..6aee97b072d0cb198a2fdb40693164418e6d2d89 100644
--- a/src/sync.js
+++ b/src/sync.js
@@ -120,7 +120,7 @@ SyncApi.prototype.createRoom = function(roomId) {
                           "Room.localEchoUpdated",
                           "Room.accountData",
                          ]);
-    this._registerStateListeners(room);
+    this._registerStateListeners(room, room.currentState);
     return room;
 };
 
@@ -128,15 +128,15 @@ SyncApi.prototype.createRoom = function(roomId) {
  * @param {Room} room
  * @private
  */
-SyncApi.prototype._registerStateListeners = function(room) {
+SyncApi.prototype._registerStateListeners = function(room, currentState) {
     const client = this.client;
     // we need to also re-emit room state and room member events, so hook it up
     // to the client now. We need to add a listener for RoomState.members in
     // order to hook them correctly. (TODO: find a better way?)
-    reEmit(client, room.currentState, [
+    reEmit(client, currentState, [
         "RoomState.events", "RoomState.members", "RoomState.newMember",
     ]);
-    room.currentState.on("RoomState.newMember", function(event, state, member) {
+    currentState.on("RoomState.newMember", function(event, state, member) {
         member.user = client.getUser(member.userId);
         reEmit(
             client, member,
@@ -892,14 +892,15 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
                 room.resetLiveTimeline(
                     joinObj.timeline.prev_batch,
                     self.opts.canResetEntireTimeline(room.roomId),
+                    (newLiveTimeline) => {
+                        self._registerStateListeners(room, newLiveTimeline.getState(EventTimeline.FORWARDS));
+                    }
                 );
 
                 // We have to assume any gap in any timeline is
                 // reason to stop incrementally tracking notifications and
                 // reset the timeline.
                 client.resetNotifTimelineSet();
-
-                self._registerStateListeners(room);
             }
         }