Skip to content

Commit

Permalink
Merge pull request #91 from shayypy/load-message
Browse files Browse the repository at this point in the history
Load messages from Discord with webhook URL
  • Loading branch information
maddy committed Jun 19, 2023
2 parents d130e37 + fe55d20 commit de64ac8
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 3 deletions.
39 changes: 39 additions & 0 deletions modules/editor/EditorManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { Instance, SnapshotOrInstance, types } from "mobx-state-tree"
import { delay } from "../../common/state/delay"
import type { MessageData } from "../message/state/data/MessageData"
import { MessageModel } from "../message/state/models/MessageModel"
import { WebhookModel } from "../webhook/WebhookModel"

Expand All @@ -23,6 +24,44 @@ export const EditorManager = types
self.messages.push(MessageModel.create())
},

async getMessage(reference: string) {
for (const target of self.targets) {
const headers: Record<string, string> = {
"Accept": "application/json",
"Accept-Language": "en",
}

/* eslint-disable no-await-in-loop */

const [, url] = await target.getRoute(reference)
const response = await fetch(url, { method: "GET", headers })
const data = await response.json()

if (response.headers.get("X-RateLimit-Remaining") === "0") {
const retryAfter =
Number(response.headers.get("X-RateLimit-Reset-After") ?? 2) * 1000

console.log(
"Rate limited: delaying next request by",
retryAfter,
"milliseconds",
)

await delay(retryAfter)
}

/* eslint-enable no-await-in-loop */

console.log("Reference fetched", data)

if (response.ok) {
return data as MessageData
}
}

return null
},

async save() {
for (const target of self.targets) {
for (const message of self.messages) {
Expand Down
87 changes: 87 additions & 0 deletions modules/editor/message/LoadClearMessageConfirmationModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React, { useState } from "react"
import { PrimaryButton } from "../../../common/input/button/PrimaryButton"
import { SecondaryButton } from "../../../common/input/button/SecondaryButton"
import { ModalAction } from "../../../common/modal/layout/ModalAction"
import { ModalBody } from "../../../common/modal/layout/ModalBody"
import { ModalContainer } from "../../../common/modal/layout/ModalContainer"
import { ModalFooter } from "../../../common/modal/layout/ModalFooter"
import { ModalHeader } from "../../../common/modal/layout/ModalHeader"
import { ModalTitle } from "../../../common/modal/layout/ModalTitle"
import { ModalContext } from "../../../common/modal/ModalContext"
import { useRequiredContext } from "../../../common/state/useRequiredContext"
import { remove } from "../../../icons/remove"
import { Markdown } from "../../markdown/Markdown"
import { messageOf } from "../../message/helpers/messageOf"
import type { MessageLike } from "../../message/state/models/MessageModel"
import type { EditorManagerLike } from "../EditorManager"
import { InputError } from "../../../common/input/error/InputError"

Check warning on line 17 in modules/editor/message/LoadClearMessageConfirmationModal.tsx

View workflow job for this annotation

GitHub Actions / lint (ubuntu-latest, 16)

`../../../common/input/error/InputError` import should occur before import of `../../../common/modal/layout/ModalAction`

export type LoadClearMessageConfirmationModalProps = {
editorManager: EditorManagerLike
message: MessageLike
}

export function LoadClearMessageConfirmationModal(
props: LoadClearMessageConfirmationModalProps,
) {
const { editorManager, message } = props

const modal = useRequiredContext(ModalContext)

const [error, setError] = useState<string | undefined>(undefined)

Check warning on line 31 in modules/editor/message/LoadClearMessageConfirmationModal.tsx

View workflow job for this annotation

GitHub Actions / lint (ubuntu-latest, 16)

Do not use useless `undefined`

return (
<ModalContainer>
<ModalHeader>
<ModalTitle>Clear & Load Message</ModalTitle>
<ModalAction
icon={remove}
label="Close"
onClick={() => modal.dismiss()}
/>
</ModalHeader>
<ModalBody>
<Markdown
content={
"Loading a message link will remove all content from the current editor, if any." +
" Are you sure you want to continue? This action cannot be reverted."
}
/>
<InputError error={error} />
</ModalBody>
<ModalFooter>
<SecondaryButton onClick={() => modal.dismiss()}>
Cancel
</SecondaryButton>
<PrimaryButton
onClick={async () => {
let data
try {
data = await editorManager.getMessage(message.reference)
} catch {
// Handle later
}
if (!data) {
setError(
"The message link could not be loaded. Make sure a correct webhook URL is provided.",
)
return
}

const newMessage = messageOf(data)
newMessage.reference = message.reference

const index = editorManager.messages.indexOf(message)
const messages = [...editorManager.messages]
messages.splice(index, 1, newMessage as MessageLike)
editorManager.set("messages", messages)

modal.dismiss()
}}
>
Load
</PrimaryButton>
</ModalFooter>
</ModalContainer>
)
}
28 changes: 25 additions & 3 deletions modules/editor/message/MessageEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type { MessageLike } from "../../message/state/models/MessageModel"
import type { DataEditorModalProps } from "../data/DataEditorModal"
import { EditorManagerContext } from "../EditorManagerContext"
import { EmbedEditor } from "./EmbedEditor"
import { LoadClearMessageConfirmationModal } from "./LoadClearMessageConfirmationModal"
import { PrimaryContentEditor } from "./PrimaryContentEditor"

const DataEditorModal = dynamic<DataEditorModalProps>(async () =>
Expand Down Expand Up @@ -53,6 +54,16 @@ export function MessageEditor(props: MessageEditorProps) {
render: () => <DataEditorModal message={message} />,
})

const spawnLoadClearMessageModal = () =>
modalManager.spawn({
render: () => (
<LoadClearMessageConfirmationModal
editorManager={editorManager}
message={message}
/>
),
})

return useObserver(() => (
<Stack gap={16}>
<PrimaryContentEditor message={message} form={form} />
Expand Down Expand Up @@ -94,13 +105,24 @@ export function MessageEditor(props: MessageEditorProps) {
placeholder="https://discord.com/channels/..."
error={form.field("reference").error}
{...form.field("reference").inputProps}
/>
>
<PrimaryButton
disabled={
!form.field("reference").isValid || form.field("reference").isEmpty
}
onClick={() => {
spawnLoadClearMessageModal()
}}
>
Load
</PrimaryButton>
</InputField>
<Message
content={
"*When a message link is set, pressing submit or edit will edit the" +
" message sent inside of Discord. To load a message sent in Discord, use" +
" the bot's 'restore' command found in the apps section of the right" +
" click menu on any message.*"
" the 'Load' button or the bot's 'restore' command found in the apps" +
" section of the right click menu on any message.*"
}
/>
<ButtonList>
Expand Down

0 comments on commit de64ac8

Please sign in to comment.