Merge remote-tracking branch 'origin/saveMessages'
This commit is contained in:
commit
4a2bb7cb52
6 changed files with 235 additions and 160 deletions
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
import { sendAudio } from './session'
|
||||
import { ipcMain } from "electron";
|
||||
import { backgroundMitt } from './emitter';
|
||||
|
||||
const portAudio = require('naudiodon');
|
||||
|
||||
let ai: typeof portAudio.AudioIO | null = null;
|
||||
|
|
@ -83,9 +85,14 @@ export function play(input: Buffer): void {
|
|||
|
||||
const audio = bufSplit(input, 8192);
|
||||
|
||||
// call this fuction after last audio chunk has been written.
|
||||
// Called on end of write.
|
||||
const callback = () => {
|
||||
// TODO: clear portAudio writable buffer on write end.
|
||||
|
||||
// We stop audio playback anim.
|
||||
backgroundMitt.emit('ipc-renderer', {
|
||||
endpoint: 'stop-playback-anim'
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
write();
|
||||
|
|
|
|||
|
|
@ -55,14 +55,18 @@ const onMessage = (message: string): void => {
|
|||
|
||||
// check to see if this is a Render Message.
|
||||
if (m.content) {
|
||||
|
||||
// If there is audio, play it.
|
||||
if (m.content.audio) {
|
||||
const audioBytes = Buffer.from(m.content.audio as string, 'hex');
|
||||
m.content.audio = true;
|
||||
play(audioBytes);
|
||||
} else {
|
||||
m.content.audio = false;
|
||||
}
|
||||
|
||||
// if there is no unique id, add it.
|
||||
if(!m.uid) m.uid = uuidv4();
|
||||
if(!m.uid) m.uid = uuidv4();
|
||||
}
|
||||
|
||||
backgroundMitt.emit('ipc-renderer', {
|
||||
|
|
|
|||
|
|
@ -143,28 +143,22 @@ export default defineComponent({
|
|||
.pause {
|
||||
opacity: 1;
|
||||
&::before {
|
||||
-moz-animation: audio1 1.5s infinite ease-in-out;
|
||||
-o-animation: audio1 1.5s infinite ease-in-out;
|
||||
-webkit-animation: audio1 1.5s infinite ease-in-out;
|
||||
animation: audio1 1.5s infinite ease-in-out;
|
||||
-moz-animation: circle1 1.5s infinite ease-in-out;
|
||||
-o-animation: circle1 1.5s infinite ease-in-out;
|
||||
-webkit-animation: circle1 1.5s infinite ease-in-out;
|
||||
animation: circle1 1.5s infinite ease-in-out;
|
||||
}
|
||||
&::after {
|
||||
-moz-animation: audio2 2.2s infinite ease-in-out;
|
||||
-o-animation: audio2 2.2s infinite ease-in-out;
|
||||
-webkit-animation: audio2 2.2s infinite ease-in-out;
|
||||
animation: audio2 2.2s infinite ease-in-out;
|
||||
-moz-animation: circle2 2.2s infinite ease-in-out;
|
||||
-o-animation: circle2 2.2s infinite ease-in-out;
|
||||
-webkit-animation: circle2 2.2s infinite ease-in-out;
|
||||
animation: circle2 2.2s infinite ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.animate-audio1 {
|
||||
-moz-animation: audio1 1.5s infinite ease-in-out;
|
||||
-o-animation: audio1 1.5s infinite ease-in-out;
|
||||
-webkit-animation: audio1 1.5s infinite ease-in-out;
|
||||
animation: audio1 1.5s infinite ease-in-out;
|
||||
}
|
||||
@keyframes audio1 {
|
||||
@keyframes circle1 {
|
||||
0%,
|
||||
100% {
|
||||
box-shadow: 0 0 0 0.4em rgba(195, 195, 195, 0.4);
|
||||
|
|
@ -179,13 +173,8 @@ export default defineComponent({
|
|||
box-shadow: 0 0 0 0.25em rgba(195, 195, 195, 0.25);
|
||||
}
|
||||
}
|
||||
.animate-audio2 {
|
||||
-moz-animation: audio2 2.2s infinite ease-in-out;
|
||||
-o-animation: audio2 2.2s infinite ease-in-out;
|
||||
-webkit-animation: audio2 2.2s infinite ease-in-out;
|
||||
animation: audio2 2.2s infinite ease-in-out;
|
||||
}
|
||||
@keyframes audio2 {
|
||||
|
||||
@keyframes circle2 {
|
||||
0%,
|
||||
100% {
|
||||
box-shadow: 0 0 0 0.25em rgba(195, 195, 195, 0.15);
|
||||
|
|
|
|||
|
|
@ -50,7 +50,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Render Crimata icon or friend's initials. -->
|
||||
<div v-if="message.modifier == 'fr'" class="photo">
|
||||
<div v-if="message.modifier == 'fr'" class="photo"
|
||||
:class="{ playing: isPlaying }"
|
||||
>
|
||||
{{ message.context[0] }}
|
||||
</div>
|
||||
|
||||
|
|
@ -65,7 +67,7 @@
|
|||
|
||||
<script lang='ts'>
|
||||
|
||||
import {defineComponent} from 'vue';
|
||||
import {defineComponent, ref, onMounted} from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: "MessageItem",
|
||||
|
|
@ -75,6 +77,29 @@
|
|||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
setup(props) {
|
||||
const isPlaying = ref(false);
|
||||
|
||||
const onStopPlayback = (e: any) => {
|
||||
window.ipcRenderer.removeAllListeners("stop-playback-anim");
|
||||
isPlaying.value = false
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
// Turn on audio playback animation if audio.
|
||||
if (props.message.content.audio === true) {
|
||||
window.ipcRenderer.on("stop-playback-anim", onStopPlayback);
|
||||
isPlaying.value = true
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
return {
|
||||
isPlaying
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
|
@ -152,6 +177,15 @@
|
|||
animation-duration: 0.25s;
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
from {
|
||||
transform: scale(0.3);
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
#aiBubble {
|
||||
background-color: white;
|
||||
}
|
||||
|
|
@ -191,12 +225,26 @@
|
|||
border-radius: 15px;
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
from {
|
||||
transform: scale(0.3);
|
||||
// Apply for audio message playback.
|
||||
.playing {
|
||||
animation-name: circle1;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
@keyframes circle1 {
|
||||
0%,
|
||||
100% {
|
||||
box-shadow: 0 0 0 0.4em rgba(195, 195, 195, 0.4), 0 0 0 0.25em rgba(195, 195, 195, 0.15);
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
25% {
|
||||
box-shadow: 0 0 0 0.15em rgba(195, 195, 195, 0.15), 0 0 0 0.4em rgba(195, 195, 195, 0.3);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 0 0.55em rgba(195, 195, 195, 0.55), 0 0 0 0.15em rgba(195, 195, 195, 0.05);
|
||||
}
|
||||
75% {
|
||||
box-shadow: 0 0 0 0.25em rgba(195, 195, 195, 0.25), 0 0 0 0.55em rgba(195, 195, 195, 0.45);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
150
src/components/settings.vue
Normal file
150
src/components/settings.vue
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
<template>
|
||||
|
||||
<!-- Settings icon. -->
|
||||
<button
|
||||
class="settingsIcon"
|
||||
v-if="toggleSettings == false"
|
||||
@click="onActive"
|
||||
>
|
||||
<div class="settingsButtonDot"></div>
|
||||
<div class="settingsButtonDot"></div>
|
||||
<div class="settingsButtonDot"></div>
|
||||
</button>
|
||||
|
||||
<!-- Settings div. -->
|
||||
<div id="settings" v-show="toggleSettings">
|
||||
<button
|
||||
class="settingsOption"
|
||||
@click="onLogout"
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { useAuth } from "@/modules/auth";
|
||||
import { useIpc } from "@/modules/ipc";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
name: "Settings",
|
||||
|
||||
setup() {
|
||||
const toggleSettings = ref(false);
|
||||
|
||||
const { post } = useIpc();
|
||||
const { logout } = useAuth();
|
||||
const router = useRouter();
|
||||
|
||||
// Listen for escape key to close settings.
|
||||
const onEscape = (e: any) => {
|
||||
if(e.key === "Escape") {
|
||||
toggleSettings.value = false;
|
||||
}
|
||||
window.removeEventListener("keydown", onEscape);
|
||||
}
|
||||
|
||||
// When settings is showing.
|
||||
const onActive = () => {
|
||||
toggleSettings.value = true;
|
||||
window.addEventListener("keydown", onEscape);
|
||||
}
|
||||
|
||||
// When user clicks logout button in settings.
|
||||
const onLogout = () => {
|
||||
logout();
|
||||
router.push({ name: "login" });
|
||||
post("logout", "")
|
||||
}
|
||||
|
||||
return {
|
||||
onActive,
|
||||
toggleSettings,
|
||||
onLogout
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
#settings {
|
||||
position: fixed;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
background-color: #EBEBEB;
|
||||
|
||||
opacity: 0.75;
|
||||
|
||||
z-index: 4;
|
||||
|
||||
animation-name: appear;
|
||||
animation-duration: 0.5s;
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
from {
|
||||
opacity: 0
|
||||
}
|
||||
to {
|
||||
opacity: 0.75
|
||||
}
|
||||
}
|
||||
|
||||
.settingsIcon {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
margin-right: 20px;
|
||||
margin-top: 24px;
|
||||
|
||||
z-index: 2;
|
||||
background-color: #EBEBEB;
|
||||
}
|
||||
|
||||
.settingsButtonDot {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
|
||||
margin: 2px;
|
||||
border-radius: 2.5px;
|
||||
background-color: #9B9B9B;
|
||||
}
|
||||
|
||||
.settingsOption {
|
||||
font-family: "SF Compact Display";
|
||||
background-color: #B7B7B7;
|
||||
padding: 10px 30px;
|
||||
border-radius: 20px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.settingsOption:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
|
@ -1,26 +1,6 @@
|
|||
<template>
|
||||
<InputItem />
|
||||
|
||||
<!-- Settings open button. -->
|
||||
<button
|
||||
class="settingsIcon"
|
||||
v-if="toggleSettings == false"
|
||||
@click="onSettings"
|
||||
>
|
||||
<div class="settingsButtonDot"></div>
|
||||
<div class="settingsButtonDot"></div>
|
||||
<div class="settingsButtonDot"></div>
|
||||
</button>
|
||||
|
||||
<!-- Settings "page." -->
|
||||
<div id="settings" v-show="toggleSettings">
|
||||
<button
|
||||
class="settingsOption"
|
||||
@click="onLogout"
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
<Settings />
|
||||
|
||||
<!-- List of message bubbles. -->
|
||||
<div id="messenger">
|
||||
|
|
@ -30,13 +10,11 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, onMounted, onUnmounted } from "vue";
|
||||
import { defineComponent, onMounted, onUnmounted } from "vue";
|
||||
import MessageItem from "@/components/messageItem.vue";
|
||||
import InputItem from "@/components/inputItem/inputItem.vue";
|
||||
import Settings from "@/components/settings.vue";
|
||||
import useMitt from "@/modules/mitt";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useAuth } from "@/modules/auth";
|
||||
import { useIpc } from "@/modules/ipc";
|
||||
import useMessages from '@/modules/messages';
|
||||
|
||||
export default defineComponent({
|
||||
|
|
@ -44,39 +22,13 @@ export default defineComponent({
|
|||
components: {
|
||||
MessageItem,
|
||||
InputItem,
|
||||
Settings
|
||||
},
|
||||
setup() {
|
||||
|
||||
const { post } = useIpc();
|
||||
const { logout } = useAuth();
|
||||
const router = useRouter();
|
||||
const { emitter } = useMitt();
|
||||
const { messages, addMessage, updateMessage } = useMessages();
|
||||
|
||||
// controls settings rendering.
|
||||
const toggleSettings = ref(false);
|
||||
|
||||
// Listen for escape key to close settings.
|
||||
const onEscape = (e: KeyboardEvent) => {
|
||||
if(e.key === "Escape") {
|
||||
toggleSettings.value = false;
|
||||
}
|
||||
window.removeEventListener("keydown", onEscape);
|
||||
}
|
||||
|
||||
// When user clicks settings button.
|
||||
const onSettings = () => {
|
||||
toggleSettings.value = true;
|
||||
window.addEventListener("keydown", onEscape);
|
||||
}
|
||||
|
||||
// When user clicks logout button in settings.
|
||||
const onLogout = () => {
|
||||
logout();
|
||||
router.push({ name: "login" });
|
||||
post("logout", "");
|
||||
}
|
||||
|
||||
const onRenderMessage = (_event: any, payload: any) => {
|
||||
|
||||
// render message if there is content.
|
||||
|
|
@ -101,10 +53,7 @@ export default defineComponent({
|
|||
});
|
||||
|
||||
return {
|
||||
toggleSettings,
|
||||
messages,
|
||||
onSettings,
|
||||
onLogout
|
||||
messages
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
@ -128,76 +77,4 @@ export default defineComponent({
|
|||
#messenger::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#settings {
|
||||
position: fixed;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
background-color: #EBEBEB;
|
||||
|
||||
opacity: 0.75;
|
||||
|
||||
z-index: 4;
|
||||
|
||||
animation-name: appear;
|
||||
animation-duration: 0.5s;
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
from {
|
||||
opacity: 0
|
||||
}
|
||||
to {
|
||||
opacity: 0.75
|
||||
}
|
||||
}
|
||||
|
||||
.settingsIcon {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
margin-right: 20px;
|
||||
margin-top: 24px;
|
||||
|
||||
z-index: 2;
|
||||
background-color: #EBEBEB;
|
||||
}
|
||||
|
||||
.settingsButtonDot {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
|
||||
margin: 2px;
|
||||
border-radius: 2.5px;
|
||||
background-color: #383838;
|
||||
}
|
||||
|
||||
.settingsOption {
|
||||
font-family: "SF Compact Display";
|
||||
background-color: #B7B7B7;
|
||||
padding: 10px 30px;
|
||||
border-radius: 20px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.settingsOption:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in a new issue