diff --git a/src/webpage/guild.ts b/src/webpage/guild.ts index 32b90a9..7ed7064 100644 --- a/src/webpage/guild.ts +++ b/src/webpage/guild.ts @@ -306,6 +306,9 @@ class Guild extends SnowFlake { ); //TODO mute guild button } + getChannel(id: string) { + return this.channels.find((_) => _.id === id); + } async findAdmin() { const menu = new Dialog(I18n.guild.admins()); menu.options.addText("Loading"); diff --git a/src/webpage/markdown.ts b/src/webpage/markdown.ts index 64c3c12..2cdf075 100644 --- a/src/webpage/markdown.ts +++ b/src/webpage/markdown.ts @@ -518,9 +518,15 @@ class MarkDown { appendcurrent(); const a = document.createElement("a"); //a.href=build; - MarkDown.safeLink(a, build); + a.textContent = build; + const replace = MarkDown.safeLink(a, build, this.localuser); + if (replace) { + a.textContent = replace; + a.classList.add("mentionMD"); + } a.target = "_blank"; + i = j - 1; span.appendChild(a); continue; @@ -652,8 +658,13 @@ class MarkDown { } else { appendcurrent(); const a = document.createElement("a"); - MarkDown.safeLink(a, build); - a.textContent = build; + const text = MarkDown.safeLink(a, build, this.localuser); + if (text) { + a.textContent = text; + a.classList.add("mentionMD"); + } else { + a.textContent = build; + } a.target = "_blank"; span.appendChild(a); } @@ -974,9 +985,33 @@ class MarkDown { return build; } static readonly trustedDomains = new Set([location.host]); - static safeLink(elm: HTMLElement, url: string) { + static safeLink( + elm: HTMLElement, + url: string, + localuser: Localuser | null = null, + ): string | void { + if (elm instanceof HTMLAnchorElement) { + elm.rel = "noopener noreferrer"; + } if (URL.canParse(url)) { const Url = new URL(url); + if (localuser) { + const [_, _2, ...path] = Url.pathname.split("/"); + + const guild = localuser.guildids.get(path[0]); + const channel = guild?.getChannel(path[1]); + if (channel) { + const message = isNaN(+path[2]) ? undefined : path[2]; + elm.onmouseup = (_) => { + channel.getHTML(true, true, message); + }; + if (message) { + return I18n.messageLink(channel.name); + } else { + return I18n.channelLink(channel.name); + } + } + } if (elm instanceof HTMLAnchorElement && this.trustedDomains.has(Url.host)) { elm.href = url; elm.target = "_blank"; diff --git a/src/webpage/reporting/types.ts b/src/webpage/reporting/types.ts new file mode 100644 index 0000000..a4b92c8 --- /dev/null +++ b/src/webpage/reporting/types.ts @@ -0,0 +1,287 @@ +type reportTypes = + | "message" + | "user" + | "guild_discovery" + | "guild" + | "guild_scheduled_event" + | "stage_channel" + | "first_dm" + | "user" + | "application" + | "widget" + | "guild_directory_entry"; +type buttonTypes = + | "submit" // This button shows a warning about making false reports along with the submit and back button + | "done" //This button just shows a Done button without a back button + | "cancel" //This button may provide a back button, but does not provide a button to move forward + | "next"; //This button takes you to the next node along with a back button +interface reportElementBase { + name: string; + type: string; + /** + * Data used for more complex elements to allow for things like checkboxes or additional information + */ + data: unknown; + /** + * Set only if the elements information should be submitted, only ever true with the checkbox type + */ + should_submit_data: boolean; + /** + * This tag is used for if the element should not be rendered if its not been translated into the users language + */ + skip_if_unlocalized: boolean; + is_localized: boolean | null; +} +type singleElementTypes = + | "message_preview" //Shows the user a preview of the message + | "app_preview" // Shows you what you're reporting the user for + | "guild_preview" // Shows the user a preview of reported guild + | "guild_directory_entry_preview" // Shows the user a preview of the dirrectory the user is reporting + | "guild_scheduled_event_preview" //Shows the user a preview of the event the user is reporting + | "channel_preview" //shows the user a preview of the reported channel + | "app_preview" //show the user of the reported + | "widget_preview" //show the user a preview of the reported widget + | "breadcrumbs" // I think this is a defunct element that visually does not do anything, but was at one point a signal to include the breadcrumbs of how the user got there. + | "fail" // used in the failed menus + | "share_with_parents" // Offers to the user to shares report with parents (if applicable) + | "block_users" // Offers to the user to block them + | "mute_users" // Offers to the user to mute the user (if applicable) + | "success" // Used to notate a report that succeeded + | "ignore_users" // Offers to the user to ignore them + | "settings_upsells" // Additional settings actions that can be taken + | "delete_message" // Offers to delete the message (if applicable) + | "user_preview" // Shows a preview of the user + | "skip" // Exits the menu and does not allow for going back + | "leave_guild" // An option to let you leave the guild + | "deauthorize_app"; //An option to deauthorize the app + +interface singleElementReport extends reportElementBase { + name: X; + type: X; + data: null; + /** + * If the element has no text to be localized, it is localized + */ + is_localized: true; + should_submit_data: false; +} + +interface externalLinkReport extends reportElementBase { + name: "external_link"; + type: "external_link"; + data: { + is_localized: null | boolean; + link_text: string; + url: string; + link_description: null | string; + /** + * If this is defined, it means to just not render it, not sure why this exists + */ + is_header_hidden?: true; + }; + should_submit_data: false; +} +interface selectReport extends reportElementBase { + name: string; + type: "checkbox"; + /** + * [actual_name, human_readable_name, ?description] + */ + data: ([string, string] | [string, string, string])[]; + should_submit_data: true; +} +interface dropdownReport extends reportElementBase { + name: string; + type: "dropdown"; + data: { + title: string; + options: { + /** + * Used for what to send to the APU + */ + value: string; + label: string; + }[]; + }; + should_submit_data: true; +} + +interface freeTextReport extends reportElementBase { + name: string; + type: "free_text"; + data: { + title: string; + options: { + title?: string; + subtitle?: string; + placeholder?: string; + /** + * Number of visible text rows + */ + rows: number; + character_limit: number; + /** + * It must match this regex pattern + */ + pattern: string; + }[]; + }; + should_submit_data: true; +} +interface selfHarmHelp extends reportElementBase { + name: string; + type: "text_line_resource"; + data: { + is_localized: boolean; + title: string; + body: string; + sms: string; + /** + * The information to send in the text message + */ + sms_body: string; + }; + should_submit_data: false; +} +interface reportText extends reportElementBase { + name: "text"; + type: "text"; + data: { + header: string; + body: string; + is_localized: boolean; + }; +} +type reportElements = + | singleElementReport + | externalLinkReport + | selectReport + | selfHarmHelp; +interface report { + name: reportTypes; + variant: string; + version: string; + + /** + * this is an relative URL in respect to the API base URL in JS to implement correctly do `new URL(report.postback_url, api_url)` + */ + postback_url: string; + /** + * this is the node the reporting process should start on + */ + root_node_id: number; + success_node_id: number; + fail_node_id: number; + /** + * defaults to english + */ + language?: string; + nodes: { + [key: string]: { + id: number; + /** + * this key is likely only used for translation reasons, and is not used during the reporting process + */ + key: string; + header: string; + subheader: string | null; + /** + * information about a certain section displayed in a special box + */ + info: null | string; + /** + * the button at the bottom of the report box + */ + button: { + type: buttonTypes; + target: null | number; + } | null; + /** + * these are the elements that aren't the buttons to just continue along and may provide information to the user, or present the user with more options + */ + elements: reportElements[]; + /** + * this says what type of thing the user is reporting, though isn't actually used in the process of reporting + */ + report_type: null | string; + /** + * These are the options with the strings leading to the numbers which represent the nodes to go to + */ + children: [string, number][]; + /** + * This is true if the checkbox element is required to have at least one element selected to continue + */ + is_multi_select_required: boolean; + /** + * Used if this screen automatically submits the report without additional input + */ + is_auto_submit: boolean; + }; + }; +} +interface reportPut { + version: string; + variant: string; + language: string; + /** + * This is the node path that the user took to get here, which includes the submit/auto_submit screens + */ + breadcrumbs: number[]; + /** + * This is for various elements, this will return all of the things selected with the key being the name + */ + elements: { + [key: string]: string[]; + }; + + name: reportTypes; +} + +interface reportMessagePut extends reportPut { + channel_id: string; + message_id: string; + name: "message"; +} +interface reportGuildDiscovery extends reportPut { + guild_id: string; + name: "guild_discovery"; +} +interface reportFirstDMPut extends reportPut { + channel_id: string; + message_id: string; + name: "first_dm"; +} +interface reportGuildDirPut extends reportPut { + channel_id: string; + guild_id: string; + name: "guild_directory_entry"; +} +interface reportGuildPut extends reportPut { + guild_id: string; + name: "guild"; +} +interface reportStagePut extends reportPut { + guild_id: string; + user_id: string; + stage_instance_id: string; + name: "stage_channel"; +} +interface reportGuildEventPut extends reportPut { + guild_id: string; + guild_scheduled_event_id: string; + name: "guild_scheduled_event"; +} +interface reportApplicationPut extends reportPut { + application_id: string; + name: "application"; +} +interface reportUserPut extends reportPut { + guild_id: string; + user_id: string; + name: "user"; +} +interface reportWidgetPut extends reportPut { + user_id: string; + widget_id: string; + name: "widget"; +} diff --git a/translations/en.json b/translations/en.json index 69498fa..e75b40c 100644 --- a/translations/en.json +++ b/translations/en.json @@ -190,6 +190,8 @@ "joined":"Account made: $1", "joinedMember":"Joined $1: $2" }, + "channelLink":"# $1", + "messageLink":"Message in # $1", "searchGifs": "Search Tenor", "muteDuration":"Unmute in:", "channel": {