react-bootstrap#Tabs TypeScript Examples
The following examples show how to use
react-bootstrap#Tabs.
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: InfoTabs.tsx From devex with GNU General Public License v3.0 | 6 votes |
InfoTabs: React.FC<IProps> = ({ tabs }) => {
const { tabHeaders, tabTitles, tabContents } = tabs
const [currTab, setCurrTab] = useState(tabHeaders[0])
return <>
{tabHeaders.length > 0
? <Card className='tabs-card'>
<Card.Header className='tabs-card-header'>
<Tabs id="info-tabs" activeKey={currTab} onSelect={(k: string) => setCurrTab(k)}>
{tabHeaders.map((tabHeader: string, index: number) => (
<Tab key={index} eventKey={tabHeader} title={[tabTitles[index]]} />
))}
</Tabs>
</Card.Header>
<Card.Body>
<Container>
{tabContents[tabHeaders.indexOf(currTab)]}
</Container>
</Card.Body>
</Card>
: null}
</>
}
Example #2
Source File: RcsbTabs.tsx From rcsb-saguaro-app with MIT License | 6 votes |
render(): JSX.Element {
return (<div className={classes.bootstrapGroupSequenceComponentScope}>
<Tabs
id={this.props.id}
defaultActiveKey={this.props.default}
onSelect={(eventKey: string)=>{
this.props.onSelect(eventKey as T);
}}
>
{
this.props.tabList.map((tab,n)=>(
<Tab eventKey={tab.key} title={tab.title}>
{
tab.additionalComponent
}
<div id={tab.key+RcsbTabs.UI_SUFFIX} style={{height:20}}/>
<div id={tab.key}/>
</Tab>
))
}
</Tabs>
</div>);
}
Example #3
Source File: CommunityView.tsx From 3Speak-app with GNU General Public License v3.0 | 5 votes |
export function CommunityView(props: any) {
const [communityInfo, setCommunityInfo] = useState({} as any)
const [newVideos, setNewVideos] = useState([])
const [trendingVideos, setTrendingVideos] = useState([])
const [backgroundUrl, setBackgroundUrl] = useState(null)
const reflink = useMemo(() => {
return RefLink.parse(props.match.params.reflink)
}, [props.match])
const generate = async () => {
const commInfo = await client.call('bridge', 'get_community', {
name: reflink.root,
observer: 'alice',
})
setCommunityInfo(commInfo)
const trendingVideosRes = (
await axios.get(`https://3speak.tv/apiv2/feeds/community/${reflink.root}/trending`)
).data
const newVideosRes = (
await axios.get(`https://3speak.tv/apiv2/feeds/community/${reflink.root}/new`)
).data
setTrendingVideos(trendingVideosRes)
setNewVideos(newVideosRes)
const bgUrlRes = await AccountService.getProfileBackgroundImageUrl(props.match.params.reflink)
setBackgroundUrl(bgUrlRes)
}
useEffect(() => {
void generate()
}, [reflink])
return (
<div>
<div
style={{
position: 'relative',
display: 'inline-block',
width: '100%',
minHeight: '400px',
backgroundAttachment: 'fixed',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
background: `url(${backgroundUrl})`,
}}
>
<img
className="channel-profile-img"
style={{ position: 'absolute', bottom: '10px', left: '10px' }}
alt=""
src={`https://images.hive.blog/u/${reflink.root}/avatar`}
/>
<h1 style={{ position: 'absolute', bottom: '10px', left: '150px' }}>
<b
style={{
color: 'white',
textShadow: '-1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000',
}}
>
{communityInfo.title}
</b>
</h1>
</div>
<h4 className="mt-3">
<ReactMarkdown source={communityInfo.about} />
</h4>
<p>{communityInfo.description}</p>
<hr />
<Tabs>
<Tab eventKey="videos" title="Videos" variant="secondary">
<hr />
<Tabs>
<Tab eventKey="trending" title="Show Trending">
<h3 id="videoSectionHeading">Trending Videos</h3>
<hr />
<div>
{trendingVideos !== null ? (
<GridFeedView key="community-trends" type={`#${reflink.root}/trending`} />
) : null}
</div>
</Tab>
<Tab eventKey="new" title="Show New">
<h3 id="videoSectionHeading">New Videos</h3>
<hr />
<div>
{newVideos !== null ? (
<GridFeedView key="community-new" type={`#${reflink.root}/new`} />
) : null}
</div>
</Tab>
</Tabs>
</Tab>
<Tab eventKey="polls" title="Polls"></Tab>
<Tab eventKey="stats" title="Stats">
<div className="row">
<div className="card col-lg-6 col-md-11 col-sm-12 col-xs-11 col-xl-5 ml-2 mt-2">
<div className="card-header">
<h3>More Info</h3>
</div>
<div className="card-body">
<h5>
Language: {by639_1[communityInfo.lang] ? by639_1[communityInfo.lang].name : null}
</h5>
<h5>Community:</h5>
<b className="text-success">{communityInfo.num_pending}</b> posts waiting to cash
out
<br />
<b className="text-success">${communityInfo.sum_pending}</b> pending rewards
<br />
<b className="text-success">{communityInfo.subscribers}</b> subscribers
<br />
<p>
<b className="text-success">{communityInfo.num_authors}</b> active authors
</p>
<br />
{communityInfo.is_nsfw === true ? (
<h5 className="text-danger">NSFW</h5>
) : (
<h5 className="text-success">Not NSFW</h5>
)}
</div>
</div>
<div className="card col-lg-6 col-md-8 col-sm-10 col-xs-11 col-xl-5 ml-2 mt-2">
<div className="card-header">
<h3>The team</h3>
</div>
<div className="card-body">
<table className="table">
<thead>
<tr>
<td>User</td>
<td>Role</td>
<td>Nickname</td>
</tr>
</thead>
<tbody>
{communityInfo.team
? communityInfo.team.map((value) => (
<tr key={value[0]}>
<td>
<a href={`#/user/hive:${value[0]}`}>{value[0]}</a>
</td>
<td>{value[1]}</td>
<td>{value[2]}</td>
</tr>
))
: null}
</tbody>
</table>
</div>
</div>
</div>
</Tab>
</Tabs>
</div>
)
}
Example #4
Source File: CommandCodePage.tsx From apps with MIT License | 5 votes |
render() {
if (this.state.error) return <ErrorStatus error={this.state.error} />;
if (this.state.loading || !this.state.commandCode) return <Loading />;
const commandCode = this.state.commandCode;
return (
<div>
<CommandCodePicker
region={this.props.region}
commandCodes={this.state.commandCodes}
id={commandCode.id}
/>
<hr />
<Row
style={{
marginBottom: "3%",
}}
>
<Col xs={{ span: 12, order: 2 }} lg={{ span: 6, order: 1 }}>
<CommandCodeMainData region={this.props.region} commandCode={commandCode} />
</Col>
<Col xs={{ span: 12, order: 1 }} lg={{ span: 6, order: 2 }}>
<CommandCodePortrait commandCode={commandCode} />
</Col>
</Row>
<Tabs
id={"cc-tabs"}
defaultActiveKey={this.props.tab ?? "effects"}
mountOnEnter={false}
onSelect={(key: string | null) => {
this.props.history.replace(`/${this.props.region}/command-code/${this.props.id}/${key}`);
}}
>
<Tab eventKey={"effects"} title={"Effects"}>
<br />
{commandCode.skills.map((skill) => {
return (
<SkillBreakdown
key={skill.id}
region={this.props.region}
skill={skill}
cooldowns={true}
/>
);
})}
</Tab>
<Tab eventKey={"assets"} title={"Assets"}>
<br />
<CommandCodeAsset region={this.props.region} commandCode={commandCode} />
</Tab>
</Tabs>
</div>
);
}
Example #5
Source File: ItemsPage.tsx From apps with MIT License | 5 votes |
render() {
if (this.state.error) return <ErrorStatus error={this.state.error} />;
if (this.state.loading) return <Loading />;
const tabProperty = this.props.tab ?? "servant-materials",
index = this.state.tabs.findIndex((tab) => tab.key === tabProperty);
const tab = index < 0 ? { items: [] } : this.state.tabs[index],
items = this.applySearch(tab.items, this.state.search ?? ""),
hasPaginator = items.length > this.state.perPage;
return (
<div id="items" className="listing-page">
<Row>
<Col xs={12} sm={6} md={9}>
{hasPaginator && <div>{this.paginator(items.length)}</div>}
</Col>
<Col xs={12} sm={6} md={3} id="item-search">
<Form inline>
<Form.Control
placeholder={"Search"}
value={this.state.search ?? ""}
onChange={(ev: ChangeEvent) => {
this.setState({ search: ev.target.value });
}}
/>
</Form>
</Col>
</Row>
<hr />
<Tabs
id={"items-tabs"}
defaultActiveKey={this.props.tab ?? "servant-materials"}
mountOnEnter={true}
onSelect={(key: string | null) => {
this.props.history.replace(`/${this.props.region}/items/${key}`);
}}
>
{this.state.tabs.map((_, index) => this.renderTab(index))}
</Tabs>
{hasPaginator && <div>{this.paginator(items.length)}</div>}
</div>
);
}
Example #6
Source File: WarPage.tsx From apps with MIT License | 5 votes |
WarMapList = (props: {
region: Region;
title: string;
maps: War.Map[];
spots: War.Spot[];
warName: string;
warId: number;
last?: boolean;
}) => {
const groupBy = <T,>(array: T[], property: (x: T) => string): { [key: string]: Array<T> } =>
array.reduce((acc: { [key: string]: Array<T> }, cur: T) => {
if (!acc[property(cur)]) {
acc[property(cur)] = [];
}
acc[property(cur)].push(cur);
return acc;
}, {});
const mapsById = groupBy(props.maps, (map) => `${map.id}`);
let last = false;
const warMaps = (
<Tabs id="war-maps-tabs" className="mb-3">
{Object.keys(mapsById).map((mapId, index, array) => {
let mapSpots = props.spots
.filter((spot) => spot.mapId === +mapId)
.filter((spot) => spot.quests.some((quest) => quest.afterClear === "repeatLast"))
.filter((spot) => spot.x || spot.y);
last = index === array.length - 1;
return mapSpots.length > 0 ? (
<Tab eventKey={`${mapId}`} key={mapId} title={`#${mapId}`}>
<WarMap
region={props.region}
key={index}
map={mapsById[mapId][0]}
spots={mapSpots}
warName={props.warName}
warId={props.warId}
/>
</Tab>
) : null;
})}
</Tabs>
);
return renderCollapsibleContent(
{
title: props.title,
content: warMaps,
subheader: false,
},
!last
);
}
Example #7
Source File: index.tsx From polkabtc-ui with Apache License 2.0 | 5 votes |
InterlayTabs = (props: TabsProps): JSX.Element => ( <Tabs {...props} /> )
Example #8
Source File: UploaderView.tsx From 3Speak-app with GNU General Public License v3.0 | 4 votes |
export function UploaderView() {
const videoUpload = useRef<any>()
const thumbnailUpload = useRef<any>()
const thumbnailPreview = useRef('')
const publishForm = useRef()
const hwaccelOption = useRef()
const ipfs = useRef<any>()
const [logData, setLogData] = useState([])
const [videoSourceFile, setVideoSourceFile] = useState()
const [encodingInProgress, setEncodingInProgress] = useState(false)
const [progress, setProgress] = useState<any>({})
const [statusInfo, setStatusInfo] = useState<any>({ progress: {} })
const [estimatedTimeRemaining, setEstimatedTimeRemaining] = useState('')
const [videoInfo, setVideoInfo] = useState<any>({
path: null,
size: 0,
cid: null,
language: '',
duration: null,
})
const [thumbnailInfo, setThumbnailInfo] = useState({
path: null,
size: 0,
cid: null,
})
const [startTime, setStartTime] = useState<number>()
const [endTime, setEndTime] = useState<number>(0)
const [publishReady, setPublishReady] = useState(false)
const [blockedGlobalMessage, setBlockedGlobalMessage] = useState('')
useEffect(() => {
ipfs.current = IPFSHTTPClient.create({ host: IPFS_HOST })
}, [])
const caluclatePercentage = () => {
return progress.percent / statusInfo.nstages + statusInfo.stage * (100 / statusInfo.nstages)
}
const normalizeSize = () => {
const size = videoInfo.size + thumbnailInfo.size
return bytesAsString(size)
}
const compileVideoCid = async () => {
const videoCid = videoInfo.cid
if (thumbnailInfo.cid) {
const obj = await ipfs.current.object.stat(thumbnailInfo.cid)
console.log(obj)
console.log(thumbnailInfo)
const output = await ipfs.current.object.patch.addLink(videoCid, {
name: thumbnailInfo.path,
size: thumbnailInfo.size,
cid: thumbnailInfo.cid,
})
console.log(output)
return output.toString()
}
return videoCid
}
/**
* Note: example metadata https://hiveblocks.com/hive-181335/@taskmaster4450/tqxwimhy
*/
const publish = async () => {
const videoCid = await compileVideoCid()
const formData = FormUtils.formToObj(new FormData(publishForm.current))
let tags: string[] = []
if (formData.tags) {
tags = formData.tags.replace(/\s/g, '').split(',')
}
console.log(`thumbnail info`, thumbnailInfo)
const sourceMap = []
if (thumbnailInfo.path) {
sourceMap.push({
type: 'thumbnail',
url: `ipfs://${videoCid}/${thumbnailInfo.path}`,
})
}
if (videoInfo) {
sourceMap.push({
type: 'video',
url: `ipfs://${videoCid}/${videoInfo.path}`,
format: 'm3u8',
})
}
const permlink = `speak-${randomstring
.generate({
length: 8,
charset: 'alphabetic',
})
.toLowerCase()}`
// console.log(permlink)
console.log(`source map`)
console.log(sourceMap)
// console.log(videoInfo)
// console.log(typeof formData.description)
// console.log(videoCid)
setBlockedGlobalMessage('Publishing')
const filesize = videoInfo.size + thumbnailInfo.size
console.log(`formdata is `, formData)
formData.title = formData.title || 'no form data'
formData.description = formData.description || 'no form data'
console.log(`publish form is `, publishForm.current)
try {
const [reflink] = await AccountService.postComment({
accountType: 'hive',
title: formData.title,
body: formData.description,
permlink,
tags,
json_metadata: {
title: formData.title,
description: formData.description,
tags,
sourceMap,
filesize,
created: new Date(),
lang: videoInfo.language,
video: {
duration: videoInfo.duration,
},
app: '3speak/app-beta',
type: '3speak/video',
},
})
setTimeout(() => {
location.hash = `#/watch/${reflink}`
setBlockedGlobalMessage('done')
}, 15000)
} catch (error) {
console.error(`Error in postComment operation ${error.message}`)
throw error
}
}
const handleVideoSelect = async (e) => {
const file = e.target.files[0]
setVideoSourceFile(file.path)
setLogData([...logData, `Selected: ${videoInfo.path}`])
}
const handleThumbnailSelect = async (e) => {
console.log(`handling thumbnail selectr`)
const file = e.target.files[0]
const imgblob = URL.createObjectURL(file)
const size = file.size
console.log(`uploading file with size ${size}`)
thumbnailPreview.current = imgblob
const fileDetails = {
path: e.target.files[0].name,
content: e.target.files[0],
}
const ipfsOut = await ipfs.current.add(fileDetails, { pin: false })
console.log(`setting thumbnail info to cid`, ipfsOut.cid.toString())
setThumbnailInfo({
size,
path: `thumbnail.${file.type.split('/')[1]}`,
cid: ipfsOut.cid.toString(),
})
}
const handleStartEncode = async (event) => {
event.preventDefault()
if (videoSourceFile === null) {
NotificationManager.error('No video source file selected')
return
}
if (!Fs.existsSync(videoSourceFile)) {
NotificationManager.error('Source file does not exist')
return
}
setEncodingInProgress(true)
setStartTime(new Date().getTime())
setEndTime(null)
const jobInfo = (await PromiseIpc.send('encoder.createJob', {
sourceUrl: videoSourceFile,
profiles: [
{
name: '1080p',
size: '1920x1080',
},
{
name: '720p',
size: '1080x720',
},
{
name: '480p',
size: '720x480',
},
],
options: {
hwaccel:
hwaccelOption.current !== '' && hwaccelOption.current !== 'none'
? hwaccelOption.current
: undefined,
},
} as any)) as any
NotificationManager.success('Encoding Started.')
let savePct = 0
const progressTrack = async () => {
const status = (await PromiseIpc.send('encoder.status', jobInfo.id)) as any
console.log(`Encoder status: `, status)
setProgress(status.progress || {})
setStatusInfo(status)
const val = caluclatePercentage()
const diffPct = val - savePct
savePct = val
const pctPerSec = diffPct / 3
const totalTimeRemaining = (100 - val) / pctPerSec
setEstimatedTimeRemaining(secondsAsString(totalTimeRemaining))
setEndTime(new Date().getTime())
}
const pid = setInterval(progressTrack, 3000)
void progressTrack()
const encodeOutput = (await PromiseIpc.send('encoder.getjoboutput', jobInfo.id)) as any
console.log(`got encode output`)
console.log(encodeOutput)
setVideoInfo({
size: encodeOutput.size,
cid: encodeOutput.ipfsHash,
path: encodeOutput.path,
duration: Number(DateTime.parse(encodeOutput.duration, 'hh:mm:ss.SS', true)) / 1000,
})
clearInterval(pid)
setEncodingInProgress(false)
setEstimatedTimeRemaining(null)
setEndTime(new Date().getTime())
setPublishReady(true)
NotificationManager.success('Encoding complete.')
}
if (blockedGlobalMessage) {
return (
<LoadingMessage
loadingMessage={blockedGlobalMessage}
subtitle="Note: you will need to keep the app open for your video to play for other users. A process called 'shunting' will be released in the future to relieve this issue."
/>
)
}
return (
<div style={{ width: '95%', marginRight: 'auto', marginLeft: 'auto' }}>
<Row style={{ marginTop: '1.45rem' }}>
<div>
<div
className="d-table-cell align-middle card dz-clickable"
onClick={() => videoUpload.current.click()}
style={{
width: '4000px',
textAlign: 'center',
height: '150px',
fontSize: '16px',
fontWeight: 'bold',
cursor: 'pointer',
}}
>
Drop a file or click to start the upload <br />
<p>
Selected: <kbd>{videoSourceFile}</kbd>
</p>
<input
accept="video/*"
type="file"
id="video"
className="d-none"
ref={videoUpload}
onChange={handleVideoSelect}
/>
</div>
</div>
</Row>
<Row style={{ marginTop: '15px' }}>
<Col xl={6} sm={12} style={{ paddingLeft: '0px' }}>
<div className="card" style={{ padding: '10px' }}>
<Form ref={publishForm.current}>
<Form.Group>
<Form.Label>Title</Form.Label>
<Form.Control type="text" name="title"></Form.Control>
</Form.Group>
<Form.Group>
<Form.Label>Description</Form.Label>
<textarea className="form-control" name="description"></textarea>
</Form.Group>
<Form.Group>
<Form.Label>Tags</Form.Label>
<Form.Control type="text" name="tags"></Form.Control>
<small className="text-muted">
Separate multiple tags with a <kbd>,</kbd>{' '}
</small>
</Form.Group>
<Form.Group>
<Form.Label>Language</Form.Label>
<select disabled={false} name="language" className="form-control mb-4">
<option selected={false} value="en">
English
</option>
<option value="de">Deutsch</option>
<option value="fr">Français</option>
<option value="es">Español</option>
<option value="nl">Nederlands</option>
<option value="ko">한국어</option>
<option value="ru">русский</option>
<option value="hu">Magyar</option>
<option value="ro">Română</option>
<option value="cs">čeština</option>
<option value="pl">Polskie</option>
<option value="in">bahasa Indonesia</option>
<option value="bn">Bangla</option>
<option value="it">Italian</option>
</select>
</Form.Group>
<span className="form-check mb-3">
<input
type="checkbox"
className="form-check-input"
id="nsfwContent"
name="nsfwContent"
/>
<label className="form-check-label" htmlFor="nsfwContent">
Content is graphic and/or NSFW
<span className="text-danger">
Warning: you should check this option if your content is
<a href="https://en.wikipedia.org/wiki/Not_safe_for_work">NSFW</a>.
</span>
</label>
</span>
<Form.Group>
<Form.Label>Thumbnail</Form.Label>
<div></div>
<img
src={thumbnailPreview.current || DefaultThumbnail}
style={{
width: '720px',
aspectRatio: '16/9',
cursor: 'pointer',
}}
alt=""
onClick={() => thumbnailUpload.current.click()}
/>
<input
accept="image/*"
type="file"
id="thumbnail_input"
className="d-none"
ref={thumbnailUpload}
onChange={handleThumbnailSelect}
/>
<p>Click the thumbnail to change it</p>
<p>Recommended 5MB. Ideally 1280px×720px.</p>
</Form.Group>
<Button onClick={handleStartEncode}>Start Encode</Button>
<Button
style={{ marginLeft: '5px' }}
onClick={publish}
disabled={encodingInProgress || !publishReady}
>
Publish
</Button>
</Form>
</div>
</Col>
<Col style={{ paddingRight: '0px', paddingLeft: '0px' }}>
<Card>
<Card.Header>Encoder status</Card.Header>
<Card.Body>
<Card.Text>This area will show live encoding statistics</Card.Text>
<Button style={{ marginBottom: '5px' }} variant="primary">
FPS: {progress.currentFps}
</Button>{' '}
<br />
<Button variant="primary">Video Size: {normalizeSize()}</Button>
<ProgressBar
style={{
display: encodingInProgress ? '' : 'none',
}}
striped
variant="success"
now={caluclatePercentage()}
label={progress.percent ? `${Math.round(caluclatePercentage())}%` : null}
/>
<div
style={{
display: encodingInProgress ? '' : 'none',
}}
>
Time Remaining:{' '}
{estimatedTimeRemaining !== 'NaNns' ? estimatedTimeRemaining : 'Calculating'}
</div>
<div style={{ display: endTime ? '' : 'none' }}>
Total Time (so far):{' '}
{endTime !== 0 ? millisecondsAsString(endTime - startTime) : 'Calculating'}
</div>
</Card.Body>
</Card>
<div className="card" style={{ marginTop: '15px' }}>
<div className="card-header">
<h5>Control Panel</h5>
</div>
<Tabs style={{ background: 'white' }} defaultActiveKey="encode">
<Tab
style={{ background: 'white', padding: '10px' }}
eventKey="encode"
title="Encode Settings"
>
<Form.Group>
<Form.Label>
<strong>Format</strong>
</Form.Label>
<select
style={{ width: '6em' }}
disabled={encodingInProgress}
id="language"
className="form-control mb-4"
>
<option selected={false} value="hls">
HLS
</option>
</select>
</Form.Group>
<Form.Group>
<Form.Label>
<strong>Hardware Accel</strong>
</Form.Label>
<Form.Text>
Use hardware acceleration to speed up video encode. Not available on all
systems, results may vary.
</Form.Text>
<select
style={{ width: '6em' }}
ref={hwaccelOption}
disabled={encodingInProgress}
id="language"
className="form-control mb-4"
>
<option selected={false} value="none">
none
</option>
<option value="qsv">QSV</option>
<option value="nvenc">nvenc</option>
</select>
</Form.Group>
</Tab>
<Tab eventKey="info" title="Info" style={{ padding: '10px' }}>
<Form.Group>
<Form.Label>Video IpfsPath</Form.Label>
<Form.Control
type="text"
name="vidoeHash"
disabled
value={videoInfo.cid}
></Form.Control>
</Form.Group>
<Form.Group>
<Form.Label>Thumbnail IpfsPath</Form.Label>
<Form.Control
type="text"
name="thumbnailHash"
disabled
value={thumbnailInfo.cid}
></Form.Control>
</Form.Group>
<Form.Group>
<Form.Label>Total Size</Form.Label>
<Form.Control
style={{ width: '15%' }}
type="text"
name="videoSize"
value={normalizeSize()}
disabled
></Form.Control>
</Form.Group>
</Tab>
{/*<Tab eventKey="networks" title="Networks">
<Table striped bordered hover size="sm">
<thead>
<tr>
<th>Enabled</th>
<th>ID</th>
<th>Username</th>
</tr>
</thead>
<tbody>
<tr>
<td><Form.Check/></td>
<td>Hive</td>
<td>vaultec</td>
</tr>
</tbody>
</Table>
</Tab>*/}
<Tab eventKey="log" title="Log" style={{ padding: '10px' }}>
<textarea
disabled
className="form-control"
value={(() => logData.join('\n'))()}
></textarea>
</Tab>
</Tabs>
</div>
</Col>
</Row>
</div>
)
}
Example #9
Source File: WatchView.tsx From 3Speak-app with GNU General Public License v3.0 | 4 votes |
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 #10
Source File: CraftEssencePage.tsx From apps with MIT License | 4 votes |
render() {
if (this.state.error) return <ErrorStatus error={this.state.error} />;
if (this.state.loading || !this.state.craftEssence) return <Loading />;
const craftEssence = this.state.craftEssence;
return (
<div>
<CraftEssencePicker
region={this.props.region}
craftEssences={this.state.craftEssences}
id={this.state.craftEssence.collectionNo}
/>
<hr />
<Row
style={{
marginBottom: "3%",
}}
>
<Col xs={{ span: 12, order: 2 }} lg={{ span: 6, order: 1 }}>
<CraftEssenceMainData region={this.props.region} craftEssence={this.state.craftEssence} />
</Col>
<Col xs={{ span: 12, order: 1 }} lg={{ span: 6, order: 2 }}>
<CraftEssencePortrait craftEssence={this.state.craftEssence} />
</Col>
</Row>
<Tabs
id={"ce-tabs"}
defaultActiveKey={this.props.tab ?? "effects"}
mountOnEnter={false}
onSelect={(key: string | null) => {
this.props.history.replace(`/${this.props.region}/craft-essence/${this.props.id}/${key}`);
}}
>
<Tab eventKey={"effects"} title={"Effects"}>
<br />
<Row>
{this.state.craftEssence.skills
.sort((a, b) => (a.num || 0) - (b.num || 0) || (a.priority || 0) - (b.priority || 0))
.map((skill) => {
return (
<Col key={skill.id} xs={12} lg={craftEssence.skills.length > 1 ? 6 : 12}>
<SkillBreakdown
region={this.props.region}
skill={skill}
cooldowns={false}
/>
</Col>
);
})}
</Row>
</Tab>
<Tab eventKey={"stat-growth"} title={"Stat Growth"}>
<br />
<CraftEssenceStatGrowth region={this.props.region} craftEssence={craftEssence} />
</Tab>
<Tab eventKey={"profile"} title={"Profile"}>
<br />
<CraftEssenceProfileComments
region={this.props.region}
comments={craftEssence.profile?.comments ?? []}
/>
</Tab>
<Tab eventKey={"assets"} title={"Assets"}>
<br />
<CraftEssenceAssets region={this.props.region} craftEssence={craftEssence} />
</Tab>
{(craftEssence.profile?.voices.length ?? 0) > 0 && (
<Tab eventKey={"voices"} title={"Voices"}>
<br />
<ServantVoiceLines
region={this.props.region}
servants={new Map()}
servant={craftEssence}
servantName={craftEssence.name}
/>
</Tab>
)}
</Tabs>
</div>
);
}
Example #11
Source File: EnemyPage.tsx From apps with MIT License | 4 votes |
render() {
if (this.state.error) return <ErrorStatus error={this.state.error} />;
if (this.state.loading || !this.state.enemy) return <Loading />;
const enemy = this.state.enemy;
return (
<div id={"enemy"}>
<h1 style={{ marginBottom: "1rem" }}>
<ClassIcon className={enemy.className} rarity={enemy.rarity} height={50} />
{enemy.name}
</h1>
<Row
style={{
marginBottom: "3%",
}}
>
<Col xs={{ span: 12 }} lg={{ span: 6 }}>
<EnemyMainData region={this.props.region} enemy={enemy} />
</Col>
<Col xs={{ span: 12 }} lg={{ span: 6 }}>
<EnemySubData region={this.props.region} enemy={enemy} />
</Col>
</Row>
<Tabs
id={"enemy-tabs"}
defaultActiveKey={this.props.tab ?? "noble-phantasms"}
mountOnEnter={false}
onSelect={(key: string | null) => {
this.props.history.replace(`/${this.props.region}/enemy/${this.props.id}/${key}`);
}}
>
{enemy.skills.length > 0 ? (
<Tab eventKey={"skills"} title={"Skills"}>
<br />
{enemy.skills.map((skill) => {
return (
<SkillBreakdown
region={this.props.region}
key={skill.id}
skill={skill}
cooldowns={true}
levels={10}
/>
);
})}
</Tab>
) : null}
<Tab eventKey={"noble-phantasms"} title={"Noble Phantasms"}>
<br />
{enemy.noblePhantasms.map((noblePhantasm) => {
return (
<NoblePhantasmBreakdown
key={noblePhantasm.id}
region={this.props.region}
servant={enemy as unknown as Servant.Servant}
noblePhantasm={noblePhantasm}
hideCard={true}
/>
);
})}
</Tab>
{enemy.classPassive.length > 0 ? (
<Tab eventKey={"passives"} title={"Passives"}>
<br />
<Row>
{enemy.classPassive.map((skill) => {
return (
<Col xs={12} lg={(enemy.classPassive.length ?? 1) > 1 ? 6 : 12} key={skill.id}>
<SkillBreakdown
region={this.props.region}
skill={skill}
cooldowns={false}
/>
</Col>
);
})}
</Row>
</Tab>
) : null}
<Tab eventKey={"stat-growth"} title={"Stat Growth"}>
<br />
<ServantStatGrowth region={this.props.region} servant={enemy as unknown as Servant.Servant} />
</Tab>
<Tab eventKey={"profile"} title={"Profile"}>
<br />
<ServantProfileStat
region={this.props.region}
profile={(enemy as unknown as Servant.Servant).profile}
/>
</Tab>
<Tab eventKey={"assets"} title={"Assets"}>
<br />
<ServantAssets region={this.props.region} servant={enemy as unknown as Servant.Servant} />
</Tab>
</Tabs>
</div>
);
}
Example #12
Source File: EventPage.tsx From apps with MIT License | 4 votes |
render() {
if (this.state.error) return <ErrorStatus error={this.state.error} />;
if (this.state.loading || !this.state.event) return <Loading />;
const event = this.state.event;
let tabs: TabInfo[] = [];
if (event.missions.length > 0) {
tabs.push({
type: "mission",
id: 0,
title: "Missions",
tabKey: "missions",
});
}
const lotteries = new Set(event.lotteries.map((lottery) => lottery.id));
tabs = tabs.concat(
event.lotteries
.sort((a, b) => a.id - b.id)
.map((lottery) => {
return {
type: "lottery",
id: lottery.id,
title: lotteries.size === 1 ? "Lottery" : `Lottery ${lottery.id}`,
tabKey: `lottery-${lottery.id}`,
};
})
);
const towers = new Set(event.towers.map((tower) => tower.towerId));
tabs = tabs.concat(
event.towers
.sort((a, b) => a.towerId - b.towerId)
.map((tower) => {
return {
type: "tower",
id: tower.towerId,
title: towers.size === 1 ? "Tower" : tower.name,
tabKey: `tower-${tower.towerId}`,
};
})
);
const pointGroupMap = new Map(event.pointGroups.map((pointGroup) => [pointGroup.groupId, pointGroup]));
tabs = tabs.concat(
Array.from(new Set(event.rewards.map((reward) => reward.groupId)))
.sort((a, b) => a - b)
.map((groupId) => {
let title: string | React.ReactNode = `Ladder ${groupId}`;
const pointGroupInfo = pointGroupMap.get(groupId);
if (groupId === 0) {
title = "Ladder";
} else if (pointGroupInfo !== undefined) {
title = (
<>
<img
style={{ height: "1.75em" }}
src={pointGroupInfo.icon}
alt={`${pointGroupInfo.name} Icon`}
/>
{pointGroupInfo.name}
</>
);
}
return {
type: "ladder",
id: groupId,
title: title,
tabKey: `ladder-${groupId}`,
};
})
);
const treasureBoxSlots = Array.from(new Set(event.treasureBoxes.map((tb) => tb.slot)));
tabs = tabs.concat(
treasureBoxSlots
.sort((a, b) => a - b)
.map((slot) => {
return {
type: "treasureBox",
id: slot,
title: treasureBoxSlots.length === 1 ? "Treasure Box" : `Treasure Box ${slot}`,
tabKey: `treasure-box-${slot}`,
};
})
);
const shopSlots = Array.from(new Set(event.shop.map((shop) => shop.slot)));
tabs = tabs.concat(
shopSlots
.sort((a, b) => a - b)
.map((shopSlot) => {
return {
type: "shop",
id: shopSlot,
title: shopSlots.length === 1 ? "Shop" : `Shop ${shopSlot}`,
tabKey: `shop-${shopSlot}`,
};
})
);
const wars =
this.state.wars.length === 1 ? (
<WarDescriptor region={this.props.region} war={this.state.wars[0]} />
) : (
<ul className="mb-0">
{this.state.wars.map((war) => (
<li key={war.id}>
<WarDescriptor region={this.props.region} war={war} />
</li>
))}
</ul>
);
return (
<div>
<h1>{replacePUACodePoints(event.name)}</h1>
<br />
<div style={{ marginBottom: "3%" }}>
<DataTable
data={{
ID: event.id,
Name: replacePUACodePoints(event.name),
...(event.name !== event.originalName && { "Original Name": event.originalName }),
Wars: wars,
Status: getEventStatus(event.startedAt, event.endedAt),
Start: new Date(event.startedAt * 1000).toLocaleString(),
End: new Date(event.endedAt * 1000).toLocaleString(),
Raw: (
<Row>
<Col>
<RawDataViewer text="Nice" data={event} />
</Col>
<Col>
<RawDataViewer
text="Raw"
data={`${Host}/raw/${this.props.region}/event/${event.id}`}
/>
</Col>
</Row>
),
}}
/>
</div>
<Tabs
id={"event-reward-tabs"}
defaultActiveKey={this.props.tab ?? (tabs.length > 0 ? tabs[0].tabKey : undefined)}
mountOnEnter={true}
onSelect={(key: string | null) => {
this.props.history.replace(`/${this.props.region}/event/${this.props.eventId}/${key}`);
}}
>
{tabs.map((tab) => {
return (
<Tab key={tab.tabKey} eventKey={tab.tabKey} title={tab.title}>
{this.renderTab(
this.props.region,
event,
tab,
this.state.servantCache,
this.state.itemCache,
this.state.questCache,
this.state.enums
)}
</Tab>
);
})}
{event.voices.length > 0 && (
<Tab eventKey="voices" title="Voices">
<EventVoices
region={this.props.region}
voiceGroups={event.voices}
servants={this.state.servantCache}
eventRewardScenes={this.state.event.rewardScenes}
/>
</Tab>
)}
</Tabs>
</div>
);
}
Example #13
Source File: ItemPage.tsx From apps with MIT License | 4 votes |
private renderMaterialBreakdown(): JSX.Element {
let tabs = [
ClassName.SABER,
ClassName.LANCER,
ClassName.ARCHER,
ClassName.RIDER,
ClassName.CASTER,
ClassName.ASSASSIN,
ClassName.BERSERKER,
ClassName.EXTRA,
]
.map((className) => ({
class: className,
usageData: this.getUsageData(className),
}))
.filter((tab) => tab.usageData.length > 0);
let allUsageData = tabs
.reduce((acc, tab) => acc.concat(tab.usageData), [] as MaterialUsageData[])
.sort((a, b) => a.collectionNo - b.collectionNo);
tabs.unshift({
class: ClassName.ALL,
usageData: allUsageData,
});
let totalUsage = { ascensions: 0, skills: 0, appendSkills: 0, costumes: 0, total: 0 };
for (let usage of allUsageData) {
totalUsage.ascensions += usage.ascensions;
totalUsage.skills += usage.skills;
totalUsage.appendSkills += usage.appendSkills;
totalUsage.costumes += usage.costumes;
}
totalUsage.total =
totalUsage.ascensions + totalUsage.skills * 3 + totalUsage.appendSkills * 3 + totalUsage.costumes;
return (
<>
<h3>Servant Material Requirements</h3>
<Table hover responsive className={"materialUsage"}>
<thead>
<tr>
<th></th>
{usageDataColumns.map((field) => (
<th key={field.title}>{field.title}</th>
))}
</tr>
<tr key="total">
<td className="materialOwner">Total</td>
{usageDataColumns.map((field) => (
<td key={field.title}>
{field?.displayExtractor?.(totalUsage) ??
field?.extractor(totalUsage as MaterialUsageColumn).toLocaleString()}
</td>
))}
</tr>
<tr key="switches">
<td className="materialOwner">Show below?</td>
{usageDataColumns.map((_, index) => {
let blacklisted = this.state.blacklistedColumnIndexes.includes(index);
return (
<td key={_.title}>
<Button
variant={blacklisted ? "danger" : "success"}
onClick={() => {
let out = new Set(this.state.blacklistedColumnIndexes);
out[blacklisted ? "delete" : "add"](index);
this.setState({ blacklistedColumnIndexes: [...out] });
}}
>
{blacklisted ? "No" : "Yes"}
</Button>
</td>
);
})}
</tr>
</thead>
</Table>
<Tabs
id={"material-tabs"}
defaultActiveKey={this.props.tab ?? tabs[0]?.class.toLowerCase()}
mountOnEnter={true}
onSelect={(key: string | null) => {
this.props.history.replace(`/${this.props.region}/item/${this.props.id}/${key}`);
}}
>
{tabs.map((tab) => this.renderBreakdownTab(tab.class, tab.usageData))}
</Tabs>
</>
);
}
Example #14
Source File: MysticCodePage.tsx From apps with MIT License | 4 votes |
render() {
if (this.state.error) return <ErrorStatus error={this.state.error} />;
if (this.state.loading || !this.state.mysticCode) return <Loading />;
return (
<div>
<MysticCodePicker
region={this.props.region}
mysticCodes={this.state.mysticCodes}
id={this.state.mysticCode.id}
/>
<hr />
<Row
style={{
marginBottom: "3%",
}}
>
<Col xs={{ span: 12, order: 2 }} lg={{ span: 6, order: 1 }}>
<MysticCodeMainData region={this.props.region} mysticCode={this.state.mysticCode} />
</Col>
<Col xs={{ span: 12, order: 1 }} lg={{ span: 6, order: 2 }}>
<MysticCodePortrait mysticCode={this.state.mysticCode} />
</Col>
</Row>
<Tabs
id={"mystic-code-tabs"}
defaultActiveKey={this.props.tab ?? "skill-1"}
mountOnEnter={false}
onSelect={(key: string | null) => {
this.props.history.replace(`/${this.props.region}/mystic-code/${this.props.id}/${key}`);
}}
>
<Tab eventKey={"skill-1"} title={"Skill 1"}>
<br />
{this.state.mysticCode.skills[0] ? (
<SkillBreakdown
region={this.props.region}
skill={this.state.mysticCode.skills[0]}
cooldowns={true}
levels={10}
/>
) : null}
</Tab>
<Tab eventKey={"skill-2"} title={"Skill 2"}>
<br />
{this.state.mysticCode.skills[1] ? (
<SkillBreakdown
region={this.props.region}
skill={this.state.mysticCode.skills[1]}
cooldowns={true}
levels={10}
/>
) : null}
</Tab>
<Tab eventKey={"skill-3"} title={"Skill 3"}>
<br />
{this.state.mysticCode.skills[2] ? (
<SkillBreakdown
region={this.props.region}
skill={this.state.mysticCode.skills[2]}
cooldowns={true}
levels={10}
/>
) : null}
</Tab>
<Tab eventKey={"exp"} title={"EXP"}>
<br />
<MysticCodeExp mysticCode={this.state.mysticCode} />
</Tab>
<Tab eventKey={"assets"} title={"Assets"}>
<br />
<MysticCodeAssets mysticCode={this.state.mysticCode} />
</Tab>
</Tabs>
</div>
);
}
Example #15
Source File: QuestPage.tsx From apps with MIT License | 4 votes |
render() {
if (this.state.error) return <ErrorStatus error={this.state.error} />;
if (this.state.loading || !this.state.quest) return <Loading />;
const quest = this.state.quest;
return (
<div>
<h1>{quest.name}</h1>
<br />
<Row style={{ marginBottom: "3%" }}>
<Col xs={{ span: 12 }} lg={{ span: 6 }}>
<QuestMainData
region={this.props.region}
quest={quest}
phase={this.state.phase}
setPhase={(phase) => {
this.setState({ phase });
}}
/>
</Col>
<Col xs={{ span: 12 }} lg={{ span: 6 }}>
<QuestSubData region={this.props.region} quest={quest} />
</Col>
</Row>
{quest.messages.length > 0 ? (
<Alert variant="success" className="newline">
{quest.messages.length > 1 ? (
<ul className="mb-0">
{quest.messages.map((message) => (
<li key={message.idx}>{colorString(message.message)}</li>
))}
</ul>
) : (
colorString(quest.messages[0].message)
)}
</Alert>
) : null}
{quest.scripts.length > 0 ? (
<Alert variant="success">
{quest.scripts.length > 1 ? (
<ul className="mb-0">
{sortScript(quest.scripts.map((script) => script.scriptId)).map((scriptId) => (
<li key={scriptId}>
<ScriptDescriptor region={this.props.region} scriptId={scriptId} />
</li>
))}
</ul>
) : (
<ScriptDescriptor region={this.props.region} scriptId={quest.scripts[0].scriptId} />
)}
</Alert>
) : null}
{quest.extraDetail.questSelect !== undefined &&
quest.extraDetail.questSelect.filter((questId) => questId !== this.props.id).length > 0 ? (
<Alert variant="success">
{quest.extraDetail.questSelect.filter((questId) => questId !== this.props.id).length > 1
? "Other versions"
: "Another version"}{" "}
this quest:
<ul className="mb-0">
{quest.extraDetail.questSelect
.filter((questId) => questId !== this.props.id)
.map((questId) => (
<li key={questId}>
{questId}:{" "}
<QuestDescriptorId
region={this.props.region}
questId={questId}
questPhase={this.state.phase}
/>
</li>
))}
</ul>
</Alert>
) : null}
<QuestDrops region={this.props.region} drops={quest.drops} />
{quest.supportServants.length > 0 ? (
<>
{renderCollapsibleContent({
title: `${quest.isNpcOnly ? "Forced " : ""}Support Servant${
quest.supportServants.length > 1 ? "s" : ""
}`,
content: (
<SupportServantTables
region={this.props.region}
supportServants={quest.supportServants}
/>
),
subheader: false,
initialOpen: false,
})}
</>
) : null}
{quest.stages.length > 0 ? (
<Tabs
defaultActiveKey={this.props.stage !== undefined ? this.props.stage : 1}
onSelect={(key: string | null) => {
this.props.history.replace(
`/${this.props.region}/quest/${this.props.id}/${this.state.phase}` +
(key ? `/stage-${key}` : "")
);
}}
>
{quest.stages.map((stage) => (
<Tab key={stage.wave} eventKey={stage.wave} title={`Stage ${stage.wave}`}>
<QuestStage region={this.props.region} stage={stage} />
</Tab>
))}
</Tabs>
) : null}
</div>
);
}
Example #16
Source File: ServantPage.tsx From apps with MIT License | 4 votes |
render() {
if (this.state.error) return <ErrorStatus error={this.state.error} />;
if (this.state.loading || !this.state.servant) return <Loading />;
const servant = this.state.servant;
document.title = `[${this.props.region}] Servant - ${this.getOverwriteName()} - Atlas Academy DB`;
let remappedCostumeMaterials: Entity.EntityLevelUpMaterialProgression = {};
if (servant.profile) {
for (const [costumeId, costume] of Object.entries(servant.costumeMaterials)) {
if (servant.profile?.costume[costumeId] !== undefined) {
remappedCostumeMaterials[servant.profile?.costume[costumeId].name] = costume;
}
}
}
const rawUrl = `${Host}/raw/${this.props.region}/servant/${servant.id}?expand=true&lore=true`;
return (
<div id={"servant"}>
<ServantPicker region={this.props.region} servants={this.state.servants} id={servant.collectionNo} />
<hr style={{ marginBottom: "1rem" }} />
<div style={{ display: "flex", flexDirection: "row", marginBottom: 3 }}>
<h1 style={{ marginBottom: "1rem" }}>
<ClassIcon className={servant.className} rarity={servant.rarity} height={50} />
{this.getOverwriteName()}
</h1>
<span style={{ flexGrow: 1 }} />
</div>
<Row
style={{
marginBottom: "3%",
}}
>
<Col xs={{ span: 12, order: 2 }} lg={{ span: 6, order: 1 }}>
<ServantMainData
region={this.props.region}
servant={this.state.servant}
servantName={this.getOverwriteName()}
originalServantName={this.getOverwriteName(true)}
assetType={this.state.assetType}
assetId={this.state.assetId}
/>
<Row>
<Col>
<RawDataViewer text="Nice" data={servant} />
</Col>
<Col>
<RawDataViewer text="Raw" data={rawUrl} />
</Col>
</Row>
</Col>
<Col xs={{ span: 12, order: 1 }} lg={{ span: 6, order: 2 }}>
<ServantPortrait
servant={this.state.servant}
assetType={this.state.assetType}
assetId={this.state.assetId}
assetExpand={this.state.assetExpand}
updatePortraitCallback={(assetType, assetId, assetExpand) => {
this.updatePortrait(assetType, assetId, assetExpand);
}}
/>
</Col>
</Row>
<Tabs
id={"servant-tabs"}
defaultActiveKey={this.props.tab ?? "skill-1"}
mountOnEnter={true}
onSelect={(key: string | null) => {
this.props.history.replace(`/${this.props.region}/servant/${this.props.id}/${key}`);
}}
>
{[1, 2, 3].map((i) => (
<Tab key={`skill-${i}`} eventKey={`skill-${i}`} title={`Skill ${i}`}>
{servant.skills
.filter((skill) => skill.num === i)
.sort((a, b) => b.id - a.id)
.map((skill, i2) => {
return (
<div key={i2}>
<SkillBreakdown
region={this.props.region}
key={skill.id}
skill={skill}
cooldowns={true}
levels={10}
/>
{this.skillRankUps(skill.id).map((rankUpSkill, rankUp) => {
return (
<SkillReferenceBreakdown
key={rankUpSkill}
region={this.props.region}
id={rankUpSkill}
cooldowns={true}
levels={10}
rankUp={rankUp + 1}
/>
);
})}
</div>
);
})}
</Tab>
))}
<Tab eventKey={"noble-phantasms"} title={"NPs"}>
{servant.noblePhantasms
.filter((noblePhantasm) => noblePhantasm.functions.length > 0)
// Card change NPs have 0 priority.
// Card change NPs are sorted by ascending ID number so the main NP is on top.
.sort(
(a, b) =>
b.strengthStatus - a.strengthStatus ||
(a.priority === 0 || b.priority === 0 ? a.id - b.id : b.id - a.id)
)
.map((noblePhantasm) => {
return (
<NoblePhantasmBreakdown
key={noblePhantasm.id}
region={this.props.region}
servant={servant}
noblePhantasm={noblePhantasm}
assetType={this.state.assetType}
assetId={this.state.assetId}
/>
);
})}
</Tab>
<Tab eventKey={"passives"} title={"Passives"}>
<ServantPassive region={this.props.region} servant={servant} />
</Tab>
<Tab eventKey={"traits"} title={"Traits"}>
<ServantTraits region={this.props.region} servant={this.state.servant} />
</Tab>
<Tab eventKey={"materials"} title={"Materials"}>
<Row>
<Col xs={12} lg={6}>
<ServantMaterialBreakdown
region={this.props.region}
materials={servant.ascensionMaterials}
title={"Ascension Materials"}
showNextLevelInDescription={true}
/>
</Col>
<Col xs={12} lg={6}>
<ServantMaterialBreakdown
region={this.props.region}
materials={servant.skillMaterials}
title={"Skill Materials"}
showNextLevelInDescription={true}
/>
</Col>
</Row>
{servant.appendPassive.length > 0 && servant.coin !== undefined ? (
<Row>
<Col xs={12} lg={6}>
<ServantMaterialBreakdown
region={this.props.region}
materials={{
"Summon Get": {
items: [
{
item: servant.coin.item,
amount: servant.coin.summonNum,
},
],
qp: 0,
},
"Append Skill Unlock Cost": {
items: servant.appendPassive[0].unlockMaterials,
qp: 0,
},
}}
title="Servant Coin"
/>
</Col>
<Col xs={12} lg={6}>
<ServantMaterialBreakdown
region={this.props.region}
materials={servant.appendSkillMaterials}
title="Append Skill Level Up Materials"
showNextLevelInDescription={true}
/>
</Col>
</Row>
) : null}
{Object.keys(servant.costumeMaterials).length ? (
<ServantMaterialBreakdown
region={this.props.region}
materials={remappedCostumeMaterials}
title={"Costume Materials"}
idMinWidth="10em"
/>
) : null}
</Tab>
<Tab eventKey={"stat-growth"} title={"Growth"}>
<ServantStatGrowth region={this.props.region} servant={servant} />
</Tab>
<Tab eventKey={"lore"} title={"Profile"}>
<Alert variant="success" style={{ lineHeight: "2em" }}>
<IllustratorDescriptor
region={this.props.region}
illustrator={servant.profile?.illustrator}
/>
<br />
<VoiceActorDescriptor region={this.props.region} cv={servant.profile?.cv} />
</Alert>
<ServantBondGrowth bondGrowth={servant.bondGrowth} />
<ServantProfileStats region={this.props.region} profile={servant.profile} />
<ServantRelatedQuests region={this.props.region} questIds={servant.relateQuestIds} />
<ServantRelatedQuests
region={this.props.region}
questIds={servant.trialQuestIds}
title="Trial Quest"
/>
<ServantValentine region={this.props.region} servant={servant} />
<ServantCostumeDetails costumes={servant.profile?.costume} />
<ServantProfileComments region={this.props.region} comments={servant.profile?.comments ?? []} />
</Tab>
<Tab eventKey={"assets"} title={"Assets"}>
<ServantAssets region={this.props.region} servant={servant} />
</Tab>
<Tab eventKey={"voices"} title={"Voices"}>
<ServantVoiceLines
region={this.props.region}
servants={new Map(this.state.servants.map((servant) => [servant.id, servant]))}
servant={servant}
servantName={this.getOverwriteName()}
/>
</Tab>
</Tabs>
</div>
);
}