html bubbles, more simplifications, new session protocol
This commit is contained in:
parent
0c6a58ea24
commit
d3f99d6133
21 changed files with 340 additions and 169 deletions
|
|
@ -1,7 +1,6 @@
|
|||
import Splash from "./splash.js";
|
||||
import Messenger from "./messenger.js";
|
||||
import Login from "./login.js";
|
||||
import Header from "./header.js";
|
||||
|
||||
const account = Vue.ref(null);
|
||||
|
||||
|
|
@ -10,8 +9,7 @@ const App =
|
|||
components: {
|
||||
Splash,
|
||||
Messenger,
|
||||
Login,
|
||||
Header
|
||||
Login
|
||||
},
|
||||
|
||||
setup()
|
||||
|
|
@ -22,7 +20,7 @@ const App =
|
|||
template: `
|
||||
<main id="app" v-if="account !== null">
|
||||
|
||||
<Header />
|
||||
<button id="header-titlebar" />
|
||||
|
||||
<Messenger v-if="account"/>
|
||||
<Login v-else />
|
||||
|
|
|
|||
|
|
@ -1,23 +1,36 @@
|
|||
const Bubble =
|
||||
{
|
||||
props: ["modifier", "text", "child"],
|
||||
props: ["category", "text", "blob", "modifier", "child"],
|
||||
|
||||
setup(props)
|
||||
{
|
||||
const getUrl = () => {
|
||||
return `data:${props.category};base64,${props.blob}`;
|
||||
}
|
||||
|
||||
Vue.onMounted(() => {
|
||||
|
||||
const messengerEl = document.getElementById("messenger");
|
||||
|
||||
if (messengerEl) {
|
||||
messengerEl.dispatchEvent(new CustomEvent('adjust-scroll'));
|
||||
}
|
||||
|
||||
document.getElementById("messenger").dispatchEvent(new CustomEvent('adjust-scroll'));
|
||||
});
|
||||
|
||||
return { getUrl };
|
||||
},
|
||||
|
||||
template: `
|
||||
<div :class="'bubble ' + modifier + '-bubble ' + modifier +'-'+ child" v-html="text">
|
||||
</div> `
|
||||
<div :class="'bubble ' + modifier + '-bubble ' + modifier +'-'+ child">
|
||||
|
||||
<p v-if="category=='text/plain'">{{ text }}</p>
|
||||
|
||||
<p v-else-if="category=='audio/L16'">{{ text }}</p>
|
||||
|
||||
<div v-else-if="category=='text/html'" v-html="text"></div>
|
||||
|
||||
<img v-else-if="category.startsWith('image/')" :src="getUrl()" />
|
||||
|
||||
<a v-else :href="getUrl()">{{ text }}</a>
|
||||
|
||||
</div>`
|
||||
};
|
||||
|
||||
export default Bubble;
|
||||
|
||||
// TODO: should include other styling/actions for audio bubbles
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
const Context =
|
||||
{
|
||||
props: ["modifier", "context", "avatar", "audio", "id"],
|
||||
props: ["modifier", "context", "avatar", "id"],
|
||||
|
||||
setup(props) {
|
||||
const playback = Vue.ref(false);
|
||||
|
|
@ -9,7 +9,6 @@ const Context =
|
|||
Vue.onMounted(() => {
|
||||
window.mainApi.on("playback", (id, status) => {
|
||||
if (id === props.id) {
|
||||
console.log(id, status);
|
||||
playback.value = status;
|
||||
}
|
||||
});
|
||||
|
|
@ -25,10 +24,10 @@ const Context =
|
|||
v-show="avatar"
|
||||
class="context-avatar"
|
||||
:class="{ audio: playback }"
|
||||
>
|
||||
>
|
||||
|
||||
<img :src="'data:image/png;base64,' + avatar" />
|
||||
|
||||
<img :src="avatar">
|
||||
|
||||
</div>
|
||||
|
||||
{{ context }}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,10 @@ function useText(elementId, left, typing)
|
|||
if (e.key === "Enter" && el.value)
|
||||
{
|
||||
window.mainApi.send("message", {
|
||||
text: el.value, audio: null });
|
||||
category: "text/plain",
|
||||
text: el.value,
|
||||
blob: null
|
||||
});
|
||||
show.value = false;
|
||||
}
|
||||
});
|
||||
|
|
@ -70,4 +73,4 @@ function useText(elementId, left, typing)
|
|||
return { leftSide, show };
|
||||
}
|
||||
|
||||
export default useText;
|
||||
export default useText;
|
||||
|
|
|
|||
|
|
@ -53,5 +53,3 @@ function getInitials(name)
|
|||
}
|
||||
|
||||
export default Input;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const Message =
|
|||
Context
|
||||
},
|
||||
|
||||
props: ["modifier", "content", "context", "avatar", "audio", "id"],
|
||||
props: ["modifier", "content", "context", "avatar", "id"],
|
||||
|
||||
setup(props)
|
||||
{
|
||||
|
|
@ -19,9 +19,11 @@ const Message =
|
|||
<div :id="id" :class="'message ' + modifier + '-message'">
|
||||
|
||||
<Bubble
|
||||
v-for="(text, index) in content"
|
||||
v-for="(c, index) in content"
|
||||
:category="c.category"
|
||||
:text="c.text"
|
||||
:blob="c.blob"
|
||||
:modifier="modifier"
|
||||
:text="text"
|
||||
:child="calcChild(index, content.length)"
|
||||
/>
|
||||
|
||||
|
|
@ -29,7 +31,6 @@ const Message =
|
|||
:modifier="modifier"
|
||||
:context="context"
|
||||
:avatar="avatar"
|
||||
:audio="audio"
|
||||
:id="id"
|
||||
/>
|
||||
|
||||
|
|
@ -56,4 +57,4 @@ function calcChild(index, len)
|
|||
}
|
||||
}
|
||||
|
||||
export default Message;
|
||||
export default Message;
|
||||
|
|
|
|||
|
|
@ -17,33 +17,54 @@ const Messenger = {
|
|||
setup()
|
||||
{
|
||||
const person = Vue.ref("");
|
||||
const messages = Vue.ref([]);
|
||||
const messages = Vue.ref([{
|
||||
modifier: "ai",
|
||||
content: [{
|
||||
category: "text/html",
|
||||
text: `<div class="mail">
|
||||
<h4>About HTML Parser</h4>
|
||||
<p>Return the text of the most recently opened start tag. This should not normally be needed for structured processing, but may be useful in dealing with HTML “as deployed” or for re-generating input with minimal changes (whitespace between attributes can be preserved, etc.).</p>
|
||||
</div>`,
|
||||
blob: null
|
||||
}],
|
||||
context: "Email from David Spade",
|
||||
avatar: null,
|
||||
id: 0
|
||||
}]);
|
||||
|
||||
const onDrop = async (e) => {
|
||||
const file = e.dataTransfer.items[0].getAsFile();
|
||||
|
||||
const buffer = await file.arrayBuffer();
|
||||
const b64String = await window.mainApi.invoke("encode", buffer);
|
||||
|
||||
window.mainApi.send("message", {
|
||||
category: file.type,
|
||||
text: file.name,
|
||||
blob: b64String
|
||||
});
|
||||
}
|
||||
|
||||
Vue.onMounted(() => {
|
||||
|
||||
window.mainApi.on("ui-init", (ui) => {
|
||||
person.value = ui.person;
|
||||
messages.value = ui.messages;
|
||||
});
|
||||
|
||||
window.mainApi.on("ui-update", (message) => {
|
||||
|
||||
if (message.hasOwnProperty("person"))
|
||||
window.mainApi.on("message", (message) => {
|
||||
if (message.person)
|
||||
{
|
||||
person.value = message.person;
|
||||
return;
|
||||
messages.value = message.messages;
|
||||
}
|
||||
|
||||
let index = messages.value.findIndex(m => m.id===message.id);
|
||||
if (index >= 0)
|
||||
else if (message.name)
|
||||
{
|
||||
messages.value[index] = message;
|
||||
}
|
||||
else
|
||||
person.value = message;
|
||||
}
|
||||
else if (message.content)
|
||||
{
|
||||
messages.value.push(message);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
messages.value[messages.value.length - 1].content.push(message);
|
||||
}
|
||||
});
|
||||
|
||||
initScroll("messenger");
|
||||
|
|
@ -54,7 +75,7 @@ const Messenger = {
|
|||
messages.value = [];
|
||||
});
|
||||
|
||||
return { person, messages };
|
||||
return { person, messages, onDrop };
|
||||
},
|
||||
|
||||
template: `
|
||||
|
|
@ -62,28 +83,20 @@ const Messenger = {
|
|||
<WS />
|
||||
<Input :person="person" />
|
||||
|
||||
<div id="messenger">
|
||||
<div id="messenger"
|
||||
@drop="onDrop($event)"
|
||||
@dragover.prevent
|
||||
@dragenter.prevent
|
||||
>
|
||||
<Message
|
||||
v-for="message in messages"
|
||||
:modifier="message.modifier"
|
||||
:content="message.content"
|
||||
:context="message.context"
|
||||
:avatar="message.avatar"
|
||||
:audio="message.audio"
|
||||
:id="message.id"
|
||||
/>
|
||||
</div> `
|
||||
}
|
||||
|
||||
export default Messenger;
|
||||
|
||||
// {
|
||||
// modifier: "ai",
|
||||
// content: [
|
||||
// "This is test 2\r\n"
|
||||
// ],
|
||||
// context: "Email from adgundersen@gmail.com",
|
||||
// avatar: "./assets/mailAvatar.svg",
|
||||
// audio: null,
|
||||
// id: 0
|
||||
// }
|
||||
|
|
@ -4,23 +4,15 @@ const Settings =
|
|||
setup() {
|
||||
const active = Vue.ref(false);
|
||||
|
||||
function hideSettings(e) {
|
||||
if (e.key == "Escape") {
|
||||
active.value = false;
|
||||
window.removeEventListener("keydown", hideSettings);
|
||||
}
|
||||
}
|
||||
window.addEventListener("keydown", (e) => {
|
||||
console.log("toggle settings");
|
||||
})
|
||||
|
||||
function showSettings() {
|
||||
active.value = true;
|
||||
window.addEventListener("keydown", hideSettings);
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
const logout = async () => {
|
||||
await window.mainApi.send("logout");
|
||||
}
|
||||
|
||||
return { showSettings, active, logout };
|
||||
return { active, logout };
|
||||
},
|
||||
|
||||
template: `
|
||||
|
|
@ -30,11 +22,7 @@ const Settings =
|
|||
Logout
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<button v-else class="settings-icon" @click="showSettings">
|
||||
<img src="./assets/settingsIcon.svg">
|
||||
</button> `
|
||||
</div>`
|
||||
}
|
||||
|
||||
export default Settings;
|
||||
Loading…
Reference in a new issue