}
#app {
+ font-family: "SF Pro Text";
+
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
- color: #2c3e50;
-
height: 100vh;
width: 100vw;
width: 100vw;
height: 54px;
- -webkit-user-select: none;
- -webkit-app-region: drag;
+ opacity: 0.75;
- background-color: red;
+ background-color: #EBEBEB;
- opacity: 0.0;
+ -webkit-user-select: none;
+ -webkit-app-region: drag;
border: none;
- outline:none;
+ outline: none;
}
<script lang="ts">
-
-// mock messages
-import Messages from "./mockMessages";
-
-
import TextBubble from "@/components/textBubble/index.vue";
+
import { Message } from "@/types/message/index";
+import { useIpc } from "@/modules/ipc";
+import { Queue } from '@/modules/queue';
+
import useTransitions from "./viewDetails/transitions";
import useScrollView from "@/modules/scrollView";
+
import useMitt from "@/modules/mitt";
-import { useIpc } from "@/modules/ipc";
-import { Queue } from '@/modules/queue';
+
import { defineComponent, reactive, onMounted, onUnmounted, watch, ref } from "vue";
export default defineComponent({
+
name: "MessageView",
components: { TextBubble },
const renderQ = new Queue();
const { invoke } = useIpc("message");
+
+ // Each method will be called on their respective lifecycle event.
const { beforeEnter, enter, afterEnter, rendering } = useTransitions();
const { scrollView } = useScrollView({
targetId: "messageView",
});
-
- const messages = Messages;
- const count = ref(0);
-
- function emittMessage() {
- console.log("emitting mock message")
- if (count.value < messages.length) {
- emitter.emit("render-message", messages[count.value]);
- count.value++;
- return;
- }
- }
-
const render = (m: Message) => {
if (rendering.value) {
renderQ.enqueue(m);
})
onMounted(() => {
+
+ // Call render function on event "render-message."
window.ipcRenderer.on("render-message", (event, payload) => {
const m = payload.message;
if (m.content) render(m);
enter,
afterEnter,
scrollView,
- emittMessage,
};
},
});
</script>
+
+<style lang="scss" scoped>
+
+ #messageView {
+ width: 1000px;
+ height: 100vh;
+ color: red;
+ }
+
+</style>
+++ /dev/null
-import { Message } from "@/types/message/index";
-
-const Messages: Message[] = [
- {
- content: "Welcome",
- context: "ai",
- subContext: "",
- modifier: "ai",
- id: "2",
- time: ""
- },
- {
- content:
- "add friend",
- context: "launch",
- subContext: "",
- modifier: "sf",
- id: "1",
- time: ""
- },
- {
- content: "Did you mean to addcontact?",
- context: "addcontact",
- subContext: "",
- modifier: "ai",
- id: "5",
- time: ""
- },
- {
- content: "yes",
- context: "fulfill",
- subContext: "",
- modifier: "sf",
- id: "6",
- time: ""
- },
-
- {
- content: "Hey Anna, did you get the paper done?",
- context: "Mesage from Tommy McConville",
- subContext: "New Conversation",
- modifier: "ai",
- id: "3",
- time: ""
- },
- {
- content: "Hey do you know Enrique Hernandez?",
- context: "Message from Crimata",
- subContext: "",
- modifier: "ai",
- id: "7",
- time: ""
- },
-
- {
- content: "Yes posting it online soon.",
- context: "You to Tommy McConville",
- subContext: "",
- modifier: "sf",
- id: "4",
- time: ""
- },
- {
- content: "Hey do you know Enrique Hernandez?",
- context: "Message from Crimata",
- subContext: "",
- modifier: "ai",
- id: "8",
- time: ""
- },
- {
- content: "Hey Anna, did you get the paper done?",
- context: "Message from Tommy McConville",
- subContext: "",
- modifier: "ai",
- id: "9",
- time: ""
- },
- {
- content: "Hey Anna, did you get the paper done?",
- context: "Message from Tommy McConville",
- subContext: "",
- modifier: "ai",
- id: "10",
- time: ""
- },
- {
- content: "Yes posting it online soon.",
- context: "Message from Tommy McConville",
- subContext: "",
- modifier: "sf",
- id: "11",
- time: ""
- },
- {
- content:
- "I can be there at 5 pm tomorrow to greet you at a Chick file with my almond and chive salad.",
- context: "You to Tommy McConville",
- subContext: "",
- modifier: "sf",
- id: "12",
- time: ""
- },
- {
- content: "Hey Anna, did you get the paper done?",
- context: "Message from Tommy McConville",
- subContext: "",
- modifier: "ai",
- id: "13",
- time: ""
- },
- {
- content: "Hey Anna, did you get the paper done?",
- context: "Message from Tommy McConville",
- subContext: "",
- modifier: "ai",
- id: "14",
- time: ""
- },
- {
- content: "Yes posting it online soon.",
- context: "You to Tommy McConville",
- subContext: "",
- modifier: "sf",
- id: "15",
- time: ""
- },
- {
- content: "Sweet, thanks!",
- context: "Message from Tommy McConville",
- subContext: "",
- modifier: "ai",
- id: "16",
- time: ""
- },
- {
- content: "Hey do you know Enrique Hernandez?",
- context: "Message from Crimata AI",
- subContext: "",
- modifier: "ai",
- id: "17",
- time: ""
- },
- {
- content: "Yes posting it online soon.",
- context: "You to Tommy McConville",
- subContext: "",
- modifier: "sf",
- id: "18",
- time: ""
- },
- {
- content: "Sweet, thanks!",
- context: "Message from Tommy McConville",
- subContext: "",
- modifier: "ai",
- id: "19",
- time: ""
- },
-];
-export default Messages;
\ No newline at end of file
export default function useOffsetCalculator() {
const xOffset = ref(0);
const yOffset = ref(0);
- const firstChildMarginTop = 0;
+ const firstChildMarginTop = 45;
let previousMessageHeight = 0;
const messagePadding = 15;
const animateMessage = async (el: SVGGElement) => {
+ // Where the message gets translated to.
const elTransform = `translate(${messageOffset.x} ${messageOffset.y})`;
+
const viewTransform = `translate(0 ${-vB})`;
+ // Shift of view height > window height.
if (shift) {
await shiftAnimate(el, elTransform, messageView, viewTransform);
} else {
rendering.value = false;
}
+ // Get height of messageView.
const beforeEnter: VueTransitionCallback = () => {
rendering.value = true;
// query DOM for messageView element
<template>
- <audio-stream v-if="visualizeStream" :bubbleWidth="audioBubbleWidth" :paths="paths" />
- <text-input v-else :input="input" :textXoffset="textInputXoffset" />
+
+ <!-- render audio or text input-->
+ <AudioStream
+ v-if="visualizeStream"
+ :bubbleWidth="audioBubbleWidth"
+ :paths="paths"
+ />
+
+ <TextInput
+ v-else
+ :input="input"
+ :textXoffset="textInputXoffset"
+ :textYoffset="textInputYoffset"
+ />
+
</template>
<script lang="ts">
-// input child components
+
+// import child components
import TextInput from "./textDetails/text.vue";
import AudioStream from "./audioDetails/audio.vue";
import useInputController from "./inputController";
import { defineComponent } from "vue";
+
export default defineComponent({
+
name: "TextAudioInput",
components: { TextInput, AudioStream },
setup() {
const {
input,
visualizeStream,
+
textInputXoffset,
+ textInputYoffset,
+
audioBubbleWidth,
paths,
} = useInputController();
return {
input,
+
textInputXoffset,
+ textInputYoffset,
+
visualizeStream,
audioBubbleWidth,
paths,
emitter.emit("user-message", message);
}
- const { visualizeStreamAsPaths, expandBubble } = useAudioVisualizer(visualizeStream);
+ const {
+ visualizeStreamAsPaths,
+ expandBubble
+ } = useAudioVisualizer(visualizeStream);
+
const { keyDownHandler, input } = useKeyDownHandler(enterCallback);
- const { textInputXoffset, renderTextInput } = useTextVisualizer(input);
+
+ const {
+ textInputXoffset,
+ textInputYoffset,
+ renderTextInput
+ } = useTextVisualizer(input);
// stops stream recording on space key up
function keyupHandler(e: any) {
input,
visualizeStream,
textInputXoffset,
+ textInputYoffset,
audioBubbleWidth,
paths
}
<template>
- <foreignObject class="inputContainer" :x="textXoffset" y="450" width="55%" height="50%">
+
+ <foreignObject
+ class="inputContainer"
+ :x="textXoffset"
+ :y="textYoffset"
+ width="55%"
+ height="50%"
+ >
<div xmlns="http://www.w3.org/1999/xhtml">
- <div class="inputBubble" contenteditable v-show="input.length > 0" font-family="SF Pro">
+ <div class="inputBubble" v-show="input.length > 0">
<p>{{ input }}</p>
</div>
</div>
+
</foreignObject>
+
</template>
<script lang="ts">
+
import { defineComponent } from "vue";
+
export default defineComponent({
+
name: "TextInput",
props: {
input: String,
textXoffset: String,
+ textYoffset: String,
},
});
+
</script>
<style lang="scss" scoped>
-div[contenteditable] {
- width: auto;
+.inputBubble {
+
max-width: 180px;
- height: auto;
- min-height: 36px;
+ min-height: 32px;
+
display: table;
justify-items: center;
- border: 0;
+
border-radius: 10px;
- color: #000;
- background-color: #b9b9b9;
- font-size: 14;
- text-align: left;
+ background-color: #B9B9B9;
+
+ font-size: 14px;
+
p {
max-width: 150px;
overflow-wrap: break-word;
return `calc(50% - ${inputWidth.value / 2})`;
});
+ const textInputYoffset = computed(() => {
+ return `calc(97% - ${inputHeight.value})`;
+ });
+
return {
textInputXoffset,
+ textInputYoffset,
renderTextInput
}
}
export default function useComputedBubble(m: string, id: string) {
const x = ref(0)
const paddingLeft = 30;
+
const winW = 350;
const messageWidth = ref(0);
const messageMarginTop = 75;
const calculateXoffset = async () => {
switch (m) {
case "sf":
- x.value = winW - messageWidth.value - selfMessageMarginRight;
+ x.value = 800 - messageWidth.value - selfMessageMarginRight;
break;
default:
x.value = paddingLeft;
<template>
<g :id="item.id" :class="item.modifier" :transform="startingOffset">
<rect ref="messageRect" x="30" y="10" width="196" height="63" rx="10" :fill="bubbleFill" />
- <text font-size="14" font-family="SF Pro" ref="messageText" :fill="textFill">
+ <text font-size="14" font-family="SF Pro Text" ref="messageText" :fill="textFill">
<tspan dy="16" x="0" v-for="(item, index) in textArr" :key="index">{{ item }}</tspan>
</text>
<g :transform="signatureTranslate">
let topBound: number;
const scrollStartHeight = 445;
- const clampedScroll = computed(() => {
- if (targetHeight.value === 0) { return 0; }
- topBound = -targetHeight.value + 450;
- return Math.max(topBound, Math.min(scroll.value, 0)) as number;
- })
-
const scrollView = computed(() => {
+
if (clampedScroll.value === 0) {
scroll.value = 0;
} else if (clampedScroll.value === topBound) {
scroll.value = topBound;
}
+
+ // x, y, width, height
+ // width and height should be dynamic
return `0 ${clampedScroll.value} 350 500`;
});
+ const clampedScroll = computed(() => {
+ if (targetHeight.value === 0) { return 0; }
+ topBound = -targetHeight.value + 450;
+ return Math.max(topBound, Math.min(scroll.value, 0)) as number;
+ })
+
const getHeight = () => {
if (scrollTarget) {
return scrollTarget.getBoundingClientRect().height as number;
}
}
+ const getWidth = () => {
+ if (scrollTarget) {
+ return scrollTarget.getBoundingClientRect().width as number;
+ }
+ }
+
const updateView = (verticalScroll: number) => {
+
if (scrollTarget) {
+
const heightResult = getHeight();
+ const widthResult = getWidth();
+
if (heightResult) {
// only scroll if renderer scrollStart
if (heightResult > scrollStartHeight) {
<script lang="ts">
+// Home consists on MessageView and Text/Audio imput component.
import MessageView from "@/components/messageView/index.vue";
import TextAudioInput from "@/components/taInput/index.vue";
+
import { defineComponent } from "vue";
export default defineComponent({
name: "Messenger",
components: { MessageView, TextAudioInput },
});
+
</script>
<style lang="scss" scoped>