const {store} = require("../store/store.js") const lsm = require("../lsm.js") const {resolveMxc} = require("../functions.js") let lastBatch = null function sync() { const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/sync`) url.searchParams.append("access_token", lsm.get("access_token")) const filter = { room: { // pulling more from the timeline massively increases download size timeline: { limit: 1 }, // members are not currently needed state: { lazy_load_members: true } }, presence: { // presence is not implemented, ignore it types: [] } } url.searchParams.append("filter", JSON.stringify(filter)) url.searchParams.append("timeout", 20000) if (lastBatch) { url.searchParams.append("since", lastBatch) } return fetch(url.toString()).then(res => res.json()).then(root => { lastBatch = root.next_batch return root }) } function manageSync(root) { try { let newEvents = false let notificationsChange = false // set up directs if (root.account_data) { const directs = root.account_data.events.find(e => e.type === "m.direct") if (directs) { Object.values(directs.content).forEach(ids => { ids.forEach(id => store.directs.add(id)) }) } } // set up rooms if (root.rooms) { if (root.rooms.join) { Object.entries(root.rooms.join).forEach(([id, data]) => { if (!store.rooms.has(id)) { store.rooms.askAdd(id, data) } const room = store.rooms.get(id).value() const timeline = room.timeline if (data.state && data.state.events) timeline.updateStateEvents(data.state.events) if (data.timeline && data.timeline.events) { if (!timeline.from) timeline.from = data.timeline.prev_batch if (data.timeline.events.length) { newEvents = true timeline.updateEvents(data.timeline.events) } } if (data.ephemeral && data.ephemeral.events) timeline.updateEphemeral(data.ephemeral.events) if (data.unread_notifications) { timeline.updateNotificationCount(data.unread_notifications.notification_count) notificationsChange = true } if (data["org.matrix.msc2654.unread_count"] != undefined) { timeline.updateUnreadCount(data["org.matrix.msc2654.unread_count"]) notificationsChange = true } }) } } // set up groups if (root.groups) { Promise.all( Object.keys(root.groups.join).map(id => { if (!store.groups.has(id)) { return Promise.all(["profile", "rooms"].map(path => { const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/groups/${id}/${path}`) url.searchParams.append("access_token", lsm.get("access_token")) return fetch(url.toString()).then(res => res.json()) })).then(([profile, rooms]) => { rooms = rooms.chunk let order = 999 let orderEvent = root.account_data.events.find(e => e.type === "im.vector.web.tag_ordering") if (orderEvent) { if (orderEvent.content.tags.includes(id)) { order = orderEvent.content.tags.indexOf(id) } } const data = { name: profile.name, icon: resolveMxc(profile.avatar_url, 96, "crop"), order } store.groups.askAdd(id, data) rooms.forEach(groupRoom => { if (store.rooms.has(groupRoom.room_id)) { store.rooms.get(groupRoom.room_id).value().setGroup(id) } }) store.newEvents.broadcast("changeSelf") // trigger a room list update }) } }) ).then(() => { store.rooms.sort() }) } if (newEvents) store.newEvents.broadcast("changeSelf") if (notificationsChange) store.notificationsChange.broadcast("changeSelf") } catch (e) { console.error(root) throw e } } function syncLoop() { return sync().then(manageSync).then(syncLoop) } ;[ { id: "directs", name: "Directs", icon: staticFiles.get("/assets/icons/directs.svg"), order: -2 }, { id: "channels", name: "Channels", icon: staticFiles.get("/assets/icons/channels.svg"), order: -1 } ].forEach(data => store.groups.askAdd(data.id, data)) store.activeGroup.set(store.groups.get("directs").value()) if (lsm.get("access_token")) { syncLoop() }