Skip to content

Commit

Permalink
new: You can now define canned messages in configuration
Browse files Browse the repository at this point in the history
* Implement templates for messages
  • Loading branch information
ybizeul authored Sep 6, 2024
1 parent c07a567 commit 93a8ae7
Show file tree
Hide file tree
Showing 15 changed files with 292 additions and 48 deletions.
28 changes: 13 additions & 15 deletions html/src/Components/MarkdownEditor/FullHeightTextArea.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
import { Textarea, TextareaProps } from "@mantine/core";
import { ChangeEvent, useState } from "react";
import { Textarea } from "@mantine/core";

import classes from './FullHeightTextArea.module.css';
import { useUncontrolled } from "@mantine/hooks";

// interface FullHeightTextAreaProps extends TextareaProps {
// value: string | number | readonly string[];
// label: string;
// onChange: ChangeEventHandler<HTMLTextAreaElement>;
// }
export function FullHeightTextArea(props: TextareaProps) {
interface FullHeightTextAreaProps {
value?: string;
onChange?: (value: string) => void;
}
export function FullHeightTextArea(props: FullHeightTextAreaProps) {
const { value, onChange } = props;
const [_value, setValue] = useState<string | number | readonly string[] | undefined>(value);

function notifyChange(v: ChangeEvent<HTMLTextAreaElement>) {
setValue(v.target.value)
onChange && onChange(v)
}
const [_value, handleChange] = useUncontrolled({
value,
onChange,
});

return (
<Textarea {...props} resize="vertical" value={_value}
<Textarea w="100%" flex="1" label="Message" description="This markdown will be displayed to the user" resize="vertical" value={_value}
classNames={{root: classes.root, wrapper: classes.wrapper, input: classes.input}}
onChange={(v) => { notifyChange(v) }}
onChange={(e) => { handleChange(e.currentTarget.value) }}
/>
)
}
23 changes: 14 additions & 9 deletions html/src/Components/MarkdownEditor/MarkdownEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,47 @@
import { ActionIcon, Box, BoxComponentProps, InputWrapper, Paper, rem } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { useDisclosure, useUncontrolled } from "@mantine/hooks";
import { IconEye } from "@tabler/icons-react";
import { useState } from "react";
import { Message } from "../Message";
import { FullHeightTextArea } from "./FullHeightTextArea";

interface MarkDownEditorProps {
onChange: (message: string) => void;
markdown: string;
message: string;
}

export function MarkDownEditor(props: MarkDownEditorProps&BoxComponentProps) {
// Initialize props
const { onChange, markdown } = props;
const { onChange, message } = props;

// Initialize state
const [_markdown, setMarkdown] = useState<string>(markdown);
const [markdown, setMarkdown] = useUncontrolled({
value: message,
defaultValue: '',
finalValue: 'Final',
onChange,
});
//const [markdown, setMarkdown] = useState<string>(message);
const [preview, previewH] = useDisclosure(false);

// Functions
const notifyChange = (m: string) => {
setMarkdown(m)
onChange(m)
}

return(
<Box display="flex" flex="1" w={{base: '100%', xs: rem(500)}} pl={props.pl} style={props.style} pos={"relative"}>
<Box display="flex" flex="1" pl={props.pl} style={props.style} pos={"relative"}>
<ActionIcon size="xs" id="preview" variant={preview?"filled":"subtle"} m={rem(3)} radius="xl" onClick={previewH.toggle} style={{position:"absolute", top: 0, right: 0}}>
<IconEye style={{ width: rem(16), height: rem(16) }} stroke={1.5} />
</ActionIcon>
{preview?
<InputWrapper display="flex" style={{flexDirection:"column"}} label="Message" description="This markdown will be displayed to the user" w="100%">
<Paper flex="1" withBorder mt="5" pt="5.5" px="12" display="flex">
<Message value={_markdown} />
<Message value={markdown} />
</Paper>
</InputWrapper>
:
<FullHeightTextArea w="100%" flex="1" label="Message" description="This markdown will be displayed to the user" value={markdown} onChange={(v) => { notifyChange(v.target.value); }}/>
<FullHeightTextArea value={markdown} onChange={(v) => { notifyChange(v); }}/>
}
</Box>
)
Expand Down
21 changes: 13 additions & 8 deletions html/src/Components/ShareEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useDisclosure, useMediaQuery } from "@mantine/hooks";
import { useState } from "react";
import classes from './ShareEditor.module.css';
import { MarkDownEditor } from "./MarkdownEditor";
import { TemplatesMenu } from "./TemplatesMenu";

interface ShareEditorProps {
onChange: (options: Share["options"]) => void;
Expand All @@ -28,8 +29,8 @@ export function ShareEditor(props: ShareEditorProps&BoxComponentProps) {

// Functions
const notifyChange = (o: Share["options"]) => {
setOptions(o)
onChange(o)
setOptions(o)
onChange(o)
}

return (
Expand Down Expand Up @@ -82,12 +83,16 @@ export function ShareEditor(props: ShareEditorProps&BoxComponentProps) {

{/* Right section */}
{(showMessage||!isInBrowser)&&
<MarkDownEditor
pl={isInBrowser?"sm":"0"}
style={{borderLeft: isInBrowser?"1px solid lightGray":""}}
onChange={(v) => { notifyChange({..._options, message:v}); }}
markdown={_options.message?_options.message:""}
/>
<Box display="flex" flex="1" w={{base: '100%', xs: rem(500)}} pos={"relative"}>
<MarkDownEditor
pl={isInBrowser?"sm":"0"}
style={{borderLeft: isInBrowser?"1px solid lightGray":""}}
onChange={(v) => { notifyChange({..._options, message:v}); }}
message={_options.message?_options.message:""}
/>
<TemplatesMenu onChange={(v) => { notifyChange({..._options, message: v}); }}/>
</Box>

}
</Flex>
<Flex >
Expand Down
49 changes: 49 additions & 0 deletions html/src/Components/TemplatesMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { H } from "@/APIClient";
import { Message } from "@/hupload";
import { ActionIcon, ActionIconProps, Menu, rem } from "@mantine/core";
import { IconListCheck } from "@tabler/icons-react";
import { useEffect, useState } from "react";

interface TemplatesMenuProps {
onChange: (message: string) => void;
}

export function TemplatesMenu(props:TemplatesMenuProps&ActionIconProps) {
// Initialize state
const [messages, setMessages] = useState<string[]>([])

// effects
useEffect(() => {
H.get('/messages').then((res) => {
setMessages(res as string[])
})
},[])

const selectMessage = (index: number) => {
H.get('/messages/'+index).then((res) => {
const m = res as Message
props.onChange(m.message)
})
}

if (messages.length === 0) {
return
}

return (
<Menu withArrow withinPortal={false}>
<Menu.Target>
<ActionIcon size="xs" id="template" variant={"subtle"} m={rem(3)} radius="xl" style={{position:"absolute", top:0, right: 25}}>
<IconListCheck style={{ width: rem(16), height: rem(16) }} stroke={1.5} />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
{messages.map((m, i) => (
<Menu.Item key={i+1} onClick={() => {selectMessage(i+1)}}>
{m}
</Menu.Item>
))}
</Menu.Dropdown>
</Menu>
);
}
14 changes: 9 additions & 5 deletions html/src/hupload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export interface ItemInfo {
Size: number;
}

export interface Message {
title: string;
message: string;
}

// Utilities

Expand All @@ -51,12 +55,12 @@ export function humanFileSize(bytes: number, si=false, dp=1) {
return bytes.toFixed(dp) + ' ' + units[u];
}

export function prettyfiedCount(count: number|null, singular: string, plural: string, empty: string|null) {
export function prettyfiedCount(count: number|null, singular: string, plural: string, empty: string|null) {
if (count === 0 || count === null|| count === undefined) {
return empty
return empty
} else if (count === 1) {
return count.toFixed() + ' ' + singular
return count.toFixed() + ' ' + singular
} else {
return count.toFixed() + ' ' + plural
return count.toFixed() + ' ' + plural
}
}
}
2 changes: 1 addition & 1 deletion hupload/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/coreos/go-oidc v2.2.1+incompatible
golang.org/x/crypto v0.25.0
golang.org/x/oauth2 v0.21.0
gopkg.in/square/go-jose.v2 v2.6.0
)

require (
Expand All @@ -34,5 +35,4 @@ require (
github.com/aws/smithy-go v1.20.4 // indirect
github.com/pquerna/cachecontrol v0.2.0 // indirect
github.com/stretchr/testify v1.8.2 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
)
29 changes: 29 additions & 0 deletions hupload/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,35 @@ func (h *Hupload) postLogin(w http.ResponseWriter, r *http.Request) {
writeSuccessJSON(w, u)
}

func (h *Hupload) getMessages(w http.ResponseWriter, r *http.Request) {
titles := []string{}

for _, m := range h.Config.Values.MessageTemplates {
titles = append(titles, m.Title)
}

writeSuccessJSON(w, titles)
}

var ErrMessageInvalidIndex = errors.New("invalid index")
var ErrMessageIndexOutOfBounds = errors.New("index out of bounds")

func (h *Hupload) getMessage(w http.ResponseWriter, r *http.Request) {
index, err := strconv.Atoi(r.PathValue("index"))
if err != nil {
writeError(w, http.StatusBadRequest, ErrMessageInvalidIndex.Error())
return
}
t := h.Config.Values.MessageTemplates
if index <= len(t) && index > 0 {
writeSuccessJSON(w, t[index-1])
return
} else {
writeError(w, http.StatusBadRequest, ErrMessageIndexOutOfBounds.Error())
return
}
}

// getVersion returns hupload version
func (h *Hupload) getVersion(w http.ResponseWriter, r *http.Request) {
v := struct {
Expand Down
Loading

0 comments on commit 93a8ae7

Please sign in to comment.