diff --git a/web/pages/components/config/constants.tsx b/web/pages/components/config/constants.tsx index 51b6fd954..55ac2d0e6 100644 --- a/web/pages/components/config/constants.tsx +++ b/web/pages/components/config/constants.tsx @@ -238,4 +238,6 @@ export const DEFAULT_VARIANT_STATE:VideoVariant = { export const DEFAULT_SOCIAL_HANDLE:SocialHandle = { url: '', platform: '', -}; \ No newline at end of file +}; + +export const OTHER_SOCIAL_HANDLE_OPTION = 'OTHER_SOCIAL_HANDLE_OPTION'; diff --git a/web/pages/components/config/edit-social-links.tsx b/web/pages/components/config/edit-social-links.tsx deleted file mode 100644 index 8b483e0ec..000000000 --- a/web/pages/components/config/edit-social-links.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import React, { useContext, useState, useEffect } from 'react'; -import { Typography, Input } from 'antd'; - -import { ServerStatusContext } from '../../../utils/server-status-context'; -import { TEXTFIELD_DEFAULTS, RESET_TIMEOUT, SUCCESS_STATES, postConfigUpdateToAPI } from './constants'; - -const { Title } = Typography; - -export default function EditSocialLinks() { - const [newTagInput, setNewTagInput] = useState(''); - const [submitStatus, setSubmitStatus] = useState(null); - const [submitStatusMessage, setSubmitStatusMessage] = useState(''); - const serverStatusData = useContext(ServerStatusContext); - const { serverConfig, setFieldInConfigState } = serverStatusData || {}; - - const { instanceDetails } = serverConfig; - const { tags = [] } = instanceDetails; - - const configPath = 'instanceDetails'; - - const { - apiPath, - maxLength, - placeholder, - } = TEXTFIELD_DEFAULTS[configPath].tags || {}; - - - let resetTimer = null; - - useEffect(() => { - return () => { - clearTimeout(resetTimer); - } - }, []); - - const resetStates = () => { - setSubmitStatus(null); - setSubmitStatusMessage(''); - resetTimer = null; - clearTimeout(resetTimer); - } - - // posts all the tags at once as an array obj - const postUpdateToAPI = async (postValue: any) => { - await postConfigUpdateToAPI({ - apiPath, - data: { value: postValue }, - onSuccess: () => { - setFieldInConfigState({ fieldName: 'socialHandles', value: postValue, path: configPath }); - setSubmitStatus('success'); - setSubmitStatusMessage('Tags updated.'); - setNewTagInput(''); - resetTimer = setTimeout(resetStates, RESET_TIMEOUT); - }, - onError: (message: string) => { - setSubmitStatus('error'); - setSubmitStatusMessage(message); - resetTimer = setTimeout(resetStates, RESET_TIMEOUT); - }, - }); - }; - - const handleInputChange = e => { - if (submitStatusMessage !== '') { - setSubmitStatusMessage(''); - } - setNewTagInput(e.target.value); - }; - - // send to api and do stuff - const handleSubmitNewLink = () => { - resetStates(); - const newTag = newTagInput.trim(); - if (newTag === '') { - setSubmitStatusMessage('Please enter a tag'); - return; - } - if (tags.some(tag => tag.toLowerCase() === newTag.toLowerCase())) { - setSubmitStatusMessage('This tag is already used!'); - return; - } - - const updatedTags = [...tags, newTag]; - postUpdateToAPI(updatedTags); - }; - - const handleDeleteLink = index => { - resetStates(); - const updatedTags = [...tags]; - updatedTags.splice(index, 1); - postUpdateToAPI(updatedTags); - } - - const { - icon: newStatusIcon = null, - message: newStatusMessage = '', - } = SUCCESS_STATES[submitStatus] || {}; - - return ( -
- - Add Tags -

This is a great way to categorize your Owncast server on the Directory!

- -
- {tags.map((tag, index) => { - const handleClose = () => { - handleDeleteLink(index); - }; - return ( - {tag} - ); - })} -
-
- {newStatusIcon} {newStatusMessage} {submitStatusMessage} -
-
- -
-
- ); -} diff --git a/web/pages/components/config/social-icons-dropdown.tsx b/web/pages/components/config/social-icons-dropdown.tsx index 8368a84ca..14bf6ec0f 100644 --- a/web/pages/components/config/social-icons-dropdown.tsx +++ b/web/pages/components/config/social-icons-dropdown.tsx @@ -1,66 +1,36 @@ -import React, { useState } from 'react'; -import { PlusOutlined } from "@ant-design/icons"; -import { Select, Divider, Input } from "antd"; -import classNames from 'classnames'; +import React from 'react'; +import { Select } from "antd"; import { SocialHandleDropdownItem } from "../../../types/config-section"; import { NEXT_PUBLIC_API_HOST } from '../../../utils/apis'; +import { OTHER_SOCIAL_HANDLE_OPTION } from './constants'; interface DropdownProps { iconList: SocialHandleDropdownItem[]; - selectedOption?: string; -} -interface DropdownOptionProps extends SocialHandleDropdownItem { - isSelected: boolean; + selectedOption: string; + onSelected: any; } -// Add "Other" item which creates a text field -// Add fixed custom ones - "url", "donate", "follow", "rss" +export default function SocialDropdown({ iconList, selectedOption, onSelected }: DropdownProps) { -function dropdownRender(menu) { - console.log({menu}) - return 'hi'; -} - -export default function SocialDropdown({ iconList, selectedOption }: DropdownProps) { - const [name, setName] = useState(''); - - const handleNameChange = event => { - setName(event.target.value); + const handleSelected = value => { + if (onSelected) { + onSelected(value); + } }; - - const handleAddItem = () => { - console.log('addItem'); - // const { items, name } = this.state; - // this.setState({ - // items: [...items, name || `New item ${index++}`], - // name: '', - // }); - }; - - + const inititalSelected = selectedOption === '' ? null : selectedOption; return (
+

If you are looking for a platform name not on this list, please select Other and type in your own name. A logo will not be provided.

+

If you DO have a logo, drop it in to the /webroot/img/platformicons directory and update the /socialHandle.go list. Then restart the server and it will show up in the list.

+ - - Add item - -
- - )} + defaultValue={inititalSelected} + value={inititalSelected} + onSelect={handleSelected} > {iconList.map(item => { const { platform, icon, key } = item; @@ -74,8 +44,10 @@ export default function SocialDropdown({ iconList, selectedOption }: DropdownPro ); }) } + + Other... + - ); } diff --git a/web/pages/config-social-links.tsx b/web/pages/config-social-links.tsx index f4af7cde3..e703eeff3 100644 --- a/web/pages/config-social-links.tsx +++ b/web/pages/config-social-links.tsx @@ -1,25 +1,23 @@ import React, { useState, useContext, useEffect } from 'react'; -import { Typography, Table, Button, Modal } from 'antd'; +import { Typography, Table, Button, Modal, Input } from 'antd'; import { ColumnsType } from 'antd/lib/table'; import { DeleteOutlined } from '@ant-design/icons'; import SocialDropdown from './components/config/social-icons-dropdown'; import { fetchData, NEXT_PUBLIC_API_HOST, SOCIAL_PLATFORMS_LIST } from '../utils/apis'; import { ServerStatusContext } from '../utils/server-status-context'; -import { API_SOCIAL_HANDLES, postConfigUpdateToAPI, RESET_TIMEOUT, SUCCESS_STATES, DEFAULT_SOCIAL_HANDLE } from './components/config/constants'; +import { API_SOCIAL_HANDLES, postConfigUpdateToAPI, RESET_TIMEOUT, SUCCESS_STATES, DEFAULT_SOCIAL_HANDLE, OTHER_SOCIAL_HANDLE_OPTION } from './components/config/constants'; import { SocialHandle } from '../types/config-section'; const { Title } = Typography; - -// get icons - export default function ConfigSocialLinks() { const [availableIconsList, setAvailableIconsList] = useState([]); const [currentSocialHandles, setCurrentSocialHandles] = useState([]); const [displayModal, setDisplayModal] = useState(false); + const [displayOther, setDisplayOther] = useState(false); const [modalProcessing, setModalProcessing] = useState(false); - const [editId, setEditId] = useState(0); + const [editId, setEditId] = useState(-1); // current data inside modal const [modalDataState, setModalDataState] = useState(DEFAULT_SOCIAL_HANDLE); @@ -27,7 +25,6 @@ export default function ConfigSocialLinks() { const [submitStatus, setSubmitStatus] = useState(null); const [submitStatusMessage, setSubmitStatusMessage] = useState(''); - const serverStatusData = useContext(ServerStatusContext); const { serverConfig, setFieldInConfigState } = serverStatusData || {}; @@ -43,7 +40,6 @@ export default function ConfigSocialLinks() { key: item, ...result[item], })); - console.log({result}) setAvailableIconsList(list); } catch (error) { @@ -52,12 +48,17 @@ export default function ConfigSocialLinks() { } }; + const selectedOther = modalDataState.platform !== '' && !availableIconsList.find(item => item.key === modalDataState.platform); + + useEffect(() => { getAvailableIcons(); }, []); useEffect(() => { - setCurrentSocialHandles(initialSocialHandles); + if (instanceDetails.socialHandles) { + setCurrentSocialHandles(initialSocialHandles); + } }, [instanceDetails]); @@ -66,12 +67,42 @@ export default function ConfigSocialLinks() { setSubmitStatusMessage(''); resetTimer = null; clearTimeout(resetTimer); - } - - const handleModalCancel = () => { + }; + const resetModal = () => { setDisplayModal(false); setEditId(-1); - } + setDisplayOther(false); + setModalProcessing(false); + setModalDataState({...DEFAULT_SOCIAL_HANDLE}); + }; + + const handleModalCancel = () => { + resetModal(); + }; + + const updateModalState = (fieldName: string, value: string) => { + setModalDataState({ + ...modalDataState, + [fieldName]: value, + }); + }; + const handleDropdownSelect = (value: string) => { + if (value === OTHER_SOCIAL_HANDLE_OPTION) { + setDisplayOther(true); + updateModalState('platform', ''); + } else { + setDisplayOther(false); + updateModalState('platform', value); + } + }; + const handleOtherNameChange = event => { + const { value } = event.target; + updateModalState('platform', value); + }; + const handleUrlChange = event => { + const { value } = event.target; + updateModalState('url', value); + }; // posts all the variants at once as an array obj @@ -80,14 +111,14 @@ export default function ConfigSocialLinks() { apiPath: API_SOCIAL_HANDLES, data: { value: postValue }, onSuccess: () => { - setFieldInConfigState({ fieldName: 'socialHandles', value: postValue, path: 'instancesDetails' }); + setFieldInConfigState({ fieldName: 'socialHandles', value: postValue, path: 'instanceDetails' }); // close modal setModalProcessing(false); handleModalCancel(); setSubmitStatus('success'); - setSubmitStatusMessage('Variants updated.'); + setSubmitStatusMessage('Social Handles updated.'); resetTimer = setTimeout(resetStates, RESET_TIMEOUT); }, onError: (message: string) => { @@ -103,11 +134,10 @@ export default function ConfigSocialLinks() { // show loading // close modal when api is done const handleModalOk = () => { - setModalProcessing(true); - - const postData = [ + setModalProcessing(true); + const postData = currentSocialHandles.length ? [ ...currentSocialHandles, - ]; + ]: []; if (editId === -1) { postData.push(modalDataState); } else { @@ -116,15 +146,14 @@ export default function ConfigSocialLinks() { postUpdateToAPI(postData); }; - const handleDeleteVariant = index => { + const handleDeleteItem = index => { const postData = [ ...currentSocialHandles, ]; postData.splice(index, 1); - postUpdateToAPI(postData) + postUpdateToAPI(postData); }; - const socialHandlesColumns: ColumnsType = [ { title: "#", @@ -136,7 +165,7 @@ export default function ConfigSocialLinks() { dataIndex: "platform", key: "platform", render: (platform: string) => { - const platformInfo = availableIconsList[platform]; + const platformInfo = availableIconsList.find(item => item.key === platform); if (!platformInfo) { return platform; } @@ -153,7 +182,7 @@ export default function ConfigSocialLinks() { }, { - title: "Url to profile", + title: "Url Link", dataIndex: "url", key: "url", }, @@ -166,7 +195,7 @@ export default function ConfigSocialLinks() { - ); } diff --git a/web/styles/globals.scss b/web/styles/globals.scss index 1681c71b3..bc7931a20 100644 --- a/web/styles/globals.scss +++ b/web/styles/globals.scss @@ -97,7 +97,12 @@ code { font-weight: bold; font-size: 1.5em; } - +.ant-modal-body { + background-color: #33333c; +} +.ant-modal-footer { + background-color: #222229; +} .ant-select-dropdown {