diff --git a/NOTES.md b/NOTES.md index 9b49b98..fe1d68f 100644 --- a/NOTES.md +++ b/NOTES.md @@ -1,5 +1,11 @@ # Doing +New chat message modal: +- submit button in modal component (check for button on click and button text) + - primary button (save) + - secondary button (delete) +- on close: reset values (in stream chat message component) + Create loading indicator before API is called If api query returns error: diff --git a/app.go b/app.go index 464e85e..b7ae59e 100644 --- a/app.go +++ b/app.go @@ -10,6 +10,7 @@ import ( "time" "github.com/tylertravisty/rum-goggles/internal/api" + "github.com/tylertravisty/rum-goggles/internal/chatbot" "github.com/tylertravisty/rum-goggles/internal/config" rumblelivestreamlib "github.com/tylertravisty/rumble-livestream-lib-go" ) @@ -27,6 +28,8 @@ type App struct { cfgMu sync.Mutex api *api.Api apiMu sync.Mutex + cb *chatbot.ChatBot + cbMu sync.Mutex logError *log.Logger logInfo *log.Logger } @@ -126,6 +129,193 @@ func (a *App) AddChannel(url string) (*config.App, error) { return a.cfg, nil } +func (a *App) ChatBotMessages(cid string) (map[string]config.ChatMessage, error) { + a.cfgMu.Lock() + defer a.cfgMu.Unlock() + channel, exists := a.cfg.Channels[cid] + if !exists { + a.logError.Println("channel does not exist:", cid) + return nil, fmt.Errorf("Cannot find channel. Try reloading.") + } + + return channel.ChatBot.Messages, nil +} + +func (a *App) AddChatMessage(cid string, asChannel bool, interval time.Duration, message string) (map[string]config.ChatMessage, error) { + var err error + a.cfgMu.Lock() + defer a.cfgMu.Unlock() + _, err = a.cfg.NewChatMessage(cid, asChannel, interval, message) + if err != nil { + a.logError.Println("error creating new chat:", err) + return nil, fmt.Errorf("Error creating new chat message. Try again.") + } + + err = a.cfg.Save() + if err != nil { + a.logError.Println("error saving config:", err) + return nil, fmt.Errorf("Error saving chat message information. Try again.") + } + + a.updateChatBotConfig(a.cfg.Channels[cid].ChatBot) + + return a.cfg.Channels[cid].ChatBot.Messages, nil +} + +func (a *App) DeleteChatMessage(mid string, cid string) (map[string]config.ChatMessage, error) { + a.cbMu.Lock() + if a.cb != nil { + err := a.cb.StopMessage(mid) + if err != nil { + a.logError.Println("error stopping chat bot message:", err) + return nil, fmt.Errorf("Error stopping message. Try Again.") + } + } + a.cbMu.Unlock() + + a.cfgMu.Lock() + defer a.cfgMu.Unlock() + err := a.cfg.DeleteChatMessage(mid, cid) + if err != nil { + a.logError.Println("error deleting chat message:", err) + return nil, fmt.Errorf("Error deleting chat message. Try again.") + } + + err = a.cfg.Save() + if err != nil { + a.logError.Println("error saving config:", err) + return nil, fmt.Errorf("Error saving chat message information. Try again.") + } + + a.updateChatBotConfig(a.cfg.Channels[cid].ChatBot) + + return a.cfg.Channels[cid].ChatBot.Messages, nil +} + +func (a *App) UpdateChatMessage(id string, cid string, asChannel bool, interval time.Duration, message string) (map[string]config.ChatMessage, error) { + var err error + a.cfgMu.Lock() + defer a.cfgMu.Unlock() + _, err = a.cfg.UpdateChatMessage(id, cid, asChannel, interval, message) + if err != nil { + a.logError.Println("error updating chat message:", err) + return nil, fmt.Errorf("Error updating chat message. Try again.") + } + + err = a.cfg.Save() + if err != nil { + a.logError.Println("error saving config:", err) + return nil, fmt.Errorf("Error saving chat message information. Try again.") + } + + a.updateChatBotConfig(a.cfg.Channels[cid].ChatBot) + + return a.cfg.Channels[cid].ChatBot.Messages, nil +} + +func (a *App) NewChatBot(cid string, username string, password string, streamUrl string) error { + a.cbMu.Lock() + defer a.cbMu.Unlock() + + if a.cb != nil { + err := a.resetChatBot() + if err != nil { + a.logError.Println("error resetting chat bot:", err) + return fmt.Errorf("Error creating chat bot. Try Again.") + } + } + channel, exists := a.cfg.Channels[cid] + if !exists { + a.logError.Println("channel does not exist:", cid) + return fmt.Errorf("Channel does not exist.") + } + + var err error + a.cb, err = chatbot.NewChatBot(a.ctx, streamUrl, channel.ChatBot, a.logError) + if err != nil { + a.logError.Println("error creating new chat bot:", err) + return fmt.Errorf("Error creating new chat bot. Try Again.") + } + + err = a.cb.Login(username, password) + if err != nil { + a.logError.Println("error logging into chat bot:", err) + return fmt.Errorf("Error logging in. Try Again.") + } + + // a.cb = cb + return nil +} + +func (a *App) ResetChatBot() error { + a.cbMu.Lock() + defer a.cbMu.Unlock() + + err := a.resetChatBot() + if err != nil { + a.logError.Println("error resetting chat bot:", err) + return fmt.Errorf("Error resetting chat bot. Try Again.") + } + + return nil +} + +func (a *App) resetChatBot() error { + if a.cb == nil { + // return fmt.Errorf("chat bot is nil") + return nil + } + + err := a.cb.StopAllMessages() + if err != nil { + return fmt.Errorf("error stopping all chat bot messages: %v", err) + } + + err = a.cb.Logout() + if err != nil { + return fmt.Errorf("error logging out of chat bot: %v", err) + } + + a.cb = nil + + return nil +} + +func (a *App) StartChatBotMessage(mid string) error { + a.cbMu.Lock() + defer a.cbMu.Unlock() + + if a.cb == nil { + return fmt.Errorf("Chat bot not initialized.") + } + + err := a.cb.StartMessage(mid) + if err != nil { + a.logError.Println("error starting chat bot message:", err) + return fmt.Errorf("Error starting message. Try Again.") + } + + return nil +} + +func (a *App) StopChatBotMessage(mid string) error { + a.cbMu.Lock() + defer a.cbMu.Unlock() + + // If chat bot not initialized, then stop does nothing + if a.cb == nil { + return nil + } + + err := a.cb.StopMessage(mid) + if err != nil { + a.logError.Println("error stopping chat bot message:", err) + return fmt.Errorf("Error stopping message. Try Again.") + } + + return nil +} + func (a *App) StartApi(cid string) error { channel, found := a.cfg.Channels[cid] if !found { @@ -145,3 +335,11 @@ func (a *App) StartApi(cid string) error { func (a *App) StopApi() { a.api.Stop() } + +func (a *App) updateChatBotConfig(cfg config.ChatBot) { + a.cbMu.Lock() + defer a.cbMu.Unlock() + if a.cb != nil { + a.cb.Cfg = cfg + } +} diff --git a/build/appicon.png b/build/appicon.png index 63617fe..a2b4d08 100644 Binary files a/build/appicon.png and b/build/appicon.png differ diff --git a/frontend/src/assets/icons/gear-fill.png b/frontend/src/assets/icons/gear-fill.png new file mode 100644 index 0000000..e0bf42b Binary files /dev/null and b/frontend/src/assets/icons/gear-fill.png differ diff --git a/frontend/src/assets/icons/index.jsx b/frontend/src/assets/icons/index.jsx index 6e2b0b5..2c394db 100644 --- a/frontend/src/assets/icons/index.jsx +++ b/frontend/src/assets/icons/index.jsx @@ -1,6 +1,7 @@ import eye from './eye.png'; import eye_slash from './eye-slash.png'; import gear from './gear.png'; +import gear_fill from './gear-fill.png'; import heart from './heart-fill.png'; import house from './house.png'; import pause from './pause-fill.png'; @@ -9,10 +10,12 @@ import plus_circle from './plus-circle-fill.png'; import star from './star-fill.png'; import thumbs_down from './hand-thumbs-down.png'; import thumbs_up from './hand-thumbs-up.png'; +import x_lg from './x-lg.png'; export const Eye = eye; export const EyeSlash = eye_slash; export const Gear = gear; +export const GearFill = gear_fill; export const Heart = heart; export const House = house; export const Pause = pause; @@ -21,3 +24,4 @@ export const PlusCircle = plus_circle; export const Star = star; export const ThumbsDown = thumbs_down; export const ThumbsUp = thumbs_up; +export const XLg = x_lg; diff --git a/frontend/src/assets/icons/x-lg.png b/frontend/src/assets/icons/x-lg.png new file mode 100644 index 0000000..88a3d2b Binary files /dev/null and b/frontend/src/assets/icons/x-lg.png differ diff --git a/frontend/src/components/ChatBot.css b/frontend/src/components/ChatBot.css new file mode 100644 index 0000000..332cb42 --- /dev/null +++ b/frontend/src/components/ChatBot.css @@ -0,0 +1,47 @@ +.chat-bot-error { + border: 1px solid red; + box-sizing: border-box; + color: red; + font-family: monospace; + font-size: 16px; + padding: 5px; + text-align: center; + width: 100%; +} + +.chat-bot-modal { + align-items: left; + display: flex; + flex-direction: column; + height: 100%; + justify-content: center; + width: 100%; +} + +.chat-bot-setting { + align-items: start; + display: flex; + flex-direction: column; + padding-top: 10px; + width: 100%; +} + +.chat-bot-setting-label { + color: white; + font-family: sans-serif; + font-size: 20px; + padding-bottom: 5px; + width: 100%; +} + +.chat-bot-setting-input { + border: none; + border-radius: 5px; + box-sizing: border-box; + font-family: monospace; + font-size: 16px; + outline: none; + padding: 10px; + resize: none; + width: 100%; +} \ No newline at end of file diff --git a/frontend/src/components/ChatBot.jsx b/frontend/src/components/ChatBot.jsx new file mode 100644 index 0000000..eef6f57 --- /dev/null +++ b/frontend/src/components/ChatBot.jsx @@ -0,0 +1,129 @@ +import { useEffect, useState } from 'react'; + +import { Modal } from './Modal'; + +import { NewChatBot } from '../../wailsjs/go/main/App'; + +import './ChatBot.css'; + +export function ChatBotModal(props) { + const [error, setError] = useState(''); + const [password, setPassword] = useState(''); + const [saving, setSaving] = useState(false); + const updatePassword = (event) => setPassword(event.target.value); + const [url, setUrl] = useState(''); + const updateUrl = (event) => setUrl(event.target.value); + const [username, setUsername] = useState(''); + const updateUsername = (event) => setUsername(event.target.value); + + useEffect(() => { + if (saving) { + // let user = username; + // let p = password; + // let u = url; + // props.onSubmit(user, p, u); + NewChatBot(props.cid, username, password, url) + .then(() => { + reset(); + props.onClose(); + }) + .catch((error) => { + setSaving(false); + setError(error); + console.log('Error creating new chat bot:', error); + }); + } + }, [saving]); + + const reset = () => { + setError(''); + setPassword(''); + setSaving(false); + setUrl(''); + setUsername(''); + }; + + const close = () => { + reset(); + props.onClose(); + }; + + const submit = () => { + if (username === '') { + setError('Add username'); + return; + } + + if (password === '') { + setError('Add password'); + return; + } + + if (url === '') { + setError('Add stream URL'); + return; + } + + setSaving(true); + // let user = username; + // let p = password; + // let u = url; + // reset(); + // props.onSubmit(user, p, u); + }; + + return ( + { + console.log('Saving'); + } + : submit + } + title={'Chat Bot'} + > +
+ {error && {error}} +
+ Username + +
+
+ Password + +
+
+ Stream URL + +
+
+
+ ); +} + +export function StreamChatMessageItem() {} diff --git a/frontend/src/components/Modal.css b/frontend/src/components/Modal.css new file mode 100644 index 0000000..d4da5b4 --- /dev/null +++ b/frontend/src/components/Modal.css @@ -0,0 +1,176 @@ + +.modal-background { + align-items: center; + background-color: transparent; + display: flex; + height: 100vh; + justify-content: center; + left: 0; + position: absolute; + top: 0; + width: 100vw; +} + +.modal-body { + align-items: center; + display: flex; + height: 80%; + justify-content: center; + width: 100%; +} + +.modal-button { + background-color: #85c742; + border: none; + border-radius: 5px; + color: #061726; + cursor: pointer; + font-size: 18px; + font-weight: bold; + text-decoration: none; + /* width: 20%; */ + width: 70px; +} + +.modal-button-cancel { + background-color: transparent; + border: 1px solid #495a6a; + border-radius: 5px; + color: #495a6a; + cursor: pointer; + font-size: 18px; + font-weight: bold; + text-decoration: none; + /* width: 20%; */ + width: 70px; +} + +.modal-button-delete { + background-color: transparent; + border: 1px solid red; + border-radius: 5px; + color: red; + cursor: pointer; + font-size: 18px; + font-weight: bold; + text-decoration: none; + /* width: 20%; */ + width: 70px; +} + +.modal-close { + align-items: center; + background-color: transparent; + border: none; + display: flex; + flex-direction: center; + padding: 0px; +} + +.modal-close:hover { + cursor: pointer; +} + +.modal-close-icon { + height: 24px; + padding: 0px; + width: 24px; +} + +.modal-container { + align-items: center; + background-color: rgba(6,23,38,1); + border: 1px solid #495a6a; + border-radius: 15px; + color: black; + display: flex; + flex-direction: column; + height: 50%; + justify-content: space-between; + opacity: 1; + padding: 10px 20px; + width: 50%; +} + +.modal-footer { + align-items: center; + display: flex; + flex-direction: row; + justify-content: space-between; + height: 10%; + width: 100%; +} + +.modal-header { + align-items: center; + display: flex; + flex-direction: row; + justify-content: space-between; + height: 10%; + width: 100%; +} + +.modal-title { + color: white; + font-family: sans-serif; + font-size: 24px; +} + +.small-modal-button-delete { + background-color: red; + border: none; + border-radius: 5px; + color: white; + cursor: pointer; + font-size: 18px; + font-weight: bold; + text-decoration: none; + /* width: 20%; */ + width: 70px; +} + +.small-modal-container { + align-items: center; + /* background-color: rgba(6,23,38,1); */ + background-color: white; + border: 1px solid #495a6a; + /* border: 1px solid black; */ + border-radius: 15px; + color: black; + display: flex; + flex-direction: column; + height: 50%; + justify-content: space-between; + opacity: 1; + padding: 10px 20px; + width: 50%; +} + +.small-modal-header { + align-items: center; + display: flex; + flex-direction: row; + justify-content: space-between; + height: 10%; + width: 100%; +} + +.small-modal-footer { + align-items: center; + display: flex; + flex-direction: row; + justify-content: space-between; + height: 20%; + width: 100%; +} + +.small-modal-message { + font-family: sans-serif; + font-size: 18px; +} + +.small-modal-title { + color: black; + font-family: sans-serif; + font-size: 24px; +} \ No newline at end of file diff --git a/frontend/src/components/Modal.jsx b/frontend/src/components/Modal.jsx new file mode 100644 index 0000000..1be4fe8 --- /dev/null +++ b/frontend/src/components/Modal.jsx @@ -0,0 +1,86 @@ +import { XLg } from '../assets/icons'; +import './Modal.css'; + +export function Modal(props) { + return ( +
+
event.stopPropagation()} + style={props.style} + > +
+ {props.title} + +
+
{props.children}
+
+ {props.cancelButton && ( + + )} + {props.deleteButton && ( + + )} + {props.submitButton && ( + + )} +
+
+
+ ); +} + +export function SmallModal(props) { + return ( +
+
event.stopPropagation()} + style={props.style} + > +
+ {props.title} + +
+
+ {props.message} +
+
+ {props.cancelButton && ( + + )} + {props.deleteButton && ( + + )} + {props.submitButton && ( + + )} +
+
+
+ ); +} diff --git a/frontend/src/components/StreamChat.css b/frontend/src/components/StreamChat.css index c8fea5b..0560425 100644 --- a/frontend/src/components/StreamChat.css +++ b/frontend/src/components/StreamChat.css @@ -3,24 +3,43 @@ height: 100%; } -.stream-chat-add-button { +.stream-chat-button { align-items: center; - background-color: rgba(6,23,38,1); border: none; display: flex; justify-content: center; padding: 0px; } -.stream-chat-add-button:hover { +.stream-chat-button:hover { cursor: pointer; } -.stream-chat-add-icon { +.stream-chat-button-title { + background-color: rgba(6,23,38,1); +} + +.stream-chat-button-chat { + align-items: center; + background-color: #000312; + display: flex; + justify-content: center; + width: 10%; +} + +.stream-chat-icon { height: 24px; width: 24px; } +.stream-chat-controls { + align-items: center; + display: flex; + flex-direction: row; + justify-content: space-between; + width: 55px; +} + .stream-chat-header { align-items: center; background-color: rgba(6,23,38,1); @@ -33,6 +52,50 @@ text-align: left; } +.stream-chat-item { + border-bottom: 1px solid #82b1ff; + box-sizing: border-box; + color: white; + display: flex; + flex-direction: row; + font-family: sans-serif; + justify-content: space-between; + padding: 10px 20px; + width: 100%; +} + +.stream-chat-item-sender { + align-items: center; + box-sizing: border-box; + display: flex; + justify-content: left; + padding-left: 10px; + width: 20%; +} + +.stream-chat-item-interval { + align-items: center; + box-sizing: border-box; + display: flex; + justify-content: left; + padding-left: 10px; + width: 20%; +} + +.stream-chat-item-message { + align-items: center; + display: flex; + justify-content: left; + overflow: hidden; + white-space: nowrap; + width: 50%; +} + +.stream-chat-list { + overflow-y: auto; + height: calc(100vh - 84px - 40px - 179px); +} + .stream-chat-title { color: white; font-family: sans-serif; diff --git a/frontend/src/components/StreamChat.jsx b/frontend/src/components/StreamChat.jsx index 49de3bc..6394850 100644 --- a/frontend/src/components/StreamChat.jsx +++ b/frontend/src/components/StreamChat.jsx @@ -1,20 +1,182 @@ -import { PlusCircle } from '../assets/icons'; +import { useEffect, useState } from 'react'; +import { StartChatBotMessage, StopChatBotMessage } from '../../wailsjs/go/main/App'; +import { EventsOn } from '../../wailsjs/runtime/runtime'; +import { GearFill, Pause, Play, PlusCircle } from '../assets/icons'; import './StreamChat.css'; +import { SmallModal } from './Modal'; function StreamChat(props) { + const sortChatsAlpha = () => { + let keys = Object.keys(props.chats); + + let sorted = [...keys].sort((a, b) => + props.chats[a].text.toLowerCase() > props.chats[b].text.toLowerCase() ? 1 : -1 + ); + return sorted; + }; + return (
{props.title} - +
+ + +
+
+
+ {sortChatsAlpha().map((chat, index) => ( + + ))}
); } export default StreamChat; + +function StreamChatItem(props) { + const [active, setActive] = useState(props.chat.active); + const [error, setError] = useState(''); + + const changeActive = (bool) => { + console.log('ChangeActive:', bool); + props.chat.active = bool; + setActive(bool); + }; + + useEffect(() => { + EventsOn('ChatBotMessageActive-' + props.chat.id, (mid) => { + console.log('ChatBotMessageActive', props.chat.id, mid); + if (mid === props.chat.id) { + changeActive(true); + } + }); + + EventsOn('ChatBotMessageError-' + props.chat.id, (mid) => { + console.log('ChatBotMessageError', props.chat.id, mid); + if (mid === props.chat.id) { + changeActive(false); + } + }); + }, []); + + const prependZero = (value) => { + if (value < 10) { + return '0' + value; + } + + return '' + value; + }; + + const printInterval = (interval) => { + let hours = Math.floor(interval / 3600); + let minutes = Math.floor(interval / 60 - hours * 60); + let seconds = Math.floor(interval - hours * 3600 - minutes * 60); + + // hours = prependZero(hours); + // minutes = prependZero(minutes); + // seconds = prependZero(seconds); + // return hours + ':' + minutes + ':' + seconds; + + return hours + 'h ' + minutes + 'm ' + seconds + 's'; + }; + + const intervalToTimer = (interval) => { + let hours = Math.floor(interval / 3600); + let minutes = Math.floor(interval / 60 - hours * 60); + let seconds = Math.floor(interval - hours * 3600 - minutes * 60); + + if (minutes !== 0) { + seconds = prependZero(seconds); + } + if (hours !== 0) { + minutes = prependZero(minutes); + } + if (hours === 0) { + hours = ''; + if (minutes === 0) { + minutes = ''; + if (seconds === 0) { + seconds = ''; + } + } + } + + return hours + minutes + seconds; + }; + + const openChat = () => { + props.onItemClick( + props.chat.id, + props.chat.as_channel, + intervalToTimer(props.chat.interval), + props.chat.text + ); + }; + + const startMessage = () => { + StartChatBotMessage(props.chat.id) + .then(() => { + changeActive(true); + }) + .catch((error) => { + setError(error); + }); + }; + + const stopMessage = () => { + StopChatBotMessage(props.chat.id).then(() => { + changeActive(false); + }); + }; + + return ( + <> + setError('')} + show={error !== ''} + style={{ minWidth: '300px', maxWidth: '200px', maxHeight: '200px' }} + title={'Error'} + message={error} + submitButton={'OK'} + onSubmit={() => setError('')} + /> +
openChat()}> + {props.chat.text} + + {printInterval(props.chat.interval)} + + + {props.chat.as_channel ? 'Channel' : 'User'} + + +
+ + ); +} diff --git a/frontend/src/components/StreamChatMessage.css b/frontend/src/components/StreamChatMessage.css index 6187703..143efaf 100644 --- a/frontend/src/components/StreamChatMessage.css +++ b/frontend/src/components/StreamChatMessage.css @@ -1,4 +1,4 @@ -.modal-chat { +/* .modal-chat { align-items: center; background-color: red; color: black; @@ -18,4 +18,170 @@ position: absolute; top: 0; width: 100vw; +} */ + +.chat-as-channel { + align-items: center; + display: flex; + justify-content: space-between; + padding-top: 10px; + width: 100%; +} + +.chat-as-channel-label { + color: white; + font-family: sans-serif; + padding-right: 10px; +} + +.chat-interval { + align-items: center; + display: flex; + flex-direction: row; + justify-content: space-between; + padding-top: 10px; + width: 100%; +} + +.chat-interval-input { + border: none; + border-radius: 34px; + box-sizing: border-box; + font-family: monospace; + font-size: 16px; + outline: none; + padding: 5px 10px 5px 10px; + text-align: right; +} + +.chat-interval-input-zero::placeholder { + text-align: center; +} + +.chat-interval-input-value::placeholder { + color: black; + opacity: 1; + text-align: center; +} + +.chat-interval-label { + color: white; + font-family: sans-serif; + padding-right: 10px; +} + +.chat-options { + display: flex; + flex-direction: column; + width: 100%; +} + +.stream-chat-message { + align-items: center; + color: white; + display: flex; + flex-direction: column; + font-family: sans-serif; + justify-content: start; + width: 100%; +} + +.stream-chat-message-error { + border: 1px solid red; + box-sizing: border-box; + color: red; + font-family: monospace; + font-size: 16px; + padding: 5px; + text-align: center; + width: 100%; +} + +.stream-chat-message-label { + padding: 5px 0px; + /* width: 50%; */ +} + +.stream-chat-message-modal { + align-items: left; + display: flex; + flex-direction: column; + height: 100%; + justify-content: center; + width: 100%; +} + +.stream-chat-message-textarea { + border: none; + border-radius: 5px; + box-sizing: border-box; + font-family: monospace; + font-size: 16px; + outline: none; + padding: 10px; + resize: none; + width: 100%; +} + +.stream-chat-message-title { + align-items: center; + display: flex; + flex-direction: row; + justify-content: start; + width: 100%; +} + +.chat-as-channel-switch { + position: relative; + display: inline-block; + width: 50px; + height: 24px; +} + +.chat-as-channel-switch input { + opacity: 0; + width: 0; + height: 0; +} + +.chat-as-channel-slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #495a6a; + -webkit-transition: .4s; + transition: .4s; +} + +.chat-as-channel-slider:before { + position: absolute; + content: ""; + height: 16px; + width: 16px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; +} + +input:checked + .chat-as-channel-slider { + background-color: #85c742; +} + +input:checked + .chat-as-channel-slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} +/* Rounded sliders */ +.chat-as-channel-slider.round { + border-radius: 34px; +} + +.chat-as-channel-slider.round:before { + border-radius: 50%; } \ No newline at end of file diff --git a/frontend/src/components/StreamChatMessage.jsx b/frontend/src/components/StreamChatMessage.jsx index 88a6f37..6a29869 100644 --- a/frontend/src/components/StreamChatMessage.jsx +++ b/frontend/src/components/StreamChatMessage.jsx @@ -1,12 +1,194 @@ +import { useEffect, useState } from 'react'; + +import { Modal, SmallModal } from './Modal'; + import './StreamChatMessage.css'; -export function StreamChatMessageModal() { +export function StreamChatMessageModal(props) { + const [asChannel, setAsChannel] = useState(props.asChannel); + const [openDelete, setOpenDelete] = useState(false); + const [error, setError] = useState(''); + const [message, setMessage] = useState(props.message); + const updateMessage = (event) => setMessage(event.target.value); + const [timer, setTimer] = useState(props.interval); + + useEffect(() => { + console.log('update chat'); + setAsChannel(props.asChannel); + setError(''); + setMessage(props.message); + setTimer(props.interval); + }, []); + + const reset = () => { + setAsChannel(false); + setError(''); + setMessage(''); + setTimer(''); + }; + + const close = () => { + reset(); + props.onClose(); + }; + + const submit = () => { + if (message === '') { + setError('Add message'); + return; + } + + if (timer === '') { + setError('Set timer'); + return; + } + + let ac = asChannel; + let msg = message; + let int = timerToInterval(); + reset(); + props.onSubmit(props.chatID, ac, int, msg); + }; + + const deleteMessage = () => { + if (props.chatID === '') { + close(); + return; + } + + setOpenDelete(true); + }; + + const confirmDelete = () => { + reset(); + setOpenDelete(false); + props.onDelete(props.chatID); + }; + + const updateTimerBackspace = (e) => { + if (timer.length === 0) { + return; + } + + if (e.keyCode === 8) { + setTimer(timer.substring(0, timer.length - 1)); + } + }; + + const updateTimer = (e) => { + let nums = '0123456789'; + let digit = e.target.value; + + if (!nums.includes(digit)) { + return; + } + + if (timer.length === 6) { + return; + } + + if (timer.length === 0 && digit === '0') { + return; + } + + setTimer(timer + digit); + }; + + const timerToInterval = () => { + let prefix = '0'.repeat(6 - timer.length); + let t = prefix + timer; + + let hours = parseInt(t.substring(0, 2)); + let minutes = parseInt(t.substring(2, 4)); + let seconds = parseInt(t.substring(4, 6)); + + return hours * 3600 + minutes * 60 + seconds; + }; + + const printTimer = () => { + if (timer === '') { + return '00:00:00'; + } + + let prefix = '0'.repeat(6 - timer.length); + let t = prefix + timer; + + return t.substring(0, 2) + ':' + t.substring(2, 4) + ':' + t.substring(4, 6); + }; + + const checkToggle = (e) => { + setAsChannel(e.target.checked); + }; + return ( -
-
- hello world -
-
+ <> + +
+
+ {error && {error}} +
+ Message +
+