expand_less
+
+
+
+
+
+
+ {{
+ member.name.substring(0, 1).toUpperCase()
+ }}
+
+
+
+
@@ -11,7 +24,7 @@
@click.stop="otherAvatarClicked($refs.avatar.$el)">
{{
- eventSenderDisplayName(currentAudioEvent).substring(0, 1).toUpperCase()
+ eventSenderDisplayName(currentAudioEvent).substring(0, 1).toUpperCase()
}}
@@ -25,7 +38,7 @@
{{ currentTime }} / {{ totalTime }}
@@ -77,7 +90,13 @@ export default {
default: function () {
return null;
}
- }
+ },
+ recordingMembers: {
+ type: Array,
+ default: function () {
+ return []
+ }
+ },
},
data() {
return {
@@ -98,8 +117,26 @@ export default {
document.body.classList.add("dark");
this.$root.$on('playback-start', this.onPlaybackStart);
this.player = this.$refs.player;
+ this.player.autoplay = false;
this.player.addEventListener("timeupdate", this.updateProgressBar);
this.player.addEventListener("play", () => {
+ if (!this.analyser) {
+
+ const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+ let audioSource = null;
+ if (audioCtx) {
+ audioSource = audioCtx.createMediaElementSource(this.player);
+ this.analyser = audioCtx.createAnalyser();
+ audioSource.connect(this.analyser);
+ this.analyser.connect(audioCtx.destination);
+
+ this.analyser.fftSize = 128;
+ const bufferLength = this.analyser.frequencyBinCount;
+ this.analyzerDataArray = new Uint8Array(bufferLength);
+ }
+ }
+
+
this.playing = true;
this.updateVisualization();
if (this.currentAudioEvent) {
@@ -116,19 +153,6 @@ export default {
this.clearVisualization();
this.onPlaybackEnd();
});
-
- const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
- let audioSource = null;
- if (audioCtx) {
- audioSource = audioCtx.createMediaElementSource(this.player);
- this.analyser = audioCtx.createAnalyser();
- audioSource.connect(this.analyser);
- this.analyser.connect(audioCtx.destination);
-
- this.analyser.fftSize = 128;
- const bufferLength = this.analyser.frequencyBinCount;
- this.analyzerDataArray = new Uint8Array(bufferLength);
- }
},
beforeDestroy() {
document.body.classList.remove("dark");
@@ -162,6 +186,11 @@ export default {
}
},
},
+ recordingMembersExceptMe() {
+ return this.recordingMembers.filter((member) => {
+ return member.userId !== this.$matrix.currentUserId;
+ });
+ },
},
watch: {
autoplay: {
@@ -204,7 +233,17 @@ export default {
return;
}
this.src = null;
+ const autoPlayWasSet = this.autoPlayNextEvent;
this.autoPlayNextEvent = false;
+
+ if (value.getSender() == this.$matrix.currentUserId) {
+ // Sent by us. Don't autoplay if we just sent this (i.e. it is ahead of our read marker)
+ if (this.room && !this.room.getReceiptsForEvent(value).includes(value.getSender())) {
+ this.player.autoplay = false;
+ this.autoPlayNextEvent = autoPlayWasSet;
+ }
+ }
+
this.loadAudioAttachmentSource();
}
},
@@ -397,6 +436,18 @@ export default {
});
}
},
+ memberAvatar(member) {
+ if (member) {
+ return member.getAvatarUrl(
+ this.$matrix.matrixClient.getHomeserverUrl(),
+ 40,
+ 40,
+ "scale",
+ true
+ );
+ }
+ return null;
+ },
}
};
diff --git a/src/components/Chat.vue b/src/components/Chat.vue
index e3e48c2..bf26cf4 100644
--- a/src/components/Chat.vue
+++ b/src/components/Chat.vue
@@ -4,20 +4,21 @@
{{ $tc("room.invitations", invitationCount) }}
-
-
+
-
+
-
arrow_downward
@@ -421,7 +422,7 @@ export default {
chatContainer() {
const container = this.$refs.chatContainer;
console.log("GOT CONTAINER", container);
- if (this.useAudioLayout) {
+ if (this.useVoiceMode) {
return container.$el;
}
return container;
@@ -522,15 +523,10 @@ export default {
me && this.room.currentState && this.room.currentState.hasSufficientPowerLevelFor("redact", me.powerLevel);
return isAdmin;
},
- useAudioLayout: {
+ useVoiceMode: {
get: function () {
- if (this.room) {
- const tags = this.room.tags;
- if (tags && tags["ui_options"]) {
- return tags["ui_options"]["audio_layout"] === 1;
- }
- }
- return false;
+ if (!this.$config.experimental_voice_mode) return false;
+ return util.useVoiceMode(this.room);
},
}
},
@@ -611,6 +607,12 @@ export default {
});
}
},
+ showRecorder(show) {
+ if (this.useVoiceMode) {
+ // Send typing indicators when recorder UI is opened/closed
+ this.$matrix.matrixClient.sendTyping(this.roomId, show, 10 * 60 * 1000);
+ }
+ }
},
methods: {
@@ -826,7 +828,7 @@ export default {
const loadingDone = this.initialLoadDone;
this.$matrix.matrixClient.decryptEventIfNeeded(event, {});
- if (this.initialLoadDone && !this.useAudioLayout) {
+ if (this.initialLoadDone && !this.useVoiceMode) {
this.paginateBackIfNeeded();
}
@@ -1055,7 +1057,7 @@ export default {
.then((success) => {
if (success) {
this.events = this.timelineWindow.getEvents();
- if (!this.useAudioLayout) {
+ if (!this.useVoiceMode) {
this.scrollPosition.prepareFor("down");
this.$nextTick(() => {
// restore scroll position!
@@ -1312,7 +1314,7 @@ export default {
let eventIdFirst = null;
let eventIdLast = null;
- if (!this.useAudioLayout) {
+ if (!this.useVoiceMode) {
const container = this.chatContainer;
const elFirst = util.getFirstVisibleElement(container);
const elLast = util.getLastVisibleElement(container);
diff --git a/src/components/CreateRoom.vue b/src/components/CreateRoom.vue
index 3332e2a..85d08ea 100644
--- a/src/components/CreateRoom.vue
+++ b/src/components/CreateRoom.vue
@@ -3,29 +3,23 @@
{{ $t("new_room.new_room") }}
- -->
@@ -39,51 +33,43 @@
-
+
{{ $t("new_room.name_room") }}
-
- {{ $t("new_room.room_topic") }}
-
- {{roomCreationErrorMsg}}
-
-
- {{ status }}
-
-
- {{ $t("new_room.create") }}
-
+
+ {{ $t("new_room.room_topic") }}
+
+
+
+
+
+
{{ $t("new_room.options") }}
+
expand_more
+
expand_less
+
+
+
+
+
{{ $t('room_info.voice_mode') }}
+
{{ $t('room_info.voice_mode_info') }}
+
+
+
+
+
+
+ {{ roomCreationErrorMsg }}
+
+
+ {{ status }}
+
+
+ {{ $t("new_room.create") }}
+
@@ -91,126 +77,101 @@
+
+ {{ $t("new_room.link_copied") }}
+
+ -->
+
+ {{ status }}
+ -->
-
-
+
+
{{ $t("join.choose_name") }}
-
+
-
+
@@ -219,33 +180,23 @@
{{ data.item.name }}
-
-
-
+
+
+
-
+
-
- {{ $t("join.enter_room") }}
-
+
+ {{ $t("join.enter_room") }}
+
@@ -253,7 +204,7 @@
diff --git a/src/plugins/utils.js b/src/plugins/utils.js
index 740c2a4..95ea715 100644
--- a/src/plugins/utils.js
+++ b/src/plugins/utils.js
@@ -3,6 +3,8 @@ import * as ContentHelpers from "matrix-js-sdk/lib/content-helpers";
import dataUriToBuffer from "data-uri-to-buffer";
import ImageResize from "image-resize";
+export const ROOM_TYPE_VOICE_MODE = "im.keanu.room_type_voice";
+
const sizeOf = require("image-size");
var dayjs = require('dayjs');
@@ -424,6 +426,36 @@ class Util {
});
}
+ /**
+ * Return 'true' if we should use voice mode for the given room.
+ *
+ * The default value is given by the room itself. If the "type" of the
+ * room is set to 'im.keanu.room_type_voice' then we default to voice mode,
+ * else not. The user can then override this default by flipping the "voice mode"
+ * swicth on room settings (it will be persisted as a user specific tag on the room)
+ */
+ useVoiceMode(roomOrNull) {
+ if (roomOrNull) {
+ const room = roomOrNull;
+
+ // Have we changed our local view mode of this room?
+ const tags = room.tags;
+ if (tags && tags["ui_options"]) {
+ return tags["ui_options"]["voice_mode"] === 1;
+ }
+
+ // Was the room created with a voice mode type?
+ const createEvent = room.currentState.getStateEvents(
+ "m.room.create",
+ ""
+ );
+ if (createEvent) {
+ return createEvent.getContent().type === ROOM_TYPE_VOICE_MODE;
+ }
+ }
+ return false;
+ }
+
/** Generate a random user name */
randomUser(prefix) {
var pfx = prefix ? prefix.replace(/[^0-9a-zA-Z\-_]/gi, '') : null;