kopia lustrzana https://codeberg.org/nmkj/audon
support emoji reactions #12
rodzic
7545855b00
commit
1f084e12bc
|
@ -29,7 +29,7 @@
|
|||
{% end %}
|
||||
</head>
|
||||
<body>
|
||||
<div id="app" data-version='0.1.0-alpha'></div>
|
||||
<div id="app" data-version='0.1.0-alpha2'></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
{
|
||||
"name": "audon-fe",
|
||||
"version": "0.1.0-dev",
|
||||
"version": "0.1.0-alpha",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "audon-fe",
|
||||
"version": "0.1.0-dev",
|
||||
"version": "0.1.0-alpha",
|
||||
"dependencies": {
|
||||
"@intlify/unplugin-vue-i18n": "^0.8.1",
|
||||
"@picmo/popup-picker": "^5.7.2",
|
||||
"@uriopass/nosleep.js": "^0.12.2",
|
||||
"@vuelidate/core": "^2.0.0",
|
||||
"@vuelidate/validators": "^2.0.0",
|
||||
|
@ -18,6 +19,7 @@
|
|||
"lodash-es": "^4.17.21",
|
||||
"luxon": "^3.1.1",
|
||||
"masto": "^4.9.0",
|
||||
"picmo": "^5.7.2",
|
||||
"pinia": "^2.0.26",
|
||||
"vue": "^3.2.45",
|
||||
"vue-i18n": "^9.2.2",
|
||||
|
@ -100,6 +102,19 @@
|
|||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.1.0.tgz",
|
||||
"integrity": "sha512-zbsLwtnHo84w1Kc8rScAo5GMk1GdecSlrflIbfnEBJwvTSj1SL6kkOYV+nHraMCPEy+RNZZUaZyL8JosDGCtGQ=="
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.1.0.tgz",
|
||||
"integrity": "sha512-TSogMPVxbRe77QCj1dt8NmRiJasPvuc+eT5jnJ6YpLqgOD2zXc5UA3S1qwybN+GVCDNdKfpKy1oj8RpzLJvh6A==",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
|
||||
|
@ -331,6 +346,20 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@picmo/popup-picker": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@picmo/popup-picker/-/popup-picker-5.7.2.tgz",
|
||||
"integrity": "sha512-AORn2fsYph2NSHZaXViIhDhr0KE2QBjDQnX02TU78Jt76GWxWVV17Y7Kv9aKTO4K7lMSuCCMnq6cZnz+zjgxOQ==",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/joeattardi"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"picmo": "^5.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/aspromise": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||
|
@ -1103,6 +1132,15 @@
|
|||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/emojibase": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/emojibase/-/emojibase-6.1.0.tgz",
|
||||
"integrity": "sha512-1GkKJPXP6tVkYJHOBSJHoGOr/6uaDxZ9xJ6H7m6PfdGXTmQgbALHLWaVRY4Gi/qf5x/gT/NUXLPuSHYLqtLtrQ==",
|
||||
"funding": {
|
||||
"type": "ko-fi",
|
||||
"url": "https://ko-fi.com/milesjohnson"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.15.18",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz",
|
||||
|
@ -2622,6 +2660,17 @@
|
|||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-1.0.0.tgz",
|
||||
"integrity": "sha512-nPdMG0Pd09HuSsr7QOKUXO2Jr9eqaDiZvDwdyIhNG5SHYujkQHYKDfGQkulBxvbDHz8oHLsTgKN86LSwYzSHAg=="
|
||||
},
|
||||
"node_modules/picmo": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/picmo/-/picmo-5.7.2.tgz",
|
||||
"integrity": "sha512-A7c5O8x1Xwq11KBYFY93+GIbHnw9PVz35HaWWHn/dgT08GA67M6cXKjjwzLnEAyXSdxXKrEk8/gPyTs+ibzWfQ==",
|
||||
"dependencies": {
|
||||
"emojibase": "^6.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/joeattardi"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "audon-fe",
|
||||
"version": "0.1.0-alpha",
|
||||
"version": "0.1.0-alpha2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "cp -v index.dev.html index.html && vite",
|
||||
|
@ -10,6 +10,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@intlify/unplugin-vue-i18n": "^0.8.1",
|
||||
"@picmo/popup-picker": "^5.7.2",
|
||||
"@uriopass/nosleep.js": "^0.12.2",
|
||||
"@vuelidate/core": "^2.0.0",
|
||||
"@vuelidate/validators": "^2.0.0",
|
||||
|
@ -19,6 +20,7 @@
|
|||
"lodash-es": "^4.17.21",
|
||||
"luxon": "^3.1.1",
|
||||
"masto": "^4.9.0",
|
||||
"picmo": "^5.7.2",
|
||||
"pinia": "^2.0.26",
|
||||
"vue": "^3.2.45",
|
||||
"vue-i18n": "^9.2.2",
|
||||
|
|
|
@ -2,19 +2,24 @@
|
|||
import { mdiMicrophone, mdiMicrophoneOff } from "@mdi/js";
|
||||
import { webfinger } from "../assets/utils";
|
||||
export default {
|
||||
setup() {
|
||||
return {
|
||||
mdiMicrophone,
|
||||
mdiMicrophoneOff,
|
||||
webfinger,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
talking: Boolean,
|
||||
type: String,
|
||||
data: Object,
|
||||
muted: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mdiMicrophone,
|
||||
mdiMicrophoneOff,
|
||||
};
|
||||
emoji: String,
|
||||
},
|
||||
computed: {
|
||||
showEmoji() {
|
||||
return this.emoji !== undefined;
|
||||
},
|
||||
canSpeak() {
|
||||
return (
|
||||
this.type === "host" ||
|
||||
|
@ -47,9 +52,6 @@ export default {
|
|||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
webfinger,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -62,6 +64,17 @@ export default {
|
|||
:color="badgeProps.colour"
|
||||
>
|
||||
<v-avatar :class="{ rounded: true, talk: talking }" size="70">
|
||||
<v-overlay
|
||||
v-model="showEmoji"
|
||||
contained
|
||||
persistent
|
||||
scroll-strategy="none"
|
||||
no-click-animation
|
||||
scrim="#000000"
|
||||
class="align-center justify-center reaction"
|
||||
>
|
||||
<span>{{ emoji }}</span>
|
||||
</v-overlay>
|
||||
<v-img :src="data?.avatar"></v-img>
|
||||
</v-avatar>
|
||||
</v-badge>
|
||||
|
@ -70,6 +83,17 @@ export default {
|
|||
:class="{ rounded: true, talk: talking, 'mt-2': true }"
|
||||
size="70"
|
||||
>
|
||||
<v-overlay
|
||||
v-model="showEmoji"
|
||||
contained
|
||||
persistent
|
||||
scroll-strategy="none"
|
||||
no-click-animation
|
||||
scrim="#000000"
|
||||
class="align-center justify-center reaction"
|
||||
>
|
||||
<span>{{ emoji }}</span>
|
||||
</v-overlay>
|
||||
<v-img :src="data?.avatar"></v-img>
|
||||
</v-avatar>
|
||||
<h4 :class="canSpeak ? 'mt-1' : 'mt-2'">
|
||||
|
@ -88,4 +112,10 @@ export default {
|
|||
.talk {
|
||||
outline: 3px solid cornflowerblue;
|
||||
}
|
||||
|
||||
.reaction span {
|
||||
font-size: 2rem;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -3,12 +3,13 @@ import axios from "axios";
|
|||
import { pushNotFound, webfinger } from "../assets/utils";
|
||||
import { useMastodonStore } from "../stores/mastodon";
|
||||
import { map, some, omit, filter, trim, clone } from "lodash-es";
|
||||
import { darkTheme } from "picmo";
|
||||
import { createPopup } from "@picmo/popup-picker";
|
||||
import Participant from "../components/Participant.vue";
|
||||
import {
|
||||
mdiMicrophone,
|
||||
mdiMicrophoneOff,
|
||||
mdiMicrophoneQuestion,
|
||||
mdiDoorClosed,
|
||||
mdiVolumeOff,
|
||||
mdiClose,
|
||||
mdiCheck,
|
||||
|
@ -16,6 +17,7 @@ import {
|
|||
mdiLogout,
|
||||
mdiDotsVertical,
|
||||
mdiPencil,
|
||||
mdiEmoticon,
|
||||
} from "@mdi/js";
|
||||
import {
|
||||
Room,
|
||||
|
@ -68,10 +70,13 @@ export default {
|
|||
mdiCheck,
|
||||
mdiDotsVertical,
|
||||
mdiPencil,
|
||||
mdiEmoticon,
|
||||
v$: useVuelidate(),
|
||||
donStore: useMastodonStore(),
|
||||
decoder: new TextDecoder(),
|
||||
encoder: new TextEncoder(),
|
||||
roomClient: new Room(),
|
||||
emojiPicker: null,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
|
@ -98,7 +103,6 @@ export default {
|
|||
roomID: this.$route.params.id,
|
||||
loading: true,
|
||||
mainHeight: 700,
|
||||
roomClient: new Room(),
|
||||
roomInfo: {
|
||||
title: this.$t("connecting"),
|
||||
description: "",
|
||||
|
@ -122,6 +126,7 @@ export default {
|
|||
{ title: this.$t("form.relationships.private"), value: "private" },
|
||||
],
|
||||
participants: {},
|
||||
emojiReactions: {},
|
||||
cachedMastoData: {},
|
||||
activeSpeakerIDs: new Set(),
|
||||
mutedSpeakerIDs: new Set(),
|
||||
|
@ -315,11 +320,15 @@ export default {
|
|||
{ "kind": "speak_request" }
|
||||
{ "kind": "chat", "data": "..." }
|
||||
{ "kind": "request_declined", "audon_id": "..."}
|
||||
{ "kind": "emoji", "emoji": "..." }
|
||||
*/
|
||||
const strData = self.decoder.decode(payload);
|
||||
const jsonData = JSON.parse(strData);
|
||||
const metadata = JSON.parse(participant.metadata);
|
||||
switch (jsonData?.kind) {
|
||||
case "emoji":
|
||||
self.addEmojiReaction(participant.identity, jsonData.emoji);
|
||||
break;
|
||||
case "speak_request": // someone is wanting to be a speaker
|
||||
self.onSpeakRequestReceived(participant);
|
||||
break;
|
||||
|
@ -496,6 +505,53 @@ export default {
|
|||
this.showRequestedNotification = true;
|
||||
}
|
||||
},
|
||||
onPickerPopup() {
|
||||
const btn = document.getElementById("pickerButton");
|
||||
if (!this.emojiPicker) {
|
||||
const picker = createPopup(
|
||||
{
|
||||
theme: darkTheme,
|
||||
emojiSize: "1.8rem",
|
||||
autoFocus: "none",
|
||||
},
|
||||
{
|
||||
referenceElement: btn,
|
||||
triggerElement: btn,
|
||||
position: "top",
|
||||
hideOnEmojiSelect: true,
|
||||
}
|
||||
);
|
||||
const self = this;
|
||||
picker.addEventListener("emoji:select", ({ emoji }) => {
|
||||
self.onEmojiSelected(emoji);
|
||||
});
|
||||
this.emojiPicker = picker;
|
||||
}
|
||||
this.emojiPicker.open();
|
||||
},
|
||||
async onEmojiSelected(emoji) {
|
||||
this.showEmojiMenu = false;
|
||||
const data = { kind: "emoji", emoji };
|
||||
const payload = this.encoder.encode(JSON.stringify(data));
|
||||
await this.roomClient.localParticipant.publishData(
|
||||
payload,
|
||||
DataPacket_Kind.RELIABLE
|
||||
);
|
||||
this.addEmojiReaction(this.roomClient.localParticipant.identity, emoji);
|
||||
},
|
||||
addEmojiReaction(identity, emoji) {
|
||||
const self = this;
|
||||
if (self.emojiReactions[identity]) {
|
||||
clearTimeout(self.emojiReactions[identity].timeoutID);
|
||||
}
|
||||
const timeoutID = setTimeout(() => {
|
||||
self.emojiReactions = omit(self.emojiReactions, identity);
|
||||
}, 5000);
|
||||
self.emojiReactions[identity] = {
|
||||
timeoutID,
|
||||
emoji,
|
||||
};
|
||||
},
|
||||
async publishDataToHostAndCohosts(data) {
|
||||
const payload = this.encoder.encode(JSON.stringify(data));
|
||||
// participants - speakers
|
||||
|
@ -807,6 +863,7 @@ export default {
|
|||
type="host"
|
||||
:data="cachedMastoData[key]"
|
||||
:muted="mutedSpeakerIDs.has(key)"
|
||||
:emoji="emojiReactions[key]?.emoji"
|
||||
></Participant>
|
||||
<Participant
|
||||
v-if="isCohost(value)"
|
||||
|
@ -814,6 +871,7 @@ export default {
|
|||
type="cohost"
|
||||
:data="cachedMastoData[key]"
|
||||
:muted="mutedSpeakerIDs.has(key)"
|
||||
:emoji="emojiReactions[key]?.emoji"
|
||||
></Participant>
|
||||
<Participant
|
||||
v-if="isSpeaker(key)"
|
||||
|
@ -821,6 +879,7 @@ export default {
|
|||
type="speaker"
|
||||
:data="cachedMastoData[key]"
|
||||
:muted="mutedSpeakerIDs.has(key)"
|
||||
:emoji="emojiReactions[key]?.emoji"
|
||||
>
|
||||
</Participant>
|
||||
</template>
|
||||
|
@ -831,6 +890,7 @@ export default {
|
|||
v-if="!isHost(key) && !isCohost(value) && !isSpeaker(key)"
|
||||
:data="cachedMastoData[key]"
|
||||
type="listener"
|
||||
:emoji="emojiReactions[key]?.emoji"
|
||||
></Participant>
|
||||
</template>
|
||||
</v-row>
|
||||
|
@ -845,7 +905,15 @@ export default {
|
|||
>{{ $t("enterRoom") }}</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
<v-card-actions v-else class="justify-center" style="gap: 50px">
|
||||
<v-card-actions v-else class="justify-center" style="gap: 20px">
|
||||
<v-btn
|
||||
:icon="mdiEmoticon"
|
||||
color="white"
|
||||
variant="flat"
|
||||
@click="onPickerPopup"
|
||||
id="pickerButton"
|
||||
>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
:icon="micStatusIcon"
|
||||
color="white"
|
||||
|
|
Ładowanie…
Reference in New Issue