Compare commits
2 Commits
31eff22d95
...
afe555f51e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afe555f51e | ||
|
|
7a567292ae |
@@ -154,7 +154,7 @@ server:
|
|||||||
max-concurrent-connections: 16
|
max-concurrent-connections: 16
|
||||||
|
|
||||||
# whether to restrict the rate of new connections per IP/CIDR
|
# whether to restrict the rate of new connections per IP/CIDR
|
||||||
throttle: true
|
throttle: false
|
||||||
# how long to keep track of connections for
|
# how long to keep track of connections for
|
||||||
window: 10m
|
window: 10m
|
||||||
# maximum number of new connections per IP/CIDR within the given duration
|
# maximum number of new connections per IP/CIDR within the given duration
|
||||||
|
|||||||
@@ -1,21 +1,17 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
import { useIRCStore } from "@/stores/irc";
|
import { useIRCStore } from "@/stores/irc";
|
||||||
|
|
||||||
const store = useIRCStore();
|
const store = useIRCStore();
|
||||||
const inputBuffer = ref();
|
|
||||||
|
|
||||||
onMounted(store.connect);
|
onMounted(store.connect);
|
||||||
|
|
||||||
function send() {
|
|
||||||
store.sendActiveBuffer(inputBuffer.value);
|
|
||||||
inputBuffer.value = "";
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="d-flex flex-row" style="height: 100vh">
|
<div class="d-flex flex-row" style="height: 100vh">
|
||||||
<v-sheet border class="buffers">
|
<v-sheet border class="buffers">
|
||||||
|
<UserCard />
|
||||||
|
<v-divider />
|
||||||
<BufferList />
|
<BufferList />
|
||||||
</v-sheet>
|
</v-sheet>
|
||||||
<div class="messages d-flex flex-column">
|
<div class="messages d-flex flex-column">
|
||||||
@@ -30,14 +26,16 @@ function send() {
|
|||||||
:me="store.clientInfo.nick"
|
:me="store.clientInfo.nick"
|
||||||
/>
|
/>
|
||||||
<v-sheet>
|
<v-sheet>
|
||||||
<v-text-field
|
<!-- <v-text-field -->
|
||||||
variant="outlined"
|
<!-- variant="outlined" -->
|
||||||
:placeholder="`Message ${store.activeBufferName}`"
|
<!-- :placeholder="`Message ${store.activeBufferName}`" -->
|
||||||
v-model="inputBuffer"
|
<!-- v-model="inputBuffer" -->
|
||||||
hide-details
|
<!-- hide-details -->
|
||||||
class="ma-2"
|
<!-- class="ma-2" -->
|
||||||
@keydown.enter.exact.prevent="send"
|
<!-- @keydown.enter.exact.prevent="send" -->
|
||||||
/>
|
<!-- /> -->
|
||||||
|
|
||||||
|
<InputBuffer @send="store.sendActiveBuffer" />
|
||||||
</v-sheet>
|
</v-sheet>
|
||||||
</div>
|
</div>
|
||||||
<v-sheet class="user-list h-100" border>
|
<v-sheet class="user-list h-100" border>
|
||||||
|
|||||||
97
irchad-web/src/components/InputBuffer.vue
Normal file
97
irchad-web/src/components/InputBuffer.vue
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useIRCStore } from "@/stores/irc";
|
||||||
|
const emit = defineEmits(["send"]);
|
||||||
|
const store = useIRCStore();
|
||||||
|
const text = ref();
|
||||||
|
const menu = ref({
|
||||||
|
open: false,
|
||||||
|
activator: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const menuList = ref({
|
||||||
|
density: "compact",
|
||||||
|
slim: true,
|
||||||
|
items: [],
|
||||||
|
itemTitle: "title",
|
||||||
|
itemValue: "value",
|
||||||
|
selected: [],
|
||||||
|
selectable: true,
|
||||||
|
mandatory: true,
|
||||||
|
returnObject: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const cursorPos = ref(0);
|
||||||
|
const completionPos = ref(0);
|
||||||
|
|
||||||
|
function clickItem() {}
|
||||||
|
function send() {
|
||||||
|
emit("send", text.value);
|
||||||
|
menu.value.open = false;
|
||||||
|
text.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterUsers(s) {
|
||||||
|
if (store.activeBuffer) {
|
||||||
|
return store.activeBuffer.users.filter((u) => u.nick.startsWith(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function trigger(ev) {
|
||||||
|
const input = ev.target;
|
||||||
|
const cursor = input.selectionStart;
|
||||||
|
cursorPos.value = cursor;
|
||||||
|
const text = input.value;
|
||||||
|
|
||||||
|
const textBefore = text.slice(0, cursor);
|
||||||
|
const mentionMatch = textBefore.match(/@(\w*)$/);
|
||||||
|
if (mentionMatch) {
|
||||||
|
menu.value.open = true;
|
||||||
|
menuList.value.items = filterUsers(mentionMatch[1]);
|
||||||
|
menuList.value.itemTitle = "nick";
|
||||||
|
menuList.value.itemValue = "nick";
|
||||||
|
} else {
|
||||||
|
menu.value.open = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tabComplete() {
|
||||||
|
if (!menu.value.open) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextSelection = 0;
|
||||||
|
if (menuList.value.selected.length) {
|
||||||
|
const currentIdx = menuList.value.items.indexOf(menuList.value.selected[0]);
|
||||||
|
const nextIdx = currentIdx + 1;
|
||||||
|
if (menuList.value.items[nextIdx]) nextSelection = nextIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
menuList.value.selected = [menuList.value.items[nextSelection]];
|
||||||
|
|
||||||
|
// hello @
|
||||||
|
|
||||||
|
const beforeCursor = text.value.splice(0, cursor);
|
||||||
|
const afterCursor = text.value.slice(cursor);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<v-menu
|
||||||
|
v-model="menu.open"
|
||||||
|
location="top"
|
||||||
|
activator="parent"
|
||||||
|
:open-on-click="false"
|
||||||
|
:open-on-focus="false"
|
||||||
|
>
|
||||||
|
<v-list v-bind="menuList" @click:select="clickItem" />
|
||||||
|
</v-menu>
|
||||||
|
<v-text-field
|
||||||
|
v-model="text"
|
||||||
|
autofocus
|
||||||
|
hide-details
|
||||||
|
@input="trigger"
|
||||||
|
@keydown.enter.prevent="send"
|
||||||
|
@keydown.tab.prevent="tabComplete"
|
||||||
|
variant="outlined"
|
||||||
|
></v-text-field>
|
||||||
|
</template>
|
||||||
44
irchad-web/src/components/UserCard.vue
Normal file
44
irchad-web/src/components/UserCard.vue
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useIRCStore } from "@/stores/irc";
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
|
|
||||||
|
const { selfAvatar } = storeToRefs(useIRCStore());
|
||||||
|
const { client, clientInfo, setAvatar, setNick } = useIRCStore();
|
||||||
|
const avatarDialog = ref(false);
|
||||||
|
const newNick = ref();
|
||||||
|
|
||||||
|
function changeAvatar() {
|
||||||
|
avatarDialog.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitAvatar() {
|
||||||
|
setAvatar(selfAvatar.value);
|
||||||
|
avatarDialog.value = false;
|
||||||
|
if (newNick.value && clientInfo.nick !== newNick.value) {
|
||||||
|
console.log("nick changed");
|
||||||
|
client.changeNick(newNick.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<v-dialog v-model="avatarDialog">
|
||||||
|
<v-card title="Edit Profile">
|
||||||
|
<v-card-text>
|
||||||
|
<v-text-field v-model="selfAvatar" label="Avatar URL" />
|
||||||
|
<v-text-field v-model="newNick" label="Nick" />
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-btn text="OK" @click="submitAvatar" />
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
<v-card>
|
||||||
|
<v-card-title>
|
||||||
|
<v-avatar @click="changeAvatar" v-if="selfAvatar" :image="selfAvatar" />
|
||||||
|
{{ clientInfo.nick }}
|
||||||
|
</v-card-title>
|
||||||
|
<v-card-text> </v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
@@ -13,6 +13,17 @@ export const useIRCStore = defineStore("irc", () => {
|
|||||||
gecos: "IrChad",
|
gecos: "IrChad",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const selfAvatar = ref("https://placekittens.com/128/128");
|
||||||
|
|
||||||
|
function setAvatar(v) {
|
||||||
|
selfAvatar.value = v;
|
||||||
|
client.raw(`METADATA * SET avatar ${selfAvatar.value}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNick(v) {
|
||||||
|
client.changeNick(v);
|
||||||
|
}
|
||||||
|
|
||||||
const buffers = ref({});
|
const buffers = ref({});
|
||||||
const activeBufferName = ref();
|
const activeBufferName = ref();
|
||||||
const metadata = ref({});
|
const metadata = ref({});
|
||||||
@@ -87,7 +98,18 @@ export const useIRCStore = defineStore("irc", () => {
|
|||||||
client.on("registered", function () {
|
client.on("registered", function () {
|
||||||
client.list();
|
client.list();
|
||||||
client.raw("METADATA * SUB avatar");
|
client.raw("METADATA * SUB avatar");
|
||||||
client.raw("METADATA * SET avatar https://placekittens.com/128/128");
|
client.raw(`METADATA * SET avatar ${selfAvatar.value}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("nick", function ({ nick, new_nick }) {
|
||||||
|
if (nick === clientInfo.value.nick) {
|
||||||
|
clientInfo.value.nick = new_nick;
|
||||||
|
}
|
||||||
|
for (let buff of Object.values(buffers.value)) {
|
||||||
|
const idx = buff.users.findIndex((u) => u.nick === nick);
|
||||||
|
if (idx === -1) continue;
|
||||||
|
buff.users[idx].nick = new_nick;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
client.on("unknown command", function (ircCommand) {
|
client.on("unknown command", function (ircCommand) {
|
||||||
@@ -192,5 +214,8 @@ export const useIRCStore = defineStore("irc", () => {
|
|||||||
setActiveBuffer,
|
setActiveBuffer,
|
||||||
getMetadata,
|
getMetadata,
|
||||||
metadata,
|
metadata,
|
||||||
|
selfAvatar,
|
||||||
|
setAvatar,
|
||||||
|
setNick,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user