Merge branch 'main' into permissions-management
This commit is contained in:
commit
9fe067ec5a
55 changed files with 1106 additions and 387 deletions
|
@ -1,21 +0,0 @@
|
|||
import { render } from "vue";
|
||||
import ContextMenu from "~/components/UserInterface/ContextMenu.vue";
|
||||
import type { ContextMenuItem } from "~/types/interfaces";
|
||||
|
||||
export default (e: PointerEvent | MouseEvent, menuItems: ContextMenuItem[]) => {
|
||||
console.log("Rendering new context menu");
|
||||
const menuContainer = document.createElement("div");
|
||||
console.log("hello");
|
||||
menuContainer.id = "context-menu";
|
||||
document.body.appendChild(menuContainer);
|
||||
console.log("pointer x:", e.clientX);
|
||||
console.log("pointer y:", e.clientY);
|
||||
console.log("menu items:", menuItems);
|
||||
const contextMenu = h(ContextMenu, {
|
||||
menuItems,
|
||||
pointerX: e.clientX,
|
||||
pointerY: e.clientY
|
||||
});
|
||||
render(contextMenu, menuContainer);
|
||||
console.log("Rendered");
|
||||
}
|
|
@ -1,9 +1,12 @@
|
|||
import type { MessageProps } from "~/types/props";
|
||||
|
||||
const { fetchMe } = useApi()
|
||||
|
||||
export default async (element: HTMLDivElement, props: MessageProps) => {
|
||||
console.log("message:", element);
|
||||
const me = await fetchWithApi("/me") as any;
|
||||
if (props.author?.uuid == me.uuid) {
|
||||
const me = await fetchMe();
|
||||
|
||||
if (me && props.author?.uuid == me.uuid) {
|
||||
const text = element.getElementsByClassName("message-text")[0] as HTMLDivElement;
|
||||
text.contentEditable = "true";
|
||||
text.focus();
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
export default (name: string, seed: string): string => {
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (canvas && ctx) {
|
||||
canvas.width = 256;
|
||||
canvas.height = 256;
|
||||
|
||||
// get the first char from every word in the guild name
|
||||
let previewName = "";
|
||||
if (name.length > 3) {
|
||||
let guildName: string[] = name.split(' ')
|
||||
for (let i = 0; i < 3; i ++) {
|
||||
if (guildName.length > i) {
|
||||
previewName += guildName[i].charAt(0)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
previewName = name
|
||||
}
|
||||
|
||||
// fill background using seeded colour
|
||||
ctx.fillStyle = generateIrcColor(seed, 50)
|
||||
ctx.fillRect(0, 0, 256, 256)
|
||||
|
||||
ctx.fillStyle = 'white'
|
||||
ctx.textAlign = 'center'
|
||||
ctx.textBaseline = 'middle'
|
||||
ctx.font = `bold 96px Arial, Helvetica, sans-serif`
|
||||
// 136 isn't actually centered, but it *looks* centered
|
||||
ctx.fillText(previewName, 128, 136)
|
||||
|
||||
return canvas.toDataURL("image/png");
|
||||
}
|
||||
|
||||
return "https://tenor.com/view/dame-da-ne-guy-kiryukazuma-kiryu-yakuza-yakuza-0-gif-14355451116903905918"
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import type { GuildMemberResponse, UserResponse } from "~/types/interfaces";
|
||||
|
||||
export default (user: UserResponse, member?: GuildMemberResponse): string => {
|
||||
if (member?.nickname) return member.nickname
|
||||
if (user.display_name) return user.display_name
|
||||
return user.username
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
//
|
||||
// Canvas Blocker &
|
||||
// Firefox privacy.resistFingerprinting Detector.
|
||||
// (c) 2018 // JOHN OZBAY // CRYPT.EE
|
||||
// MIT License
|
||||
//
|
||||
export default () => {
|
||||
// create a 1px image data
|
||||
var blocked = false;
|
||||
var canvas = document.createElement("canvas");
|
||||
var ctx = canvas.getContext("2d");
|
||||
|
||||
// some blockers just return an undefined ctx. So let's check that first.
|
||||
if (ctx) {
|
||||
var imageData = ctx.createImageData(1,1);
|
||||
var originalImageData = imageData.data;
|
||||
|
||||
// set pixels to RGB 128
|
||||
originalImageData[0]=128;
|
||||
originalImageData[1]=128;
|
||||
originalImageData[2]=128;
|
||||
originalImageData[3]=255;
|
||||
|
||||
// set this to canvas
|
||||
ctx.putImageData(imageData,1,1);
|
||||
|
||||
try {
|
||||
// now get the data back from canvas.
|
||||
var checkData = ctx.getImageData(1, 1, 1, 1).data;
|
||||
|
||||
// If this is firefox, and privacy.resistFingerprinting is enabled,
|
||||
// OR a browser extension blocking the canvas,
|
||||
// This will return RGB all white (255,255,255) instead of the (128,128,128) we put.
|
||||
|
||||
// so let's check the R and G to see if they're 255 or 128 (matching what we've initially set)
|
||||
if (originalImageData[0] !== checkData[0] && originalImageData[1] !== checkData[1]) {
|
||||
blocked = true;
|
||||
console.log("Canvas is blocked. Will display warning.");
|
||||
}
|
||||
} catch (error) {
|
||||
// some extensions will return getImageData null. this is to account for that.
|
||||
blocked = true;
|
||||
console.log("Canvas is blocked. Will display warning.");
|
||||
}
|
||||
} else {
|
||||
blocked = true;
|
||||
console.log("Canvas is blocked. Will display warning.");
|
||||
}
|
||||
return blocked;
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
let themeLinkElement: HTMLLinkElement | null;
|
||||
|
||||
export default function loadPreferredTheme() {
|
||||
const currentTheme = settingsLoad().selectedThemeId ?? "dark"
|
||||
|
||||
if (themeLinkElement) {
|
||||
themeLinkElement.href = getThemeUrl(currentTheme);
|
||||
} else {
|
||||
// create the theme link if one doesn't already exist
|
||||
useHead({
|
||||
link: [{
|
||||
id: "main-theme",
|
||||
rel: "stylesheet",
|
||||
href: getThemeUrl(currentTheme)
|
||||
}]
|
||||
})
|
||||
|
||||
themeLinkElement = document.getElementById('main-theme') as HTMLLinkElement;
|
||||
}
|
||||
}
|
||||
|
||||
function getThemeUrl(id: string): string {
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
const baseURL = runtimeConfig.app.baseURL;
|
||||
|
||||
// this should preferrably use version hash, but that's not implemented yet
|
||||
return `${baseURL}themes/${id}.css?v=${runtimeConfig.public.buildTimeString}`
|
||||
}
|
52
utils/loadPreferredThemes.ts
Normal file
52
utils/loadPreferredThemes.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
let styleLinkElement: HTMLLinkElement | null;
|
||||
let layoutLinkElement: HTMLLinkElement | null;
|
||||
|
||||
|
||||
export default () => {
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
const baseURL = runtimeConfig.app.baseURL;
|
||||
|
||||
let currentStyle = settingsLoad().selectedThemeStyle ?? undefined
|
||||
let currentLayout = settingsLoad().selectedThemeLayout ?? `${baseURL}themes/layout/gorb.css`
|
||||
|
||||
if (!currentStyle) {
|
||||
if (prefersLight()) {
|
||||
currentStyle = `${baseURL}themes/style/light.css`
|
||||
} else {
|
||||
currentStyle = `${baseURL}themes/style/dark.css`
|
||||
}
|
||||
}
|
||||
|
||||
if (styleLinkElement) {
|
||||
styleLinkElement.href = currentStyle;
|
||||
} else {
|
||||
createStyleHead("style-theme", currentStyle)
|
||||
styleLinkElement = document.getElementById('style-theme') as HTMLLinkElement;
|
||||
}
|
||||
|
||||
if (layoutLinkElement) {
|
||||
layoutLinkElement.href = currentLayout;
|
||||
} else {
|
||||
createStyleHead("style-layout", currentLayout)
|
||||
layoutLinkElement = document.getElementById('style-layout') as HTMLLinkElement;
|
||||
}
|
||||
}
|
||||
|
||||
// create a new theme link if one doesn't already exist
|
||||
function createStyleHead(id: string, themeUrl: string) {
|
||||
useHead({
|
||||
link: [{
|
||||
id: id,
|
||||
rel: "stylesheet",
|
||||
href: themeUrl
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
function prefersLight(): boolean {
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
|
@ -1,6 +1,12 @@
|
|||
export default () => {
|
||||
const contextMenu = document.getElementById("context-menu");
|
||||
if (contextMenu) {
|
||||
contextMenu.remove();
|
||||
import type { ContextMenuInterface } from "~/types/interfaces";
|
||||
|
||||
export default (contextMenu: Ref<ContextMenuInterface>) => {
|
||||
console.log("resetting and hiding context menu");
|
||||
contextMenu.value = {
|
||||
show: false,
|
||||
pointerX: 0,
|
||||
pointerY: 0,
|
||||
items: []
|
||||
}
|
||||
console.log("hidden context menu");
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ import { render } from "vue";
|
|||
import MessageReply from "~/components/UserInterface/MessageReply.vue";
|
||||
import type { MessageProps } from "~/types/props";
|
||||
|
||||
const { getDisplayName } = useProfile()
|
||||
|
||||
export default (element: HTMLDivElement, props: MessageProps) => {
|
||||
console.log("element:", element);
|
||||
const messageBox = document.getElementById("message-box") as HTMLDivElement;
|
||||
|
@ -10,5 +12,9 @@ export default (element: HTMLDivElement, props: MessageProps) => {
|
|||
const messageReply = h(MessageReply, { author: getDisplayName(props.author), text: props.text || "", id: props.message.uuid, replyId: props.replyMessage?.uuid || element.dataset.messageId!, maxWidth: "full" });
|
||||
messageBox.prepend(div);
|
||||
render(messageReply, div);
|
||||
const message = document.querySelector(`.message[data-message-id='${props.message.uuid}']`);
|
||||
if (message) {
|
||||
message.classList.add("replying-to");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
12
utils/showContextMenu.ts
Normal file
12
utils/showContextMenu.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { render } from "vue";
|
||||
import ContextMenu from "~/components/UserInterface/ContextMenu.vue";
|
||||
import type { ContextMenuInterface, ContextMenuItem } from "~/types/interfaces";
|
||||
|
||||
export default (e: MouseEvent | PointerEvent, contextMenu: ContextMenuInterface, menuItems: ContextMenuItem[]) => {
|
||||
console.log("Showing context menu");
|
||||
contextMenu.show = true;
|
||||
contextMenu.pointerX = e.clientX;
|
||||
contextMenu.pointerY = e.clientY;
|
||||
contextMenu.items = menuItems;
|
||||
console.log("Showed");
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
import type { GuildMemberResponse } from "~/types/interfaces";
|
||||
const { getDisplayName } = useProfile()
|
||||
|
||||
export default (members: GuildMemberResponse[]): GuildMemberResponse[] => {
|
||||
return members.sort((a, b) => {
|
||||
return getDisplayName(a.user, a).localeCompare(getDisplayName(b.user, b))
|
||||
return getDisplayName(a).localeCompare(getDisplayName(b))
|
||||
})
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import type { UserResponse } from "~/types/interfaces";
|
||||
const { getDisplayName } = useProfile()
|
||||
|
||||
export default (users: UserResponse[]): UserResponse[] => {
|
||||
return users.sort((a, b) => {
|
||||
|
|
6
utils/uuidToDate.ts
Normal file
6
utils/uuidToDate.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export default (uuid: string): Date => {
|
||||
const timeHex = uuid.substring(0, 8) + uuid.substring(9, 13);
|
||||
const timestamp = parseInt(timeHex, 16);
|
||||
|
||||
return new Date(timestamp);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue