react-bootstrap#Dropdown TypeScript Examples

The following examples show how to use react-bootstrap#Dropdown. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Searchbar.tsx    From devex with GNU General Public License v3.0 5 votes vote down vote up
Searchbar: React.FC<IProps> = ({ isHeaderSearchbar, isISSearchbar }) => {

  const history = useHistory()
  const location = useLocation()
  const [input, setInput] = useState("")
  const [searchType, setSearchType] = useState('Txn/Addr')

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => (
    setInput(e.target.value)
  )

  const handleSubmit = (e: React.SyntheticEvent) => {
    e.preventDefault()
    const trimmedInput = input.trim()
    switch (searchType) {
      case 'Txn/Addr':
        if (isValidAddr(trimmedInput))
          history.push({
            pathname: `/address/${trimmedInput}`,
            search: location.search
          })
        else
          history.push({
            pathname: `/tx/${trimmedInput}`,
            search: location.search
          })
        break
      case 'Tx Block':
        history.push({
          pathname: `/txbk/${trimmedInput}`,
          search: location.search
        })
        break
      case 'DS Block':
        history.push({
          pathname: `/dsbk/${trimmedInput}`,
          search: location.search
        })
        break
    }
    setInput('')
  }

  return <>
    <Form onSubmit={handleSubmit}>
      <InputGroup className="searchbar-ig" id={isHeaderSearchbar ? "header-searchbar-ig" : "searchbar-ig"}>
        {isISSearchbar
          ? <InputGroup.Prepend>
            <DropdownButton id='searchbar-dropdown' title={searchType}>
              <Dropdown.Item onClick={() => setSearchType('Txn/Addr')}>Txn/Addr</Dropdown.Item>
              <Dropdown.Item onClick={() => setSearchType('Tx Block')}>Tx Block</Dropdown.Item>
            </DropdownButton>
          </InputGroup.Prepend>
          :
          <InputGroup.Prepend>
            <DropdownButton id='searchbar-dropdown' title={searchType}>
              <Dropdown.Item onClick={() => setSearchType('Txn/Addr')}>Txn/Addr</Dropdown.Item>
              <Dropdown.Item onClick={() => setSearchType('Tx Block')}>Tx Block</Dropdown.Item>
              <Dropdown.Item onClick={() => setSearchType('DS Block')}>DS Block</Dropdown.Item>
            </DropdownButton>
          </InputGroup.Prepend>
        }
        <Form.Control type="text" value={input} autoFocus={!isHeaderSearchbar}
          placeholder={
            searchType === 'Txn/Addr'
              ? 'Search for a transaction or an address'
              : searchType === 'Tx Block'
                ? 'Search by Tx Block height'
                : 'Search by DS Block height'}
          onChange={handleChange} />
        <InputGroup.Append>
          <Button type="submit">
            {isHeaderSearchbar ? <FontAwesomeIcon icon={faSearch} /> : <div>Search</div>}
          </Button>
        </InputGroup.Append>
      </InputGroup>
    </Form>
  </>
}
Example #2
Source File: SideNavbar.tsx    From 3Speak-app with GNU General Public License v3.0 4 votes vote down vote up
export function SideNavbar(props: any) {
  const [login, setLogin] = useState('')
  const [myChannelLink, setMyChannelLink] = useState('')

  useEffect(() => {
    const load = async () => {
      const login = localStorage.getItem('SNProfileID')

      if (login) {
        const user = (await AccountService.getAccount(login)) as any

        const ringItem = user.keyring[0]
        setLogin(user.nickname)
        setMyChannelLink(`${ringItem.type}:${ringItem.username}`)
      }
    }

    void load()
  }, [])

  const logOut = async () => {
    //TODO: logout logic

    const profileID = localStorage.getItem('SNProfileID')

    const user = await AccountService.logout(profileID)
    const accountsInit = (await AccountService.getAccounts()) as any
    localStorage.removeItem('SNProfileID')
    console.log(accountsInit)
    if (accountsInit.length > 0) {
      localStorage.setItem('SNProfileID', accountsInit[0]._id)
    }
    window.location.reload()
  }

  return (
    <Navbar bg="white" expand="lg" id="layoutNav" className="bg_white fixed-left">
      <Navbar.Brand>
        <img src={SpeakLogo} />
      </Navbar.Brand>
      <Navbar.Toggle aria-controls="basic-navbar-nav">
        <span className="navbar-toggler-icon"></span>
      </Navbar.Toggle>
      <Navbar.Collapse>
        <Nav className="mr-auto nav_dist">
          {login && (
            <NavDropdown
              id="nav-dropdown"
              title={
                <>
                  <div className="nav_icons">
                    <VscKey size="21px" />
                  </div>
                  <span>@{login}</span>
                </>
              }
            >
              <NavDropdown.Item href="#/accounts">Switch account</NavDropdown.Item>
              <NavDropdown.Item href={`#/user/${myChannelLink}`}>Go to my channel</NavDropdown.Item>
              <NavDropdown.Item href="#/login">Add account</NavDropdown.Item>
              {login && (
                <NavDropdown.Item
                  onClick={() => {
                    logOut()
                  }}
                >
                  Log out
                </NavDropdown.Item>
              )}
            </NavDropdown>
          )}
          {!login && (
            <Nav.Link href="#/login" className="display-mobile">
              <button className="btn btn-dark text-white btn-sm">Add account</button>
            </Nav.Link>
          )}
          <hr />
          <Nav.Link href="#/">
            <div className="nav_icons">
              <img src={iconHome} height="14px" />
            </div>
            Home
          </Nav.Link>
          <Nav.Item></Nav.Item>
          <Nav.Link href="#/trends">
            <div className="nav_icons">
              <img src={iconTrend} height="21px" />
            </div>
            Trending Content
          </Nav.Link>
          <Nav.Link href="#/new">
            <div className="nav_icons">
              <img src={iconNewContent} height="17px" />
            </div>
            New Content
          </Nav.Link>

          <NavDropdown
            id="nav-dropdown"
            title={
              <>
                <div className="nav_icons">
                  <img src={shakeHands} style={{ height: '21px' }} />
                </div>
                Communities
              </>
            }
          >
            <Nav.Link href="#/communities">
              <FaGlobe /> All Communities...
            </Nav.Link>
            <NavDropdown.Item href="#/community/hive:hive-181335">
              <FaUsers /> Threespeak
            </NavDropdown.Item>
            <NavDropdown.Item href="#/community/hive:hive-153014">
              <FaUsers /> Citizen Journalists
            </NavDropdown.Item>

            <NavDropdown.Item href="#/community/hive:hive-112355">
              <FaUsers /> Threeshorts
            </NavDropdown.Item>
            <NavDropdown.Item href="#/community/hive:hive-129768">
              <FaUsers />
              &nbsp;Coronavirus Pandemic
            </NavDropdown.Item>
            <NavDropdown.Item href="#/community/hive:hive-196427">
              <FaUsers /> &nbsp;COVID-19
            </NavDropdown.Item>
          </NavDropdown>
          <Nav.Link href="#/leaderboard">
            <div className="nav_icons">
              <img src={iconLeaderboard} height="12px" />
            </div>
            Leaderboard
          </Nav.Link>
          <Nav.Link href="#/newcomers">
            <div className="nav_icons">
              <img src={iconNewcomer} height="19px" />
            </div>
            First Uploads
          </Nav.Link>
          <Nav.Link href="#/uploader">
            <div className="nav_icons">
              <FaToolbox />
            </div>
            Uploader
          </Nav.Link>
          <Nav.Link href="#/creatorstudio">
            <div className="nav_icons">
              <FaToolbox />
            </div>
            Creator Studio
          </Nav.Link>
          <NavDropdown
            id="nav-dropdown"
            title={
              <>
                <div className="nav_icons">
                  <BsFillGearFill style={{ height: '21px' }} />
                </div>
                Settings
              </>
            }
          >
            <Nav.Link href="#/blocklist">
              <FaGlobe /> Blocklist
            </Nav.Link>
            <Nav.Link href="#/pins">
              <FaGlobe /> Pins
            </Nav.Link>
            <Nav.Link href="#/ipfsconsole">
              <FaGlobe /> Ipfs Console
            </Nav.Link>
          </NavDropdown>
        </Nav>

        <Nav>
          <li className="nav-item">
            <div className="pad_l">
              <h5>3Speak</h5>
            </div>
          </li>
          <li className="nav-item">
            <a className="nav-link" href="https://3speak.co/intl/about_us">
              About us
            </a>
          </li>
          <li className="nav-item">
            <a className="nav-link" href="https://3speak.co/intl/about_us">
              FAQ
            </a>
          </li>

          <li className="nav-item text-center">
            <a
              className=""
              target="_blank"
              href="https://twitter.com/3speakonline?utm_source=3speak.co"
            >
              <FaTwitter size={28} />
            </a>
            <a className="ml-2" target="_blank" href="https://t.me/threespeak?utm_source=3speak.co">
              <FaTelegram size={28} />
            </a>
            <a
              className="ml-2"
              target="_blank"
              href="https://discord.me/3speak?utm_source=3speak.co"
            >
              <i className="fab fa-discord text-muted fa-2x"></i>
              <FaDiscord size={28} />
            </a>
            <a
              className="ml-2"
              target="_blank"
              title="Visit Our Blog"
              href="https://hive.blog/@threespeak"
            >
              <img
                style={{ width: '32px', marginTop: '-15px', color: 'black' }}
                src={iconBlog}
                alt=""
              />
            </a>
          </li>

          <Dropdown title="Find us" className="nav-item dropdown mt-2 display-mobile">
            <Dropdown.Toggle
              className="btn btn-secondary btn-sm dropdown-toggle"
              variant="secondary"
              data-toggle="dropdown"
              aria-haspopup="true"
            >
              Find us
            </Dropdown.Toggle>
            <Dropdown.Menu>
              <a className="dropdown-item" href="https://t.me/threespeak?utm_source=3speak.co">
                Telegram
              </a>
              <a className="dropdown-item" href="https://discord.me/3speak?utm_source=3speak.co">
                Discord
              </a>
              <a
                className="dropdown-item"
                target="_blank"
                href="https://twitter.com/3speakonline?utm_source=3speak.co"
              >
                Twitter
              </a>
            </Dropdown.Menu>
          </Dropdown>
        </Nav>
      </Navbar.Collapse>
    </Navbar>
  )
}
Example #3
Source File: TopNavbar.tsx    From 3Speak-app with GNU General Public License v3.0 4 votes vote down vote up
export function TopNavbar() {
  const [inEdit, setInEdit] = useState(false)
  const urlForm = useRef<any>()
  const [urlSplit, setUrlSplit] = useState([])

  const startEdit = () => {
    setInEdit(true)
  }

  useEffect(() => {
    if (inEdit) {
      urlForm.current?.focus()
    }
  }, [inEdit])

  const exitEdit = () => {
    setInEdit(false)
  }

  const finishEdit = (e) => {
    if (e.keyCode === 13) {
      if (location.hash !== `#${e.target.value}`) {
        location.replace(`#${e.target.value}`)
        location.reload()
      }
      setInEdit(false)
    } else if (e.keyCode === 27) {
      exitEdit()
    }
  }

  const updateUrlSplit = () => {
    const hash = window.location.hash
    const theUrlSplit = hash.split('/')
    theUrlSplit.splice(0, 1)

    if (theUrlSplit[0] === 'watch') {
      const pagePerm = theUrlSplit[1]
      const pagePermSpliced = pagePerm.split(':')
      pagePermSpliced.splice(0, 1)
      theUrlSplit.pop()
      pagePermSpliced.forEach((onePagePerm) => {
        theUrlSplit.push(onePagePerm)
      })

      setUrlSplit(theUrlSplit)
    } else {
      setUrlSplit(theUrlSplit)
    }
  }

  useEffect(() => {
    updateUrlSplit()
  }, [])

  useEffect(() => {
    window.addEventListener('hashchange', function (event) {
      updateUrlSplit()
    })
  }, [])

  const userProfileUrl = useMemo(() => {
    const windowLocationHash = window.location.hash
    const windowLocationSearch = windowLocationHash.search('#')
    const windowLocationHref = windowLocationHash.slice(windowLocationSearch)
    const hrefSegments = windowLocationHref.split('/')
    hrefSegments.splice(0, 1)

    let userProfileUrl = '#/user/'

    if (hrefSegments[0] === 'watch') {
      const userProfileUrlInit = hrefSegments[1]
      const userProfileUrlSpliced = userProfileUrlInit.split(':')
      userProfileUrlSpliced.pop()

      userProfileUrlSpliced.forEach((one) => {
        if (one === userProfileUrlSpliced[0]) {
          userProfileUrl = userProfileUrl + one + ':'
        } else {
          userProfileUrl = userProfileUrl + one
        }
      })
    }

    return userProfileUrl
  }, [])

  return (
    <div>
      <Navbar bg="light" expand="lg">
        <Navbar.Collapse id="basic-navbar-nav">
          <Nav className="mr-auto">
            {!inEdit ? (
              <>
                <Breadcrumb>
                  <Breadcrumb.Item href="#/">Home</Breadcrumb.Item>
                  {urlSplit.map((el) =>
                    el === updateUrlSplit[1] && updateUrlSplit[0] === 'watch' ? (
                      <Breadcrumb.Item href={userProfileUrl} key={el} id={el}>
                        {el}
                      </Breadcrumb.Item>
                    ) : (
                      <Breadcrumb.Item href={'#'} key={el} id={el}>
                        {el}
                      </Breadcrumb.Item>
                    ),
                  )}
                </Breadcrumb>
                <Button
                  className="btn btn-light btn-sm"
                  style={{
                    marginLeft: '5px',
                    width: '40px',
                    height: '40px',
                    padding: '3.5%',
                    verticalAlign: 'baseline',
                  }}
                  onClick={startEdit}
                >
                  <FaEdit style={{ textAlign: 'center', verticalAlign: 'initial' }} />
                </Button>
              </>
            ) : (
              <FormControl
                ref={urlForm}
                defaultValue={(() => {
                  return location.hash.slice(1)
                })()}
                onKeyDown={finishEdit}
                onBlur={exitEdit}
              />
            )}
          </Nav>
          <Dropdown>
            <Dropdown.Toggle variant="secondary" size="lg">
              Options
            </Dropdown.Toggle>

            <Dropdown.Menu>
              <Dropdown.Item onClick={() => copyToClip(window.location.hash)}>
                Copy Current URL{' '}
                <FaCopy size={28} onClick={() => copyToClip(window.location.hash)} />
              </Dropdown.Item>
              <Dropdown.Item onClick={goToClip}>
                Go to Copied URL <FaArrowRight size={28} />
              </Dropdown.Item>
            </Dropdown.Menu>
          </Dropdown>
          <Nav>
            <Nav.Link>
              <FaAngleLeft size={28} onClick={goBack} />
            </Nav.Link>
            <Nav.Link>
              <FaAngleRight size={28} onClick={goForth} />
            </Nav.Link>
          </Nav>
        </Navbar.Collapse>
      </Navbar>
    </div>
  )
}
Example #4
Source File: PostComment.tsx    From 3Speak-app with GNU General Public License v3.0 4 votes vote down vote up
/**
 * @todo Implement displaying numbers of comments and comment value. Requires support on backend to count votes.
 * @todo Implement interactibility.
 * @todo Implement 3 dot action menu.
 */
export function PostComment(props: any) {
  const [commentInfo, setCommentInfo] = useState({ description: '', creation: 0 })

  const [replying, setReplying] = useState(false)
  const [profilePicture, setProfilePicture] = useState('')

  useEffect(() => {
    const load = async () => {
      let info
      let profilePicture
      if (props.commentInfo) {
        info = props.commentInfo
      } else {
        info = await AccountService.permalinkToVideoInfo(props.reflink)
      }
      if (info) {
        profilePicture = setProfilePicture(await AccountService.getProfilePictureURL(info.reflink))
        setCommentInfo(info)
      }
    }

    void load()
  }, [])

  const handleAction = async (eventKey) => {
    const reflink = props.reflink
    switch (eventKey) {
      case 'block_post': {
        await electronIpc.send('blocklist.add', reflink, {
          reason: 'manual block',
        })
        break
      }
      case 'block_user': {
        const ref = RefLink.parse(reflink) as any as any
        await electronIpc.send('blocklist.add', `${ref.source.value}:${ref.root}`, {
          reason: 'manual block',
        })
        break
      }
      case 'copy_reflink': {
        clipboard.writeText(reflink, clipboard as any)
        break
      }
      default: {
        throw new Error(`Unrecognized action: ${eventKey}!`)
      }
    }
  }

  const postTimeDistance = useMemo(() => {
    return millisecondsAsString((new Date() as any) - (new Date(commentInfo.creation) as any))
  }, [commentInfo])

  const postTime = useMemo(() => {
    return DateAndTime.format(new Date(commentInfo.creation), 'YYYY/MM/DD HH:mm:ss')
  }, [commentInfo])

  return (
    <div>
      <div className="col">
        <div className="thumbnail mr-2 float-left">
          <img className="img-responsive user-photo" width="24" src={profilePicture} />
        </div>
      </div>
      <div className="col" style={{ zIndex: 1000 }}>
        <div className="mr-3 float-right">
          <Dropdown onSelect={handleAction}>
            <Dropdown.Toggle as={CustomToggle}></Dropdown.Toggle>
            <Dropdown.Menu>
              <Dropdown.Item style={{ color: 'red' }} eventKey="block_post">
                Block post
              </Dropdown.Item>
              <Dropdown.Item style={{ color: 'red' }} eventKey="block_user">
                Block user
              </Dropdown.Item>
              <Dropdown.Item eventKey="copy_reflink">Copy to clipboard permlink</Dropdown.Item>
            </Dropdown.Menu>
          </Dropdown>
        </div>
      </div>
      <div className="col-12">
        <div className="panel ml-2 panel-default">
          <div className="panel-heading ml-4">
            <strong>
              <a href={`#/user?=${props.reflink}`}>{RefLink.parse(props.reflink).root}</a>{' '}
            </strong>
            •{' '}
            <span className="text-muted">
              <OverlayTrigger overlay={<Tooltip id="post-time">{postTime}</Tooltip>}>
                <div>{postTimeDistance}</div>
              </OverlayTrigger>
            </span>
          </div>
          <div className="panel-body mt-1">
            <ReactMarkdown
              escapeHtml={false}
              source={DOMPurify.sanitize(commentInfo.description)}
            ></ReactMarkdown>
          </div>
          <div className="panel-footer ml-0 ml-md-4">
            <hr />
            <ul className="list-inline list-inline-separate">
              <li className="list-inline-item">
                <VoteWidget reflink={props.reflink} />
              </li>
              <li
                className="list-inline-item"
                style={{ cursor: 'pointer' }}
                onClick={() => {
                  setReplying(!replying)
                }}
              >
                {replying ? 'Cancel' : 'Reply'}
              </li>
            </ul>
          </div>
        </div>
      </div>
      {replying ? (
        <div className="box mb-3 clearfix">
          <CommentForm parent_reflink={props.reflink} onCommentPost={props.onCommentPost} />
        </div>
      ) : null}
    </div>
  )
}
Example #5
Source File: LoginView.tsx    From 3Speak-app with GNU General Public License v3.0 4 votes vote down vote up
export function LoginView() {
  const submitRef = useRef<any>()
  const [username, setUsername] = useState('')
  const [key, setKey] = useState('')
  const [profile, setProfile] = useState('')
  const [encryption, setEncryption] = useState(false)
  const [symKey, setSymKey] = useState('')
  const [accountType, setAccountType] = useState('hive')
  const [submitting, setSubmitting] = useState(false)

  const resetForm = () => {
    console.log(`resetting form`)
    setUsername('')
    setKey('')
  }

  const onUsernameChange = useCallback(async (event) => {
    console.log(`username change ${event.target.value}`)
    setUsername(event.target.value)
  }, [])

  const onKeyChange = useCallback(async (event) => {
    setKey(event.target.value)
  }, [])

  const onProfileChange = useCallback(async (event) => {
    setProfile(event.target.value)
  }, [])

  const onEncryptionChange = useCallback(async (event) => {
    setEncryption(event.target.checked)
  }, [])

  const onSymKeyChange = useCallback(async (event) => {
    setSymKey(event.target.value)
  }, [])

  const handleSubmit = useCallback(
    async (event) => {
      event.preventDefault()

      const login = {
        username: username,
        key: key,
        profile: profile,
        accountType: accountType,
        symKey: symKey,
        isEncrypted: encryption,
      }

      setSubmitting(true)

      submitRef?.current?.setAttribute('disabled', 'disabled')

      try {
        const loginHandler = (await AccountService.login(login)) as any
        console.log(loginHandler)

        if (loginHandler.nickname === login.username) {
          window.location.reload()
        } else {
          console.log({ loginHandler, response: 'unsucessful' })
        }
      } catch (ex) {
        NotificationManager.error(ex.toString())
        throw ex
      }

      setSubmitting(false)
      resetForm()
      submitRef.current.removeAttribute('disabled')
    },
    [username, key, profile, accountType, symKey, encryption],
  )

  return (
    <>
      <Form
        id="contact-form"
        onSubmit={(event) => {
          void handleSubmit(event)
        }}
        style={{
          maxWidth: '600px',
          width: '100%',
          padding: '20px',
          alignItems: 'center',
        }}
      >
        <div className="p-3" style={{ width: '100%' }}>
          <Form.Label className="text-secondary">Account type</Form.Label>
          <Dropdown className="mb-2">
            <Dropdown.Toggle variant="secondary">{accountType}</Dropdown.Toggle>

            <Dropdown.Menu>
              <Dropdown.Item
                onClick={() => {
                  setAccountType('hive')
                }}
              >
                Hive
              </Dropdown.Item>
              <Dropdown.Item
                onClick={() => {
                  setAccountType('IDX')
                }}
              >
                IDX
              </Dropdown.Item>
              <Dropdown.Item
                onClick={() => {
                  setAccountType('other')
                }}
              >
                Other
              </Dropdown.Item>
            </Dropdown.Menu>
          </Dropdown>
          {accountType !== 'hive' ? (
            <OverlayTrigger
              placement={'top'}
              overlay={<Tooltip id="coming-soon">Disabled (Coming Soon!)</Tooltip>}
            >
              <div>
                <Form.Group>
                  <Form.Label className="text-secondary">Profile name</Form.Label>
                  <Form.Control
                    type="text"
                    value={profile}
                    onChange={onProfileChange}
                    className="bg-secondary text-light"
                    disabled
                    required
                  />
                </Form.Group>
                <Form.Group>
                  <Form.Label className="text-secondary">Username</Form.Label>
                  <Form.Control
                    type="text"
                    value={username}
                    onChange={onUsernameChange}
                    className="bg-secondary text-light"
                    disabled
                    required
                  />
                </Form.Group>
              </div>
            </OverlayTrigger>
          ) : (
            <>
              <Form.Group>
                <Form.Label className="text-secondary">Profile name</Form.Label>
                <Form.Control
                  type="text"
                  value={profile}
                  onChange={onProfileChange}
                  className="bg-secondary text-light"
                  required
                />
              </Form.Group>
              <Form.Group>
                <Form.Label className="text-secondary">Username</Form.Label>
                <Form.Control
                  type="text"
                  value={username}
                  onChange={onUsernameChange}
                  className="bg-secondary text-light"
                  required
                />
              </Form.Group>
            </>
          )}

          {accountType === 'hive' && (
            <Form.Group>
              <Form.Label className="text-secondary">Hive Private Posting Key</Form.Label>
              <Form.Control
                type="password"
                value={key}
                onChange={onKeyChange}
                className="bg-secondary text-light"
                pattern="5[HJK][1-9A-HJ-NP-Za-km-z]{49}"
                required
              />
            </Form.Group>
          )}
          <OverlayTrigger
            placement={'top'}
            overlay={<Tooltip id="coming-soon">Disabled (Coming Soon!)</Tooltip>}
          >
            <div>
              <label className="text-secondary mr-2" htmlFor="enable-encryption">
                Enable Encryption
              </label>
              <input
                name="enable-encryption"
                type="checkbox"
                checked={encryption}
                disabled
                onChange={onEncryptionChange}
              />
            </div>
          </OverlayTrigger>

          {encryption && (
            <Form.Group>
              <Form.Label className="text-secondary">Symmetric Key</Form.Label>
              <Form.Control
                type="text"
                value={symKey}
                onChange={onSymKeyChange}
                className="bg-secondary text-light"
              />
            </Form.Group>
          )}
          <br />
          <span className="tag-wrap">
            <Button type="submit" ref={submitRef} variant="secondary">
              {submitting ? <FontAwesomeIcon icon={faSpinner as any} spin /> : 'Submit'}
            </Button>
          </span>
        </div>
      </Form>
    </>
  )
}
Example #6
Source File: PinsView.tsx    From 3Speak-app with GNU General Public License v3.0 4 votes vote down vote up
export function PinsView() {
  const [pinList, setPinList] = useState([])
  const [newVideos, setNewVideos] = useState([])
  const [trendingVideos, setTrendingVideos] = useState([])
  const [showExplorer, setShowExplorer] = useState(false)

  const pid = useRef<any>()

  const updateSearchTables = (community = null, creator = null) => {
    const ids = pinList.map((x) => {
      return x._id
    })
    console.log(ids)
    const params = '?limit=10&ipfsOnly=true'
    let newUrl = `https://3speak.tv/apiv2/feeds/new${params}`
    let trendingUrl = `https://3speak.tv/apiv2/feeds/trending${params}`
    if (community) {
      newUrl = `https://3speak.tv/apiv2/feeds/community/${community}/new${params}`
      trendingUrl = `https://3speak.tv/apiv2/feeds/community/${community}/trending${params}`
    } else if (creator && creator.length > 2) {
      newUrl = `https://3speak.tv/apiv2/feeds/@${creator}`
      trendingUrl = null
    }

    fetch(newUrl)
      .then((r) => r.json())
      .then((r) => {
        for (const video of r) {
          const id = `hive:${video.author}:${video.permlink}`
          video.isPinned = ids.includes(id)
          video.id = id
        }
        console.log(r)
        setNewVideos(r)
      })

    if (!trendingUrl) {
      setTrendingVideos([])
    } else {
      fetch(trendingUrl)
        .then((r) => r.json())
        .then((r) => {
          for (const video of r) {
            const id = `hive:${video.author}:${video.permlink}`
            video.isPinned = ids.includes(id)
            video.id = id
          }
          setTrendingVideos(r)
        })
    }
  }

  const generate = async () => {
    // type error - 2 arguments expected
    setPinList(await PromiseIpc.send('pins.ls', undefined as any))
  }

  const PinLocally = async (cids, title, _id) => {
    debug(`CIDs to store ${JSON.stringify(cids)}`)
    if (cids.length !== 0) {
      NotificationManager.info('Pinning in progress')

      await PromiseIpc.send('pins.add', {
        _id,
        source: 'Pins page',
        cids,
        expire: null,
        meta: {
          title,
        },
      } as any)

      NotificationManager.success(
        `Video with title of ${title} has been successfully pinned! Thank you for contributing!`,
        'Pin Successful',
      )
    } else {
      NotificationManager.warning('This video is not available on IPFS')
    }
    await generate()
  }

  const actionSelect = async (key) => {
    console.log(key)
    switch (key) {
      case '1': {
        const func = () =>
          new Promise(async (resolve, reject) => {
            const ref = React.createRef() as any
            Popup.create({
              content: (
                <div>
                  <Form ref={ref}>
                    <Form.Label>Reflink</Form.Label>
                    <FormControl
                      name="reflink"
                      placeholder="hive:username:123permlink"
                    ></FormControl>
                  </Form>
                </div>
              ),
              buttons: {
                left: [
                  {
                    text: 'Cancel',
                    className: 'secondary',
                    action: function () {
                      Popup.close()
                    },
                  },
                ],
                right: [
                  {
                    text: 'Done',
                    className: 'success',
                    action: function () {
                      resolve(FormUtils.formToObj(new FormData(ref.current)))
                      Popup.close()
                    },
                  },
                ],
              },
            })
          })
        const ret = (await func()) as any
        const video_info = await AccountService.permalinkToVideoInfo(ret.reflink)
        const cids = []
        for (const source of video_info.sources) {
          const url = new (require('url').URL)(source.url)
          try {
            new CID(url.host)
            cids.push(url.host)
          } catch (ex) {
            console.error(ex)
          }
        }
        if (cids.length !== 0) {
          NotificationManager.info('Pinning in progress')
          await PromiseIpc.send('pins.add', {
            _id: ret.reflink,
            source: 'Manual Add',
            cids,
            expire: null,
            meta: {
              title: video_info.title,
            },
          } as any)
          NotificationManager.success(
            `Video with reflink of ${ret.reflink} has been successfully pinned! Thank you for contributing!`,
            'Pin Successful',
          )
        } else {
          NotificationManager.warning('This video is not available on IPFS')
        }
        break
      }
      case '2': {
        NotificationManager.info('GC has started')
        const { ipfs } = await IpfsHandler.getIpfs()
        ipfs.repo.gc()
        break
      }
      default: {
      }
    }
  }

  const removePin = async (reflink) => {
    try {
      await PromiseIpc.send('pins.rm', reflink)
      NotificationManager.success('IPFS pin removal complete')
      await generate()
    } catch (ex) {
      NotificationManager.error('IPFS pin removal resulted in error')
      console.error(ex)
    }
  }

  useEffect(() => {
    document.title = '3Speak - Tokenised video communities'
    void generate()
    pid.current = setInterval(generate, 1500)
    updateSearchTables()

    return () => {
      clearInterval(pid.current)
    }
  }, [])

  const pinRows = useMemo(() => {
    const rows = []
    for (const pin of pinList) {
      const sizeBest = bytesAsString(pin.size)

      rows.push(
        <tr key={pin._id}>
          <td>
            <a href={`#/watch/${pin._id}`}>{pin._id}</a>
            <br />(<strong>{RefLink.parse(pin._id).root}</strong>)
          </td>
          <td>
            <a href={`#/watch/${pin._id}`}>{pin.meta ? pin.meta.title : null} </a>
          </td>
          <td>
            {pin.cids.length > 1 ? (
              <a
                onClick={() => {
                  Popup.create({
                    title: 'CIDs',
                    content: (
                      <div>
                        <Editor value={pin.cids} ace={ace} theme="ace/theme/github"></Editor>
                      </div>
                    ),
                    buttons: {
                      left: [],
                      right: [
                        {
                          text: 'close',
                          key: '⌘+s',
                          className: 'success',
                          action: function () {
                            Popup.close()
                          },
                        },
                      ],
                    },
                  })
                }}
              >
                View ({pin.cids.length})
              </a>
            ) : (
              pin.cids
            )}
          </td>
          <td>{pin.source}</td>
          <td>
            {pin.expire
              ? (() => {
                  console.log(pin.expire)
                  return 'In ' + millisecondsAsString((pin.expire = new Date().getTime()))
                })()
              : 'Permanent'}
          </td>
          <td>
            {pin.meta.pin_date
              ? (() => {
                  console.log(pin.meta.pin_date)
                  return new Date(pin.meta.pin_date).toLocaleString()
                })()
              : null}
          </td>
          <td>{pin.size === 0 ? <strong>Pinning In Progress</strong> : sizeBest}</td>
          <td>
            <Button variant="danger" onClick={() => removePin(pin._id)}>
              X
            </Button>
          </td>
        </tr>,
      )
    }
    return rows
  }, [pinList])

  return (
    <div>
      <Row>
        <Col style={{ textAlign: 'right' }}>
          <Dropdown onSelect={actionSelect}>
            <Dropdown.Toggle as={CustomPinsViewToggle} id="dropdown-custom-components">
              <Button>Actions</Button>
            </Dropdown.Toggle>

            <Dropdown.Menu as={CustomPinsViewMenu}>
              <Dropdown.Item eventKey="1">Manual Pin</Dropdown.Item>
              <Dropdown.Item eventKey="2">Manual GC</Dropdown.Item>
            </Dropdown.Menu>
          </Dropdown>
        </Col>
      </Row>
      <Table striped bordered hover size="sm">
        <thead>
          <tr>
            <th>Reflink</th>
            <th>Title</th>
            <th>CID(s)</th>
            <th>Source</th>
            <th>Expiration</th>
            <th>Pin Date</th>
            <th>Size/Status</th>
            <th>Remove?</th>
          </tr>
        </thead>
        <tbody>{pinRows}</tbody>
      </Table>
      <Button
        onClick={() => {
          setShowExplorer(!showExplorer)
        }}
      >
        Toggle pin explorer
      </Button>
      {showExplorer && (
        <>
          <h6>Select to pin and help secure the network by backing up videos</h6>
          <input
            type="text"
            placeholder="Enter community ID..."
            onChange={(event) => {
              if (event.target.value.match(/\bhive-\d{6}\b/g)) {
                updateSearchTables(event.target.value, null)
              }
            }}
          />
          <input
            type="text"
            placeholder="Enter a username"
            onChange={(event) => {
              updateSearchTables(null, event.target.value)
            }}
          />
          <Row>
            {['new', 'trending'].map((type: 'new' | 'trending') => (
              <Col key={type}>
                <Table striped bordered hover size="sm">
                  <thead>
                    <tr>
                      <th>{type} videos</th>
                      <th>Title</th>
                      <th>Creator</th>
                      <th>pinned</th>
                    </tr>
                  </thead>
                  <tbody>
                    {/* {this.state[`${type}Videos`].map((video) => ( */}
                    {(type === 'new' ? newVideos : trendingVideos).map((video) => (
                      <tr key={`${type}-${video.author}-${video.permlink}`}>
                        <td>
                          <div className="teaser_holder video-card-image">
                            <div className="card-label">
                              {(() => {
                                const pattern = DateTime.compile('mm:ss')
                                return DateTime.format(new Date(video.duration * 1000), pattern)
                              })()}
                            </div>
                            <a href={`#/watch/hive:${video.author}:${video.permlink}`}>
                              <img
                                className="img-fluid bg-dark"
                                src={video.images.thumbnail}
                                alt=""
                              />
                            </a>
                          </div>
                        </td>
                        <td>{video.title}</td>
                        <td>{video.author}</td>
                        <td>
                          {video.isPinned ? (
                            <Button
                              variant="danger"
                              onClick={async () => {
                                await removePin(video.id)
                                updateSearchTables()
                              }}
                            >
                              X
                            </Button>
                          ) : (
                            <Button
                              variant="success"
                              onClick={async () => {
                                await PinLocally([video.ipfs], video.title, video.id)
                                updateSearchTables()
                              }}
                            >
                              O
                            </Button>
                          )}
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </Table>
              </Col>
            ))}
          </Row>
        </>
      )}
    </div>
  )
}
Example #7
Source File: WatchView.tsx    From 3Speak-app with GNU General Public License v3.0 4 votes vote down vote up
export function WatchView(props: any) {
  const player = useRef<any>()
  const [videoInfo, setVideoInfo] = useState<any>({})
  const [postInfo, setPostInfo] = useState<any>({})
  const [profilePictureURL, setProfilePictureUrl] = useState(EmptyProfile)
  const [commentGraph, setCommentGraph] = useState()
  const [videoLink, setVideoLink] = useState('')
  const [recommendedVideos, setRecommendedVideos] = useState([])
  const [loaded, setLoaded] = useState(false)
  const [loadingMessage, setLoadingMessage] = useState('')
  const [rootCid, setRootCid] = useState()

  const reflink = useMemo(() => {
    return props.match.params.reflink
  }, [])

  const reflinkParsed = useMemo(() => {
    return RefLink.parse(reflink) as any
  }, [reflink])

  const generalFetch = async () => {
    const info = await AccountService.permalinkToVideoInfo(reflink, { type: 'video' })
    setVideoInfo(info)
    setPostInfo(await AccountService.permalinkToPostInfo(reflink))
    try {
      //Leave profileURL default if error is thrown when attempting to retrieve profile picture
      setProfilePictureUrl(await AccountService.getProfilePictureURL(reflink))
    } catch (ex) {
      console.error(ex)
      throw ex
    }
    document.title = `3Speak - ${info.title}`
    const cids = []
    for (const source of info.sources) {
      const url = new URL(source.url)
      try {
        new CID(url.host)
        cids.push(url.host)
      } catch {}
    }
    setRootCid(cids[0])
  }

  const mountPlayer = async () => {
    try {
      const playerType = 'standard'
      switch (playerType) {
        case 'standard': {
          setVideoLink(await VideoService.getVideoSourceURL(reflink))
        }
      }
      recordView()
    } catch (ex) {
      console.error(ex)
    }
  }

  const recordView = async () => {
    return
    /*let cids = [];
        for(const source of videoInfo.sources) {
            const url = new (require('url').URL)(source.url)
            try {
                new CID(url.host)
                cids.push(url.host)
            } catch  {

            }
        }
        console.log(`CIDs to cache ${JSON.stringify(cids)}`)

        if(cids.length !== 0) {
            await PromiseIpc.send("pins.add", {
                _id: reflink,
                source: "Watch Page",
                cids,
                expire: (new Date().getTime()) + convert("1").from("d").to("ms"),
                meta: {
                    title: videoInfo.title
                }
            })
        }*/
  }

  const gearSelect = async (eventKey) => {
    switch (eventKey) {
      case 'mute_post': {
        await PromiseIpc.send('blocklist.add', reflinkParsed.toString())
        break
      }
      case 'mute_user': {
        await PromiseIpc.send(
          'blocklist.add',
          `${reflinkParsed.source.value}:${reflinkParsed.root}` as any,
        )
        break
      }
    }
  }

  const retrieveRecommended = async () => {
    const query = knex.raw(
      `SELECT TOP 25 x.* FROM DBHive.dbo.Comments x WHERE CONTAINS(json_metadata , '3speak/video') AND category LIKE '${postInfo.category}' ORDER BY NEWID()`,
    )
    const blob = []
    query.stream().on('data', async (val) => {
      if (await PromiseIpc.send('blocklist.has', `hive:${val.author}:${val.permlink}` as any)) {
        console.log(`${val.author} is blocked`)
        return
      }
      val.json_metadata = JSON.parse(val.json_metadata)
      //console.log(val)
      if (!val.json_metadata.video) {
        val.json_metadata.video = {
          info: {},
        }
      }
      let thumbnail
      if (val.json_metadata.sourceMap) {
        thumbnail = Finder.one.in(val.json_metadata.sourceMap).with({ type: 'thumbnail' }).url
        console.log(thumbnail)
      }
      blob.push({
        reflink: `hive:${val.author}:${val.permlink}`,
        created: val.created,
        author: val.author,
        permlink: val.permlink,
        tags: val.json_metadata.tags,
        title: val.title,
        duration: val.json_metadata.video.info.duration || val.json_metadata.video.duration,
        isIpfs: val.json_metadata.video.info.ipfs || thumbnail ? true : false,
        ipfs: val.json_metadata.video.info.ipfs,
        images: {
          ipfs_thumbnail: thumbnail
            ? `/ipfs/${thumbnail.slice(7)}`
            : `/ipfs/${val.json_metadata.video.info.ipfsThumbnail}`,
          thumbnail: `https://threespeakvideo.b-cdn.net/${val.permlink}/thumbnails/default.png`,
          poster: `https://threespeakvideo.b-cdn.net/${val.permlink}/poster.png`,
          post: `https://threespeakvideo.b-cdn.net/${val.permlink}/post.png`,
        },
        views: val.total_vote_weight ? Math.log(val.total_vote_weight / 1000).toFixed(2) : 0,
      })

      setRecommendedVideos(blob)
    })
    query.on('query-response', (ret, det, aet) => {
      console.log(ret, det, aet)
    })
    query.on('end', (err) => {
      console.log(err)
    })
    /*
        let ref = RefLink.parse(reflink)
        let data = (await axios.get(`https://3speak.tv/apiv2/recommended?v=${ref.root}/${ref.permlink}`)).data
        data.forEach((value => {
            let link = value.link.split("=")[1].split("/")
            value.reflink = `hive:${link[0]}:${link[1]}`
        }))*/
  }

  const PinLocally = async () => {
    const cids = []
    for (const source of videoInfo.sources) {
      const url = new URL(source.url)
      try {
        new CID(url.host)
        cids.push(url.host)
      } catch {}
    }

    debug(`CIDs to store ${JSON.stringify(cids)}`)
    if (cids.length !== 0) {
      NotificationManager.info('Pinning in progress')
      await PromiseIpc.send('pins.add', {
        _id: reflink,
        source: 'Watch Page',
        cids,
        expire: null,
        meta: {
          title: videoInfo.title,
        },
      } as any)
      NotificationManager.success(
        `Video with reflink of ${reflink} has been successfully pinned! Thank you for contributing!`,
        'Pin Successful',
      )
    } else {
      NotificationManager.warning('This video is not available on IPFS')
    }
  }
  const showDebug = () => {
    const metadata = videoInfo
    Popup.registerPlugin('watch_debug', async function () {
      this.create({
        content: (
          <div>
            <Tabs defaultActiveKey="meta" id="uncontrolled-tab-example">
              <Tab eventKey="meta" title="Metadata">
                <Editor value={metadata} ace={ace} theme="ace/theme/github"></Editor>
              </Tab>
            </Tabs>
          </div>
        ),
        buttons: {
          right: [
            {
              text: 'Close',
              className: 'success',
              action: function () {
                Popup.close()
              },
            },
          ],
        },
      })
    })
    Popup.plugins().watch_debug()
  }

  useEffect(() => {
    const load = async () => {
      try {
        await generalFetch()
        setLoadingMessage('Loading: Mounting player...')
        await mountPlayer()
      } catch (ex) {
        setLoadingMessage('Loading resulted in error')
        throw ex
      }
      setLoaded(true)
      await retrieveRecommended()
    }

    void load()
  }, [])

  useEffect(() => {
    window.scrollTo(0, 0)

    const update = async () => {
      await generalFetch()
      await mountPlayer()
      await retrieveRecommended()
      player.current?.ExecUpdate()
    }

    void update()
  }, [reflink])

  return (
    <div>
      {loaded ? (
        <Container fluid>
          {/* <Container fluid pb={0}> */}
          {/* <Row fluid="md"> */}
          <Row>
            <Col md={8}>
              <div>
                <Player reflink={reflink}></Player>
              </div>
              <div className="single-video-title box mb-3 clearfix">
                <div className="float-left">
                  <h2 style={{ fontSize: '18px' }}>
                    <a>{videoInfo.title}</a>
                  </h2>
                  <DHTProviders rootCid={rootCid} />
                </div>
                <div
                  className="float-right"
                  style={
                    {
                      textAlign: 'right !important',
                      float: 'right !important',
                      display: 'inline-block !important',
                    } as any
                  }
                >
                  <span>
                    <VoteWidget reflink={reflink} />
                  </span>
                  <Dropdown onSelect={gearSelect} style={{ paddingTop: '10px' }}>
                    <Dropdown.Toggle
                      as={CustomToggle}
                      id="dropdown-custom-components"
                    ></Dropdown.Toggle>
                    <Dropdown.Menu>
                      <Dropdown.Item eventKey="mute_post">
                        <p style={{ color: 'red' }}>Mute Post</p>
                      </Dropdown.Item>
                      <Dropdown.Item eventKey="mute_user">
                        <p style={{ color: 'red' }}>Mute User</p>
                      </Dropdown.Item>
                    </Dropdown.Menu>
                  </Dropdown>
                </div>
              </div>
              <div className="single-video-author box mb-3">
                <div className="float-right">
                  <Row>
                    <FollowWidget reflink={reflink} />
                    <a
                      target="_blank"
                      style={{ marginRight: '5px', marginLeft: '5px' }}
                      className="btn btn-light btn-sm"
                      onClick={PinLocally}
                    >
                      <FaDownload /> Download to IPFS node
                    </a>
                    <a
                      target="_blank"
                      style={{ marginRight: '5px' }}
                      className="btn btn-light btn-sm"
                      href={(() => {
                        const videoSource = Finder.one.in(videoInfo.sources).with({
                          format: 'mp4',
                        })
                        if (videoSource) {
                          return videoSource.url
                        }
                      })()}
                    >
                      <FaDownload /> Download
                    </a>
                  </Row>
                </div>
                <img className="img-fluid" src={profilePictureURL} alt="" />
                <p>
                  <a href={`#/user/${reflinkParsed.source.value}:${reflinkParsed.root}`}>
                    <strong>{postInfo.author}</strong>
                  </a>
                </p>
                <small>
                  Published on{' '}
                  {(() => {
                    const pattern = DateTime.compile('MMMM D, YYYY')
                    return DateTime.format(new Date(videoInfo.creation), pattern)
                  })()}
                </small>
              </div>
              <div className="single-video-info-content box mb-3">
                <h6>About :</h6>
                <CollapsibleText>
                  <ReactMarkdown
                    escapeHtml={false}
                    source={DOMPurify.sanitize(videoInfo.description)}
                  ></ReactMarkdown>
                  <hr />
                  <Container style={{ marginBottom: '10px', textAlign: 'center' }}>
                    <a
                      target="_blank"
                      style={{ marginRight: '5px' }}
                      className="btn btn-light btn-sm"
                      onClick={() => showDebug()}
                    >
                      <BsInfoSquare /> Debug Info
                    </a>
                  </Container>
                </CollapsibleText>
                <h6>Tags: </h6>
                <p className="tags mb-0">
                  {(() => {
                    const out = []
                    if (videoInfo.tags) {
                      for (const tag of videoInfo.tags) {
                        out.push(
                          <span style={{ paddingLeft: '3px' }} key={tag}>
                            <a>{tag}</a>
                          </span>,
                        )
                      }
                    }
                    return out
                  })()}
                </p>
              </div>
              <CommentSection reflink={reflink.toString()} />
            </Col>
            <Col md={4}>
              <Row>
                <Col md={12}>
                  {recommendedVideos.map((value) => (
                    <VideoTeaser key={value.reflink} reflink={value.reflink} />
                  ))}
                </Col>
              </Row>
            </Col>
          </Row>
        </Container>
      ) : (
        <div>
          <LoopCircleLoading />
          <div
            style={{
              textAlign: 'center',
              margin: 'auto',
              position: 'absolute',
              left: '0px',
              right: '0px',
              top: '60%',
              bottom: '0px',
            }}
          >
            <h1 style={{ top: '60%', fontSize: '20px' }}>{loadingMessage}</h1>
          </div>
        </div>
      )}
    </div>
  )
}
Example #8
Source File: LabelsPage.tsx    From devex with GNU General Public License v3.0 4 votes vote down vote up
LabelsPage: React.FC = () => {

  const userPrefContext = useContext(UserPrefContext)
  const { labelMap, networkMap, setLabelMap } = userPrefContext!

  const [searchFilter, setSearchFilter] = useState('')
  const [typefilter, setTypefilter] = useState('All')
  const [networkNameFilter, setNetworkNameFilter] = useState('All')

  return (
    <>
      <Container>
        <Row className='m-0'>
          <h4>
            Labels
          </h4>
        </Row>
        <Row className='m-0 pb-3'>
          <span className='subtext'>Label data is stored in your browser&apos;s local storage.</span>
        </Row>
        <Row className='justify-content-between flex-nowrap m-0'>
          <div className='filter-div'>
            <span>
              Network:
            </span>
            <Dropdown className="ml-3">
              <Dropdown.Toggle id="label-network-toggle">
                {networkMap.get(networkNameFilter) || defaultNetworks.get(networkNameFilter) || networkNameFilter}
              </Dropdown.Toggle>
              <Dropdown.Menu>
                <Dropdown.Item onClick={() => setNetworkNameFilter('All')}>All</Dropdown.Item>
                {
                  [...new Set(Object.values(labelMap).map(label => label.networkName))]
                    .map((labelName, index) => (
                      <Dropdown.Item onClick={() => setNetworkNameFilter(labelName)} key={index}>{labelName}</Dropdown.Item>
                    ))
                }
              </Dropdown.Menu>
            </Dropdown>
            <span className='ml-3'>
              Label Type:
            </span>
            <Dropdown className="ml-3">
              <Dropdown.Toggle id="label-type-toggle">{typefilter}</Dropdown.Toggle>
              <Dropdown.Menu>
                <Dropdown.Item onClick={() => setTypefilter('All')}>All</Dropdown.Item>
                <Dropdown.Item onClick={() => setTypefilter('Account')}>Accounts</Dropdown.Item>
                <Dropdown.Item onClick={() => setTypefilter('Contract')}>Contracts</Dropdown.Item>
                <Dropdown.Item onClick={() => setTypefilter('Transaction')}>Transactions</Dropdown.Item>
                <Dropdown.Item onClick={() => setTypefilter('Tx Block')}>Tx Blocks</Dropdown.Item>
                <Dropdown.Item onClick={() => setTypefilter('DS Block')}>DS Blocks</Dropdown.Item>
              </Dropdown.Menu>
            </Dropdown>
            <Form.Control
              className='search-filter-form'
              type="text"
              value={searchFilter}
              autoFocus
              placeholder='Search for label'
              onChange={(e: React.ChangeEvent<HTMLInputElement>) => { setSearchFilter(e.target.value) }} />
          </div>
          <ImportExport
            type='labels'
            map={labelMap}
            setMapCb={setLabelMap}
            />
        </Row>
        <Row className='mt-3'>
          {Object.entries(labelMap).length === 0
            ? <Dropzone
              fromJson={(x: any) => x}
              dropCb={setLabelMap} />
            : Object.entries(labelMap)
              .filter(([, v]) => (typefilter === 'All' || v.type === typefilter))
              .filter(([, v]) => (networkNameFilter === 'All' || v.networkName === networkNameFilter))
              .filter(([, v]) => (v.name.includes(searchFilter)))
              .map(([k, v]) => (
                <Col className='my-3' key={k} md={6} lg={4} >
                  <LabelCard k={k} v={v} />
                </Col>
              ))}
        </Row>
      </Container>
    </>
  )
}
Example #9
Source File: Shop.tsx    From apps with MIT License 4 votes vote down vote up
ShopTab = ({ region, shops, filters, onChange, itemCache, questCache }: IProps) => {
    let [forceEnablePlanner, setForceEnablePlanner] = useState<boolean | undefined>(undefined);
    let [itemFilters, setItemFilters] = useState(new Set<number>());

    const allItems = new Map(shops.map((shop) => [shop.cost.item.id, shop.cost.item]));

    let shopEnabled = forceEnablePlanner === undefined ? Manager.shopPlannerEnabled() : forceEnablePlanner;
    let counted = shops
        .filter((shop) => (shopEnabled ? filters.has(shop.id) : true))
        .map(
            (shop) =>
                [shop.cost.item, (shopEnabled ? filters.get(shop.id)! : shop.limitNum) * shop.cost.amount] as const
        );

    let items = new Map(counted.map((tuple) => [tuple[0].id, tuple[0]]));

    let amounts = new Map<number, number>();
    for (let [item, amount] of counted)
        if (amounts.has(item.id)) amounts.set(item.id, (amounts.get(item.id) ?? 0) + amount);
        else amounts.set(item.id, amount);

    // reset filter if nothing is chosen
    if (!amounts.size && itemFilters.size) setItemFilters(new Set());

    const excludeItemIds = (itemIds: number[]) => {
        return new Map(
            shops
                .filter((shop) => shop.payType !== Shop.PayType.FREE)
                .filter((shop) => shop.limitNum !== 0)
                .filter((shop) => !itemIds.includes(shop.targetIds[0]) || shop.purchaseType !== Shop.PurchaseType.ITEM)
                .map((shop) => [shop.id, shop.limitNum])
        );
    };

    return (
        <>
            <Alert variant="success" style={{ margin: "1em 0", display: "flex" }}>
                <div style={{ flexGrow: 1 }}>
                    {shopEnabled
                        ? amounts.size > 0
                            ? "Total amount for chosen items: "
                            : "No item was chosen. Choose at least one to get calculations."
                        : "Total currency amount needed to clear the shop: "}
                    {[...amounts]
                        .filter(([_, amount]) => amount > 0)
                        .map(([itemId, amount]) => (
                            <span style={{ whiteSpace: "nowrap", paddingRight: "1ch" }} key={itemId}>
                                <IconLink region={region} item={items.get(itemId)!} />
                                <b>×{amount.toLocaleString()}</b>
                            </span>
                        ))}
                </div>
                <div style={{ flexBasis: "auto", paddingLeft: "10px" }}>
                    <Button
                        variant={shopEnabled ? "dark" : "success"}
                        onClick={() => setForceEnablePlanner(!forceEnablePlanner)}
                    >
                        <FontAwesomeIcon icon={faEdit} title={shopEnabled ? "Disable planner" : "Enable planner"} />
                    </Button>
                </div>
            </Alert>
            {shopEnabled && (
                <>
                    <ButtonGroup>
                        <Button disabled variant="outline-dark">
                            Quick toggle
                        </Button>
                        <Button variant="outline-success" onClick={() => onChange?.(excludeItemIds([]))}>
                            All
                        </Button>
                        <Button variant="outline-success" onClick={() => onChange?.(new Map())}>
                            None
                        </Button>
                        <Dropdown as={ButtonGroup}>
                            <Dropdown.Toggle variant="outline-success">Exclude</Dropdown.Toggle>
                            <Dropdown.Menu>
                                <Dropdown.Item
                                    as={Button}
                                    onClick={() =>
                                        onChange?.(excludeItemIds([...gemIds, ...magicGemIds, ...secretGemIds]))
                                    }
                                >
                                    Gems
                                </Dropdown.Item>
                                <Dropdown.Item
                                    as={Button}
                                    onClick={() => onChange?.(excludeItemIds([...monumentIds, ...pieceIds]))}
                                >
                                    Monuments & Pieces
                                </Dropdown.Item>
                                <Dropdown.Item as={Button} onClick={() => onChange?.(excludeItemIds(monumentIds))}>
                                    Monuments
                                </Dropdown.Item>
                                <Dropdown.Item as={Button} onClick={() => onChange?.(excludeItemIds(pieceIds))}>
                                    Pieces
                                </Dropdown.Item>
                            </Dropdown.Menu>
                        </Dropdown>
                    </ButtonGroup>
                    <div>&nbsp;</div>
                </>
            )}
            <Table hover responsive className="shopTable">
                <thead>
                    <tr>
                        <th style={{ textAlign: "left" }}>Detail</th>
                        <th style={{ whiteSpace: "nowrap" }}>
                            Currency&nbsp;
                            <Dropdown as={ButtonGroup}>
                                <Dropdown.Toggle size="sm">
                                    <FontAwesomeIcon style={{ display: "inline" }} icon={faFilter} />
                                </Dropdown.Toggle>
                                <Dropdown.Menu>
                                    {/* Actually a checkbox is the best here */}
                                    <Dropdown.Item
                                        as={Button}
                                        onClick={() => {
                                            setItemFilters(new Set());
                                        }}
                                    >
                                        Reset
                                    </Dropdown.Item>
                                    {[...allItems].map(([itemId, item]) => (
                                        <Dropdown.Item
                                            key={item.id}
                                            as={Button}
                                            onClick={() => {
                                                setItemFilters(new Set([itemId]));
                                            }}
                                        >
                                            <ItemIcon region={region} item={item} height={40} />
                                            {item.name}
                                        </Dropdown.Item>
                                    ))}
                                </Dropdown.Menu>
                            </Dropdown>
                        </th>
                        <th>Cost</th>
                        <th>Item</th>
                        <th>Set</th>
                        <th>Limit</th>
                        {shopEnabled && <th>Target</th>}
                    </tr>
                </thead>
                <tbody>
                    {shops
                        .filter((shop) =>
                            itemFilters.size && amounts.size ? itemFilters.has(shop.cost.item.id) : true
                        )
                        .sort((a, b) => a.priority - b.priority)
                        .map((shop) => {
                            let limitNumIndicator = shopEnabled ? (
                                <Button
                                    variant="light"
                                    onClick={() => {
                                        filters.set(shop.id, shop.limitNum);
                                        onChange?.(filters);
                                    }}
                                >
                                    {shop.limitNum.toLocaleString()}
                                </Button>
                            ) : (
                                <>{shop.limitNum.toLocaleString()}</>
                            );

                            return (
                                <tr key={shop.id}>
                                    <td style={{ minWidth: "10em" }}>
                                        <b>{shop.name}</b>
                                        <div style={{ fontSize: "0.75rem" }} className="newline">
                                            {colorString(shop.detail)}
                                            <ScriptLink region={region} shop={shop} />
                                            <br />
                                            <div>
                                                {shop.releaseConditions.length ? (
                                                    <ul className="condition-list">
                                                        {shop.releaseConditions.map((cond, index) => (
                                                            <li key={index} style={{ fontSize: "0.75rem" }}>
                                                                {cond.closedMessage && `${cond.closedMessage} — `}
                                                                <CondTargetNumDescriptor
                                                                    region={region}
                                                                    cond={cond.condType}
                                                                    targets={cond.condValues}
                                                                    num={cond.condNum}
                                                                    quests={questCache}
                                                                    items={itemCache}
                                                                />
                                                            </li>
                                                        ))}
                                                    </ul>
                                                ) : (
                                                    ""
                                                )}
                                            </div>
                                        </div>
                                    </td>
                                    <td style={{ textAlign: "center" }}>
                                        {shop.payType !== Shop.PayType.FREE ? (
                                            <IconLink region={region} item={shop.cost.item} />
                                        ) : null}
                                    </td>
                                    <td style={{ textAlign: "center" }}>
                                        {shop.payType !== Shop.PayType.FREE ? shop.cost.amount.toLocaleString() : null}
                                    </td>
                                    <td>
                                        <ShopPurchaseDescriptor region={region} shop={shop} itemMap={itemCache} />
                                    </td>
                                    <td style={{ textAlign: "center" }}>{shop.setNum.toLocaleString()}</td>
                                    <td style={{ textAlign: "center" }}>
                                        {shop.limitNum === 0 ? <>Unlimited</> : limitNumIndicator}
                                    </td>
                                    {shopEnabled && (
                                        <>
                                            <td style={{ textAlign: "center", maxWidth: "5em" }}>
                                                <InputGroup size="sm">
                                                    <Form.Control
                                                        type="number"
                                                        value={filters.get(shop.id) ?? 0}
                                                        min={0}
                                                        max={shop.limitNum || undefined}
                                                        onChange={(event) => {
                                                            let value = +event.target.value;
                                                            if (value) filters.set(shop.id, value);
                                                            else filters.delete(shop.id);
                                                            onChange?.(filters);
                                                        }}
                                                    />
                                                </InputGroup>
                                            </td>
                                        </>
                                    )}
                                </tr>
                            );
                        })}
                </tbody>
            </Table>
        </>
    );
}
Example #10
Source File: ServantVoiceLines.tsx    From apps with MIT License 4 votes vote down vote up
VoiceLinesTable = ({
    region,
    voice,
    mergedDownloadNamePrefix,
    servants,
    costumes,
}: {
    region: Region;
    voice: Profile.VoiceGroup;
    mergedDownloadNamePrefix: string;
    servants: Map<number, Servant.ServantBasic>;
    costumes?: {
        [key: string]: Profile.CostumeDetail;
    };
}) => {
    const voiceLines = voice.voiceLines.sort((a, b) => (b.priority || 0) - (a.priority || 0));
    const voiceLineNames: string[] = [];
    const voiceNameCount: Record<string, number> = {};
    for (const line of voiceLines) {
        line.conds = line.conds.filter(
            (cond) => !(cond.condType === Profile.VoiceCondType.EVENT_END && cond.value === 0)
        );
        let lineName = line.overwriteName || line.name || "";
        if (lineName in voiceNameCount) {
            voiceNameCount[lineName]++;
        } else {
            voiceNameCount[lineName] = 1;
        }
        voiceLineNames.push(lineName.replace("{0}", voiceNameCount[lineName].toString()));
    }

    return (
        <Table bordered className="mb-0">
            <tbody>
                {voiceLines.map((line, index) => (
                    <tr key={`line_${index}`}>
                        <td style={{ verticalAlign: "middle" }}>
                            <b className="newline">{voiceLineNames[index]}</b>
                            <br />
                            <div className="newline">
                                {voiceTextField(region, voice.type) ? (
                                    line.text.map((line, i) => (
                                        <VoiceSubtitleFormat key={i} region={region} inputString={line} />
                                    ))
                                ) : (
                                    <VoiceSubtitleFormat region={region} inputString={line.subtitle} />
                                )}
                            </div>
                            {line.conds.length || line.playConds.length || line.summonScript ? (
                                <>
                                    <Alert variant="info" style={{ marginBottom: 0, marginTop: "1em" }}>
                                        {line.summonScript === undefined ? null : (
                                            <>
                                                Summoning Script:{" "}
                                                <ScriptDescriptor
                                                    region={region}
                                                    scriptId={line.summonScript.scriptId}
                                                    scriptType=""
                                                />
                                            </>
                                        )}
                                        {line.conds.length > 1 && (
                                            <>
                                                <b>Unlock Requirements (all of the following):</b>
                                                <br />
                                                <ul style={{ marginBottom: 0 }}>
                                                    {line.conds.map((cond, index) => (
                                                        <li key={index}>
                                                            <VoiceCondTypeDescriptor
                                                                region={region}
                                                                servants={servants}
                                                                costumes={costumes}
                                                                cond={cond}
                                                            />
                                                        </li>
                                                    ))}
                                                </ul>
                                            </>
                                        )}
                                        {line.conds.length === 1 && (
                                            <>
                                                <b>Unlock Requirement:</b>
                                                <br />
                                                <VoiceCondTypeDescriptor
                                                    region={region}
                                                    servants={servants}
                                                    costumes={costumes}
                                                    cond={line.conds[0]}
                                                />
                                                <br />
                                            </>
                                        )}
                                        <VoicePlayCondDescriptor
                                            region={region}
                                            playConds={line.playConds}
                                            servants={servants}
                                        />
                                    </Alert>
                                </>
                            ) : (
                                ""
                            )}
                        </td>
                        <td style={{ verticalAlign: "middle", width: "1px" }}>
                            <ButtonGroup>
                                <VoiceLinePlayer
                                    audioAssetUrls={line.audioAssets}
                                    delay={line.delay}
                                    title={voiceLineNames[index]}
                                />
                                <Dropdown as={ButtonGroup}>
                                    <Dropdown.Toggle variant={"info"} title={`Download ${voiceLineNames[index]}`}>
                                        <FontAwesomeIcon icon={faFileAudio} />
                                        &nbsp;
                                    </Dropdown.Toggle>

                                    <Dropdown.Menu title={`Download ${voiceLineNames[index]}`}>
                                        <Dropdown.Item
                                            title={`Download ${voiceLineNames[index]} merged file`}
                                            onClick={() => {
                                                const fileName = `${mergedDownloadNamePrefix} - ${voiceLineNames[index]}`;
                                                mergeVoiceLine(line.audioAssets, line.delay, fileName);
                                            }}
                                        >
                                            Merged
                                        </Dropdown.Item>
                                        {line.audioAssets.map((asset, i) => (
                                            <Dropdown.Item
                                                key={i}
                                                href={asset}
                                                target="_blank"
                                                title={`Download ${voiceLineNames[index]} part ${i + 1}`}
                                            >
                                                Part {i + 1}
                                            </Dropdown.Item>
                                        ))}
                                    </Dropdown.Menu>
                                </Dropdown>
                            </ButtonGroup>
                        </td>
                    </tr>
                ))}
            </tbody>
        </Table>
    );
}
Example #11
Source File: group-modal.component.tsx    From cwa-quick-test-frontend with Apache License 2.0 4 votes vote down vote up
GroupModal = (props: any) => {

    const [btnOkDisabled, setBtnOkDisabled] = React.useState(true);
    const { t } = useTranslation();
    const { keycloak } = useKeycloak();

    const [data, setData] = React.useState('');
    const [validated, setValidated] = React.useState(false);
    const [isReady, setIsReady] = React.useState(false);
    const [isNew, setIsNew] = React.useState(true);
    const [options, setOptions] = React.useState<JSX.Element[]>();
    const [dropdownItems, setDropdownItems] = React.useState<JSX.Element[]>();
    const [dropdownList] = React.useState<string[]>(['https://', 'http://']);
    const [selectedDropdownValue, setSelectedDropdownValue] = React.useState<string>(dropdownList[0]);
    const [websiteValue, setWebsiteValue] = React.useState('');
    const [displayOpeningHours, setDisplayOpeningHours] = React.useState('');
    const [errorOpeningHour, setErrorOpeningHour] = React.useState('');

    const groupReloaded = (group: IGroupDetails) => {
        if (group) {
            setData(unpackData(group.pocDetails))
            group.parentGroup = props.parentGroupId;

            if (group.website) {
                let website = '';

                for (const item of dropdownList) {
                    if (group.website.startsWith(item)) {
                        website = group.website.slice(item.length, group.website.length);
                        setSelectedDropdownValue(item);
                    }
                }

                setWebsiteValue(website);
            }
            else {
                setWebsiteValue('');
                setSelectedDropdownValue(dropdownList[0]);
            }
        }
        setBtnOkDisabled(false);
    }

    const [group, updateGroup, setGroup] = useGetGroupDetails(groupReloaded, props.handleError);


    React.useEffect(() => {
        getDropdownItems();
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [])

    React.useEffect(() => {
        if (group) {

            setOptions(getOptions());
            setDisplayOpeningHours(
                group.openingHours?.map(
                    (element: string) => element)
                    .join('\n')
            );

            setIsReady(true);
        }

        setIsNew(!(group && group.id));
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [group])

    React.useEffect(() => {
        setValidated(props.isSuccess)
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [props.isSuccess]);

    React.useEffect(() => {
        if (props.isCreationError) {
            setBtnOkDisabled(false)
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [props.isCreationError]);

    const handleCancel = () => {
        setErrorOpeningHour('');
        props.onCancel();
    }

    const unpackData = (data: string) => {
        if (data) {
            data = data.replaceAll(',', '\n')
        } else {
            data = ''
        }
        return data;
    }

    const packData = (data: string) => {
        if (data) {
            data = data.replaceAll('\n', ',')
        }
        return data;
    }

    const handleOk = () => {
        if (props.handleOk) {
            setBtnOkDisabled(true);
            group.pocDetails = packData(data);
            if (websiteValue
                && (
                    websiteValue.startsWith('www.')
                    || !(
                        websiteValue.startsWith(dropdownList[0])
                        || websiteValue.startsWith(dropdownList[1])
                    )
                )) {
                group.website = selectedDropdownValue + websiteValue;
            }
            else {
                group.website = websiteValue;
            }

            props.handleOk(group);
        }
    }

    const handleEnter = () => {
        if (props.onEnter) {
            props.onEnter();
        }

        setBtnOkDisabled(false);

        if (props.groupId) {
            updateGroup(props.groupId);
        }
        else {
            setGroup({ ...emptyGroup });
            setSelectedDropdownValue(dropdownList[0]);
            setData('');
            setWebsiteValue('');
        }
    }

    const handleExited = () => {
        setIsReady(false);
        props.onExit();
    }

    const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
        const form = event.currentTarget;
        event.preventDefault();
        event.stopPropagation();

        if (errorOpeningHour) {
            document.getElementById('formPocOpeningHours')?.focus();
            return;
        }

        if (form.checkValidity()) {
            handleOk();
        }
    }

    const updateGroupProp = (name: string, value: any) => {
        const ngroup = { ...group, [name]: value };
        setGroup({ ...ngroup });
    }

    const changeOpeningHoursHandler = (name: string, value: string) => {

        setDisplayOpeningHours(value);

        let error = undefined;
        const openingHours = value.split('\n');
        if (openingHours.length > 7) {
            setErrorOpeningHour('opening-hours-to-much-lines-error');
            return;
        }

        error = openingHours.find(element => {
            return !utils.isOpeningHoursValid(element);
        });

        if (error) {
            setErrorOpeningHour('openening-hours-to-long-error');
        } else {
            setErrorOpeningHour('');
            updateGroupProp("openingHours", openingHours);
        }
    }

    const updateSearchPortalConsent = (name: string, value: any) => {
        const ngroup: IGroupDetails = { ...group, [name]: value };

        if (value === false) {
            ngroup.email = '';
            ngroup.website = '';
            ngroup.openingHours = [];
            ngroup.appointmentRequired = false;
        }
        setGroup(ngroup);
    }

    const collectChildren = (idlist: string[], parentNode: IGroup) => {
        if (parentNode) {
            idlist.push(parentNode.id);
            parentNode.children.forEach(child => collectChildren(idlist, child as IGroup));
        }
    }

    const getOptions = (): JSX.Element[] => {
        let result: JSX.Element[] = [];

        if (group && group.id) {

            const node = props.groups.find((groupNode: IGroupNode) => groupNode.group.id === group.id);
            const selfIdOrChildren: string[] = [];

            if (node) {
                collectChildren(selfIdOrChildren, node.group);
            }

            const fList = props.groups.filter((groupNode: IGroupNode) => selfIdOrChildren.indexOf(groupNode.group.id) < 0)

            result = fList.map((groupNode: IGroupNode) =>
                <option key={groupNode.group.id} value={groupNode.group.id}>{"\u00A0\u00A0\u00A0\u00A0".repeat(groupNode.level) + groupNode.group.name}</option>
            );

            // result.push(<option key="empty" value="empty">{t('translation:no-parentgroup-option')}</option>);
        }


        return result;
    }

    const getDropdownItems = () => {
        setDropdownItems(
            dropdownList.map(
                (item: string) =>
                    <Dropdown.Item
                        onSelect={(eventKey: any) => setSelectedDropdownValue(eventKey)}
                        eventKey={item}
                        key={item}
                    >
                        {item}
                    </Dropdown.Item>));
    }

    return (
        <Modal
            contentClassName='data-modal'
            size="lg"
            show={props.show}
            backdrop="static"
            keyboard={false}
            centered
            onEnter={handleEnter}
            onExited={handleExited}
        >
            {!isReady
                ? <CwaSpinner background='#eeeeee' />
                : <Fade appear={true} in={true} >
                    <Form
                        className='form-flex'
                        onSubmit={handleSubmit}
                        validated={validated}
                    >
                        <Modal.Header id='data-header' className='pb-0' >
                            <Modal.Title>{isNew ? t('translation:add-group') : t('translation:edit-group')}</Modal.Title>
                        </Modal.Header>

                        <Modal.Body className='bg-light'>
                            {isNew
                                ? <></>
                                : <>
                                    <FormGroupSelect controlId='formGroupSelect'
                                        title={t('translation:parentgroup')}
                                        placeholder={t('translation:no-parentgroup-option')}
                                        value={group.parentGroup}
                                        onChange={(ent: any) => updateGroupProp('parentGroup', ent.target.value)}
                                        options={options}
                                    />

                                    <hr />
                                </>
                            }

                            < FormGroupInput controlId='formFirstName' title={t('translation:name')}
                                value={group ? group.name : ''}
                                required
                                onChange={(evt: any) => {
                                    updateGroupProp('name', evt.target.value);
                                    props.resetError();
                                }}
                                maxLength={45}
                                isInvalid={props.isCreationError}
                                InvalidText={t('translation:group-conflict-error')}
                            />

                            {/* <hr /> */}

                            < FormGroupTextarea controlId='formAdressData' title={t('translation:address-testcenter')} placeholder={t('translation:address-testcenter-placeholder')}
                                value={data}
                                required
                                onChange={(evt: any) => setData(evt.target.value)}
                                type='textarea'
                                maxLength={300}
                            />

                            {utils.hasRole(keycloak, 'c19_quick_test_poc_nat_admin')
                                ?
                                <FormGroupPermissionCkb controlId='formenablePcr' title={t('translation:enablePcr')}
                                    //label={t('translation:for-counter')}
                                    onChange={(evt: any) => updateSearchPortalConsent('enablePcr', evt.currentTarget.checked)}
                                    type='checkbox'
                                    checked={group.enablePcr}
                                />
                                : <></>
                            }

                            <hr />

                            {/* < FormGroupInput controlId='formBSNRInput' title={t('translation:bsnr')} placeholder={t('translation:bsnr-placeholder')}
                                value={group ? group.bsnr : ''}
                                onChange={(evt: any) => {
                                    updateGroupProp('bsnr', evt.target.value);
                                    props.resetError();
                                }}
                                maxLength={9}
                                prepend='i'
                                tooltip={t('translation:bsnr-tooltip')}
                                pattern={utils.pattern.BSNR}
                            /> */}

                            <FormGroupPermissionCkb controlId='formsearchPortalConsent' title={t('translation:searchPortalConsent')}
                                //label={t('translation:for-counter')}
                                onChange={(evt: any) => updateSearchPortalConsent('searchPortalConsent', evt.currentTarget.checked)}
                                type='checkbox'
                                checked={group.searchPortalConsent}
                            />

                            <Collapse in={group.searchPortalConsent}>
                                <div>
                                    < FormGroupInput controlId='formEmailInput' title={t('translation:email-address')}
                                        value={group?.email ? group.email : ''}
                                        onChange={(evt: any) => {
                                            updateGroupProp('email', evt.target.value);
                                            props.resetError();
                                        }}
                                        type='email'
                                        pattern={utils.pattern.eMail}
                                        minLength={5}
                                        maxLength={255}
                                    />
                                    < FormGroupInput controlId='formPocWebsite' title={t('translation:searchPortalWebsite')} placeholder={t('translation:searchPortalWebsitePlaceholder')}
                                        value={websiteValue}
                                        dropdown={dropdownItems}
                                        dropdownTitle={selectedDropdownValue}
                                        prepend='i'
                                        tooltip={t('translation:searchPortalWebsiteTooltip')}
                                        onChange={(evt: any) => {
                                            setWebsiteValue(evt.target.value);
                                            props.resetError();
                                        }}
                                        maxLength={100}
                                        pattern={utils.pattern.url}
                                    />

                                    < FormGroupTextarea controlId='formPocOpeningHours' title={t('translation:searchPortalOpeningHours')}
                                        value={displayOpeningHours}
                                        onChange={(evt: any) => {
                                            changeOpeningHoursHandler('openingHours', evt.target.value);
                                            props.resetError();
                                        }}
                                        type='textarea'
                                        rows={7}
                                        pattern={utils.pattern.email}
                                        isInvalid={errorOpeningHour}
                                        invalidText={errorOpeningHour && t('translation:' + errorOpeningHour)}
                                    />
                                    <FormGroupPermissionCkb controlId='formAppointmentRequired' title={t('translation:searchPortalAppointmentRequired')}
                                        onChange={(evt: any) => updateGroupProp('appointmentRequired', evt.currentTarget.checked)}
                                        type='checkbox'
                                        checked={group?.appointmentRequired ? group.appointmentRequired : false}
                                    />
                                </div>
                            </Collapse>

                            {!(group && group.pocId)
                                ? <></>
                                : <>
                                    <hr />
                                    < FormGroupInput controlId='formPocId' title={t('translation:poc-id')}
                                        value={group && group.pocId ? group.pocId : ''}
                                        readOnly
                                    />
                                </>
                            }

                        </Modal.Body>

                        <Modal.Footer id='data-footer'>
                            <Container className='p-0'>
                                <Row>
                                    <Col sm='6' lg='4' className='mb-2 mb-sm-0 p-0 pr-sm-2'>
                                        <Button
                                            className='p-0'
                                            block
                                            variant='outline-primary'
                                            onClick={handleCancel}
                                        >
                                            {t('translation:cancel')}
                                        </Button>
                                    </Col>
                                    <Col sm='6' lg='4' className='p-0 pl-sm-2'>
                                        <Button
                                            className='p-0'
                                            block
                                            type='submit'
                                            disabled={btnOkDisabled}
                                        >
                                            {isNew ? t('translation:add') : t('translation:edit')}

                                            <Spinner
                                                as="span"
                                                className='btn-spinner'
                                                animation="border"
                                                hidden={!btnOkDisabled}
                                                size="sm"
                                                role="status"
                                                aria-hidden="true"
                                            />
                                        </Button>
                                    </Col>
                                </Row>
                            </Container>
                        </Modal.Footer>
                    </Form>
                </Fade>
            }
        </Modal>
    )
}
Example #12
Source File: index.tsx    From nouns-monorepo with GNU General Public License v3.0 4 votes vote down vote up
NavLocaleSwitcher: React.FC<NavLocalSwitcherProps> = props => {
  const { buttonStyle } = props;

  const [buttonUp, setButtonUp] = useState(false);
  const history = useHistory();
  const [showLanguagePickerModal, setShowLanguagePickerModal] = useState(false);
  const activeLocale = useActiveLocale();

  const statePrimaryButtonClass = usePickByState(
    navDropdownClasses.whiteInfo,
    navDropdownClasses.coolInfo,
    navDropdownClasses.warmInfo,
    history,
  );

  const stateSelectedDropdownClass = usePickByState(
    navDropdownClasses.whiteInfoSelected,
    navDropdownClasses.dropdownActive,
    navDropdownClasses.dropdownActive,
    history,
  );

  const buttonStyleTop = usePickByState(
    navDropdownClasses.whiteInfoSelectedTop,
    navDropdownClasses.coolInfoSelected,
    navDropdownClasses.warmInfoSelected,
    history,
  );

  const buttonStyleBottom = usePickByState(
    navDropdownClasses.whiteInfoSelectedBottom,
    navDropdownClasses.coolInfoSelected,
    navDropdownClasses.warmInfoSelected,
    history,
  );

  const customDropdownToggle = React.forwardRef<RefType, Props>(({ onClick, value }, ref) => (
    <>
      <div
        className={clsx(
          navDropdownClasses.wrapper,
          buttonUp ? stateSelectedDropdownClass : statePrimaryButtonClass,
        )}
        onClick={e => {
          e.preventDefault();
          onClick(e);
        }}
      >
        <div className={navDropdownClasses.button}>
          <div className={navDropdownClasses.dropdownBtnContent}>
            {<FontAwesomeIcon icon={faGlobe} />}
          </div>
          <div className={buttonUp ? navDropdownClasses.arrowUp : navDropdownClasses.arrowDown}>
            <FontAwesomeIcon icon={buttonUp ? faSortUp : faSortDown} />{' '}
          </div>
        </div>
      </div>
    </>
  ));

  const CustomMenu = React.forwardRef((props: CustomMenuProps, ref: React.Ref<HTMLDivElement>) => {
    return (
      <div
        ref={ref}
        style={props.style}
        className={props.className}
        aria-labelledby={props.labeledBy}
      >
        {SUPPORTED_LOCALES.map((locale: SupportedLocale, index: number) => {
          let dropDownStyle;
          let buttonStyle;

          switch (index) {
            case 0:
              dropDownStyle = classes.dropDownTop;
              buttonStyle = buttonStyleTop;
              break;
            case SUPPORTED_LOCALES.length - 1:
              dropDownStyle = classes.dropDownBottom;
              buttonStyle = buttonStyleBottom;
              break;
            default:
              dropDownStyle = classes.dropDownInterior;
              buttonStyle = buttonStyleBottom;
          }

          return (
            <div
              className={clsx(
                navDropdownClasses.button,
                navDropdownClasses.dropdownPrimaryText,
                buttonStyle,
                dropDownStyle,
                classes.desktopLanguageButton,
              )}
              onClick={() => setLocale(locale)}
            >
              {LOCALE_LABEL[locale]}
              {activeLocale === locale && <FontAwesomeIcon icon={faCheck} height={24} width={24} />}
            </div>
          );
        })}
      </div>
    );
  });

  return (
    <>
      {showLanguagePickerModal && (
        <LanguageSelectionModal onDismiss={() => setShowLanguagePickerModal(false)} />
      )}

      <div
        className={clsx(navDropdownClasses.nounsNavLink, responsiveUiUtilsClasses.mobileOnly)}
        onClick={() => setShowLanguagePickerModal(true)}
      >
        <NavBarButton
          buttonText={<Trans>Language</Trans>}
          buttonIcon={<FontAwesomeIcon icon={faGlobe} />}
          buttonStyle={buttonStyle}
        />
      </div>

      <Dropdown
        className={clsx(navDropdownClasses.nounsNavLink, responsiveUiUtilsClasses.desktopOnly)}
        onToggle={() => setButtonUp(!buttonUp)}
        autoClose={true}
      >
        <Dropdown.Toggle as={customDropdownToggle} id="dropdown-custom-components" />
        <Dropdown.Menu className={`${navDropdownClasses.desktopDropdown} `} as={CustomMenu} />
      </Dropdown>
    </>
  );
}
Example #13
Source File: index.tsx    From nouns-monorepo with GNU General Public License v3.0 4 votes vote down vote up
NavWallet: React.FC<NavWalletProps> = props => {
  const { address, buttonStyle } = props;

  const [buttonUp, setButtonUp] = useState(false);
  const [showConnectModal, setShowConnectModal] = useState(false);
  const history = useHistory();
  const { library: provider } = useEthers();
  const activeAccount = useAppSelector(state => state.account.activeAccount);
  const { deactivate } = useEthers();
  const ens = useReverseENSLookUp(address);
  const shortAddress = useShortAddress(address);
  const activeLocale = useActiveLocale();

  const setModalStateHandler = (state: boolean) => {
    setShowConnectModal(state);
  };

  const switchWalletHandler = () => {
    setShowConnectModal(false);
    setButtonUp(false);
    deactivate();
    setShowConnectModal(false);
    setShowConnectModal(true);
  };

  const disconectWalletHandler = () => {
    setShowConnectModal(false);
    setButtonUp(false);
    deactivate();
  };

  const statePrimaryButtonClass = usePickByState(
    navDropdownClasses.whiteInfo,
    navDropdownClasses.coolInfo,
    navDropdownClasses.warmInfo,
    history,
  );

  const stateSelectedDropdownClass = usePickByState(
    navDropdownClasses.whiteInfoSelected,
    navDropdownClasses.dropdownActive,
    navDropdownClasses.dropdownActive,
    history,
  );

  const mobileTextColor = usePickByState(
    'rgba(140, 141, 146, 1)',
    'rgba(121, 128, 156, 1)',
    'rgba(142, 129, 127, 1)',
    history,
  );

  const mobileBorderColor = usePickByState(
    'rgba(140, 141, 146, .5)',
    'rgba(121, 128, 156, .5)',
    'rgba(142, 129, 127, .5)',
    history,
  );

  const connectWalletButtonStyle = usePickByState(
    NavBarButtonStyle.WHITE_WALLET,
    NavBarButtonStyle.COOL_WALLET,
    NavBarButtonStyle.WARM_WALLET,
    history,
  );

  const customDropdownToggle = React.forwardRef<RefType, Props>(({ onClick, value }, ref) => (
    <>
      <div
        className={clsx(
          navDropdownClasses.wrapper,
          buttonUp ? stateSelectedDropdownClass : statePrimaryButtonClass,
        )}
        onClick={e => {
          e.preventDefault();
          onClick(e);
        }}
      >
        <div className={navDropdownClasses.button}>
          <div className={classes.icon}>
            {' '}
            <Davatar size={21} address={address} provider={provider} />
          </div>
          <div className={navDropdownClasses.dropdownBtnContent}>{ens ? ens : shortAddress}</div>
          <div className={buttonUp ? navDropdownClasses.arrowUp : navDropdownClasses.arrowDown}>
            <FontAwesomeIcon icon={buttonUp ? faSortUp : faSortDown} />{' '}
          </div>
        </div>
      </div>
    </>
  ));

  const CustomMenu = React.forwardRef((props: CustomMenuProps, ref: React.Ref<HTMLDivElement>) => {
    return (
      <div
        ref={ref}
        style={props.style}
        className={props.className}
        aria-labelledby={props.labeledBy}
      >
        <div>
          <div
            onClick={switchWalletHandler}
            className={clsx(
              classes.dropDownTop,
              navDropdownClasses.button,
              navDropdownClasses.dropdownPrimaryText,
              usePickByState(
                navDropdownClasses.whiteInfoSelectedTop,
                navDropdownClasses.coolInfoSelected,
                navDropdownClasses.warmInfoSelected,
                history,
              ),
            )}
          >
            <Trans>Switch wallet</Trans>
          </div>

          <div
            onClick={disconectWalletHandler}
            className={clsx(
              classes.dropDownBottom,
              navDropdownClasses.button,
              usePickByState(
                navDropdownClasses.whiteInfoSelectedBottom,
                navDropdownClasses.coolInfoSelected,
                navDropdownClasses.warmInfoSelected,
                history,
              ),
              classes.disconnectText,
            )}
          >
            <Trans>Disconnect</Trans>
          </div>
        </div>
      </div>
    );
  });

  const renderENS = (ens: string) => {
    if (activeLocale === 'ja-JP') {
      return veryShortENS(ens);
    }
    return shortENS(ens);
  };

  const renderAddress = (address: string) => {
    if (activeLocale === 'ja-JP') {
      return veryShortAddress(address);
    }
    return shortAddress;
  };

  const walletConnectedContentMobile = (
    <div className={clsx(navDropdownClasses.nounsNavLink, responsiveUiUtilsClasses.mobileOnly)}>
      <div
        className={'d-flex flex-row justify-content-between'}
        style={{
          justifyContent: 'space-between',
        }}
      >
        <div className={navDropdownClasses.connectContentMobileWrapper}>
          <div className={clsx(navDropdownClasses.wrapper, getNavBarButtonVariant(buttonStyle))}>
            <div className={navDropdownClasses.button}>
              <div className={classes.icon}>
                {' '}
                <Davatar size={21} address={address} provider={provider} />
              </div>
              <div className={navDropdownClasses.dropdownBtnContent}>
                {ens ? renderENS(ens) : renderAddress(address)}
              </div>
            </div>
          </div>
        </div>

        <div className={`d-flex flex-row  ${classes.connectContentMobileText}`}>
          <div
            style={{
              borderRight: `1px solid ${mobileBorderColor}`,
              color: mobileTextColor,
            }}
            className={classes.mobileSwitchWalletText}
            onClick={switchWalletHandler}
          >
            <Trans>Switch</Trans>
          </div>
          <div className={classes.disconnectText} onClick={disconectWalletHandler}>
            <Trans>Sign out</Trans>
          </div>
        </div>
      </div>
    </div>
  );

  const walletConnectedContentDesktop = (
    <Dropdown
      className={clsx(navDropdownClasses.nounsNavLink, responsiveUiUtilsClasses.desktopOnly)}
      onToggle={() => setButtonUp(!buttonUp)}
    >
      <Dropdown.Toggle as={customDropdownToggle} id="dropdown-custom-components" />
      <Dropdown.Menu className={`${navDropdownClasses.desktopDropdown} `} as={CustomMenu} />
    </Dropdown>
  );

  return (
    <>
      {showConnectModal && activeAccount === undefined && (
        <WalletConnectModal onDismiss={() => setModalStateHandler(false)} />
      )}
      {activeAccount ? (
        <>
          {walletConnectedContentDesktop}
          {walletConnectedContentMobile}
        </>
      ) : (
        <WalletConnectButton
          className={clsx(navDropdownClasses.nounsNavLink, navDropdownClasses.connectBtn)}
          onClickHandler={() => setModalStateHandler(true)}
          buttonStyle={connectWalletButtonStyle}
        />
      )}
    </>
  );
}