message pins and better welcome messages
This commit is contained in:
parent
71ee9d3f36
commit
1ade94470a
11 changed files with 261 additions and 21 deletions
|
@ -330,6 +330,7 @@ class Channel extends SnowFlake {
|
|||
this.readbottom.bind(this),
|
||||
);
|
||||
}
|
||||
last_pin_timestamp?: string;
|
||||
constructor(json: channeljson | -1, owner: Guild, id: string = json === -1 ? "" : json.id) {
|
||||
super(id);
|
||||
if (json === -1) {
|
||||
|
@ -401,6 +402,61 @@ class Channel extends SnowFlake {
|
|||
get info() {
|
||||
return this.owner.info;
|
||||
}
|
||||
pinnedMessages?: Message[];
|
||||
async pinnedClick(rect: DOMRect) {
|
||||
const div = document.createElement("div");
|
||||
div.classList.add("flexttb", "pinnedMessages");
|
||||
div.style.top = rect.bottom + 20 + "px";
|
||||
div.style.right = window.innerWidth - rect.right + "px";
|
||||
document.body.append(div);
|
||||
Contextmenu.keepOnScreen(div);
|
||||
if (Contextmenu.currentmenu !== "") {
|
||||
Contextmenu.currentmenu.remove();
|
||||
}
|
||||
this.last_pin_timestamp = this.lastpin;
|
||||
const l = (e: MouseEvent) => {
|
||||
if (e.target instanceof HTMLElement && div.contains(e.target)) {
|
||||
return;
|
||||
}
|
||||
div.remove();
|
||||
document.removeEventListener("click", l);
|
||||
};
|
||||
document.addEventListener("mouseup", l);
|
||||
if (!this.pinnedMessages) {
|
||||
const pinnedM = (await (
|
||||
await fetch(`${this.info.api}/channels/${this.id}/pins`, {headers: this.headers})
|
||||
).json()) as messagejson[];
|
||||
this.pinnedMessages = pinnedM.map((_) => {
|
||||
if (this.messages.has(_.id)) {
|
||||
return this.messages.get(_.id) as Message;
|
||||
} else {
|
||||
return new Message(_, this);
|
||||
}
|
||||
});
|
||||
}
|
||||
const pinnedM = document.getElementById("pinnedMDiv");
|
||||
if (pinnedM) {
|
||||
pinnedM.classList.remove("unreadPin");
|
||||
}
|
||||
if (this.pinnedMessages.length === 0) {
|
||||
const b = document.createElement("b");
|
||||
b.textContent = I18n.noPins();
|
||||
div.append(b);
|
||||
return;
|
||||
}
|
||||
div.append(
|
||||
...this.pinnedMessages.map((_) => {
|
||||
const html = _.buildhtml(undefined, true);
|
||||
html.style.cursor = "pointer";
|
||||
html.onclick = async () => {
|
||||
div.remove();
|
||||
await this.focus(_.id);
|
||||
};
|
||||
Message.contextmenu.bindContextmenu(html, _);
|
||||
return html;
|
||||
}),
|
||||
);
|
||||
}
|
||||
readStateInfo(json: readyjson["d"]["read_state"]["entries"][0]) {
|
||||
const next = this.messages.get(this.idToNext.get(this.lastreadmessageid as string) as string);
|
||||
this.lastreadmessageid = json.last_message_id;
|
||||
|
@ -927,7 +983,19 @@ class Channel extends SnowFlake {
|
|||
html.classList.add("messagecontainer");
|
||||
messages.append(html);
|
||||
}
|
||||
unreadPins() {
|
||||
if (!this.last_pin_timestamp && !this.lastpin) return false;
|
||||
return this.last_pin_timestamp !== this.lastpin;
|
||||
}
|
||||
async getHTML(addstate = true) {
|
||||
const pinnedM = document.getElementById("pinnedMDiv");
|
||||
if (pinnedM) {
|
||||
if (this.unreadPins()) {
|
||||
pinnedM.classList.add("unreadPin");
|
||||
} else {
|
||||
pinnedM.classList.remove("unreadPin");
|
||||
}
|
||||
}
|
||||
const ghostMessages = document.getElementById("ghostMessages") as HTMLElement;
|
||||
ghostMessages.innerHTML = "";
|
||||
for (const thing of this.fakeMessages) {
|
||||
|
|
|
@ -429,6 +429,14 @@ class Group extends Channel {
|
|||
return div;
|
||||
}
|
||||
async getHTML(addstate = true) {
|
||||
const pinnedM = document.getElementById("pinnedMDiv");
|
||||
if (pinnedM) {
|
||||
if (this.unreadPins()) {
|
||||
pinnedM.classList.add("unreadPin");
|
||||
} else {
|
||||
pinnedM.classList.remove("unreadPin");
|
||||
}
|
||||
}
|
||||
const ghostMessages = document.getElementById("ghostMessages") as HTMLElement;
|
||||
ghostMessages.innerHTML = "";
|
||||
for (const thing of this.fakeMessages) {
|
||||
|
|
1
src/webpage/icons/pin.svg
Normal file
1
src/webpage/icons/pin.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg"><path style="fill:#fd0000;fill-opacity:1;stroke:#fe0000;stroke-width:38.1973;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" d="m287 26 196 196-44-24-192 193-20 87L31 283l88-21L311 70Z"/><path style="fill:#fd0000;stroke:#fe0000;stroke-width:42.3359;stroke-linecap:round;stroke-linejoin:round" d="m83 400 19 20-76 57z"/></svg>
|
After Width: | Height: | Size: 403 B |
|
@ -74,6 +74,10 @@
|
|||
<span id="channelname">Channel name</span>
|
||||
<span id="channelTopic" class="ellipsis" hidden>Channel topic</span>
|
||||
</span>
|
||||
<div class="spaceElm"></div>
|
||||
<div id="pinnedMDiv">
|
||||
<span class="svgicon svg-pin" id="pinnedM"></span>
|
||||
</div>
|
||||
<div contenteditable="true" class="searchBox" id="searchBox"></div>
|
||||
<label for="memberlisttoggle" id="memberlisttoggleicon">
|
||||
<span class="svgicon svg-friends"></span>
|
||||
|
|
|
@ -251,6 +251,12 @@ import {I18n} from "./i18n.js";
|
|||
}
|
||||
}
|
||||
});
|
||||
const pinnedM = document.getElementById("pinnedM") as HTMLElement;
|
||||
pinnedM.onclick = (e) => {
|
||||
thisUser.pinnedClick(pinnedM.getBoundingClientRect());
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
};
|
||||
(document.getElementById("upload") as HTMLElement).onclick = () => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
|
|
|
@ -617,6 +617,15 @@ type wsjson =
|
|||
t: "GUILD_UPDATE";
|
||||
d: extendedProperties;
|
||||
s: number;
|
||||
}
|
||||
| {
|
||||
op: 0;
|
||||
t: "CHANNEL_PINS_UPDATE";
|
||||
d: {
|
||||
channel_id: string;
|
||||
guild_id: string;
|
||||
};
|
||||
s: number;
|
||||
};
|
||||
|
||||
type memberChunk = {
|
||||
|
|
|
@ -606,6 +606,17 @@ class Localuser {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case "CHANNEL_PINS_UPDATE":
|
||||
temp.d.guild_id ??= "@me";
|
||||
const channel = this.channelids.get(temp.d.channel_id);
|
||||
if (!channel) break;
|
||||
delete channel.pinnedMessages;
|
||||
channel.lastpin = new Date() + "";
|
||||
const pinnedM = document.getElementById("pinnedMDiv");
|
||||
if (pinnedM) {
|
||||
pinnedM.classList.add("unreadPin");
|
||||
}
|
||||
break;
|
||||
case "CHANNEL_UPDATE":
|
||||
if (this.initialized) {
|
||||
this.updateChannel(temp.d);
|
||||
|
@ -2252,6 +2263,10 @@ class Localuser {
|
|||
this.search(document.getElementById("searchOptions") as HTMLDivElement, typeMd, str, pre);
|
||||
};
|
||||
}
|
||||
async pinnedClick(rect: DOMRect) {
|
||||
if (!this.channelfocus) return;
|
||||
await this.channelfocus.pinnedClick(rect);
|
||||
}
|
||||
async makeGifBox(rect: DOMRect) {
|
||||
interface fullgif {
|
||||
id: string;
|
||||
|
|
|
@ -16,7 +16,7 @@ import {Hover} from "./hover.js";
|
|||
import {Dialog} from "./settings.js";
|
||||
|
||||
class Message extends SnowFlake {
|
||||
static contextmenu = new Contextmenu<Message, undefined>("message menu");
|
||||
static contextmenu = new Contextmenu<Message, void>("message menu");
|
||||
owner: Channel;
|
||||
headers: Localuser["headers"];
|
||||
embeds!: Embed[];
|
||||
|
@ -50,6 +50,7 @@ class Message extends SnowFlake {
|
|||
div: HTMLDivElement | undefined;
|
||||
member: Member | undefined;
|
||||
reactions!: messagejson["reactions"];
|
||||
pinned!: boolean;
|
||||
static setup() {
|
||||
this.del = new Promise((_) => {
|
||||
this.resolve = _;
|
||||
|
@ -96,6 +97,9 @@ class Message extends SnowFlake {
|
|||
icon: {
|
||||
css: "svg-emoji",
|
||||
},
|
||||
visable: function () {
|
||||
return this.channel.hasPermission("ADD_REACTIONS");
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -111,6 +115,48 @@ class Message extends SnowFlake {
|
|||
},
|
||||
},
|
||||
);
|
||||
Message.contextmenu.addButton(
|
||||
() => I18n.pinMessage(),
|
||||
async function (this: Message) {
|
||||
const f = await fetch(`${this.info.api}/channels/${this.channel.id}/pins/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: this.headers,
|
||||
});
|
||||
if (!f.ok) alert(I18n.unableToPin());
|
||||
},
|
||||
{
|
||||
icon: {
|
||||
css: "svg-pin",
|
||||
},
|
||||
visable: function () {
|
||||
if (this.pinned) return false;
|
||||
if (this.channel.guild.id === "@me") return true;
|
||||
return this.channel.hasPermission("MANAGE_MESSAGES");
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
Message.contextmenu.addButton(
|
||||
() => I18n.unpinMessage(),
|
||||
async function (this: Message) {
|
||||
const f = await fetch(`${this.info.api}/channels/${this.channel.id}/pins/${this.id}`, {
|
||||
method: "DELETE",
|
||||
headers: this.headers,
|
||||
});
|
||||
if (!f.ok) alert(I18n.unableToPin());
|
||||
},
|
||||
{
|
||||
icon: {
|
||||
css: "svg-pin",
|
||||
},
|
||||
visable: function () {
|
||||
if (!this.pinned) return false;
|
||||
if (this.channel.guild.id === "@me") return true;
|
||||
return this.channel.hasPermission("MANAGE_MESSAGES");
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
Message.contextmenu.addButton(
|
||||
() => I18n.getTranslation("copymessageid"),
|
||||
function (this: Message) {
|
||||
|
@ -201,17 +247,20 @@ class Message extends SnowFlake {
|
|||
this.embeds[thing] = new Embed(messagejson.embeds[thing], this);
|
||||
}
|
||||
continue;
|
||||
} else if (thing === "author") {
|
||||
continue;
|
||||
}
|
||||
(this as any)[thing] = (messagejson as any)[thing];
|
||||
}
|
||||
if (messagejson.reactions?.length) {
|
||||
console.log(messagejson.reactions, ":3");
|
||||
}
|
||||
console.log(messagejson.webhook);
|
||||
if (messagejson.webhook) {
|
||||
messagejson.author.webhook = messagejson.webhook;
|
||||
}
|
||||
this.author = new User(messagejson.author, this.localuser, false);
|
||||
if (messagejson.author.id) {
|
||||
this.author = new User(messagejson.author, this.localuser, false);
|
||||
}
|
||||
for (const thing in messagejson.mentions) {
|
||||
this.mentions[thing] = new User(messagejson.mentions[thing], this.localuser);
|
||||
}
|
||||
|
@ -504,7 +553,7 @@ class Message extends SnowFlake {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (this.message_reference) {
|
||||
if (this.message_reference && this.type !== 6) {
|
||||
const replyline = document.createElement("div");
|
||||
|
||||
const minipfp = document.createElement("img");
|
||||
|
@ -688,11 +737,15 @@ class Message extends SnowFlake {
|
|||
}
|
||||
//
|
||||
} else if (this.type === 7) {
|
||||
const messages = I18n.welcomeMessages("|||").split("\n");
|
||||
const message = messages[Number(BigInt(this.id) % BigInt(messages.length))];
|
||||
const [first, second] = message.split("|||");
|
||||
const text = document.createElement("div");
|
||||
build.appendChild(text);
|
||||
const messaged = document.createElement("span");
|
||||
messaged.textContent = "welcome: ";
|
||||
text.appendChild(messaged);
|
||||
|
||||
const firstspan = document.createElement("span");
|
||||
firstspan.textContent = first;
|
||||
text.appendChild(firstspan);
|
||||
|
||||
const username = document.createElement("span");
|
||||
username.textContent = this.author.username;
|
||||
|
@ -701,6 +754,37 @@ class Message extends SnowFlake {
|
|||
text.appendChild(username);
|
||||
username.classList.add("username");
|
||||
|
||||
const secondspan = document.createElement("span");
|
||||
secondspan.textContent = second;
|
||||
text.appendChild(secondspan);
|
||||
|
||||
const time = document.createElement("span");
|
||||
time.textContent = " " + formatTime(new Date(this.timestamp));
|
||||
time.classList.add("timestamp");
|
||||
text.append(time);
|
||||
div.classList.add("topMessage");
|
||||
} else if (this.type === 6) {
|
||||
const text = document.createElement("div");
|
||||
build.appendChild(text);
|
||||
|
||||
const m = I18n.message.pin("||").split("||");
|
||||
if (m.length === 2) text.append(m.shift() as string);
|
||||
|
||||
const username = document.createElement("span");
|
||||
username.textContent = this.author.username;
|
||||
//this.author.profileclick(username);
|
||||
this.author.bind(username, this.guild);
|
||||
text.appendChild(username);
|
||||
username.classList.add("username");
|
||||
|
||||
const afterText = document.createElement("span");
|
||||
afterText.textContent = m[0];
|
||||
afterText.onclick = (_) => {
|
||||
this.channel.infinite.focus(this.message_reference.message_id);
|
||||
};
|
||||
afterText.classList.add("pinText");
|
||||
text.append(afterText);
|
||||
|
||||
const time = document.createElement("span");
|
||||
time.textContent = " " + formatTime(new Date(this.timestamp));
|
||||
time.classList.add("timestamp");
|
||||
|
|
|
@ -57,6 +57,24 @@ body {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.pinnedMessages {
|
||||
position: absolute;
|
||||
background: var(--secondary-bg);
|
||||
width: 3.5in;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 1px 1px 10px black;
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
min-height: 1in;
|
||||
b {
|
||||
width: 100%;
|
||||
height: 1in;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
.searchOptions {
|
||||
padding: 0.05in 0.15in;
|
||||
border-radius: 0.1in;
|
||||
|
@ -337,6 +355,10 @@ textarea {
|
|||
.svg-spoiler {
|
||||
mask: url(/icons/spoiler.svg);
|
||||
}
|
||||
.svg-pin {
|
||||
mask: url(/icons/pin.svg);
|
||||
mask-size: cover;
|
||||
}
|
||||
.svg-unspoiler {
|
||||
mask: url(/icons/unspoiler.svg);
|
||||
}
|
||||
|
@ -436,6 +458,24 @@ textarea {
|
|||
aspect-ratio: 1/1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
#pinnedM {
|
||||
width: 0.25in;
|
||||
height: 0.25in;
|
||||
cursor: pointer;
|
||||
}
|
||||
.unreadPin {
|
||||
position: relative;
|
||||
}
|
||||
.unreadPin::after {
|
||||
width: 0.1in;
|
||||
height: 0.1in;
|
||||
content: "";
|
||||
background: var(--red);
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
bottom: -5px;
|
||||
border-radius: 1in;
|
||||
}
|
||||
#emojiTB {
|
||||
width: 0.2in;
|
||||
height: 0.2in;
|
||||
|
@ -1197,10 +1237,13 @@ span.instanceStatus {
|
|||
width: 3in;
|
||||
margin: 0 0.1in;
|
||||
overflow: hidden;
|
||||
margin-left: auto;
|
||||
|
||||
flex-shrink: 0;
|
||||
transition: width 0.2s;
|
||||
}
|
||||
.spaceElm {
|
||||
margin: auto;
|
||||
}
|
||||
.outerTypeBox > span::before {
|
||||
content: "\feff";
|
||||
}
|
||||
|
@ -1373,6 +1416,12 @@ span.instanceStatus {
|
|||
word-break: break-all;
|
||||
cursor: pointer;
|
||||
}
|
||||
.pinText {
|
||||
cursor: pointer;
|
||||
}
|
||||
.pinText:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.username:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
@ -2709,13 +2758,7 @@ fieldset input[type="radio"] {
|
|||
border-radius: 4px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
"comment": "Don't know how often I'll update this top part lol"
|
||||
},
|
||||
"readableName": "English",
|
||||
|
||||
"pinMessage": "Pin Message",
|
||||
"unableToPin": "Unable to pin message",
|
||||
"reply": "Reply",
|
||||
"copyrawtext": "Copy raw text",
|
||||
"copymessageid": "Copy message id",
|
||||
|
@ -289,6 +290,7 @@
|
|||
"INVITES_DISABLED": "Invite only",
|
||||
"DISCOVERABLE": "Discovery"
|
||||
},
|
||||
"welcomeMessages": "Welcome $1! Nice to see ya!\nHello, nice to meat you $1!\nWelcome, hope you brought pizza $1!",
|
||||
"role": {
|
||||
"displaySettings": "Display settings",
|
||||
"name": "Role name:",
|
||||
|
@ -396,6 +398,8 @@
|
|||
"genericType": "Generic",
|
||||
"copy": "Copy"
|
||||
},
|
||||
"unpinMessage": "Unpin Message",
|
||||
"noPins": "There seems to be nothing here... yet",
|
||||
"message": {
|
||||
"reactionAdd": "Add reaction",
|
||||
"delete": "Delete message",
|
||||
|
@ -403,7 +407,8 @@
|
|||
"edited": "(edited)",
|
||||
"deleted": "Deleted message",
|
||||
"attached": "Sent an attachment",
|
||||
"retry": "Resend errored message"
|
||||
"retry": "Resend errored message",
|
||||
"pin": "$1 has pined a message"
|
||||
},
|
||||
"instanceStats": {
|
||||
"name": "Instance stats: $1",
|
||||
|
|
|
@ -3,11 +3,7 @@
|
|||
"last-updated": "2024/11/4",
|
||||
"locale": "en",
|
||||
"comment": "Don't know how often I'll update this top part lol",
|
||||
"authors": [
|
||||
"MathMan05",
|
||||
"McDutchie",
|
||||
"Vl1"
|
||||
]
|
||||
"authors": ["MathMan05", "McDutchie", "Vl1"]
|
||||
},
|
||||
"readableName": "{{doc-important|This should be the name of the language you are translating into, in that language. Please DO NOT translate this into your language’s word for “English”!}}",
|
||||
"typing": "$1 is the number of people typing and $2 is the names of the people typing separated by commas",
|
||||
|
@ -23,6 +19,7 @@
|
|||
"guild": {
|
||||
"disoveryTitle": "$1 is the number of guilds discovered"
|
||||
},
|
||||
"welcomeMessages": "These are welcome messages, they are meant to be silly, you do not need to directly translated them, there may even be a different count of messages, but only have the username once per message.",
|
||||
"localuser": {
|
||||
"updateSettings": "Title of a page that allows you to adjust the update settings",
|
||||
"description": "This object contains strings related to the logged in user, which is mostly the settings for the user",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue