]> Repos - mime-chat/commitdiff
refactor core into composables & components
authorriqo <hernandeze2@xavier.edu>
Sat, 30 Jan 2021 22:05:59 +0000 (16:05 -0600)
committerEnrique Hernandez <hernandeze2@xavier.edu>
Sun, 31 Jan 2021 22:53:36 +0000 (16:53 -0600)
37 files changed:
rawAudio.raw [deleted file]
rawAudio.wav [new file with mode: 0644]
src/background.ts
src/components/UI/UIDetails/titleBar.vue [deleted file]
src/components/input/inputItem.vue [deleted file]
src/components/inputVisualizer/index.vue [new file with mode: 0644]
src/components/inputVisualizer/visualizer.ts [new file with mode: 0644]
src/components/inputVisualizer/visualizerDetails/audio.vue [moved from src/components/input/inputDetails/audioInput.vue with 50% similarity]
src/components/inputVisualizer/visualizerDetails/audioVisualizer.ts [moved from src/core/UI/input/audio/useStreamVisualizer.ts with 90% similarity]
src/components/inputVisualizer/visualizerDetails/text.vue [moved from src/components/input/inputDetails/textInput.vue with 78% similarity]
src/components/inputVisualizer/visualizerDetails/textAnime.ts [new file with mode: 0644]
src/components/inputVisualizer/visualizerDetails/textVisualizer.ts [new file with mode: 0644]
src/components/messenger/index.vue [moved from src/components/UI/index.vue with 72% similarity]
src/components/messenger/messageBubble/bubbleDetails/computed.ts [moved from src/core/messenger/message/messageDetails/useComputed.ts with 81% similarity]
src/components/messenger/messageBubble/bubbleDetails/drawer.ts [moved from src/core/messenger/message/messageDetails/useAnimations.ts with 93% similarity]
src/components/messenger/messageBubble/index.vue [moved from src/components/UI/UIDetails/messageItem.vue with 83% similarity]
src/components/messenger/messageVisualizer/index.vue [moved from src/components/UI/UIDetails/messageRenderer.vue with 80% similarity]
src/components/messenger/messageVisualizer/viewDetails/anime.ts [moved from src/core/messenger/messageView/viewDetails/useAnimations.ts with 100% similarity]
src/components/messenger/messageVisualizer/viewDetails/offsetCalculator.ts [moved from src/core/messenger/messageView/viewDetails/useOffsetCalculator.ts with 100% similarity]
src/components/messenger/messageVisualizer/viewDetails/visualizer.ts [moved from src/core/messenger/messageView/useMessageView.ts with 81% similarity]
src/components/messenger/mockMessages.ts [moved from src/components/UI/UIDetails/messages.ts with 99% similarity]
src/components/voiceBackground/index.vue [moved from src/components/AI/index.vue with 99% similarity]
src/composables/keyDownHandler/keyBoardMaps/keyboardCharMap.ts [moved from src/utils/keyDownHandler/keyBoardMaps/keyboardCharMap.ts with 100% similarity]
src/composables/keyDownHandler/keyBoardMaps/keyboardNameMap.ts [moved from src/utils/keyDownHandler/keyBoardMaps/keyboardNameMap.ts with 100% similarity]
src/composables/keyDownHandler/useKeyDownHandler.ts [moved from src/utils/keyDownHandler/useKeyDownHandler.ts with 100% similarity]
src/composables/mediaStream.ts [new file with mode: 0644]
src/composables/scrollView.ts [moved from src/core/UI/scrollView/useScrollView.ts with 72% similarity]
src/composables/textWrap.ts [moved from src/utils/textWrap/useTextWrap.ts with 100% similarity]
src/core/UI/input/audio/audioDetails/useAnimations.ts [deleted file]
src/core/UI/input/audio/useAudioRecorder.ts [deleted file]
src/core/UI/input/audio/useStreamRecord.ts [deleted file]
src/core/UI/input/text/textDetails/useAnimations.ts [deleted file]
src/core/UI/input/text/useTextRender.ts [deleted file]
src/core/UI/input/useInputController.ts
src/router/index.ts
src/views/home/index.vue
testAudio.js [new file with mode: 0644]

diff --git a/rawAudio.raw b/rawAudio.raw
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/rawAudio.wav b/rawAudio.wav
new file mode 100644 (file)
index 0000000..5582bc0
Binary files /dev/null and b/rawAudio.wav differ
index 56d341918d5db16ce2ddaa1ee37aac484514d573..71e7d6873f3e8947a258491664fe8ae76ecedfcd 100644 (file)
@@ -6,8 +6,9 @@ import installExtension, { VUEJS_DEVTOOLS } from "electron-devtools-installer";
 const isDevelopment = process.env.NODE_ENV !== "production";
 import * as path from "path";
 const fs = require('fs');
-
+import WebSocket from "ws";
 const portAudio = require('naudiodon');
+console.log(portAudio.getDevices());
 
 // Create an instance of AudioIO with inOptions (defaults are as below), which will return a ReadableStream
 const audioOptions = {
@@ -18,12 +19,20 @@ const audioOptions = {
   closeOnError: true // Close the stream if an audio error is detected, if set false then just log the error
 }
 
+// Create an instance of AudioIO with outOptions (defaults are as below), which will return a WritableStream
+const ao = new portAudio.AudioIO({
+  outOptions: audioOptions 
+});
+
 let ai = new portAudio.AudioIO({
   inOptions: audioOptions
 });
 
-import WebSocket from "ws";
+const filename = "rawAudio.wav";
 
+// Create a write stream to write out to a raw audio file
+const writeStream = fs.createWriteStream(filename);
+ai.pipe(writeStream);
 // Keep a global reference of the window object, if you don't, the window will
 // be closed automatically when the JavaScript object is garbage collected.
 let win: BrowserWindow | null;
@@ -88,15 +97,19 @@ function createWindow() {
     ipcMain.on('update-recorder', (Event: any, record: boolean) => {
      if (record) {
        ai.start();
-       ai.on('data', (buf: Buffer) => {
-          const base64Data = buf.toString('base64');
-          audioData += base64Data;
-       }); 
+       // ai.on('data', (buf: Buffer) => {
+       //    const base64Data = buf.toString('base64');
+       //    audioData += base64Data;
+       // }); 
      } else {
        ai.quit();
+
+       const audio = {
+         content: fs.readFileSync(filename).toString('base64'), 
+       }
        const message = {
          text: "",
-         audio: audioData
+         audio
        }
        ws.send(JSON.stringify(message))
        ai =  new portAudio.AudioIO({
diff --git a/src/components/UI/UIDetails/titleBar.vue b/src/components/UI/UIDetails/titleBar.vue
deleted file mode 100644 (file)
index e90db9c..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-<template>
-  <defs>
-    <linearGradient
-      id="linear-gradient"
-      x1="0.5"
-      y1="-1.438"
-      x2="0.5"
-      y2="0.633"
-      gradientUnits="objectBoundingBox"
-    >
-      <stop offset="0" stop-color="#fff33b" stop-opacity="0" />
-      <stop offset="0.152" stop-color="#ffde46" stop-opacity="0.141" />
-      <stop offset="0.467" stop-color="#ffa764" stop-opacity="0.435" />
-      <stop offset="0.581" stop-color="#ff916f" stop-opacity="0.541" />
-      <stop offset="1" stop-color="#ff5782" />
-    </linearGradient>
-    <linearGradient
-      id="linear-gradient-2"
-      x1="0.498"
-      y1="0.728"
-      x2="0.498"
-      y2="-0.433"
-      gradientUnits="objectBoundingBox"
-    >
-      <stop offset="0" stop-color="#f7cf83" />
-      <stop offset="1" stop-color="#fff" />
-    </linearGradient>
-    <rect id="rect" width="29" height="29" rx="10" />
-    <clipPath id="clip">
-      <use xlink:href="#rect" />
-    </clipPath>
-  </defs>
-
-  <g id="titlebar" transform="translate(0, 0)">
-    <g data-name="Group 73" transform="translate(20 20)">
-      <ellipse
-        id="Oval"
-        cx="8.134"
-        cy="8.134"
-        rx="8.134"
-        ry="8.134"
-        transform="translate(0 0)"
-        fill="url(#linear-gradient)"
-      />
-      <ellipse
-        id="Oval-2"
-        data-name="Oval"
-        cx="8.134"
-        cy="8.134"
-        rx="8.134"
-        ry="8.134"
-        transform="translate(27 0)"
-        fill="url(#linear-gradient-2)"
-      />
-    </g>
-  </g>
-</template>
-
-<script lang="ts">
-import { defineComponent } from "vue";
-export default defineComponent({
-  name: "TitleBar",
-});
-</script>
diff --git a/src/components/input/inputItem.vue b/src/components/input/inputItem.vue
deleted file mode 100644 (file)
index 2f41513..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<template>
-  <audio-input v-if="visualizeStream" :bubbleWidth="audioBubbleWidth" :paths="paths" />
-  <text-input v-else :input="input" :textXoffset="textInputXoffset" />
-</template>
-
-<script lang="ts">
-import { defineComponent } from "vue";
-import TextInput from "./inputDetails/textInput.vue";
-import AudioInput from "./inputDetails/audioInput.vue";
-import useInputController from "@/core/UI/input/useInputController";
-export default defineComponent({
-  name: "InputItem",
-  components: { TextInput, AudioInput },
-  setup() {
-    const {
-      input,
-      visualizeStream,
-      textInputXoffset,
-      audioBubbleWidth,
-      paths,
-    } = useInputController();
-
-    return {
-      input,
-      textInputXoffset,
-      visualizeStream,
-      audioBubbleWidth,
-      paths,
-    };
-  },
-});
-</script>
diff --git a/src/components/inputVisualizer/index.vue b/src/components/inputVisualizer/index.vue
new file mode 100644 (file)
index 0000000..a1cc955
--- /dev/null
@@ -0,0 +1,41 @@
+<template>
+  <audio-stream v-if="visualizeStream" :bubbleWidth="audioBubbleWidth" :paths="paths" />
+  <text-input v-else :input="input" :textXoffset="textInputXoffset" />
+</template>
+
+<script lang="ts">
+// input child components
+import TextInput from "./visualizerDetails/text.vue";
+import AudioStream from "./visualizerDetails/audio.vue";
+
+import useInputVisualizer from "./visualizer";
+
+import { defineComponent } from "vue";
+export default defineComponent({
+  name: "InputVisualizer",
+  components: { TextInput, AudioStream },
+  setup() {
+    // get text input
+
+    // get audio stream
+
+    // use Input Handler
+
+    const {
+      input,
+      visualizeStream,
+      textInputXoffset,
+      audioBubbleWidth,
+      paths,
+    } = useInputVisualizer();
+
+    return {
+      input,
+      textInputXoffset,
+      visualizeStream,
+      audioBubbleWidth,
+      paths,
+    };
+  },
+});
+</script>
diff --git a/src/components/inputVisualizer/visualizer.ts b/src/components/inputVisualizer/visualizer.ts
new file mode 100644 (file)
index 0000000..63f6b33
--- /dev/null
@@ -0,0 +1,92 @@
+import { ref, watch, onUnmounted, onMounted } from "vue";
+
+import useKeyDownHandler from "@/composables/keyDownHandler/useKeyDownHandler";
+
+import useMediaStream from "@/utils/mediaStream/useMediaStream";
+
+import useStreamVisualizer from './visualizerDetails/audioVisualizer';
+
+import useTextVisualizer from './visualizerDetails/textVisualizer';
+
+/*
+* Input visualizer
+* Will visualize text input by default as user types or
+* if user holds space bar down, input audio stream
+* will be visualized & recorded
+*/
+// NOTE: input params should probably be input and MediaStream
+export default function useInputVisualizer() {
+    const visualizeStream = ref(false);
+    const paths = ref([]);
+    const audioBubbleWidth = ref(10);
+    let stream: MediaStream;
+
+    const { keyDownHandler, input } = useKeyDownHandler();
+    const { textInputXoffset, renderTextInput } = useTextVisualizer(input);
+    const { visualizeStreamAsPaths, expandBubble } = useStreamVisualizer(visualizeStream);
+
+    // stops stream recording on space key up
+    function keyupHandler(e: any) {
+        if (e.key === " " && visualizeStream.value) {
+            console.log("stop recording");
+
+            // window.postMessage({
+            //   myTypeField: 'update-recorder',
+            //   record: false
+            // }, '*')
+
+            visualizeStream.value = false;
+            paths.value = []
+            audioBubbleWidth.value = 10;
+            input.value = ''
+        }
+    }
+
+    // add window event keyboard listeners
+    window.addEventListener("keydown", keyDownHandler);
+    window.addEventListener('keyup', keyupHandler);
+
+    // get stream & initialize recorder object on mount
+    onMounted(async () => {
+        stream = await useMediaStream();
+    })
+
+    // determine whether to render text or audio based one the keyboard input
+    watch(input, (input, prevInput) => {
+        if (prevInput !== "" || prevInput.length > 0) {
+            if (!visualizeStream.value) {
+                renderTextInput();
+            }
+            return;
+        }
+        if (input === " " && visualizeStream.value === false) {
+            console.log("record MediaStream");
+            visualizeStream.value = true;
+
+            // window.postMessage({
+            //   myTypeField: 'update-recorder',
+            //   record: true
+            // }, '*')
+
+            expandBubble(audioBubbleWidth);
+            visualizeStreamAsPaths(stream, paths, audioBubbleWidth);
+            return;
+        }
+        renderTextInput();
+    });
+
+    // remove Event Listeners on component unMount
+    onUnmounted(() => {
+        window.removeEventListener("keydown", keyDownHandler);
+        window.removeEventListener("keyup", keyupHandler);
+    });
+
+    return {
+        input,
+        visualizeStream,
+        textInputXoffset,
+        audioBubbleWidth,
+        paths
+    }
+
+}
similarity index 50%
rename from src/components/input/inputDetails/audioInput.vue
rename to src/components/inputVisualizer/visualizerDetails/audio.vue
index 3c8b72bf24c15aa3088f51755efbf4b2fd1ae179..09a395afdaf315016f6994be1bf01d68789b860a 100644 (file)
@@ -1,19 +1,14 @@
 <template>
-  <svg 
-    xmlns="http://www.w3.org/2000/svg" 
-    :width="bubbleWidth" 
-    height="34" 
-    :x="center" 
-    y="440" 
+  <svg
+    xmlns="http://www.w3.org/2000/svg"
+    :width="bubbleWidth"
+    height="34"
+    :x="center"
+    y="440"
     rx="10"
     class="audioInput"
   >
-    <g 
-      id="Group_126" 
-      data-name="Group 126" 
-      transform="translate(0 0)" 
-      rx="10"
-    >
+    <g id="Group_126" data-name="Group 126" transform="translate(0 0)" rx="10">
       <rect
         id="Rectangle_478"
         data-name="Rectangle 478"
         fill="#b9b9b9"
       />
       <svg>
-      <g rx="20" id="Group_125" data-name="Group 125" v-for="(path, index) in paths">
-        <path
-          :key="index"
-          id="path"
-          :data-name="path.identifier"
-          :d="path.d"
-          :transform="`translate(${path.offset} 0)`"
-          fill="none"
-          :stroke="path.color"
-          stroke-linecap="round"
-          stroke-width="1.5"
-        />
-      </g>
+        <g rx="20" id="Group_125" data-name="Group 125" v-for="(path, index) in paths">
+          <path
+            :key="index"
+            id="path"
+            :data-name="path.identifier"
+            :d="path.d"
+            :transform="`translate(${path.offset} 0)`"
+            fill="none"
+            :stroke="path.color"
+            stroke-linecap="round"
+            stroke-width="1.5"
+          />
+        </g>
       </svg>
     </g>
   </svg>
 <script lang="ts">
 import { defineComponent, computed, inject } from "vue";
 export default defineComponent({
-  name: "AudioInput",
+  name: "AudioStream",
   props: {
     bubbleWidth: Number,
     paths: Array,
   },
   setup(props) {
-
     const emitter: any = inject("mitt");
-    function audioSend() {
-
-    }
+    function audioSend() {}
 
     const center = computed(() => {
       if (props.bubbleWidth) {
similarity index 90%
rename from src/core/UI/input/audio/useStreamVisualizer.ts
rename to src/components/inputVisualizer/visualizerDetails/audioVisualizer.ts
index c31bf6cf4dbc8970b1eaa6a84ad0a8ef65668f04..549b348cbb7db76832c85ba17852633f4af6e2ef 100644 (file)
@@ -24,7 +24,7 @@ export default function useStreamVisualizer(draw: Ref<boolean>) {
     /*
      * Draws MediaStream audioTrack as 'AudioPath' objects
      */
-    function drawStreamAs(paths: Ref<any>, bubbleWidth: Ref<number>): void {
+    function drawStream(paths: Ref<any>, bubbleWidth: Ref<number>): void {
         const xInitial = ref(10);
         const maxAmplitude = 15;
         const maxBubbleWidth = 300;
@@ -51,7 +51,7 @@ export default function useStreamVisualizer(draw: Ref<boolean>) {
                         opacity: 0,
                         duration: 0
                     })
-                } 
+                }
             }
 
         }
@@ -77,7 +77,7 @@ export default function useStreamVisualizer(draw: Ref<boolean>) {
             paths.value.push(p);
         }
 
-            paths.value = [];
+        paths.value = [];
         function drawPathStream(): void {
 
             let streamPath;
@@ -94,11 +94,11 @@ export default function useStreamVisualizer(draw: Ref<boolean>) {
              * To Draw pathStream
              */
             if (bubbleWidth.value === 300) {
-              xInitial.value = bubbleWidth.value - 10;
+                xInitial.value = bubbleWidth.value - 10;
             } else if (bubbleWidth.value > 20) {
-              xInitial.value = bubbleWidth.value;
+                xInitial.value = bubbleWidth.value;
             } else {
-                xInitial.value +=3;
+                xInitial.value += 3;
             }
             streamPath = setTimeout(async () => {
                 const pathList = document.querySelectorAll('path');
@@ -106,8 +106,8 @@ export default function useStreamVisualizer(draw: Ref<boolean>) {
                 drawNewPath();
                 if (bubbleWidth.value === maxBubbleWidth) {
                     shiftPathStream(pathList);
-                } 
-             }, 200)
+                }
+            }, 200)
         }
         drawPathStream();
     }
@@ -123,9 +123,9 @@ export default function useStreamVisualizer(draw: Ref<boolean>) {
                 return;
             }
             animation = requestAnimationFrame(increase);
-         if (bubbleRef.value < 300) {
-             bubbleRef.value++;
-         }
+            if (bubbleRef.value < 300) {
+                bubbleRef.value++;
+            }
         }
         increase();
     }
@@ -137,7 +137,7 @@ export default function useStreamVisualizer(draw: Ref<boolean>) {
     function visualizeStreamAsPaths(s: MediaStream, paths: Ref<any>, bubbleWidth: Ref<number>): void {
         const source = audioContext.createMediaStreamSource(s);
         source.connect(analyzer);
-        drawStreamAs(paths, bubbleWidth);
+        drawStream(paths, bubbleWidth);
     }
 
     return {
similarity index 78%
rename from src/components/input/inputDetails/textInput.vue
rename to src/components/inputVisualizer/visualizerDetails/text.vue
index c4e1f16c581ee3a88fcf5236cb239d5cfa8c583e..488aa356f6ff00a96c8830c3980f785a0f6f8394 100644 (file)
@@ -1,11 +1,5 @@
 <template>
-  <foreignObject 
-    class="inputContainer" 
-    :x="textXoffset" 
-    y="450" 
-    width="55%" 
-    height="50%"
-  >
+  <foreignObject class="inputContainer" :x="textXoffset" y="450" width="55%" height="50%">
     <div xmlns="http://www.w3.org/1999/xhtml">
       <div class="inputBubble" contenteditable v-show="input.length > 0">
         <p>{{ input }}</p>
 </template>
 
 <script lang="ts">
-import { defineComponent, watch, onMounted, ref } from "vue";
+import { defineComponent } from "vue";
 export default defineComponent({
   name: "TextInput",
   props: {
     input: String,
-    textXoffset: String
+    textXoffset: String,
   },
 });
 </script>
diff --git a/src/components/inputVisualizer/visualizerDetails/textAnime.ts b/src/components/inputVisualizer/visualizerDetails/textAnime.ts
new file mode 100644 (file)
index 0000000..8f5eafa
--- /dev/null
@@ -0,0 +1,57 @@
+import AnimeFunc from "@/types/animejs/index";
+import { inject } from "vue";
+import { Ref } from "@/types/vueRef/index";
+/*
+ * Anime js animations used by input textController
+*/
+export default function useTextInputAnims() {
+
+    // imports animejs safely
+    let anime: AnimeFunc;
+    const animeInject: AnimeFunc | undefined = inject("animejs");
+    if (animeInject) anime = animeInject;
+
+    const animateSend = (el: HTMLElement, elHeight: number, input: Ref<string>) => {
+        anime({
+            targets: el,
+            translateY: -elHeight + 15,
+            duration: 200,
+            easing: "easeInQuad",
+            complete: function() {
+                anime({
+                    targets: el,
+                    translateY: 50,
+                    duration: 200,
+                    easing: "easeOutQuad",
+                    complete: function() {
+                        input.value = "";
+                    }
+                })
+            }
+        })
+    }
+
+    const inputAppear = (el: HTMLElement) => {
+        anime({
+            targets: el,
+            translateY: -10,
+            duration: 0
+        });
+    }
+
+    const verticalShiftInput = (el: HTMLElement, yTrans: number) => {
+        anime({
+            targets: el,
+            translateY: yTrans,
+            easing: "easeOutQuad",
+            duration: 150,
+        });
+    }
+
+    return {
+        animateSend,
+        verticalShiftInput,
+        inputAppear
+    }
+
+}
diff --git a/src/components/inputVisualizer/visualizerDetails/textVisualizer.ts b/src/components/inputVisualizer/visualizerDetails/textVisualizer.ts
new file mode 100644 (file)
index 0000000..6a27d48
--- /dev/null
@@ -0,0 +1,49 @@
+import { ref, computed, inject } from 'vue';
+import useTextInputAnims from "./textAnime";
+import { Ref } from "@/types/vueRef/index";
+/*
+ * text rendering logic used by inputController
+*/
+export default function useTextVisualizer(input: Ref<string>) {
+    const inputHeight = ref(42);
+    const inputWidth = ref(0);
+
+    const emitter: any = inject("mitt");
+    const { animateSend, verticalShiftInput, inputAppear } = useTextInputAnims();
+
+    async function renderTextInput() {
+        // first, get input dimensions
+        const inputDivs = await document.getElementsByClassName("inputBubble");
+        const height = inputDivs[0].getBoundingClientRect().height;
+        inputWidth.value = inputDivs[0].getBoundingClientRect().width;
+        // then, query for foreignObject div
+        const foreignObjectDiv = document.getElementsByClassName(
+            "inputContainer"
+        );
+        const foreignEl = foreignObjectDiv[0] as HTMLElement;
+        if (height !== inputHeight.value) {
+            inputHeight.value = height;
+        }
+        if (inputHeight.value === 0) {
+            inputAppear(foreignEl)
+        } else if (inputHeight.value > 36) {
+            const yTrans = -inputHeight.value + 30;
+            verticalShiftInput(foreignEl, yTrans);
+        }
+    }
+
+    emitter.on("newSelfMessage", async (payload: any) => {
+        const inputEl = document.getElementsByClassName("inputContainer");
+        const foreignEl = inputEl[0] as HTMLElement;
+        animateSend(foreignEl, inputHeight.value, input);
+    });
+
+    const textInputXoffset = computed(() => {
+        return `calc(50% - ${inputWidth.value / 2})`;
+    });
+
+    return {
+        textInputXoffset,
+        renderTextInput
+    }
+}
similarity index 72%
rename from src/components/UI/index.vue
rename to src/components/messenger/index.vue
index 8351c944ec4ed764b5c6addfe7de4fd20391358a..a79e2acff4e0b2d49c6dcc0d5f82664bb13d5abf 100644 (file)
@@ -6,24 +6,26 @@
     xmlns:xlink="http://www.w3.org/1999/xlink"
     @click="emittMessage"
   >
-    <!-- <AI /> -->
-    <MessageRenderer />
-    <InputItem />
-    <!-- <TitleBar /> -->
+    <MessageVisualizer />
+    <InputVisualizer />
   </svg>
 </template>
 
 <script lang="ts">
-import AI from "../AI/index.vue";
-import MessageRenderer from "./UIDetails/messageRenderer.vue";
-import InputItem from "@/components/input/inputItem.vue";
-import TitleBar from "./UIDetails/titleBar.vue";
-import Messages from "./UIDetails/messages";
+// mock messages
+import Messages from "./mockMessages";
+
+// child components
+import MessageVisualizer from "./messageVisualizer/index.vue";
+import InputVisualizer from "@/components/inputVisualizer/index.vue";
+
+// event emitter
 import { Mitt } from "@/types/mitt/index";
+
 import { defineComponent, ref, inject } from "vue";
 export default defineComponent({
-  name: "UI",
-  components: {  MessageRenderer, InputItem },
+  name: "Messenger",
+  components: { MessageVisualizer, InputVisualizer },
   setup() {
     const messages = Messages;
     const count = ref(0);
@@ -42,9 +44,9 @@ export default defineComponent({
     }
 
     window.ipcRenderer.on("render-message", (event, payload) => {
-      console.log(payload.message)
+      console.log(payload.message);
       emitter.emit("renderMessage", payload.message);
-    })
+    });
 
     return {
       emittMessage,
similarity index 81%
rename from src/core/messenger/message/messageDetails/useComputed.ts
rename to src/components/messenger/messageBubble/bubbleDetails/computed.ts
index dc4cfafb1509aa93300c36b17e1dd2ac33d116d7..4e28ec4af5b410df9a6bdfc03f5721a0d52ee9f4 100644 (file)
@@ -1,5 +1,9 @@
 import { computed, onMounted, ref } from "vue";
-export default function useMessageItemComp(m: string, id: string) {
+
+/**
+ * single message svg item computed properties
+ */
+export default function useComputedBubble(m: string, id: string) {
     const x = ref(0)
     const paddingLeft = 30;
     const winW = 350;
@@ -45,9 +49,9 @@ export default function useMessageItemComp(m: string, id: string) {
     }
 
     const startingOffset = computed(() => {
-        const messageRenderer = document.getElementById('messageRenderer');
-        if (messageRenderer) {
-            const rect = messageRenderer.getBoundingClientRect();
+        const parentView = document.getElementById('messageVisualizer');
+        if (parentView) {
+            const rect = parentView.getBoundingClientRect();
             let Y = 600;
             if (rect.height > 500) {
                 Y = rect.height + messageMarginTop;
@@ -57,10 +61,7 @@ export default function useMessageItemComp(m: string, id: string) {
     });
 
     onMounted(async () => {
-        // to calculate X offset:
-        // First: set message width ref
         await setMessageWidthRef();
-        // Then: calculate message X offset
         calculateXoffset();
     })
 
similarity index 93%
rename from src/core/messenger/message/messageDetails/useAnimations.ts
rename to src/components/messenger/messageBubble/bubbleDetails/drawer.ts
index d74a8272a73c7c965d93209b2abcaf32cb3a8fa1..9f6ee209ea68f484b914b796afc1c1c81a0e7bba 100644 (file)
@@ -1,6 +1,10 @@
 import { ref, onMounted } from "vue";
 import { Ref } from "@/types/vueRef/index";
-export default function useMessageItemAnims({
+
+/**
+ * handles visualization of single message svg items
+ */
+export default function useBubbleDrawer({
     mr,
     tr,
 }: {
similarity index 83%
rename from src/components/UI/UIDetails/messageItem.vue
rename to src/components/messenger/messageBubble/index.vue
index 21e93f927b36a6d2e5dd76006eecb51e1c92b00e..cd9ecd19aa1218d551ba4ecbca77a9e440b3a2ca 100644 (file)
 </template>
 
 <script lang="ts">
+import useComputedBubble from "./bubbleDetails/computed";
+import useBubbleDrawer from "./bubbleDetails/drawer";
+
+// text wrap composition
+import useTextWrap from "@/composables/textWrap";
+
 import { defineComponent, onMounted, ref, reactive } from "vue";
-import useMessageItemComp from "@/core/messenger/message/messageDetails/useComputed";
-import useMessageItemAnims from "@/core/messenger/message/messageDetails/useAnimations";
-import useTextWrap from "@/utils/textWrap/useTextWrap";
 export default defineComponent({
   name: "MessageItem",
   props: {
@@ -46,12 +49,12 @@ export default defineComponent({
     const messageRect = ref(`message${msgID.value}`);
     const messageText = ref(`text${msgID.value}`);
 
-    const { textFill, bubbleFill, startingOffset } = useMessageItemComp(
+    const { textFill, bubbleFill, startingOffset } = useComputedBubble(
       props.item.modifier,
       props.item.id
     );
     const { textWrap } = useTextWrap();
-    const { drawMessage, signatureTranslate } = useMessageItemAnims({
+    const { drawMessage, signatureTranslate } = useBubbleDrawer({
       mr: messageRect,
       tr: messageText,
     });
similarity index 80%
rename from src/components/UI/UIDetails/messageRenderer.vue
rename to src/components/messenger/messageVisualizer/index.vue
index 6084c170b2a7fba8e8466fdc51a6070163019aff..36db2cc321f9d000f9cf79a837fe2442ddaa71d0 100644 (file)
@@ -5,7 +5,7 @@
     xmlns:xlink="http://www.w3.org/1999/xlink"
     :viewBox="scrollView"
   >
-    <g id="messageRenderer" transform="translate(0, 5)">
+    <g id="messageVisualizer" transform="translate(0, 5)">
       <g
         v-for="(item, index) in renderArr"
         :key="index"
 </template>
 
 <script lang="ts">
-import useMessageView from "@/core/messenger/messageView/useMessageView";
-import useScrollView from "@/core/UI/scrollView/useScrollView";
-import { defineComponent, reactive, inject } from "vue";
-import MessageItem from "./messageItem.vue";
+import useMessageVisualizer from "./viewDetails/visualizer";
+
+import useScrollView from "@/composables/scrollView";
+
+import MessageItem from "../messageBubble/index.vue";
+
 import { Message } from "@/types/message/index";
+
 import { Mitt } from "@/types/mitt/index";
+import { defineComponent, reactive, inject } from "vue";
 export default defineComponent({
-  name: "MessageRenderer",
+  name: "MessageVisualizer",
   components: { MessageItem },
   setup() {
     const renderArr: Message[] = reactive([]);
@@ -49,9 +53,9 @@ export default defineComponent({
       });
     }
 
-    const { beforeEnter, enter, afterEnter } = useMessageView();
+    const { beforeEnter, enter, afterEnter } = useMessageVisualizer();
     const { scrollView } = useScrollView({
-      targetId: "messageRenderer",
+      targetId: "messageVisualizer",
     });
 
     return {
similarity index 81%
rename from src/core/messenger/messageView/useMessageView.ts
rename to src/components/messenger/messageVisualizer/viewDetails/visualizer.ts
index e1eae8d634521d5726f8b24422b680e714a5fc87..756ca67bdc49ec6e254a75f81a0ed2228086f92b 100644 (file)
@@ -1,14 +1,12 @@
-import useMessageViewAnims from "./viewDetails/useAnimations";
-import useOffsetCalculator from './viewDetails/useOffsetCalculator';
 import { VueTransitionCallback } from '@/types/vue3/vueTransitionCallback/index';
-
+import useMessageViewAnims from "./anime";
+import useOffsetCalculator from './offsetCalculator';
 
 /**
- * messageView component logic
- * handles visualization of exisiting user messages
- * as well as incoming messages
+ * handles visualization of view messages through 
+ * vue transition callbacks and anime-js animations
  */
-export default function useMessageView(): {
+export default function useMessageVisualizer(): {
     beforeEnter: VueTransitionCallback;
     enter: VueTransitionCallback;
     afterEnter: VueTransitionCallback;
@@ -24,9 +22,8 @@ export default function useMessageView(): {
     let shift = false;
     let shiftOffset = 0;
 
-
     // animates message item to 'enter' renderView
-    const animateMessage = (el: SVGGElement) => {
+    const animateMessage = (el: SVGGElement): void => {
 
         const transform = {
             initialTranslate: `translate(${messageOffset.x} ${messageOffset.y})`,
@@ -35,13 +32,11 @@ export default function useMessageView(): {
         shift ? shiftAnimate(el, transform, shiftOffset, messageList) : stackAnimate(el, transform);
     }
 
-    // callback called before message enter
     const beforeEnter: VueTransitionCallback = () => {
         const queryResult = document.querySelectorAll(".messageList");
         if (queryResult.length) messageList = queryResult;
     }
 
-    // callback called on message enter
     const enter: VueTransitionCallback = (el) => {
         // query DOM for latest message as soon as it enters
         if (el) {
@@ -49,7 +44,6 @@ export default function useMessageView(): {
         }
     }
 
-    // callback called after message enter
     const afterEnter: VueTransitionCallback = (el) => {
         // get message positioning & shift state before animating
         if (message) {
similarity index 99%
rename from src/components/UI/UIDetails/messages.ts
rename to src/components/messenger/mockMessages.ts
index 6284c8208b3d142204f94ccacfadca6b3c248a4e..8bda8342d67b6c7543954fa16b9da1c92756c880 100644 (file)
@@ -158,4 +158,4 @@ const Messages: Message[] = [
         time: ""
     },
 ];
-export default Messages;
\ No newline at end of file
+export default Messages;
similarity index 99%
rename from src/components/AI/index.vue
rename to src/components/voiceBackground/index.vue
index d263eb005118f73b42717265cfc23b6f0139feaa..124a541b4b3f5d47f5049f29e66e9dd5a3461ba7 100644 (file)
@@ -34,7 +34,7 @@
 <script>
 import { defineComponent, onMounted, ref, inject, watch } from "vue";
 export default defineComponent({
-  name: "AI",
+  name: "voiceBackground",
   setup() {
     const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
     const analyzer = audioCtx.createAnalyser();
diff --git a/src/composables/mediaStream.ts b/src/composables/mediaStream.ts
new file mode 100644 (file)
index 0000000..0319778
--- /dev/null
@@ -0,0 +1,6 @@
+export default async function useMediaStream() {
+   const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+   // var audioTrack = stream.getAudioTracks()[0]; 
+   // console.log(audioTrack.getSettings()) 
+   return stream;
+}
\ No newline at end of file
similarity index 72%
rename from src/core/UI/scrollView/useScrollView.ts
rename to src/composables/scrollView.ts
index 859b7046d247b416dc2209cfa0ed3788579981c2..8fea63ffc04a66537b834732a5e8e9d714a9bef3 100644 (file)
@@ -1,5 +1,11 @@
-import { ref, computed, onMounted } from "vue";
+import { ref, computed, onMounted, onUnmounted } from "vue";
+
+/**
+ * handles visualization of view messages through 
+ * vue transition callbacks and anime-js animations
+ */
 export default function useScrollView({ targetId }: { targetId: string }) {
+
   const scroll = ref(0);
   let scrollTarget: HTMLElement | SVGElement | null;
   const targetHeight = ref(0);
@@ -27,7 +33,7 @@ export default function useScrollView({ targetId }: { targetId: string }) {
     }
   }
 
-  const handleScrollEvent = (verticalScroll: number) => {
+  const updateView = (verticalScroll: number) => {
     if (scrollTarget) {
       const heightResult = getHeight();
       if (heightResult) {
@@ -40,16 +46,22 @@ export default function useScrollView({ targetId }: { targetId: string }) {
     }
   }
 
+  const scrollUpdate = (e: any): void => {
+    const verticalScroll = e.deltaY * 0.25;
+    updateView(verticalScroll);
+  }
+
   onMounted(() => {
     scrollTarget = document.getElementById(targetId);
     if (scrollTarget) {
-      window.addEventListener("wheel", (e) => {
-        const verticalScroll = e.deltaY * 0.25;
-        handleScrollEvent(verticalScroll);
-      })
+      window.addEventListener("wheel", scrollUpdate);
     }
   });
 
+  onUnmounted(() => {
+     window.removeEventListener("wheel", scrollUpdate);
+  })
+
   return {
     scrollView
   }
diff --git a/src/core/UI/input/audio/audioDetails/useAnimations.ts b/src/core/UI/input/audio/audioDetails/useAnimations.ts
deleted file mode 100644 (file)
index 7488a00..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-import AnimeFunc from "@/types/animejs/index";
-import {inject} from "vue";
-import {Ref} from "@/types/vueRef/index";
-
-export default function useAudioAnimations() {
-  // imports animejs safely
-  let anime: AnimeFunc;
-  const animeInject: AnimeFunc | undefined = inject("animejs");
-  if (animeInject) anime = animeInject;
-
-  function animateSend(el: Element, trigger: Ref<boolean>) {  
-    console.log(el);
-    anime({
-      targets: el,
-      opacity: 0,
-      translateY: -100,
-      duration: 0,
-      complete: function() {
-        trigger.value = false;
-      }
-    })
-  }
-
-  return {
-    animateSend
-  }
-
-}
\ No newline at end of file
diff --git a/src/core/UI/input/audio/useAudioRecorder.ts b/src/core/UI/input/audio/useAudioRecorder.ts
deleted file mode 100644 (file)
index 52ea69f..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-
-interface Window {
-  AudioContext: any;
-  webkitAudioContext: any;
-}
-
-interface Event {
-  inputBuffer: AudioBuffer;
-}
-
-import { Ref } from '@/types/vueRef/index';
-import { watch } from 'vue';
-
-export default function useAudioRecorder(record: Ref<boolean>, stream: MediaStream) {
-
-  let audioArray: Int16Array[] = [];
-  const bufferLen = 4096;
-  const numChannels = 1;
-
-  //window.AudioContext = window.AudioContext;
-  const audioContext = new AudioContext({ sampleRate: 16000 });
-
-  const source = audioContext.createMediaStreamSource(stream);
-  const context = source.context;
-  const sampleRate = context.sampleRate;
-
-  const node = context.createScriptProcessor.call(
-    context,
-    bufferLen,
-    numChannels,
-    numChannels
-  );
-
-  node.onaudioprocess = (e: Event) => {
-    const inputBuffer = new Float32Array(bufferLen);
-
-    e.inputBuffer.copyFromChannel(inputBuffer, 0);
-    const int16 = new Int16Array(inputBuffer.buffer);
-    audioArray.push(int16);
-  };
-
-  watch(record, (beginRecording) => {
-    if (beginRecording) {
-      node.connect(context.destination);
-    } else {
-      node.disconnect(context.destination);
-
-      const message = {
-        text: '',
-        audio: {
-          sampleRate: sampleRate,
-          channelCount: numChannels,
-          audio: audioArray
-        }
-      }
-      window.postMessage({
-        myTypeField: 'send-message',
-        message: message
-      }, '*')
-
-      audioArray = [];
-    }
-  })
-
-  source.connect(node);
-
-}
diff --git a/src/core/UI/input/audio/useStreamRecord.ts b/src/core/UI/input/audio/useStreamRecord.ts
deleted file mode 100644 (file)
index 4380d23..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-import { inject } from 'vue';
-export default function useStreamRecord(): {
-    initRecorder: (stream: MediaStream) => MediaRecorder;
-} {
-    let streamRecorder: MediaRecorder;
-    const chunks: Blob[] = [];
-    const emitter: any = inject("mitt");
-    const options = {
-        mimeType: "audio/webm; codecs=opus",
-    }
-
-    // console.log('ogg supported',MediaRecorder.isTypeSupported('audio/mpeg; '))
-    const initRecorder = (stream: MediaStream) => {
-
-        const audioTrack = stream.getAudioTracks()[0];
-        const audioSettings = audioTrack.getSettings()
-
-        streamRecorder = new MediaRecorder(stream, options);
-        streamRecorder.ondataavailable = (e: any) => {
-            if (e.data.size > 0) {
-                chunks.push(e.data);
-            } else {
-                // ...
-            }
-            //chunks.push(e.data) 
-        }
-        streamRecorder.onstop = async (e: any) => {
-            const audioBlob = new Blob(chunks, {
-                type: options.mimeType
-            });
-            const arrayBuffer = await audioBlob.arrayBuffer()
-            console.log(arrayBuffer)
-            const message = {
-                text: '',
-                audio: {
-                    sampleRate: audioSettings.sampleRate,
-                    sampleSize: audioSettings.sampleSize,
-                    channelCount: audioSettings.channelCount,
-                    mimeType: options.mimeType,
-                    arrayBuffer: arrayBuffer
-                }
-            }
-            window.postMessage({
-                myTypeField: 'send-message',
-                message: message
-            }, '*')
-
-        }
-        return streamRecorder
-    }
-
-    return {
-        initRecorder,
-    }
-}
diff --git a/src/core/UI/input/text/textDetails/useAnimations.ts b/src/core/UI/input/text/textDetails/useAnimations.ts
deleted file mode 100644 (file)
index 3e3fc51..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-import AnimeFunc from "@/types/animejs/index";
-import {inject} from "vue";
-import {Ref} from "@/types/vueRef/index";
-/*
- * Anime js animations used by input textController
-*/
-export default function useTextInputAnims() {
-
-    // imports animejs safely
-    let anime: AnimeFunc;
-    const animeInject: AnimeFunc | undefined = inject("animejs");
-    if (animeInject) anime = animeInject;
-    
-    const animateSend = (el: HTMLElement, elHeight: number, input: Ref<string>) => {
-      anime({
-        targets: el,
-        translateY: -elHeight + 15,
-        duration: 200,
-        easing: "easeInQuad",
-        complete: function() {
-          anime({
-            targets: el,
-            translateY: 50,
-            duration: 200,
-            easing: "easeOutQuad",
-            complete: function () {
-              input.value = "";
-            }
-          })
-        }
-      })
-    }
-
-    const inputAppear = (el: HTMLElement) => {
-        anime({
-          targets: el,
-          translateY: -10,
-          duration: 0
-        });
-    }
-
-    const verticalShiftInput = (el: HTMLElement, yTrans: number) => {
-        anime({
-          targets: el,
-          translateY: yTrans,
-          easing: "easeOutQuad",
-          duration: 150,
-        });
-    }
-
-    return {
-      animateSend,
-      verticalShiftInput,
-      inputAppear
-    }
-
-}
\ No newline at end of file
diff --git a/src/core/UI/input/text/useTextRender.ts b/src/core/UI/input/text/useTextRender.ts
deleted file mode 100644 (file)
index 9cc8571..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-import { ref, computed, inject } from 'vue';
-import useTextInputAnims from "./textDetails/useAnimations";
-import { Ref } from "@/types/vueRef/index";
-/*
- * text rendering logic used by inputController
-*/
-export default function useTextRender(input: Ref<string>) {
-  const inputHeight = ref(42);
-  const inputWidth = ref(0);
-
-  const emitter: any = inject("mitt");
-  const { animateSend, verticalShiftInput, inputAppear } = useTextInputAnims();
-
-  async function renderTextInput() {
-    // first, get input dimensions
-    const inputDivs = await document.getElementsByClassName("inputBubble");
-    const height = inputDivs[0].getBoundingClientRect().height;
-    inputWidth.value = inputDivs[0].getBoundingClientRect().width;
-    // then, query for foreignObject div
-    const foreignObjectDiv = document.getElementsByClassName(
-      "inputContainer"
-    );
-    const foreignEl = foreignObjectDiv[0] as HTMLElement;
-    if (height !== inputHeight.value) {
-      inputHeight.value = height;
-    }
-    if (inputHeight.value === 0) {
-      inputAppear(foreignEl)
-    } else if (inputHeight.value > 36) {
-      const yTrans = -inputHeight.value + 30;
-      verticalShiftInput(foreignEl, yTrans);
-    }
-  }
-
-  emitter.on("newSelfMessage", async (payload: any) => {
-    const inputEl = document.getElementsByClassName("inputContainer");
-    const foreignEl = inputEl[0] as HTMLElement;
-    animateSend(foreignEl, inputHeight.value, input);
-  });
-
-  const textInputXoffset = computed(() => {
-    return `calc(50% - ${inputWidth.value / 2})`;
-  });
-
-  return {
-    textInputXoffset,
-    renderTextInput
-  }
-}
index 55abcae805347f9d6ba8a23c8e64194f117a45e9..316409939dc5d025129043f56dd8f70741818ac1 100644 (file)
@@ -1,14 +1,21 @@
 import { ref, watch, onUnmounted, onMounted } from "vue";
+
 import useKeyDownHandler from "@/utils/keyDownHandler/useKeyDownHandler";
+
 import useMediaStream from "@/utils/mediaStream/useMediaStream";
+
 import useStreamRecord from "./audio/useStreamRecord";
+
 import useStreamVisualizer from './audio/useStreamVisualizer';
+
 import useTextRender from './text/useTextRender';
+
 import useAudioAnimations from './audio/audioDetails/useAnimations';
+
 import useAudioRecorder from './audio/useAudioRecorder';
 
 /* 
-* Input controller logic
+* Input visualizer logic
 * Will visualize text input by default as user types or
 * if user holds space bar down, input audio stream
 * will be visualized & recorded
index be6cd4b18db75159632716fcce166e7362dee034..cbb87abcc8c890526e31f1d1021331b5d78d340e 100644 (file)
@@ -6,7 +6,7 @@ const routes: Array<RouteRecordRaw> = [
     path: "/",
     name: "Home",
     component: HomeIndex
-  }
+  },
 ];
 
 const router = createRouter({
index 58054052086af5ac1724cb679cd78684ec7d5a01..a0a4eda2065f64808fc3812403c78be024584ec8 100644 (file)
@@ -1,12 +1,12 @@
 <template>
-  <UI />
+  <Messenger />
 </template>
 
 <script lang="ts">
 import { defineComponent } from "vue";
-import UI from "@/components/UI/index.vue";
+import Messenger from "@/components/messenger/index.vue";
 export default defineComponent({
   name: "HomeIndex",
-  components: { UI },
+  components: { Messenger },
 });
 </script>
diff --git a/testAudio.js b/testAudio.js
new file mode 100644 (file)
index 0000000..dd6b6a5
--- /dev/null
@@ -0,0 +1,21 @@
+const fs = require('fs');
+const portAudio = require('naudiodon');
+
+// Create an instance of AudioIO with outOptions (defaults are as below), which will return a WritableStream
+const ao = new portAudio.AudioIO({
+  outOptions: {
+    channelCount: 2,
+    sampleFormat: portAudio.SampleFormat16Bit,
+    sampleRate: 48000,
+    deviceId: -1, // Use -1 or omit the deviceId to select the default device
+    closeOnError: true // Close the stream if an audio error is detected, if set false then just log the error
+  }
+});
+
+// Create a stream to pipe into the AudioOutput
+// Note that this does not strip the WAV header so a click will be heard at the beginning
+const rs = fs.createReadStream('rawAudio.wav');
+
+// Start piping data and start streaming
+rs.pipe(ao);
+ao.start();
\ No newline at end of file