diff --git a/src/d2m/actions/update-pins.js b/src/d2m/actions/update-pins.js index 6ef477f..15febaa 100644 --- a/src/d2m/actions/update-pins.js +++ b/src/d2m/actions/update-pins.js @@ -33,9 +33,10 @@ async function updatePins(channelID, roomID, convertedTimestamp) { } throw e } - const pinned = pinsToList.pinsToList(discordPins) const kstate = await ks.roomToKState(roomID) + const pinned = pinsToList.pinsToList(discordPins, kstate) + const diff = ks.diffKState(kstate, {"m.room.pinned_events/": {pinned}}) await ks.applyKStateDiffToRoom(roomID, diff) diff --git a/src/d2m/converters/pins-to-list.js b/src/d2m/converters/pins-to-list.js index 047bb9f..3e890ea 100644 --- a/src/d2m/converters/pins-to-list.js +++ b/src/d2m/converters/pins-to-list.js @@ -4,16 +4,28 @@ const {select} = require("../../passthrough") /** * @param {import("discord-api-types/v10").RESTGetAPIChannelPinsResult} pins + * @param {{"m.room.pinned_events/"?: {pinned?: string[]}}} kstate */ -function pinsToList(pins) { +function pinsToList(pins, kstate) { + let alreadyPinned = kstate["m.room.pinned_events/"]?.pinned || [] + + // If any of the already pinned messages are bridged messages then remove them from the already pinned list. + // * If a bridged message is still pinned then it'll be added back in the next step. + // * If a bridged message was unpinned from Discord-side then it'll be unpinned from our side due to this step. + // * Matrix-only unbridged messages that are pinned will remain pinned. + alreadyPinned = alreadyPinned.filter(event_id => { + const messageID = select("event_message", "message_id", {event_id}).pluck().get() + return !messageID || pins.find(m => m.id === messageID) // if it is bridged then remove it from the filter + }) + /** @type {string[]} */ const result = [] for (const message of pins) { const eventID = select("event_message", "event_id", {message_id: message.id, part: 0}).pluck().get() - if (eventID) result.push(eventID) + if (eventID && !alreadyPinned.includes(eventID)) result.push(eventID) } result.reverse() - return result + return alreadyPinned.concat(result) } module.exports.pinsToList = pinsToList diff --git a/src/d2m/converters/pins-to-list.test.js b/src/d2m/converters/pins-to-list.test.js index 7ee89b6..d0657cb 100644 --- a/src/d2m/converters/pins-to-list.test.js +++ b/src/d2m/converters/pins-to-list.test.js @@ -3,10 +3,59 @@ const data = require("../../../test/data") const {pinsToList} = require("./pins-to-list") test("pins2list: converts known IDs, ignores unknown IDs", t => { - const result = pinsToList(data.pins.faked) + const result = pinsToList(data.pins.faked, {}) t.deepEqual(result, [ "$lnAF9IosAECTnlv9p2e18FG8rHn-JgYKHEHIh5qdFv4", "$mtR8cJqM4fKno1bVsm8F4wUVqSntt2sq6jav1lyavuA", "$X16nfVks1wsrhq4E9SSLiqrf2N8KD0erD0scZG7U5xg" ]) }) + +test("pins2list: already pinned duplicate items are not moved", t => { + const result = pinsToList(data.pins.faked, { + "m.room.pinned_events/": { + pinned: [ + "$mtR8cJqM4fKno1bVsm8F4wUVqSntt2sq6jav1lyavuA" + ] + } + }) + t.deepEqual(result, [ + "$mtR8cJqM4fKno1bVsm8F4wUVqSntt2sq6jav1lyavuA", + "$lnAF9IosAECTnlv9p2e18FG8rHn-JgYKHEHIh5qdFv4", + "$X16nfVks1wsrhq4E9SSLiqrf2N8KD0erD0scZG7U5xg" + ]) +}) + +test("pins2list: already pinned unknown items are not moved", t => { + const result = pinsToList(data.pins.faked, { + "m.room.pinned_events/": { + pinned: [ + "$unknown1", + "$mtR8cJqM4fKno1bVsm8F4wUVqSntt2sq6jav1lyavuA", + "$unknown2" + ] + } + }) + t.deepEqual(result, [ + "$unknown1", + "$mtR8cJqM4fKno1bVsm8F4wUVqSntt2sq6jav1lyavuA", + "$unknown2", + "$lnAF9IosAECTnlv9p2e18FG8rHn-JgYKHEHIh5qdFv4", + "$X16nfVks1wsrhq4E9SSLiqrf2N8KD0erD0scZG7U5xg" + ]) +}) + +test("pins2list: bridged messages can be unpinned", t => { + const result = pinsToList(data.pins.faked.slice(0, -2), { + "m.room.pinned_events/": { + pinned: [ + "$mtR8cJqM4fKno1bVsm8F4wUVqSntt2sq6jav1lyavuA", + "$lnAF9IosAECTnlv9p2e18FG8rHn-JgYKHEHIh5qdFv4" + ] + } + }) + t.deepEqual(result, [ + "$mtR8cJqM4fKno1bVsm8F4wUVqSntt2sq6jav1lyavuA", + "$X16nfVks1wsrhq4E9SSLiqrf2N8KD0erD0scZG7U5xg", + ]) +})