]> Repos - mime-chat/commitdiff
new message anim
authorAndrew Gundersen <gundersena@xavier.edu>
Fri, 25 Jun 2021 23:28:29 +0000 (18:28 -0500)
committerAndrew Gundersen <gundersena@xavier.edu>
Fri, 25 Jun 2021 23:28:29 +0000 (18:28 -0500)
12 files changed:
src/account.ts
src/messages.ts [new file with mode: 0644]
src/render/components/bubble.vue
src/render/components/controllers/helpers.ts
src/render/components/controllers/inputItem.control.audio.ts
src/render/components/controllers/inputItem.control.text.ts
src/render/components/message.vue [new file with mode: 0644]
src/render/components/messenger.vue
src/render/ipc.ts
src/render/shared/messages.ts
src/session.ts
src/types.ts

index 581da0e36b0c6b3933b79ac323ad3faf95bc67ba..ac53b23b61c87767685ac927e6171d183c242505 100644 (file)
@@ -4,6 +4,7 @@ import { endSession, launchSession } from "@/session";
 import { getToken,  setToken, setProfile, getProfile, clearStore } from "./store";
 import { parseAuthRes } from "./auth";
 import { ipcEmit } from "@/composables/useEmitter";
+import { messages } from "@/messages";
 
 export const accountAuth = async (): Promise<Error | AuthState> => {
 
@@ -89,7 +90,7 @@ export const updateAppState = (): void => {
 
     ipcEmit("set-profile", profile);
 
-    // ipcEmit('messages') etc
+    ipcEmit("init-messages", messages)
 
 }
 
diff --git a/src/messages.ts b/src/messages.ts
new file mode 100644 (file)
index 0000000..fdd9d15
--- /dev/null
@@ -0,0 +1,24 @@
+import { ipcEmit } from "@/composables/useEmitter";
+
+export let messages: Message[] = [];
+
+export const setMessages = (init: {messages: Message[]}) => {
+    messages = init.messages;
+    ipcEmit("init-messages", messages);
+}
+
+export const newMessage = (message: Message) => {
+    messages.push(message);
+    ipcEmit("add-message", message);
+}
+
+export const annotateMessage = (annotation: Annotation) => {
+    for (let i in messages) {
+        if (messages[i].uid == annotation.uid) {
+            messages[i].context = annotation.context;
+            ipcEmit("update-message", annotation)
+            break;
+        }
+    }
+}
+
index 982cef3134f6ce82f7ad3a380afcbb20293e60ce..a3ca4f795d696518f183da590e4f8a33d3232040 100644 (file)
@@ -1,19 +1,6 @@
 <template>
-  <div :class="`${type}-message`">
-
-    <!-- message bubble -->
-    <div :class="`${type}-${child}-bubble`">
-      <div class="notification-dot"/>
-      <div class="error-dot"/>
-      {{ text }}
-    </div>
-
-    <!-- message context -->
-    <span class="context">
-      <div :class="`${type}-icon`"/>
-      {{ context }}
-    </span>
-
+  <div :class="`bubble ${modifier}-bubble ${modifier}-${child}`">
+    {{ content }}
   </div>
 </template>
 
  export default defineComponent({
   name: "Bubble",
 
-  props: ["text", "context", "type", "position"],
-
-  setup() {
+  props: ["modifier", "content", "child"],
 
-    const seen = ref(false);
-    /* initialize seen state */
-    if (document.visibilityState === "visible") {
-        seen.value = true;
-    } else seen.value = false;
+  setup(props) {
 
     onMounted(() => {
 
-      if(seen.value)
-      document.addEventListener("visibilitychange", () => {
-          seen.value = true
-      });
-
     });
 
     return {
-      seen
+
     };
 
   }
 
 <style lang="scss" scoped>
 
-  .message {
-    width: 100vw;
-    display: flex;
-    flex-direction: column;
-    padding-top: 9px;
-    padding-bottom: 9px;
-  }
-
-  .message:first-child {
-    margin-top: 55px;
-  }
-
-  .message:last-child {
-    margin-bottom: 18px;
-  }
-
-  .sf-message {
-    @extend .message;
-    justify-content: flex-end;
-  }
-
-  .ai-message, .fr-message {
-    @extend .message;
-    justify-content: flex-start;
-  }
-
-  .bubble {
-    position: relative;
-    max-width: 66vw;
-    font-family: "SF Pro Text";
-    font-size: 14px;
-    padding: 10px;
-    border-radius: 18px;
-  }
-
-  .sf-bubble {
-    @extend .bubble;
-    background-color: #58c4fd;
-    color: white;
-  }
-
-
-
-
-
-
-
-
-  .firstChildMessage {
-    padding-bottom: 2px;
-  }
-
-  .middleChildMessage {
-    padding-top: 2px;
-    padding-bottom: 2px;
-  }
-
-  .lastChildMessage {
-    padding-top: 2px;
-  }
-
-  #aiMessage {
-    align-items: flex-start;
-  }
-
-  #sfMessage {
-    align-items: flex-end;
-  }
-
-  #frMessage {
-    align-items: flex-start;
-  }
-
-  .messageBox {
-    position: relative;
-
-    display: flex;
-    flex-direction: column;
-
-    // Animate on render.
-    animation-name: appear;
-    animation-duration: 0.25s;
-  }
-
-  #aiMessageBox {
-    margin-left: 20px;
-    align-items: flex-start;
-  }
-
-  #sfMessageBox {
-    margin-right: 20px;
-    align-items: flex-end;
-  }
-
-  #frMessageBox {
-    margin-left: 20px;
-    align-items: flex-start;
-  }
-
-  .bubble {
-    position: relative;
-
-    max-width: 66vw;
-
-    display: flex;
-    flex-direction: column;
-
-    font-family: "SF Pro Text";
-    font-size: 14px;
-
-    padding: 10px;
-
-    border-radius: 18px;
-  }
-
-  @keyframes appear {
-    10% {
-      transform: scale(0.3);
-    }
-    100% {
-      transform: scale(1);
-    }
-  }
-
-  #aiBubble {
-    background-color: white;
-  }
-
-  #sfBubble {
-    background-color: #58c4fd;
-    color: white;
-  }
-
-  #frBubble {
-    background-color: white;
-  }
-
-  .sf-firstChildBubble {
-    border-bottom-right-radius: 9px;
-  }
-
-  .sf-middleChildBubble {
-    border-top-right-radius: 9px;
-    border-bottom-right-radius: 9px;
-  }
-
-  .sf-lastChildBubble {
-    border-top-right-radius: 9px;
-  }
-
-  .ai-firstChildBubble, .fr-firstChildBubble {
-    border-bottom-left-radius: 9px;
-  }
-
-  .ai-middleChildBubble, .fr-middleChildBubble {
-    border-top-left-radius: 9px;
-    border-bottom-left-radius: 9px;
-  }
-
-  .ai-lastChildBubble, .fr-lastChildBubble {
-    border-top-left-radius: 9px;
-  }
-
-  .notify {
-    position: absolute;
-    width: 12px;
-    height: 12px;
-    border-radius: 50%;
-    background-color: #58D9FF;
-    top: -5px;
-    left: -5px;
-    border: 2px solid #EBEBEB;
-    transform: scale(0);
-
-    animation-name: notify-anim;
-    animation-duration: 5s;
-  }
-
-  @keyframes notify-anim {
-    0%, 90% {
-      transform: scale(1);
-    }
-    100% {
-      transform: scale(0);
-    }
-  }
-
-  .context {
-    position: relative;
-
-    display: flex;
-    align-items: center;
-
-    font-family: "SF Compact Display";
-    font-size: 12px;
-    font-weight: bold;
-    margin-top: 5px;
-  }
-
-  .photo {
-    display: flex;
-    justify-content: center;
-    align-items: center;
-
-    margin-right: 5px;
-
-    width: 30px;
-    height: 30px;
-
-    font-size: 14px;
-
-    background-color: white;
-    border-radius: 15px;
-  }
-
-  // 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);
-    }
-    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);
-    }
-  }
-
-  .contentLoader {
-    display: flex;
-  }
-
-  .contentLoaderDot {
-    width: 5px;
-    height: 5px;
-
-    margin: 2px;
-    border-radius: 2.5px;
-    background-color: #357CA2;
-  }
-
-  .questionMark {
-    font-weight: 900;
-    color: #357CA2;
-  }
-
-  // .divider {
-  //   width: 100vw;
-  //   display: flex;
-  //   justify-content: center;
-  //   align-items: center;
-
-  //   font-family: "SF Compact Display";
-  //   font-size: 12px;
-  //   font-weight: bold;
-  //   color: #9B9B9B;
-
-  //   margin-bottom: 18px;
-  // }
-
-
-</style>
-
-
+.bubble {
+  position: relative;
+  max-width: 66vw;
+  font-family: "SF Pro Text";
+  font-size: 14px;
+  padding: 10px;
+  border-radius: 18px;
+  margin-left: 15px;
+  margin-right: 15px;
+  margin-bottom: 4px;
+  animation-name: bubble-init-anim;
+  animation-duration: 0.5s;
+}
+
+@keyframes bubble-init-anim {
+  from {
+    opacity: 0;
+    transform: translateY(25px);
+  } to {
+    opacity: 1;
+    transform: translateY(0px);
+  }
+}
+
+.ai-bubble {
+  background-color: #FFFFFF;
+}
+
+.client-bubble {
+  color: white;
+  background-color: #58C4FD;
+}
+
+.ai-first-child {
+  border-bottom-left-radius: 9px;
+}
+
+.ai-middle-child {
+  border-top-left-radius: 9px;
+  border-bottom-left-radius: 9px;
+}
+
+.ai-last-child {
+  border-top-left-radius: 9px;
+}
+
+.client-first-child {
+  border-bottom-right-radius: 9px;
+}
+
+.client-middle-child {
+  border-top-right-radius: 9px;
+  border-bottom-right-radius: 9px;
+}
+
+.client-last-child {
+  border-top-right-radius: 9px;
+}
+
+.ai-none-child, .client-none-child {
+  border-top-right-radius: 9px;
+}
+
+
+// .notify {
+//   position: absolute;
+//   width: 12px;
+//   height: 12px;
+//   border-radius: 50%;
+//   background-color: #58D9FF;
+//   top: -5px;
+//   left: -5px;
+//   border: 2px solid #EBEBEB;
+//   transform: scale(0);
+
+//   animation-name: notify-anim;
+//   animation-duration: 5s;
+// }
+
+// @keyframes notify-anim {
+//   0%, 90% {
+//     transform: scale(1);
+//   }
+//   100% {
+//     transform: scale(0);
+//   }
+// }
+
+</style>
\ No newline at end of file
index 23976a898b00296c20f308b523e3bb4210be2322..f20a11a93520b62d3513f233b7725afa2c0e7eb7 100644 (file)
@@ -1,4 +1,4 @@
-import { ref } from "vue";
+import { ref, Ref } from "vue";
 import anime from "animejs";
 import { v4 as uuidv4 } from 'uuid';
 
@@ -86,17 +86,40 @@ export function animateAudioInput () {
 
 }
 
-
-export function newMessage ({
-    text=false,
-    audio=false,
-    context=false,
-    uid=uuidv4()
-}) {
-    return {
-        text: text,
-        audio: audio,
-        context: context,
-        uid: uid
-    };
+// animate a message annotation.
+//! probably could be improved.
+export function annotateAnim (uid: string, val: string, contextRef: Ref) {
+
+  anime({
+    targets: `#${uid} span`,
+    opacity: [1, 0],
+    duration: 500,
+    easing: 'easeOutExpo'
+  })
+
+  .finished.then(() => {
+    contextRef.value = val; 
+  })
+
+  .then(() => {
+    anime({
+      targets: `#${uid} span`,
+      opacity: [0, 1],
+      duration: 500,
+      easing: 'easeOutExpo'
+    })
+  })
 }
+
+// Given index and length of content, return message child status.
+export function calcChild (index: number, len: number) {
+  if (len === 1) {
+    return "none";
+  } else if (index === 0) {
+    return "first-child";
+  } else if (index === len - 1) {
+    return "last-child";
+  } else {
+    return "middle-child";
+  }
+}
\ No newline at end of file
index facf30c100a736c0d984af9efbe4bd40df5f9259..9852e53e830f92599571777c4627c9c3e0178602 100644 (file)
@@ -1,6 +1,6 @@
 import { onMounted, onUnmounted, ref, Ref } from "vue";
 import { postMessage } from "@/render/ipc";
-import { newMessage, animateAudioInput } from "./helpers";
+import { animateAudioInput } from "./helpers";
 import { invokeReturnAudio, postAudioChunk } from "@/render/ipc";
 
 export default function useAudioInputController (typing: Ref) {
index 941ae89f2a6dc9bf817c74d650ec2fe18199cfdc..51edfe066456541d5d2a18476dc3b17d87a3cf71 100644 (file)
@@ -1,6 +1,6 @@
 import { Ref, ref, watch, onMounted, onUnmounted } from "vue";
 import { postMessage } from "@/render/ipc";
-import { newMessage, animateTextInput } from "./helpers";
+import { animateTextInput } from "./helpers";
 
 
 export default function useTextInputController(elementX: Ref) {
@@ -36,11 +36,12 @@ export default function useTextInputController(elementX: Ref) {
         if (textInput) {
 
             // Send it to the backend for processing.
-            const message = newMessage({
-                text: false
-            });
+            const message: Raw = {
+                text: textInput.value,
+                audio: false
+            };
 
-            // postMessage(message);
+            postMessage(message);
 
             clearInput()
         }
diff --git a/src/render/components/message.vue b/src/render/components/message.vue
new file mode 100644 (file)
index 0000000..d27df6e
--- /dev/null
@@ -0,0 +1,142 @@
+<template>
+  <div :id="uid" :class="`${modifier}-message`">
+
+    <Bubble
+      v-for="(val, index) in content"
+      :modifier="modifier"
+      :content="val"
+      :child="calcChild(index, content.length)"
+    />
+
+    <span class="context">
+      <div v-if="modifier==='ai'" class="avatar"></div>
+      {{ contextRef }}
+    </span>
+
+  </div>
+</template>
+
+<script lang='ts'>
+
+ import { defineComponent, ref, Ref, onMounted, watch } from 'vue';
+ import Bubble from "@/render/components/bubble.vue";
+ import { annotateAnim, calcChild } from "@/render/components/controllers/helpers";
+
+ export default defineComponent({
+  name: "Message",
+
+  props: ["modifier", "content", "context", "uid"],
+
+  components: {
+    Bubble
+  },
+
+  setup(props) {
+
+    const contextRef = ref(" ");
+    contextRef.value = props.context;
+
+    watch(() => props.context, (val: string, oldval: string) => {
+      annotateAnim(props.uid, val, contextRef);
+    });
+
+    onMounted(() => {
+
+    });
+
+    return {
+      calcChild,
+      contextRef
+    };
+
+  }
+
+ })
+
+</script>
+
+<style lang="scss" scoped>
+
+  .message {
+    width: 100vw;
+    display: flex;
+    flex-direction: column;
+    padding-top: 9px;
+    padding-bottom: 9px;
+    animation-name: message-init-anim;
+    animation-duration: 0.5s; 
+  }
+
+  @keyframes message-init-anim {
+    from {
+      opacity: 0;
+      transform: translateY(50px);
+    } to {
+      opacity: 1;
+      transform: translateY(0px);
+    }
+  }
+
+  .message:first-child {
+    margin-top: 55px;
+  }
+
+  .message:last-child {
+    margin-bottom: 18px;
+  }
+
+  .client-message {
+    @extend .message;
+    align-items: flex-end;
+  }
+
+  .ai-message {
+    @extend .message;
+    align-items: flex-start;
+  }
+
+  .context {
+    position: relative;
+    display: flex;
+    align-items: center;
+    font-family: "SF Compact Display";
+    font-size: 12px;
+    font-weight: bold;
+    margin-top: 5px;
+    margin-right: 15px;
+    margin-left: 15px;
+  }
+
+  .avatar {
+    margin-right: 5px;
+    width: 30px;
+    height: 30px;
+    background-color: white;
+    border-radius: 15px;
+  }
+
+  .playing {
+    animation-name: message-playback-anim;
+    animation-duration: 2s;
+    animation-iteration-count: infinite;
+  }
+
+  @keyframes message-playback-anim {
+    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);
+    }
+    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);
+    }
+  }
+
+</style>
+
+
index 8a7f6adec5b71e71cddf5c93b7c4609745b40433..619eeffb0734ca39fe092150d22880b9609ecc01 100644 (file)
@@ -7,11 +7,12 @@
 
   <!-- List of message bubbles. -->
   <div id="messenger">
-    <Bubble
+    <Message
       v-for="message in messages"
-      :text="message.content.text"
+      :modifier="message.modifier"
+      :content="message.content"
       :context="message.context"
-      :key="message[0]"
+      :uid="message.uid"
     />
   </div>
 
@@ -22,7 +23,7 @@
 import { defineComponent, onMounted, onUnmounted } from "vue";
 import InputItem from "@/render/components/inputItem.vue";
 import Settings from "@/render/components/settings.vue";
-import Bubble from "@/render/components/bubble.vue";
+import Message from "@/render/components/message.vue";
 
 import { profile } from "@/render/shared/profile";
 import { messages } from "@/render/shared/messages";
@@ -33,7 +34,7 @@ export default defineComponent({
   components: {
     InputItem,
     Settings,
-    Bubble
+    Message
   },
 
   setup() {
index 6601dde3c98bc586052d039fcd2633ca4e02d782..cb1477895ab98f4fdeaacd4d8d20bf1cc027a4b4 100644 (file)
@@ -41,7 +41,7 @@ export const invokeReturnAudio = async (): Promise<ArrayBuffer[] | Error> => (
  *
  */
 
-export const postMessage = (payload: Message): void => (
+export const postMessage = (payload: Raw): void => (
     post('post-session-send', payload)
 );
 
index f7315846e73d5ba8ffef2e0941fdf885cd0f717f..cd8fa265a1bb31f751ace068bac7cb4b7941a894 100644 (file)
@@ -5,26 +5,54 @@ import useScroll from "@/render/composables/useScroll";
 export const messages: Ref<Array<Message>> = ref([]);
 
 export const setMessages: IpcListenerCallback<Array<Message>> = (payload) => {
-    messages.value = payload as Array<Message>;
+    // messages.value = payload as Array<Message>;
 }
 
 export const addMessage: IpcListenerCallback<Message> = (payload) => {
     messages.value.push(payload as Message);
 }
 
-export const updateMessage: IpcListenerCallback<Message> = (payload) => {
-    const message = payload as Message;
+export const updateMessage: IpcListenerCallback<Annotation> = (payload) => {
+    const annotation = payload as Annotation;
 
     let targetMessage = messages.value.filter((m: Message) => {
-        return m.uid = message.uid;
+        return m.uid === annotation.uid;
     })[0];
 
     if (targetMessage) {
-        targetMessage = message;
+        targetMessage.content = annotation.content; 
+        targetMessage.context = annotation.context;
     }
-
 }
 
+messages.value = [
+{
+    content: ["Welcome Andrew"], 
+    context: "Crimata", 
+    modifier: "ai", 
+    uid: "b5dd3d1b-cef0-4057-aa0f-c72e5c4cd67d"
+}, 
+{
+    content: ["show contacts"], 
+    context: "show contacts", 
+    modifier: "client", 
+    uid: "ee2819a3-1125-4881-9942-ca5b3d0ad502"
+}]
+
+setTimeout(() => {
+
+    console.log("updating message");
+
+    updateMessage({
+        content: ["show contacts", "msg"],
+        context: "show contacts",
+        uid: "ee2819a3-1125-4881-9942-ca5b3d0ad502"
+    });
+
+
+
+}, 2000);
+
 export default {
     messages,
     setMessages,
index 01a105c394d75c0f8b636b9382d6795680b19c15..df0c1228eb06348b91e8dce9eec83d0dcb61858c 100644 (file)
@@ -3,26 +3,27 @@
 
 
 // import useAudio from "@/audio";
-import { ipcEmit } from "@/composables/useEmitter";
+import { setMessages, newMessage, annotateMessage } from "./messages";
 import useWebsockets from "./composables/useWebsockets";
 import {config} from "@/config";
 
-/* data structure of messages that's tied to the UI */
-const uiState: any | null = null;
-
 /* start and stop audio functionality */
 // const { initAudio, closeAudio } = useAudio();
 
-const isInitMessage = (message: any): boolean => {
-    return true;
+const isInitMessage = (object: any): object is Message[] => {
+    return 'messages' in object;
 };
 
-const deauthenticate = (): void => {
-    console.log('deauthenticating')
+const isNewMessage = (object: any): object is Message => {
+    return 'content' in object;
 };
 
-const isAddMessage = (message: any): boolean => {
-    return true;
+const isAnnotation = (object: any): object is Annotation => {
+    return 'messages' in object;
+};
+
+const deauthenticate = (): void => {
+    console.log('deauthenticating')
 };
 
 /**
@@ -30,33 +31,25 @@ const isAddMessage = (message: any): boolean => {
  * Takes an onMessage callback which we define below.
  */
 
-
 const onMessageCallback = (payload: string) => {
 
-    const message  = JSON.parse(payload);
+    const message = JSON.parse(payload);
 
-    /* if the platform fails to authenticate, we must back down */
     if (message === "CLOSE_AUTH_FAIL") {
         deauthenticate();
-        return;
     }
 
-    ipcEmit("add-message", message)
-    return
-
-    /* on init, platform sends state, used to init canvas */
-    // if (isInitMessage(message)) {
-    //     ipcEmit("init-messages", message)
-    // }
-
+    else if (isInitMessage(message)) {
+        setMessages(message);
+    }
 
-    // else if (isAddMessage(message)) {
-    //     ipcEmit("add-messages", message)
-    // }
+    else if (isNewMessage(message)) {
+        newMessage(message);
+    }
 
-    // else {
-    //     ipcEmit("update-messages", message)
-    // }
+    else if (isAnnotation(message)) {
+        annotateMessage(message);
+    }
 
 }
 
index 2139a8683a7307d86a202f53359ad2d45c30cbaa..7b6d964ab2af8793e6fe68d89c737628f41bf1a9 100644 (file)
@@ -1,14 +1,20 @@
 
 interface Message {
-    text: boolean | string;
+    modifier: string;
+    content: string[];
     context: boolean | string;
-    audio: boolean | string;
-    type: 1 | 2 | 3;
     uid: string;
 }
 
-interface ViewMessage extends Message {
-    child: string;
+interface Annotation {
+    content: string[];
+    context: string;
+    uid: string;
+}
+
+interface Raw {
+    text: string | boolean;
+    audio: string | boolean;
 }
 
 interface WindowState {