diff --git a/spec.js b/spec.js index d1429c2..4244cdf 100644 --- a/spec.js +++ b/spec.js @@ -54,6 +54,26 @@ module.exports = [ source: "/assets/icons/profile-event.svg", target: "/static/profile-event.svg", }, + { + type: "file", + source: "/assets/icons/call-out.svg", + target: "/static/call-out.svg", + }, + { + type: "file", + source: "/assets/icons/call-in.svg", + target: "/static/call-in.svg", + }, + { + type: "file", + source: "/assets/icons/call-accepted.svg", + target: "/static/call-accepted.svg", + }, + { + type: "file", + source: "/assets/icons/call-rejected.svg", + target: "/static/call-rejected.svg", + }, { type: "sass", source: "/sass/main.sass", diff --git a/src/assets/icons/call-accepted.svg b/src/assets/icons/call-accepted.svg new file mode 100644 index 0000000..fea7031 --- /dev/null +++ b/src/assets/icons/call-accepted.svg @@ -0,0 +1,94 @@ + + + + + + image/svg+xml + + free-icons-solid + + + + + + + + + + free-icons-solid + + + + diff --git a/src/assets/icons/call-in.svg b/src/assets/icons/call-in.svg new file mode 100644 index 0000000..484fe5c --- /dev/null +++ b/src/assets/icons/call-in.svg @@ -0,0 +1,99 @@ + + + + + + image/svg+xml + + free-icons-solid + + + + + + + + + + free-icons-solid + + + + + + diff --git a/src/assets/icons/call-out.svg b/src/assets/icons/call-out.svg new file mode 100644 index 0000000..877bad4 --- /dev/null +++ b/src/assets/icons/call-out.svg @@ -0,0 +1,98 @@ + + + + + + image/svg+xml + + free-icons-solid + + + + + + + + + + free-icons-solid + + + + + + diff --git a/src/assets/icons/call-rejected.svg b/src/assets/icons/call-rejected.svg new file mode 100644 index 0000000..55b3994 --- /dev/null +++ b/src/assets/icons/call-rejected.svg @@ -0,0 +1,98 @@ + + + + + + image/svg+xml + + free-icons-solid + + + + + + + + + + free-icons-solid + + + + + + diff --git a/src/js/events/call.js b/src/js/events/call.js new file mode 100644 index 0000000..a918a48 --- /dev/null +++ b/src/js/events/call.js @@ -0,0 +1,67 @@ +const {UngroupableEvent} = require("./event") +const {ejs} = require("../basic") +const lsm = require("../lsm") +const {extractDisplayName, resolveMxc, extractLocalpart} = require("../functions") + +class CallEvent extends UngroupableEvent { + constructor(data) { + super(data) + this.class("c-message-event") + this.senderName = extractLocalpart(this.data.sender) + this.render() + } + + renderInner(iconURL, elements) { + this.clearChildren() + this.child( + ejs("div").class("c-message-event__inner").child( + iconURL ? ejs("img").class("c-message-event__icon").attribute("width", "20").attribute("height", "20").attribute("src", iconURL) : "", + ...elements + ) + ) + super.render() + } +} + +class CallInviteEvent extends CallEvent { + static canRender(eventData) { + return eventData.type === "m.call.invite" + } + + render() { + const icon = this.data.sender === lsm.get("mx_user_id") ? "static/call-out.svg" : "static/call-in.svg" + this.renderInner(icon, [ + this.senderName, + " started a VOIP call, but Carbon doesn't support VOIP calls" + ]) + } +} + +class CallAnswerEvent extends CallEvent { + static canRender(eventData) { + return eventData.type === "m.call.answer" + } + + render() { + this.renderInner("static/call-accepted.svg", [ + this.senderName, + " answered the call" + ]) + } +} + +class CallHangupEvent extends CallEvent { + static canRender(eventData) { + return eventData.type === "m.call.hangup" + } + + render() { + const reason = this.data.content.reason === "invite_timeout" ? "missed the call" : "hung up the call" + this.renderInner("static/call-rejected.svg", [ + this.senderName, + " " + reason + ]) + } +} + +module.exports = [CallInviteEvent, CallAnswerEvent, CallHangupEvent] diff --git a/src/js/events/image.js b/src/js/events/image.js index 2c8a7d3..1a32bcf 100644 --- a/src/js/events/image.js +++ b/src/js/events/image.js @@ -24,7 +24,7 @@ class Image extends GroupableEvent { wrapper.class("c-media--spoiler") const wall = ejs("div").class("c-media__spoiler").text("Spoiler") wrapper.child(wall) - function toggle() { + const toggle = () => { wrapper.element.classList.toggle("c-media--shown") } wrapper.on("click", toggle) diff --git a/src/js/events/render-event.js b/src/js/events/render-event.js index 9337a2d..627c60f 100644 --- a/src/js/events/render-event.js +++ b/src/js/events/render-event.js @@ -3,12 +3,14 @@ const messageEvent = require("./message") const encryptedEvent = require("./encrypted") const membershipEvent = require("./membership") const unknownEvent = require("./unknown") +const callEvent = require("./call") const events = [ ...imageEvent, ...messageEvent, ...encryptedEvent, ...membershipEvent, + ...callEvent, ...unknownEvent, ] diff --git a/src/js/functions.js b/src/js/functions.js index db43a16..3c346c4 100644 --- a/src/js/functions.js +++ b/src/js/functions.js @@ -1,7 +1,9 @@ const lsm = require("./lsm.js") function resolveMxc(url, size, method) { - let [server, id] = url.match(/^mxc:\/\/([^/]+)\/(.*)/).slice(1) + const match = url.match(/^mxc:\/\/([^/]+)\/(.*)/) + if (!match) return url + let [server, id] = match.slice(1) id = id.replace(/#.*$/, "") if (size && method) { return `${lsm.get("domain")}/_matrix/media/r0/thumbnail/${server}/${id}?width=${size}&height=${size}&method=${method}` diff --git a/src/js/timeline.js b/src/js/timeline.js index 3ef646b..4a55406 100644 --- a/src/js/timeline.js +++ b/src/js/timeline.js @@ -259,7 +259,7 @@ class Timeline extends Subscribable { this.map.get(id).update(eventData) } else { // skip displaying events that we don't know how to - if (eventData.type === "m.reaction") { + if (["m.reaction", "m.call.candidates"].includes(eventData.type)) { continue } // skip redacted events