semantic-ui-react#Icon TypeScript Examples

The following examples show how to use semantic-ui-react#Icon. 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: DisplayShareControl.tsx    From FLECT_Amazon_Chime_Meeting with Apache License 2.0 6 votes vote down vote up
generateAccordion = () =>{
        const props = this.props as any

        const grid = (
            <Accordion>
                <Accordion.Title
                    active={this.state.open}
                    index={0}
                    onClick={()=>{this.handleClick()}}
                >
                    <Icon name='dropdown' />
                    Display Share
                </Accordion.Title>
                <Accordion.Content active={this.state.open}>
                    <div style={{paddingLeft:"10px"}}>
                        <Icon basic link name="play" 
                            onClick={() => { props.sharedDisplaySelected()}}
                        />
                        <Icon basic link name="stop" 
                            onClick={() => { props.stopSharedDisplay() }}
                        />                        
                    </div>

                </Accordion.Content>
            </Accordion>
        )
        return grid
      }
Example #2
Source File: index.tsx    From chartsy with GNU General Public License v3.0 6 votes vote down vote up
Nav: React.FC<Props> = ({ collageRef, setShowDrawer }) => {
  //@ts-ignore
  const { takeScreenshot, isLoading } = useScreenshot({ ref: collageRef });

  return (
    <nav>
      <button onClick={() => setShowDrawer((show) => !show)}>
        <Icon name="setting" size="large" />
      </button>
      <Header as="h4">
        <button
          onClick={async () => {
            let img = await takeScreenshot("png");
            let link = document.createElement("a");
            link.download = "chartsy.png";
            link.href = img as string;
            link.click();
          }}
        >
          {isLoading ? "Saving..." : "Save"}
        </button>
      </Header>
    </nav>
  );
}
Example #3
Source File: index.tsx    From frontegg-react with MIT License 6 votes vote down vote up
InputChip = forwardRef<HTMLInputElement, IInputChip>(
  ({ chips, fullWidth, onDelete, className, ...inputProps }, ref) => {
    const inputRef = useRef<HTMLInputElement>(null);

    return (
      <div
        className={classNames('input-chip', className, { fluid: fullWidth })}
        onClick={() => inputRef.current && inputRef.current.focus()}
      >
        {chips.map((chip, idx) => (
          <Label as='div' key={idx} className='chip'>
            {chip}
            {!!onDelete && <Icon name='delete' onClick={() => onDelete(idx)} />}
          </Label>
        ))}
        <input {...inputProps} ref={ref} />
      </div>
    );
  }
)
Example #4
Source File: Home.tsx    From watchparty with MIT License 6 votes vote down vote up
Feature = ({
  icon,
  text,
  title,
}: {
  icon: string;
  text: string;
  title: string;
}) => {
  return (
    <div
      style={{
        display: 'flex',
        flex: '1 1 0px',
        flexDirection: 'column',
        alignItems: 'center',
        padding: '10px',
        minWidth: '180px',
      }}
    >
      <Icon fitted size="huge" name={icon as any} />
      <h4 className={styles.featureTitle}>{title}</h4>
      <div className={styles.featureText}>{text}</div>
    </div>
  );
}
Example #5
Source File: CloudinaryImageUpload.tsx    From communitymap-ui with Apache License 2.0 6 votes vote down vote up
CloudinaryImageUpload: React.FC<UploadWidgetProps> = ({
  cloudName,
  uploadPreset,
  onChange,
  children,
}) => {
  const onChangeCb = useRef(onChange);
  onChangeCb.current = onChange;

  const widget = useMemo(() => {
    return (window as any).cloudinary.createUploadWidget(
      {
        cloudName,
        uploadPreset,
      },
      (error: Error, result: any) => {
        if (!error && result && result.event === 'success') {
          console.debug('Done! Here is the image info: ', result.info);
          onChangeCb.current(result.info.secure_url);
        }
        if (error) console.error(error);
      }
    );
  }, [cloudName, uploadPreset]);

  return (
    <div className="cloudinary-upload-widget">
      {children}
      <div className="cloudinary-overlay-widget" onClick={() => widget.open()}>
        <Icon size="tiny" name="edit outline" />
      </div>
    </div>
  );
}
Example #6
Source File: CharacterList.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 6 votes vote down vote up
CharacterList: VFC<Props> = (props) => {
  const { school, characters } = props;

  return (
    <>
      <Header as="h2">{school}</Header>
      <Item.Group>
        {characters.map((character) => (
          <Item key={character.id}>
            <Icon name="user circle" size="huge" />
            <Item.Content>
              <Item.Header>{character.name}</Item.Header>
              <Item.Meta>{character.grade}年生</Item.Meta>
              <Item.Meta>
                {character.height ? character.height : '???'}
                cm
              </Item.Meta>
            </Item.Content>
          </Item>
        ))}
      </Item.Group>
    </>
  );
}
Example #7
Source File: citeoptimizer.tsx    From website with MIT License 6 votes vote down vote up
render() {
		let { citeData } = this.state;

		return (
			<>
				<Segment>
					{!citeData &&
						<>
							<Icon loading name='spinner' /> Loading citation optimizer ...
						</>
					}
					{citeData &&
						<Tab panes={[
							{ menuItem: 'Crew To Cite', render: () => this.renderTable(citeData.crewToCite, false) },
							{ menuItem: 'Crew To Train', render: () => this.renderTable(citeData.crewToTrain, true) }
						]} />
					}
					<Rail position='right'>
						<h3>Explanation</h3>
						<p>
							A crew's Expected Value (EV) is the average you can expect a crew to contribute to all voyages. EV Final accounts for the crew fully fused. EV Left, while less important, calculates the difference in contribution between fully fused and their current rank. Voyages Improved is how many of the voyage combinations the crew contributes to. Primary and secondary are taken into account, because CMD/DIP voyage will yield different results than DIP/CMD.
						</p>
						<p>
							A crew's EV for a voyage is found by finding the crew's average for the skill "Base + (Min + Max) / 2", multiplying that by 0.35 if the skill is the primary for the voyage, 0.25 if it is secondary, and 0.1 otherwise. To find how much the crew contributes to the total voyage, we find the best crew for the voyage that are fully leveled and equipped.
						</p>
						<p>
							"Training" is considered simply leveling and equipping the considered crew <u>at their current rarity</u>. This is done by comparing the current total EV of all voyages with those as if the considered crew were fully leveled and equiped <u>at current rarity</u>.
						</p>
						<p>
							"Citing" considered <u>fully fusing</u>, leveling and equipping the considered crew. This is done by comparing the current total EV of all voyages with those as if the considered crew were fully leveled and equiped <u>and fused</u>.
						</p>
					</Rail>
				</Segment>
			</>
		);
	}
Example #8
Source File: LobbyMeetingRoom.tsx    From FLECT_Amazon_Chime_Meeting with Apache License 2.0 5 votes vote down vote up
render(){
        const props = this.props as any
        const appState = props.appState as AppState
        const gs = this.props as GlobalState

        const thisAttendeeId = props.thisAttendeeId as string
        const thisMeetingId  = props.thisMeetingId as string
        const attendeeInfo = appState.joinedMeetings[thisMeetingId].roster[thisAttendeeId]
        let attendeeName = "loading...."
        //let meetingShortId = thisMeetingId.substr(0,5)
        const meetingName = gs.meetings.filter((x)=>{return x.meetingId === thisMeetingId})[0].meetingName
        let muted = <span/>
        let paused = <span/>
        let focusIcon = <span/>

        if(attendeeInfo === undefined){
        }else{
            attendeeName = (attendeeInfo.name !== undefined && attendeeInfo.name !== null)? attendeeInfo.name!.substring(0,20) : "unknown"
            muted = attendeeInfo.muted ? (<Icon name="mute"  color="red" />) : (<Icon name="unmute"/>)
            paused = attendeeInfo.paused ?
             (<Icon name="pause circle outline" color="red" link onClick={()=>{
                props.unpauseVideoTile(thisAttendeeId)
                }} />)
             : 
             (<Icon name="pause circle outline" link onClick={()=>{
                props.pauseVideoTile(thisAttendeeId)
                }} />)
            focusIcon = thisAttendeeId === appState.joinedMeetings[thisMeetingId].focusAttendeeId ? (<Icon name="eye"  color="red" />) : (<span />)

        }


        const thisTileId = getTileId(thisAttendeeId, appState.joinedMeetings[thisMeetingId].videoTileStates)
        if(thisTileId > 0 && this.tileOverlayVideoRef.current !== null){
            if(appState.joinedMeetings[thisMeetingId].videoTileStates[thisTileId].localTile){
                const videoElement = this.tileOverlayVideoRef.current.getVideoRef().current!
                appState.joinedMeetings[thisMeetingId].meetingSession?.audioVideo.bindVideoElement(thisTileId, videoElement)
                videoElement.style.setProperty('-webkit-transform', 'scaleX(1)');
                videoElement.style.setProperty('transform', 'scaleX(1)');
            }else{
                appState.joinedMeetings[thisMeetingId].meetingSession?.audioVideo.bindVideoElement(thisTileId, this.tileOverlayVideoRef.current.getVideoRef().current!)
            }
        }

        return(
            <Grid.Column width={4} >
                <div style={{padding:"5px"}}>
                <OverlayVideoElement {...props} ref={this.tileOverlayVideoRef}/>
                </div>
                <span>
                {muted}
                </span>
                <span>
                {paused}
                </span>
                {focusIcon}
                {attendeeName} @ {meetingName}
            </Grid.Column>
        )
    }
Example #9
Source File: PermanentRoomModal.tsx    From watchparty with MIT License 5 votes vote down vote up
render() {
    const { closeModal } = this.props;
    return (
      <Modal open={true} onClose={closeModal as any} closeIcon>
        <Modal.Header>Permanent Rooms</Modal.Header>
        <Modal.Content image>
          <Modal.Description>
            <div>
              Registered users have the ability to make their rooms permanent.
              Subscribed users can create multiple permanent rooms.
            </div>
            <Table definition unstackable striped celled>
              <Table.Header>
                <Table.Row>
                  <Table.HeaderCell />
                  <Table.HeaderCell>Temporary</Table.HeaderCell>
                  <Table.HeaderCell>Permanent</Table.HeaderCell>
                </Table.Row>
              </Table.Header>

              <Table.Body>
                <Table.Row>
                  <Table.Cell>Expiry</Table.Cell>
                  <Table.Cell>After 24 hours of inactivity</Table.Cell>
                  <Table.Cell>Never</Table.Cell>
                </Table.Row>
                <Table.Row>
                  <Table.Cell>Room Passwords</Table.Cell>
                  <Table.Cell></Table.Cell>
                  <Table.Cell>
                    <Icon name="check" />
                  </Table.Cell>
                </Table.Row>
                <Table.Row>
                  <Table.Cell>Disable Chat</Table.Cell>
                  <Table.Cell></Table.Cell>
                  <Table.Cell>
                    <Icon name="check" />
                  </Table.Cell>
                </Table.Row>
                <Table.Row>
                  <Table.Cell>Kick Users</Table.Cell>
                  <Table.Cell></Table.Cell>
                  <Table.Cell>
                    <Icon name="check" />
                  </Table.Cell>
                </Table.Row>
                <Table.Row>
                  <Table.Cell>Custom Room URLs (subscribers)</Table.Cell>
                  <Table.Cell></Table.Cell>
                  <Table.Cell>
                    <Icon name="check" />
                  </Table.Cell>
                </Table.Row>
                {/* <Table.Row>
                  <Table.Cell>Max Room Capacity (subscribers)</Table.Cell>
                    <Table.Cell>20</Table.Cell>
                    <Table.Cell>100</Table.Cell>
                  </Table.Row> */}
              </Table.Body>
            </Table>
          </Modal.Description>
        </Modal.Content>
      </Modal>
    );
  }
Example #10
Source File: ReportForm.tsx    From peterportal-client with MIT License 5 votes vote down vote up
ReportForm: FC<ReportFormProps> = (props) => {
    const dispatch = useAppDispatch();
    const [reason, setReason] = useState<string>('');
    const [reportSubmitted, setReportSubmitted] = useState<boolean>(false);

    const [validated, setValidated] = useState<boolean>(false);


    const postReport = async (report: ReportData) => {
        const res = await axios.post('/reports', report);
        setReportSubmitted(true);
    }

    const submitReport = (e: React.FormEvent<HTMLFormElement>) => {
        e.preventDefault();
        if (reason.length > 500) return;
        if (reason.length === 0) return;
        setValidated(true);

        const date = new Date();
        const year = date.getFullYear();
        const month = (1 + date.getMonth()).toString();
        const day = date.getDate().toString();
        const report = {
            reviewID: props.reviewID!,
            reason,
            timestamp: month + '/' + day + '/' + year,
        }
        postReport(report);
    }

    const reportForm = (
        <Form noValidate validated={validated} onSubmit={submitReport} style={{ width: '100%' }}>
            <h2 className='report-form-header'>Report Review</h2>
            <p>Does the report contain vulgar or hateful content? Submit an anonymous report here.</p>
            <div className='report-form-review-content'>
                <p className='report-form-review-content-label'>You're reporting:</p>
                <p className='report-form-review-content-text'>{props.reviewContent}</p>
            </div>
            <Form.Group className='report-form-section'>
                <Form.Label>Why are you reporting this review?</Form.Label>
                <Form.Control
                    as="textarea"
                    placeholder="Enter a reason..."
                    onChange={(e) => {
                        setReason(e.target.value);
                    }}
                />
            </Form.Group>
            <div className='d-flex justify-content-end'>
                <Button className='py-2 px-4 mr-3' variant="outline-secondary" size={isMobile ? 'sm' : undefined} onClick={props.closeForm}>Cancel</Button>
                <Button className='py-2 px-4' type="submit" variant="secondary" size={isMobile ? 'sm' : undefined}>Submit</Button>
            </div>
        </Form>
    );

    return (
        <Modal show={props.showForm} animation={false} onHide={props.closeForm}>
            <div className='report-form'>
                {reportSubmitted ? (
                    <div className='submitted-report-form'>
                        <Icon name='check circle' size='huge' />
                        <h1>Thank You</h1>
                        <p>Your report has been submitted successfully.</p>
                    </div>
                ) : reportForm}
            </div>
        </Modal>
    );
}
Example #11
Source File: Chat.tsx    From communitymap-ui with Apache License 2.0 5 votes vote down vote up
AddNewChatObject: React.FC<{
  type: ObjectItemInput['type'];
  onPost: (data: ObjectItemInput) => void;
}> = ({ type, onPost }) => {
  const [state, setState] = useState({ valid_until: 12 * 60 } as any);
  const onChange = (e: any) => {
    const { name, value } = e.target;
    console.debug(e.target.name, e.target.value);
    setState({ ...state, [name]: value });
  };
  return (
    <div className="add-new-chat">
      <h4>
        <Icon name={type2icon(type)} /> {type2title(type)}
      </h4>
      <Form
        onSubmit={(e) => {
          e.preventDefault();
          console.debug('submit', state);
          const { topic, message, valid_until } = state;

          onPost({
            type,
            title: topic || message,
            description: message,
            valid_until: dayjs().add(valid_until, 'minute').toISOString(),
          });
        }}
      >
        <Form.Input
          autoComplete="off"
          label="Topic (optional)"
          name="topic"
          onChange={onChange}
        />
        <Form.TextArea
          autoFocus
          label="Message"
          name="message"
          onChange={onChange}
        />
        <Form.Dropdown
          options={validUntilOptions}
          label="Valid for next"
          selection
          defaultValue={state.valid_until}
          onChange={(e, { value }) =>
            setState({ ...state, valid_until: value })
          }
        />

        <Form.Button primary>Post</Form.Button>
      </Form>
    </div>
  );
}
Example #12
Source File: Timer2.tsx    From Riakuto-StartingReact-ja3.1 with Apache License 2.0 5 votes vote down vote up
Timer: VFC<{ limit: number }> = ({ limit }) => {
  const [timeLeft, setTimeLeft] = useState(limit);
  const primes = useMemo(() => getPrimes(limit), [limit]);
  const timerId = useRef<NodeJS.Timeout>();
  const reset = useCallback(() => setTimeLeft(limit), [limit]);
  const tick = () => setTimeLeft((t) => t - 1);

  useEffect(() => {
    const clearTimer = () => {
      if (timerId.current) clearInterval(timerId.current);
    };

    reset();
    clearTimer();
    timerId.current = setInterval(tick, 1000);

    return clearTimer;
  }, [limit, reset]);

  useEffect(() => {
    if (timeLeft === 0) reset();
  }, [timeLeft, reset]);

  return (
    <Card>
      <Statistic className="number-board">
        <Statistic.Label>time</Statistic.Label>
        <Statistic.Value
          className={primes.includes(timeLeft) ? 'prime-number' : undefined}
        >
          {timeLeft}
        </Statistic.Value>
      </Statistic>
      <Card.Content>
        <Button color="red" fluid onClick={reset}>
          <Icon name="redo" />
          Reset
        </Button>
      </Card.Content>
    </Card>
  );
}
Example #13
Source File: announcement.tsx    From website with MIT License 5 votes vote down vote up
Announcement = () => {
	const [readyToAnnounce, setReadyToAnnounce] = React.useState(false);
	const [dateNow, setDateNow] = React.useState(new Date());
	const [dismissAnnouncement, setDismissAnnouncement] = useStateWithStorage(
		'dismissAnnouncement',	/* cookie name */
		undefined, /* initial value */
		{
			rememberForever: true,
			onInitialize: () => setReadyToAnnounce(true)
		} /* storage options */
	);

	// To avoid rendering and then hiding an announcement that was previously dismissed,
	//	wait for cookie retrieval before rendering the message in the first place
	if (!readyToAnnounce) return (<></>);

	const query = graphql`
		query AnnouncementQuery {
		  allMarkdownRemark(
			limit: 1
			sort: {fields: [frontmatter___date], order: [DESC]}
			filter: {fields: {source: {eq: "announcements"}}}
		  ) {
			edges {
			  node {
				html
				frontmatter {
				  title
				  class
				  icon
				  date
				}
				excerpt(format:HTML)
			  }
			}
		  }
		}
	`;

	const render = ({ allMarkdownRemark }) => {
		const announcements = allMarkdownRemark.edges;
		if (announcements.length === 0) return (<></>);

		const announcement = announcements[0].node;
		const datePosted = new Date(announcement.frontmatter.date);
		if (dismissAnnouncement) {
			const dateDismissed = new Date(dismissAnnouncement);
			if (dateDismissed > datePosted) return (<></>);
		}

		const dateExpires = new Date(datePosted);
		dateExpires.setDate(datePosted.getDate()+DAYS_TO_EXPIRE);
		if (dateExpires < dateNow) return (<></>);

		const isExcerpt = announcement.html !== announcement.excerpt;

		return (
			<Message icon className={announcement.frontmatter.class ?? ''} onDismiss={() => setDismissAnnouncement(new Date())}>
				<Icon name={announcement.frontmatter.icon ?? 'info'} />
				<Message.Content>
					<Message.Header>{announcement.frontmatter.title ?? 'Message from the DataCore Team'}</Message.Header>
					<div dangerouslySetInnerHTML={{ __html: announcement.excerpt }} />
					{isExcerpt && (<Link to='/announcements/'>See details...</Link>)}
				</Message.Content>
			</Message>
		);
	};

	return (
		<StaticQuery query={query} render={render} />
	);
}
Example #14
Source File: LobbyMeetingRoom.tsx    From FLECT_Amazon_Chime_Meeting with Apache License 2.0 4 votes vote down vote up
render(){
        const props = this.props as any
        const appState = props.appState as AppState
        const gs = this.props as GlobalState
        
        const thisAttendeeId = props.thisAttendeeId as string
        const thisMeetingId  = props.thisMeetingId as string
        const attendeeInfo = appState.joinedMeetings[thisMeetingId].roster[thisAttendeeId]
        let attendeeName = "no focused"
        //let meetingShortId = thisMeetingId.substr(0,5)
        const meetingName = gs.meetings.filter((x)=>{return x.meetingId === thisMeetingId})[0].meetingName
        

        let muted = <div/>
        if(attendeeInfo === undefined){
        }else{
            attendeeName = (attendeeInfo.name !== undefined && attendeeInfo.name !== null)? attendeeInfo.name!.substring(0,20) : "unknown"
            muted = attendeeInfo.muted ? (<Icon name="mute"  color="red" />) : (<Icon name="unmute"/>)
        }

        const focusedTileId = getTileId(thisAttendeeId, appState.joinedMeetings[thisMeetingId].videoTileStates)
        if(focusedTileId > 0 && this.mainOverlayVideoRef.current !== null){
            if(appState.joinedMeetings[thisMeetingId].videoTileStates[focusedTileId].localTile){
                const videoElement = this.mainOverlayVideoRef.current.getVideoRef().current!
                appState.joinedMeetings[thisMeetingId].meetingSession?.audioVideo.bindVideoElement(focusedTileId, videoElement)
                videoElement.style.setProperty('-webkit-transform', 'scaleX(1)');
                videoElement.style.setProperty('transform', 'scaleX(1)');
            }else{
                appState.joinedMeetings[thisMeetingId].meetingSession?.audioVideo.bindVideoElement(focusedTileId, this.mainOverlayVideoRef.current.getVideoRef().current!)
            }
        }else{
            console.log("not focusedid", focusedTileId, this.mainOverlayVideoRef.current)
        }

        return(
            <Grid>
                <Grid.Row>
                    <Grid.Column>
                        
                        <div>
                            <MainOverlayVideoElement {...props} ref={this.mainOverlayVideoRef}/>
                        </div>
                        <span>
                            {muted}
                            {attendeeName} @ {meetingName}
                        </span>

                        <span style={{paddingLeft:"10px"}}>
                            <Icon name="pencil" color={this.state.enableDrawing? "red":"grey"}
                                link onClick ={
                                    ()=>{
                                        this.mainOverlayVideoRef.current!.setDrawingMode(!this.state.enableDrawing)
                                        this.mainOverlayVideoRef.current!.setErasing(false)
                                        this.setState({
                                            enableDrawing:!this.state.enableDrawing,
                                            erasing:false,
                                        })
                                    }
                                }
                            />
                            {this.labelExampleCircular()}
                            <Icon name="eraser" color={this.state.erasing? "red":"grey"}
                                link onClick={
                                    ()=>{
                                        this.mainOverlayVideoRef.current!.setErasing(true)
                                        this.setState({
                                            erasing:true,
                                            enableDrawing:false
                                        })
                                    }
                                } 
                            />
                            <Icon name="file outline" link onClick={()=>{this.mainOverlayVideoRef.current!.clearDrawingCanvas()}} />
                        </span>


                        <span style={{paddingLeft:"10px"}}>
                            <Icon basic link name="long arrow alternate left"  size="large"
                                onClick={() => {
                                    props.setForegroundPosition(ForegroundPosition.BottomLeft)
                                }}
                            />
                            <Icon basic link name="long arrow alternate right"  size="large"
                                onClick={() => {
                                    props.setForegroundPosition(ForegroundPosition.BottomRight)
                                }}
                            />
                            <Icon basic link name="square outline"  size="large"
                                onClick={() => {
                                    props.setForegroundSize(ForegroundSize.Full)
                                }}
                            />
                            <Icon basic link name="expand"  size="large"
                                onClick={() => {
                                    props.setForegroundSize(ForegroundSize.Large)
                                }}
                            />
                            <Icon basic link name="compress"  size="large"
                                onClick={() => {
                                    props.setForegroundSize(ForegroundSize.Small)
                                }}
                            />
                        </span>
                        
                        <span style={{paddingLeft:"10px"}}>
                            <Label as="a" onClick={() => { props.setVirtualForeground(VirtualForegroundType.None) }} >
                                <Icon name="square outline" size="large"/>
                                None
                            </Label> 
                            <Label as="a" onClick={() => { props.setVirtualForeground(VirtualForegroundType.Canny) }} >
                                <Icon name="chess board" size="large"/>
                                canny
                            </Label> 
                            <Label as="a" onClick={() => { props.setVirtualForeground(VirtualForegroundType.Ascii) }} >
                                <Icon name="font" size="large"/>
                                ascii
                            </Label> 
                        </span>

                    </Grid.Column>
                </Grid.Row>
            </Grid>
        )
    }
Example #15
Source File: App.tsx    From watchparty with MIT License 4 votes vote down vote up
render() {
    const sharer = this.state.participants.find((p) => p.isScreenShare);
    const controls = (
      <Controls
        key={this.state.controlsTimestamp}
        togglePlay={this.togglePlay}
        onSeek={this.onSeek}
        fullScreen={this.fullScreen}
        toggleMute={this.toggleMute}
        toggleSubtitle={this.toggleSubtitle}
        setVolume={this.setVolume}
        jumpToLeader={this.jumpToLeader}
        paused={this.state.currentMediaPaused}
        muted={this.isMuted()}
        subtitled={this.isSubtitled()}
        currentTime={this.getCurrentTime()}
        duration={this.getDuration()}
        disabled={!this.haveLock()}
        leaderTime={this.isHttp() ? this.getLeaderTime() : undefined}
        isPauseDisabled={this.isPauseDisabled()}
      />
    );
    const subscribeButton = (
      <SubscribeButton
        user={this.props.user}
        isSubscriber={this.props.isSubscriber}
        isCustomer={this.props.isCustomer}
      />
    );
    const displayRightContent =
      this.state.showRightBar || this.state.fullScreen;
    const rightBar = (
      <Grid.Column
        width={displayRightContent ? 4 : 1}
        style={{ display: 'flex', flexDirection: 'column' }}
        className={`${
          this.state.fullScreen
            ? 'fullHeightColumnFullscreen'
            : 'fullHeightColumn'
        }`}
      >
        <Input
          inverted
          fluid
          label={'My name is:'}
          value={this.state.myName}
          onChange={this.updateName}
          style={{ visibility: displayRightContent ? '' : 'hidden' }}
          icon={
            <Icon
              onClick={() => this.updateName(null, { value: generateName() })}
              name="refresh"
              inverted
              circular
              link
            />
          }
        />
        {
          <Menu
            inverted
            widths={3}
            style={{
              marginTop: '4px',
              marginBottom: '4px',
              visibility: displayRightContent ? '' : 'hidden',
            }}
          >
            <Menu.Item
              name="chat"
              active={this.state.currentTab === 'chat'}
              onClick={() => {
                this.setState({ currentTab: 'chat', unreadCount: 0 });
              }}
              as="a"
            >
              Chat
              {this.state.unreadCount > 0 && (
                <Label circular color="red">
                  {this.state.unreadCount}
                </Label>
              )}
            </Menu.Item>
            <Menu.Item
              name="people"
              active={this.state.currentTab === 'people'}
              onClick={() => this.setState({ currentTab: 'people' })}
              as="a"
            >
              People
              <Label
                circular
                color={
                  getColorForString(
                    this.state.participants.length.toString()
                  ) as SemanticCOLORS
                }
              >
                {this.state.participants.length}
              </Label>
            </Menu.Item>
            <Menu.Item
              name="settings"
              active={this.state.currentTab === 'settings'}
              onClick={() => this.setState({ currentTab: 'settings' })}
              as="a"
            >
              {/* <Icon name="setting" /> */}
              Settings
            </Menu.Item>
          </Menu>
        }
        <Chat
          chat={this.state.chat}
          nameMap={this.state.nameMap}
          pictureMap={this.state.pictureMap}
          socket={this.socket}
          scrollTimestamp={this.state.scrollTimestamp}
          getMediaDisplayName={this.getMediaDisplayName}
          hide={this.state.currentTab !== 'chat' || !displayRightContent}
          isChatDisabled={this.state.isChatDisabled}
          owner={this.state.owner}
          user={this.props.user}
          ref={this.chatRef}
        />
        {this.state.state === 'connected' && (
          <VideoChat
            socket={this.socket}
            participants={this.state.participants}
            nameMap={this.state.nameMap}
            pictureMap={this.state.pictureMap}
            tsMap={this.state.tsMap}
            rosterUpdateTS={this.state.rosterUpdateTS}
            hide={this.state.currentTab !== 'people' || !displayRightContent}
            owner={this.state.owner}
            user={this.props.user}
          />
        )}
        <SettingsTab
          hide={this.state.currentTab !== 'settings' || !displayRightContent}
          user={this.props.user}
          roomLock={this.state.roomLock}
          setRoomLock={this.setRoomLock}
          socket={this.socket}
          isSubscriber={this.props.isSubscriber}
          roomId={this.state.roomId}
          isChatDisabled={this.state.isChatDisabled}
          setIsChatDisabled={this.setIsChatDisabled}
          owner={this.state.owner}
          setOwner={this.setOwner}
          vanity={this.state.vanity}
          setVanity={this.setVanity}
          roomLink={this.state.roomLink}
          password={this.state.password}
          setPassword={this.setPassword}
          clearChat={this.clearChat}
          roomTitle={this.state.roomTitle}
          setRoomTitle={this.setRoomTitle}
          roomDescription={this.state.roomDescription}
          setRoomDescription={this.setRoomDescription}
          roomTitleColor={this.state.roomTitleColor}
          setRoomTitleColor={this.setRoomTitleColor}
          mediaPath={this.state.mediaPath}
          setMediaPath={this.setMediaPath}
        />
      </Grid.Column>
    );
    return (
      <React.Fragment>
        {!this.state.isAutoPlayable && (
          <Modal inverted basic open>
            <div style={{ display: 'flex', justifyContent: 'center' }}>
              <Button
                primary
                size="large"
                onClick={() => {
                  this.setState({ isAutoPlayable: true });
                  this.setMute(false);
                  this.setVolume(1);
                }}
                icon
                labelPosition="left"
              >
                <Icon name="volume up" />
                Click to unmute
              </Button>
            </div>
          </Modal>
        )}
        {this.state.multiStreamSelection && (
          <MultiStreamModal
            streams={this.state.multiStreamSelection}
            setMedia={this.setMedia}
            resetMultiSelect={this.resetMultiSelect}
          />
        )}
        {this.state.isVBrowserModalOpen && (
          <VBrowserModal
            isSubscriber={this.props.isSubscriber}
            subscribeButton={subscribeButton}
            closeModal={() => this.setState({ isVBrowserModalOpen: false })}
            startVBrowser={this.startVBrowser}
            user={this.props.user}
            beta={this.props.beta}
          />
        )}
        {this.state.isScreenShareModalOpen && (
          <ScreenShareModal
            closeModal={() => this.setState({ isScreenShareModalOpen: false })}
            startScreenShare={this.setupScreenShare}
          />
        )}
        {this.state.isFileShareModalOpen && (
          <FileShareModal
            closeModal={() => this.setState({ isFileShareModalOpen: false })}
            startFileShare={this.setupFileShare}
          />
        )}
        {this.state.isSubtitleModalOpen && (
          <SubtitleModal
            closeModal={() => this.setState({ isSubtitleModalOpen: false })}
            socket={this.socket}
            currentSubtitle={this.state.currentSubtitle}
            src={this.state.currentMedia}
            haveLock={this.haveLock}
            getMediaDisplayName={this.getMediaDisplayName}
            beta={this.props.beta}
          />
        )}
        {this.state.error && <ErrorModal error={this.state.error} />}
        {this.state.isErrorAuth && (
          <PasswordModal
            savedPasswords={this.state.savedPasswords}
            roomId={this.state.roomId}
          />
        )}
        {this.state.errorMessage && (
          <Message
            negative
            header="Error"
            content={this.state.errorMessage}
            style={{
              position: 'fixed',
              bottom: '10px',
              right: '10px',
              zIndex: 1000,
            }}
          ></Message>
        )}
        {this.state.successMessage && (
          <Message
            positive
            header="Success"
            content={this.state.successMessage}
            style={{
              position: 'fixed',
              bottom: '10px',
              right: '10px',
              zIndex: 1000,
            }}
          ></Message>
        )}
        <TopBar
          user={this.props.user}
          isCustomer={this.props.isCustomer}
          isSubscriber={this.props.isSubscriber}
          roomTitle={this.state.roomTitle}
          roomDescription={this.state.roomDescription}
          roomTitleColor={this.state.roomTitleColor}
        />
        {
          <Grid stackable celled="internally">
            <Grid.Row id="theaterContainer">
              <Grid.Column
                width={this.state.showRightBar ? 12 : 15}
                className={
                  this.state.fullScreen
                    ? 'fullHeightColumnFullscreen'
                    : 'fullHeightColumn'
                }
              >
                <div
                  style={{
                    display: 'flex',
                    flexDirection: 'column',
                    height: '100%',
                  }}
                >
                  {!this.state.fullScreen && (
                    <React.Fragment>
                      <ComboBox
                        setMedia={this.setMedia}
                        playlistAdd={this.playlistAdd}
                        playlistDelete={this.playlistDelete}
                        playlistMove={this.playlistMove}
                        currentMedia={this.state.currentMedia}
                        getMediaDisplayName={this.getMediaDisplayName}
                        launchMultiSelect={this.launchMultiSelect}
                        streamPath={this.props.streamPath}
                        mediaPath={this.state.mediaPath}
                        disabled={!this.haveLock()}
                        playlist={this.state.playlist}
                      />
                      <Separator />
                      <div
                        className="mobileStack"
                        style={{ display: 'flex', gap: '4px' }}
                      >
                        {this.screenShareStream && (
                          <Button
                            fluid
                            className="toolButton"
                            icon
                            labelPosition="left"
                            color="red"
                            onClick={this.stopScreenShare}
                            disabled={sharer?.id !== this.socket?.id}
                          >
                            <Icon name="cancel" />
                            Stop Share
                          </Button>
                        )}
                        {!this.screenShareStream &&
                          !sharer &&
                          !this.isVBrowser() && (
                            <Popup
                              content={`Share a tab or an application. Make sure to check "Share audio" for best results.`}
                              trigger={
                                <Button
                                  fluid
                                  className="toolButton"
                                  disabled={!this.haveLock()}
                                  icon
                                  labelPosition="left"
                                  color={'instagram'}
                                  onClick={() => {
                                    this.setState({
                                      isScreenShareModalOpen: true,
                                    });
                                  }}
                                >
                                  <Icon name={'slideshare'} />
                                  Screenshare
                                </Button>
                              }
                            />
                          )}
                        {!this.screenShareStream &&
                          !sharer &&
                          !this.isVBrowser() && (
                            <Popup
                              content="Launch a shared virtual browser"
                              trigger={
                                <Button
                                  fluid
                                  className="toolButton"
                                  disabled={!this.haveLock()}
                                  icon
                                  labelPosition="left"
                                  color="green"
                                  onClick={() => {
                                    this.setState({
                                      isVBrowserModalOpen: true,
                                    });
                                  }}
                                >
                                  <Icon name="desktop" />
                                  VBrowser
                                </Button>
                              }
                            />
                          )}
                        {this.isVBrowser() && (
                          <Popup
                            content="Choose the person controlling the VBrowser"
                            trigger={
                              <Dropdown
                                icon="keyboard"
                                labeled
                                className="icon"
                                style={{ height: '36px' }}
                                button
                                value={this.state.controller}
                                placeholder="No controller"
                                clearable
                                onChange={this.changeController}
                                selection
                                disabled={!this.haveLock()}
                                options={this.state.participants.map((p) => ({
                                  text: this.state.nameMap[p.id] || p.id,
                                  value: p.id,
                                }))}
                              ></Dropdown>
                            }
                          />
                        )}
                        {this.isVBrowser() && (
                          <Dropdown
                            icon="desktop"
                            labeled
                            className="icon"
                            style={{ height: '36px' }}
                            button
                            disabled={!this.haveLock()}
                            value={this.state.vBrowserResolution}
                            onChange={(_e, data) =>
                              this.setState({
                                vBrowserResolution: data.value as string,
                              })
                            }
                            selection
                            options={[
                              {
                                text: '1080p (Plus only)',
                                value: '1920x1080@30',
                                disabled: !this.state.isVBrowserLarge,
                              },
                              {
                                text: '720p',
                                value: '1280x720@30',
                              },
                              {
                                text: '576p',
                                value: '1024x576@60',
                              },
                              {
                                text: '486p',
                                value: '864x486@60',
                              },
                              {
                                text: '360p',
                                value: '640x360@60',
                              },
                            ]}
                          ></Dropdown>
                        )}
                        {this.isVBrowser() && (
                          <Button
                            fluid
                            className="toolButton"
                            icon
                            labelPosition="left"
                            color="red"
                            disabled={!this.haveLock()}
                            onClick={this.stopVBrowser}
                          >
                            <Icon name="cancel" />
                            Stop VBrowser
                          </Button>
                        )}
                        {!this.screenShareStream &&
                          !sharer &&
                          !this.isVBrowser() && (
                            <Popup
                              content="Stream your own video file"
                              trigger={
                                <Button
                                  fluid
                                  className="toolButton"
                                  disabled={!this.haveLock()}
                                  icon
                                  labelPosition="left"
                                  onClick={() => {
                                    this.setState({
                                      isFileShareModalOpen: true,
                                    });
                                  }}
                                >
                                  <Icon name="file" />
                                  File
                                </Button>
                              }
                            />
                          )}
                        {false && (
                          <SearchComponent
                            setMedia={this.setMedia}
                            playlistAdd={this.playlistAdd}
                            type={'youtube'}
                            streamPath={this.props.streamPath}
                            disabled={!this.haveLock()}
                          />
                        )}
                        {Boolean(this.props.streamPath) && (
                          <SearchComponent
                            setMedia={this.setMedia}
                            playlistAdd={this.playlistAdd}
                            type={'stream'}
                            streamPath={this.props.streamPath}
                            launchMultiSelect={this.launchMultiSelect}
                            disabled={!this.haveLock()}
                          />
                        )}
                      </div>
                      <Separator />
                    </React.Fragment>
                  )}
                  <div style={{ flexGrow: 1 }}>
                    <div id="playerContainer">
                      {(this.state.loading ||
                        !this.state.currentMedia ||
                        this.state.nonPlayableMedia) && (
                        <div
                          id="loader"
                          className="videoContent"
                          style={{
                            display: 'flex',
                            alignItems: 'center',
                            justifyContent: 'center',
                          }}
                        >
                          {this.state.loading && (
                            <Dimmer active>
                              <Loader>
                                {this.isVBrowser()
                                  ? 'Launching virtual browser. This can take up to a minute.'
                                  : ''}
                              </Loader>
                            </Dimmer>
                          )}
                          {!this.state.loading && !this.state.currentMedia && (
                            <Message
                              color="yellow"
                              icon="hand point up"
                              header="You're not watching anything!"
                              content="Pick something to watch above."
                            />
                          )}
                          {!this.state.loading &&
                            this.state.nonPlayableMedia && (
                              <Message
                                color="red"
                                icon="frown"
                                header="It doesn't look like this is a media file!"
                                content="Maybe you meant to launch a VBrowser if you're trying to visit a web page?"
                              />
                            )}
                        </div>
                      )}
                      <iframe
                        style={{
                          display:
                            this.isYouTube() && !this.state.loading
                              ? 'block'
                              : 'none',
                        }}
                        title="YouTube"
                        id="leftYt"
                        className="videoContent"
                        allowFullScreen
                        frameBorder="0"
                        allow="autoplay"
                        src="https://www.youtube.com/embed/?enablejsapi=1&controls=0&rel=0"
                      />
                      {this.isVBrowser() &&
                      this.getVBrowserPass() &&
                      this.getVBrowserHost() ? (
                        <VBrowser
                          username={this.socket.id}
                          password={this.getVBrowserPass()}
                          hostname={this.getVBrowserHost()}
                          controlling={this.state.controller === this.socket.id}
                          setLoadingFalse={this.setLoadingFalse}
                          resolution={this.state.vBrowserResolution}
                          doPlay={this.doPlay}
                          setResolution={(data: string) =>
                            this.setState({ vBrowserResolution: data })
                          }
                        />
                      ) : (
                        <video
                          style={{
                            display:
                              (this.isVideo() && !this.state.loading) ||
                              this.state.fullScreen
                                ? 'block'
                                : 'none',
                            width: '100%',
                            maxHeight:
                              'calc(100vh - 62px - 36px - 36px - 8px - 41px - 16px)',
                          }}
                          id="leftVideo"
                          onEnded={this.onVideoEnded}
                          playsInline
                        ></video>
                      )}
                    </div>
                  </div>
                  {this.state.currentMedia && controls}
                  {Boolean(this.state.total) && (
                    <div>
                      <Progress
                        size="tiny"
                        color="green"
                        inverted
                        value={this.state.downloaded}
                        total={this.state.total}
                        // indicating
                        label={
                          Math.min(
                            (this.state.downloaded / this.state.total) * 100,
                            100
                          ).toFixed(2) +
                          '% - ' +
                          formatSpeed(this.state.speed) +
                          ' - ' +
                          this.state.connections +
                          ' connections'
                        }
                      ></Progress>
                    </div>
                  )}
                </div>
                <Button
                  style={{
                    position: 'absolute',
                    top: '50%',
                    right: 'calc(0% - 18px)',
                    zIndex: 900,
                  }}
                  circular
                  size="mini"
                  icon={this.state.showRightBar ? 'angle right' : 'angle left'}
                  onClick={() =>
                    this.setState({ showRightBar: !this.state.showRightBar })
                  }
                />
              </Grid.Column>
              {rightBar}
            </Grid.Row>
          </Grid>
        }
      </React.Fragment>
    );
  }
Example #16
Source File: AppHeader.tsx    From peterportal-client with MIT License 4 votes vote down vote up
AppHeader: FC<{}> = props => {
  const dispatch = useAppDispatch();
  const sidebarOpen = useAppSelector(state => state.ui.sidebarOpen);
  const location = useLocation();
  const [week, setWeek] = useState('');

  let splitLocation = location.pathname.split('/');
  let coursesActive = splitLocation.length > 0 && splitLocation[splitLocation.length - 1] == 'courses' || splitLocation.length > 1 && splitLocation[1] == 'course';
  let professorsActive = splitLocation.length > 0 && splitLocation[splitLocation.length - 1] == 'professors' || splitLocation.length > 1 && splitLocation[1] == 'professor';

  useEffect(() => {
    // Get the current week data
    axios.get<WeekData>('/schedule/api/currentWeek')
      .then(res => {
        // case for break and finals week
        if (res.data.week == -1) {
          setWeek(res.data.display);
        }
        // case when school is in session 
        else {
          setWeek('Week ' + res.data.week + ' • ' + res.data.quarter);
        }
      });
  }, [])

  let toggleMenu = () => {
    dispatch(setSidebarStatus(!sidebarOpen));
  }

  return (
    <header className='navbar'>
      <div className='navbar-nav'>
        <div className='navbar-left'>
          {/* Hamburger Menu */}
          <div className='navbar-menu'>
            <List className='navbar-menu-icon' onClick={toggleMenu} />
          </div>

          {/* Toggle Course and Professor */}
          {(coursesActive || professorsActive) && <div className='navbar-toggle'>
            <div className='desktop-toggle'>
              <div className={`navbar-toggle-item ${coursesActive ? 'active' : ''}`}>
                <a href='/search/courses'>
                  Courses
                </a>
              </div>
              <div className={`navbar-toggle-item ${professorsActive ? 'active' : ''}`}>
                <a href='/search/professors'>
                  Professors
                </a>
              </div>
            </div>
            <div className='mobile-toggle'>
              {coursesActive === true && (
                <div className={`navbar-toggle-item active`}>
                  <a href='/search/professors'>
                    Professors
                  </a>
                </div>
              )}
              {professorsActive === true && (
                <div className={`navbar-toggle-item active`}>
                  <a href='/search/courses'>
                    Courses
                  </a>
                </div>
              )}
            </div>
          </div>
          }
        </div>

        {/* Logo */}
        <div className='navbar-logo'>
          <a href='/'>
            <img alt='PeterPortal' id='peterportal-logo' src={Logo}></img>
          </a>
        </div>

        {/* Week */}
        <div style={{ display: 'flex', alignItems: 'center' }}>
          <div className='beta' style={{ margin: "auto 12px" }}>
            <Popup style={{ padding: "36px", width: "400px" }} position='bottom right' trigger={<Label as='a' color='yellow' image>beta<Label.Detail>v1.1</Label.Detail></Label>} flowing hoverable >
              <Grid centered columns={1}>
                <Grid.Column textAlign='left'>
                  <h4>Beta Disclaimer</h4>
                  <p>
                    Please note that this is a beta version of PeterPortal, which is still undergoing development.
                    Some content on this web application may not be accurate. Users are encouraged to double check details.
                    <br />
                    <br />
                    Should you encounter any bugs, glitches, lack of functionality or other problems on the application,
                    please let us know immediately so we can rectify these accordingly. Your help in this regard is greatly appreciated.
                  </p>
                  <div className='feedback'>
                    <a className="ui button" href="https://github.com/icssc-projects/peterportal-client/issues/new" target="_blank" rel="noopener noreferrer">
                      <Icon name='github' />Report an issue
                    </a>
                    <a className="ui button" href="https://forms.gle/JjwBmELq26daroTh9" target="_blank" rel="noopener noreferrer">
                      <Icon name='clipboard list' />Feedback
                    </a>
                  </div>
                </Grid.Column>
              </Grid>
            </Popup>
          </div>
          <p className='school-term' style={{ height: '1rem', lineHeight: '1rem' }}>
            {week}
          </p>
        </div>
      </div>
    </header>
  );
}
Example #17
Source File: Chat.tsx    From communitymap-ui with Apache License 2.0 4 votes vote down vote up
Chat: React.FC<ObjectItemComponentProps> = ({
  item,
  user,
  expanded,
  onClick,
  onVote,
  onComment,
  onClose,
}) => {
  const {
    type,
    author,
    title,
    description,
    created,
    userVoted,
    votesCount: votes,
    comments,
  } = item;

  const [showCommentsWidget, setShowCommentsWidget] = useState(false);

  const commentsCount = comments?.length || 0;

  const sortedComments = useMemo(() => {
    if (!comments) return comments;
    return comments.sort((l, r) => (l.created < r.created ? -1 : 1));
  }, [comments]);

  const icon: any = type2icon(type);

  return (
    <div
      className={cx({ item: true, 'chat-item': true, expanded })}
      onClick={onClick}
    >
      <div className="title">
        <Icon name={icon} />
        {title}
      </div>
      {expanded && (
        <div className="author-created">
          <AuthorWidget userId={author} watchForChanges />
          {' on '}
          {new Date(created).toLocaleString()}
        </div>
      )}
      <br />
      {expanded && description !== title && (
        <section className="description">{description}</section>
      )}
      {!!commentsCount && (
        <div className="replies-count">{commentsCount} replies</div>
      )}
      <div className="actions">
        <LikeWidget votes={votes} userVoted={userVoted} onVote={onVote} />

        <div>
          {expanded && user?.uid === author && (
            <Button
              icon="close"
              content="Close"
              basic
              onClick={() => {
                if (window.confirm('Are you sure you want to close it?'))
                  onClose().catch(reportError);
              }}
            />
          )}
          {!showCommentsWidget && !expanded && (
            <Button
              basic
              onClick={(e) => {
                e.stopPropagation();
                setShowCommentsWidget(true);
              }}
            >
              Reply
            </Button>
          )}
        </div>
      </div>
      {expanded && !!commentsCount && (
        <section>
          <h4 className="pale-heading">Replies</h4>
          <CommentsList comments={sortedComments!} />
        </section>
      )}
      {(showCommentsWidget || expanded) &&
        (!!user ? (
          <PostCommentWidget onComment={onComment} />
        ) : (
          <div style={{ textAlign: 'center' }}>
            You need to register or sign in to be able to post
          </div>
        ))}
    </div>
  );
}
Example #18
Source File: collectionstool.tsx    From website with MIT License 4 votes vote down vote up
CollectionsTool = (props: CollectionsToolProps) => {
	const { playerData } = props;

	const [allCollections, setAllCollections] = React.useState(undefined);

	if (!allCollections) {
		fetch('/structured/collections.json')
			.then(response => response.json())
			.then(collections => {
				setAllCollections(collections);
			});
		return (<><Icon loading name='spinner' /> Loading...</>);
	}

	const allCrew = JSON.parse(JSON.stringify(props.allCrew));
	const myCrew = JSON.parse(JSON.stringify(playerData.player.character.crew));

	const collectionCrew = [...new Set(allCollections.map(ac => ac.crew).flat())].map(acs => {
		const crew = JSON.parse(JSON.stringify(allCrew.find(ac => ac.symbol == acs)));
		crew.highest_owned_rarity = 0;
		crew.highest_owned_level = 0;
		crew.immortal = false;
		crew.collectionIds = [];
		crew.unmaxedIds = [];
		crew.immortalRewards = [];
		const owned = myCrew.filter(mc => mc.symbol === acs).sort((a, b) => {
			if (a.rarity == b.rarity) {
				if (a.level == b.level) return b.equipment.length - a.equipment.length;
				return b.level - a.level;
			}
			return b.rarity - a.rarity;
		});
		if (owned.length > 0) {
			crew.immortal = owned[0].level == 100 && owned[0].rarity == owned[0].max_rarity && owned[0].equipment.length == 4;
			crew.highest_owned_rarity = owned[0].rarity;
			crew.highest_owned_level = owned[0].level;
		}
		return crew;
	});

	const playerCollections = allCollections.map(ac => {
		let collection = { name: ac.name, progress: 0, milestone: { goal: 0 } };
		if (playerData.player.character.cryo_collections) {
			const pc = playerData.player.character.cryo_collections.find((pc) => pc.name === ac.name);
			if (pc) collection = JSON.parse(JSON.stringify(pc));
		}
		collection.id = ac.id; // Use allCollections ids instead of ids in player data
		collection.crew = ac.crew;
		collection.simpleDescription = collection.description ? simplerDescription(collection.description) : '';
		collection.progressPct = collection.milestone.goal > 0 ? collection.progress / collection.milestone.goal : 1;
		collection.neededPct = 1 - collection.progressPct;
		collection.needed = collection.milestone.goal > 0 ? Math.max(collection.milestone.goal - collection.progress, 0) : 0;
		collection.totalRewards = collection.milestone.buffs?.length + collection.milestone.rewards?.length;
		collection.owned = 0;
		ac.crew.forEach(acs => {
			let cc = collectionCrew.find(crew => crew.symbol === acs);
			cc.collectionIds.push(collection.id);
			if (collection.milestone.goal > 0) {
				cc.unmaxedIds.push(collection.id);
				if (collection.milestone.goal - collection.progress <= 1) {
					mergeRewards(cc.immortalRewards, collection.milestone.buffs);
					mergeRewards(cc.immortalRewards, collection.milestone.rewards);
				}
			}
			if (cc.highest_owned_rarity > 0) collection.owned++;
		});
		return collection;
	});

	return (
		<CollectionsUI playerCollections={playerCollections} collectionCrew={collectionCrew} />
	);

	function mergeRewards(current: any[], rewards: any[]): void {
		if (rewards.length == 0) return;
		rewards.forEach(reward => {
			const existing = current.find(c => c.symbol === reward.symbol);
			if (existing) {
				existing.quantity += reward.quantity;
			}
			else {
				current.push(JSON.parse(JSON.stringify(reward)));
			}
		});
	}

	function simplerDescription(description: string): string {
		let simple = description.replace(/(<([^>]+)>)/g, '')
			.replace('Immortalize ', '')
			.replace(/^the /i, '')
			.replace(/\.$/, '');
		return simple.substr(0, 1).toUpperCase() + simple.substr(1);
	}
}