Merge remote-tracking branch 'origin/saveMessages'

This commit is contained in:
Enrique Hernandez 2021-03-11 21:48:01 -06:00
commit 4a2bb7cb52
6 changed files with 235 additions and 160 deletions

View file

@ -4,6 +4,8 @@
import { sendAudio } from './session' import { sendAudio } from './session'
import { ipcMain } from "electron"; import { ipcMain } from "electron";
import { backgroundMitt } from './emitter';
const portAudio = require('naudiodon'); const portAudio = require('naudiodon');
let ai: typeof portAudio.AudioIO | null = null; let ai: typeof portAudio.AudioIO | null = null;
@ -83,9 +85,14 @@ export function play(input: Buffer): void {
const audio = bufSplit(input, 8192); const audio = bufSplit(input, 8192);
// call this fuction after last audio chunk has been written. // Called on end of write.
const callback = () => { const callback = () => {
// TODO: clear portAudio writable buffer on write end.
// We stop audio playback anim.
backgroundMitt.emit('ipc-renderer', {
endpoint: 'stop-playback-anim'
});
} }
write(); write();

View file

@ -55,14 +55,18 @@ const onMessage = (message: string): void => {
// check to see if this is a Render Message. // check to see if this is a Render Message.
if (m.content) { if (m.content) {
// If there is audio, play it. // If there is audio, play it.
if (m.content.audio) { if (m.content.audio) {
const audioBytes = Buffer.from(m.content.audio as string, 'hex'); const audioBytes = Buffer.from(m.content.audio as string, 'hex');
m.content.audio = true;
play(audioBytes); play(audioBytes);
} else {
m.content.audio = false;
} }
// if there is no unique id, add it. // if there is no unique id, add it.
if(!m.uid) m.uid = uuidv4(); if(!m.uid) m.uid = uuidv4();
} }
backgroundMitt.emit('ipc-renderer', { backgroundMitt.emit('ipc-renderer', {

View file

@ -143,28 +143,22 @@ export default defineComponent({
.pause { .pause {
opacity: 1; opacity: 1;
&::before { &::before {
-moz-animation: audio1 1.5s infinite ease-in-out; -moz-animation: circle1 1.5s infinite ease-in-out;
-o-animation: audio1 1.5s infinite ease-in-out; -o-animation: circle1 1.5s infinite ease-in-out;
-webkit-animation: audio1 1.5s infinite ease-in-out; -webkit-animation: circle1 1.5s infinite ease-in-out;
animation: audio1 1.5s infinite ease-in-out; animation: circle1 1.5s infinite ease-in-out;
} }
&::after { &::after {
-moz-animation: audio2 2.2s infinite ease-in-out; -moz-animation: circle2 2.2s infinite ease-in-out;
-o-animation: audio2 2.2s infinite ease-in-out; -o-animation: circle2 2.2s infinite ease-in-out;
-webkit-animation: audio2 2.2s infinite ease-in-out; -webkit-animation: circle2 2.2s infinite ease-in-out;
animation: audio2 2.2s infinite ease-in-out; animation: circle2 2.2s infinite ease-in-out;
} }
} }
} }
} }
.animate-audio1 { @keyframes circle1 {
-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 {
0%, 0%,
100% { 100% {
box-shadow: 0 0 0 0.4em rgba(195, 195, 195, 0.4); 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); box-shadow: 0 0 0 0.25em rgba(195, 195, 195, 0.25);
} }
} }
.animate-audio2 {
-moz-animation: audio2 2.2s infinite ease-in-out; @keyframes circle2 {
-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 {
0%, 0%,
100% { 100% {
box-shadow: 0 0 0 0.25em rgba(195, 195, 195, 0.15); box-shadow: 0 0 0 0.25em rgba(195, 195, 195, 0.15);

View file

@ -50,7 +50,9 @@
</div> </div>
<!-- Render Crimata icon or friend's initials. --> <!-- 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] }} {{ message.context[0] }}
</div> </div>
@ -65,7 +67,7 @@
<script lang='ts'> <script lang='ts'>
import {defineComponent} from 'vue'; import {defineComponent, ref, onMounted} from 'vue';
export default defineComponent({ export default defineComponent({
name: "MessageItem", name: "MessageItem",
@ -75,6 +77,29 @@
type: Object, type: Object,
required: true 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; animation-duration: 0.25s;
} }
@keyframes appear {
from {
transform: scale(0.3);
}
to {
transform: scale(1);
}
}
#aiBubble { #aiBubble {
background-color: white; background-color: white;
} }
@ -191,12 +225,26 @@
border-radius: 15px; border-radius: 15px;
} }
@keyframes appear { // Apply for audio message playback.
from { .playing {
transform: scale(0.3); 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 { 25% {
transform: scale(1); 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
View 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>

View file

@ -1,26 +1,6 @@
<template> <template>
<InputItem /> <InputItem />
<Settings />
<!-- 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>
<!-- List of message bubbles. --> <!-- List of message bubbles. -->
<div id="messenger"> <div id="messenger">
@ -30,13 +10,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, onMounted, onUnmounted } from "vue"; import { defineComponent, onMounted, onUnmounted } from "vue";
import MessageItem from "@/components/messageItem.vue"; import MessageItem from "@/components/messageItem.vue";
import InputItem from "@/components/inputItem/inputItem.vue"; import InputItem from "@/components/inputItem/inputItem.vue";
import Settings from "@/components/settings.vue";
import useMitt from "@/modules/mitt"; 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'; import useMessages from '@/modules/messages';
export default defineComponent({ export default defineComponent({
@ -44,39 +22,13 @@ export default defineComponent({
components: { components: {
MessageItem, MessageItem,
InputItem, InputItem,
Settings
}, },
setup() { setup() {
const { post } = useIpc();
const { logout } = useAuth();
const router = useRouter();
const { emitter } = useMitt(); const { emitter } = useMitt();
const { messages, addMessage, updateMessage } = useMessages(); 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) => { const onRenderMessage = (_event: any, payload: any) => {
// render message if there is content. // render message if there is content.
@ -101,10 +53,7 @@ export default defineComponent({
}); });
return { return {
toggleSettings, messages
messages,
onSettings,
onLogout
}; };
}, },
}); });
@ -128,76 +77,4 @@ export default defineComponent({
#messenger::-webkit-scrollbar { #messenger::-webkit-scrollbar {
display: none; 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> </style>