semantic-ui-react#Modal TypeScript Examples
The following examples show how to use
semantic-ui-react#Modal.
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: SettingControl.tsx From FLECT_Amazon_Chime_Meeting with Apache License 2.0 | 6 votes |
generateSettingModal = () => {
return (
<Modal onClose={this.settingClose} open={this.state.open}>
<Modal.Header>Setting</Modal.Header>
<Modal.Content>
<Grid>
<Grid.Row>
Virtual background
</Grid.Row>
<Grid.Row>
{this.generateVGSettingPanal()}
</Grid.Row>
</Grid>
</Modal.Content>
<Button content='Close' negative onClick={this.settingClose} />
</Modal>
)
}
Example #2
Source File: index.tsx From frontegg-react with MIT License | 6 votes |
Dialog: FC<DialogProps> = (props) => {
const modalProps = dialogPropsMapper(props);
return (
<Modal {...modalProps}>
{props.header && <Modal.Header className='fe-dialog__header'>{props.header}</Modal.Header>}
<Modal.Content className='fe-dialog__content'>{props.children}</Modal.Content>
</Modal>
);
}
Example #3
Source File: ErrorModal.tsx From watchparty with MIT License | 6 votes |
ErrorModal = ({ error }: { error: string }) => {
return (
<Modal inverted basic open>
<Header as="h1" style={{ textAlign: 'center' }}>
{error}
</Header>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Button
primary
size="huge"
onClick={() => {
window.location.href = '/';
}}
icon
labelPosition="left"
>
<Icon name="home" />
Go to home page
</Button>
</div>
</Modal>
);
}
Example #4
Source File: DirectMessage.tsx From communitymap-ui with Apache License 2.0 | 6 votes |
DirectMessageModal: React.FC<{ onClose: () => void }> = ({
onClose,
}) => {
const { header, content, action } = useDirectMessageView();
return (
<Modal
open
closeIcon
size="tiny"
className="modal-direct-message"
onClose={onClose}
>
<Modal.Header>{header}</Modal.Header>
<Modal.Content scrolling>{content}</Modal.Content>
<Modal.Actions>{action}</Modal.Actions>
</Modal>
);
}
Example #5
Source File: MultiStreamModal.tsx From watchparty with MIT License | 6 votes |
MultiStreamModal = ({
streams,
setMedia,
resetMultiSelect,
}: {
streams: any[];
setMedia: Function;
resetMultiSelect: Function;
}) => (
<Modal inverted basic open closeIcon onClose={resetMultiSelect as any}>
<Modal.Header>Select a file</Modal.Header>
<Modal.Content>
{streams.length === 0 && <Loader />}
{streams && (
<List inverted>
{streams.map((file: any) => (
<List.Item>
<List.Icon name="file" />
<List.Content>
<List.Header
as="a"
onClick={() => {
setMedia(null, { value: file.url });
resetMultiSelect();
}}
>
{file.name}
</List.Header>
<List.Description>
{file.length.toLocaleString()} bytes
</List.Description>
</List.Content>
</List.Item>
))}
</List>
)}
</Modal.Content>
</Modal>
)
Example #6
Source File: PasswordModal.tsx From watchparty with MIT License | 6 votes |
PasswordModal = ({
savedPasswords,
roomId,
}: {
savedPasswords: StringDict;
roomId: string;
}) => {
const setPassword = useCallback(() => {
window.localStorage.setItem(
'watchparty-passwords',
JSON.stringify({
...savedPasswords,
[roomId]: (document.getElementById('roomPassword') as HTMLInputElement)
?.value,
})
);
window.location.reload();
}, [savedPasswords, roomId]);
return (
<Modal inverted basic open>
<Header as="h1" style={{ textAlign: 'center' }}>
This room requires a password.
</Header>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Input
id="roomPassword"
type="password"
size="large"
onKeyPress={(e: any) => e.key === 'Enter' && setPassword()}
icon={
<Icon onClick={setPassword} name="key" inverted circular link />
}
/>
</div>
</Modal>
);
}
Example #7
Source File: LocalSettings.tsx From watchparty with MIT License | 6 votes |
SettingsModal = ({ trigger }: any) => (
<Modal trigger={trigger} basic closeIcon size="small">
<Header icon="setting" content="Settings" />
<Modal.Content>
<Form>
<TextArea rows={10} id="settings_textarea">
{window.localStorage.getItem('watchparty-setting') ||
JSON.stringify(getDefaultSettings(), null, 2)}
</TextArea>
</Form>
</Modal.Content>
<Modal.Actions>
<Button
color="green"
inverted
onClick={() => {
const newSetting = (document.getElementById(
'settings_textarea'
) as HTMLTextAreaElement)!.value;
try {
validateSettingsString(newSetting);
updateSettings(newSetting);
window.location.reload();
} catch (e) {
alert(e);
}
}}
>
<Icon name="checkmark" />
Save
</Button>
</Modal.Actions>
</Modal>
)
Example #8
Source File: ScreenShareModal.tsx From watchparty with MIT License | 6 votes |
render() {
const { closeModal } = this.props;
return (
<Modal open={true} onClose={closeModal as any}>
<Modal.Header>Share Your Screen</Modal.Header>
<Modal.Content image>
<Modal.Description>
<div>You're about to share your screen.</div>
<ul>
<li>This feature is only supported on Chrome and Edge.</li>
<li>
To share audio, you need to check the "Share audio" checkbox in
the screen selection dialog.
</li>
<li>
Audio sharing is only supported if sharing your entire screen or
a browser tab, not an application.
</li>
</ul>
<Button
onClick={() => {
this.props.startScreenShare();
this.props.closeModal();
}}
>
Start Screenshare
</Button>
</Modal.Description>
</Modal.Content>
</Modal>
);
}
Example #9
Source File: App.tsx From communitymap-ui with Apache License 2.0 | 5 votes |
Home: React.FC = () => {
const user = useAuth() || null;
const [currentLocation, setCurrentLocation] = useState(defaultCenter);
const router = useHistory();
const objectRouteMatch = useRouteMatch<{ objectId: string }>(
'/object/:objectId'
);
return (
<div id="home">
<CommunityMap
defaultCenter={defaultCenter}
onChange={(center) => setCurrentLocation(center)}
mapApiKey={GOOGLE_API_KEY}
showObjectId={objectRouteMatch?.params?.objectId}
onClickObject={(obj) => {
router.push(`/object/${obj.id}`);
return true;
}}
onObjectModalClose={() => router.push('/')}
centerPin={<Pin color="#2185d0" />}
autolocate={initialParams?.autolocate}
filterOrigin={initialParams?.filterOrigin}
profileWidget={<ProfileWidgetPlus />}
renderAuthor={renderAuthor}
// renderObject={({ item }) => (item.type === 'story' ? true : null)}
></CommunityMap>
{initialParams?.canAdd !== false && (
<NewContentWidget
authenticated={!!user}
onAdd={(item) =>
postObject(user, currentLocation, item, embedParams?.appId)
}
/>
)}
<Switch>
<Route path="/direct-messages/:dmKey">
<DirectMessageModal onClose={() => router.push('/')} />
</Route>
<Route path="/users/:userId">
<Modal open closeIcon size="tiny" onClose={() => router.push('/')}>
<Modal.Content scrolling>
<UserPage />
</Modal.Content>
</Modal>
</Route>
<Route path="/my-messages">
<Modal open closeIcon size="tiny" onClose={() => router.push('/')}>
<Modal.Header>Messages</Modal.Header>
<Modal.Content scrolling>
<DirectMessageDialogs />
</Modal.Content>
</Modal>
</Route>
<Route path="/terms">
<Modal open closeIcon size="large" onClose={() => router.push('/')}>
<Modal.Header>Terms of Service</Modal.Header>
<Modal.Content scrolling>
<TermsOfService />
</Modal.Content>
</Modal>
</Route>
<Route path="/privacy">
<Modal open closeIcon size="large" onClose={() => router.push('/')}>
<Modal.Header>Privacy and Cookie Policy</Modal.Header>
<Modal.Content scrolling>
<PrivacyPolicy />
</Modal.Content>
</Modal>
</Route>
</Switch>
</div>
);
}
Example #10
Source File: topmenu.tsx From website with MIT License | 5 votes |
render() {
const { user, password, loginDialogOpen, loggingIn, errorMessage, messageModalOpen } = this.state;
const { narrowLayout, children } = this.props;
const windowGlobal = typeof window !== 'undefined' && window;
let isLoggedIn = windowGlobal && window.localStorage && window.localStorage.getItem('token') && window.localStorage.getItem('username');
const userName = isLoggedIn ? window.localStorage.getItem('username') : '';
return (
<React.Fragment>
<NavBar narrowLayout={narrowLayout} onMessageClicked={() => this.setState({ messageModalOpen: true })}>
{children}
</NavBar>
<Modal open={loginDialogOpen} onClose={() => this._closeLoginDialog()} size='tiny'>
<Modal.Header>Log-in to your account</Modal.Header>
<Modal.Content>
<Grid textAlign='center' verticalAlign='middle'>
<Grid.Column style={{ maxWidth: 450 }}>
<Form size='large' loading={loggingIn}>
<Segment>
<Form.Input
fluid
icon='user'
iconPosition='left'
placeholder='Username'
value={user}
onChange={(e, { value }) => this.setState({ user: value })}
/>
<Form.Input
fluid
icon='lock'
iconPosition='left'
placeholder='Password'
type='password'
value={password}
onChange={(e, { value }) => this.setState({ password: value })}
/>
</Segment>
</Form>
{errorMessage && <Message error>{errorMessage}</Message>}
{!errorMessage && (
<Message>If you are an approved book editor, log in here to submit changes directly from the site.</Message>
)}
</Grid.Column>
</Grid>
</Modal.Content>
<Modal.Actions>
<Button content='Cancel' onClick={() => this._closeLoginDialog()} />
<Button positive content='Login' onClick={() => this._doLogin()} />
</Modal.Actions>
</Modal>
<Modal open={messageModalOpen} closeOnEscape={false} closeOnDimmerClick={false} onClose={() => this._closeMessageDialog()}>
<Modal.Header>The DataCore website and bot are in need of software engineers!</Modal.Header>
<Modal.Content>
<p>
We need your help! The project is <a href='https://github.com/stt-datacore'>open source</a> so we're open for contributions
from software engineers, designers, devops, testers and so on. Reach out on our{' '}
<a href='https://discord.gg/2SY8W7Aeme'>development Discord</a> if you're not sure where to start.
</p>
<p>
If you've always wanted a feature on DataCore, here's your chance to hack on the project and implement it yourself! Most of
the project is written in TypeScript, with node.js on the backend and React with Gatsby on the frontend.
</p>
</Modal.Content>
<Modal.Actions>
<Button icon='checkmark' onClick={() => this._closeMessageDialog()} content='Ok' />
</Modal.Actions>
</Modal>
</React.Fragment>
);
}
Example #11
Source File: Login.tsx From communitymap-ui with Apache License 2.0 | 5 votes |
Login: React.FC<{ title?: string; onClose?: () => void }> = ({
title = 'You need to sign in to continue',
onClose,
}) => {
useEffect(() => {
const uiConfig = {
signInFlow: 'popup',
// signInSuccessUrl: window.location.origin,
signInSuccessUrl: window.location.href,
signInOptions: [
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
// firebase.auth.FacebookAuthProvider.PROVIDER_ID,
// firebase.auth.PhoneAuthProvider.PROVIDER_ID,
],
// tosUrl and privacyPolicyUrl accept either url string or a callback
// function.
// Terms of service url/callback.
// tosUrl: '<your-tos-url>',
// Privacy policy url/callback.
// privacyPolicyUrl: function() {
// window.location.assign('<your-privacy-policy-url>');
// }
};
// Initialize the FirebaseUI Widget using Firebase.
const ui = new firebaseui.auth.AuthUI(
getFirebaseApp().auth(),
getFirebaseApp().name
);
// The start method will wait until the DOM is loaded.
ui.start('#firebaseui-auth-container', uiConfig);
return () => {
ui.delete();
};
}, []);
return (
<Modal closeIcon size="mini" open onClose={onClose}>
<Modal.Header>{title}</Modal.Header>
<Modal.Content>
<div style={{ margin: '3em 1.5em' }}>
<div id="firebaseui-auth-container"></div>
</div>
</Modal.Content>
</Modal>
);
}
Example #12
Source File: Login.tsx From communitymap-ui with Apache License 2.0 | 5 votes |
Login: React.FC<{ title?: string; onClose?: () => void }> = ({
title = 'You need to sign in to continue',
onClose,
}) => {
useEffect(() => {
const uiConfig = {
signInFlow: 'popup',
// signInSuccessUrl: window.location.origin,
signInSuccessUrl: window.location.href,
signInOptions: [
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
// firebase.auth.FacebookAuthProvider.PROVIDER_ID,
// firebase.auth.PhoneAuthProvider.PROVIDER_ID,
],
// tosUrl and privacyPolicyUrl accept either url string or a callback
// function.
// Terms of service url/callback.
tosUrl: window.location.origin + '/terms',
// Privacy policy url/callback.
privacyPolicyUrl: window.location.origin + '/privacy',
// privacyPolicyUrl: function() {
// window.location.assign('<your-privacy-policy-url>');
// }
};
// Initialize the FirebaseUI Widget using Firebase.
const ui = new firebaseui.auth.AuthUI(
getFirebaseApp().auth(),
getFirebaseApp().name
);
// The start method will wait until the DOM is loaded.
ui.start('#firebaseui-auth-container', uiConfig);
return () => {
ui.delete();
};
}, []);
return (
<Modal closeIcon size="mini" open onClose={onClose}>
<Modal.Header>{title}</Modal.Header>
<Modal.Content>
<div style={{ margin: '3em 1.5em' }}>
<div id="firebaseui-auth-container"></div>
</div>
</Modal.Content>
</Modal>
);
}
Example #13
Source File: NewContentWidget.tsx From communitymap-ui with Apache License 2.0 | 5 votes |
NewContentWidget: React.FC<{
authenticated: boolean;
onAdd: (item: ObjectItemInput) => Promise<any>;
}> = ({ authenticated, onAdd }) => {
const [addType, setAddType] = useState<ObjectItemInput['type'] | null>(null);
const showLogin = !authenticated && !!addType;
return (
<Segment id="new-content-widget">
{showLogin && <Login onClose={() => setAddType(null)} />}
{authenticated && (
<>
{!!addType && (
<Modal open size="tiny" closeIcon onClose={() => setAddType(null)}>
<Modal.Content>
<AddNewObjectRender
type={addType}
onAdd={(it) =>
onAdd(it)
.then(() => setAddType(null))
.catch(reportError)
}
/>
</Modal.Content>
</Modal>
)}
</>
)}
<h5>I want to post</h5>
{([
'chat',
'request',
'offer',
// 'donation',
] as ObjectItemInput['type'][]).map((type) => (
<Button
key={type}
icon={type2icon(type)}
// basic
primary
content={type2title(type)}
onClick={() => setAddType(type)}
/>
))}
<hr />
<Button
key="place"
icon="building"
primary
content="Place"
onClick={() => setAddType('place')}
/>
<hr />
<Button
key="story"
icon="edit outline"
primary
content="Story"
onClick={() => setAddType('story')}
/>
</Segment>
);
}
Example #14
Source File: crewfullequiptree.tsx From website with MIT License | 5 votes |
render() {
const { crew, items } = this.props;
if (!crew || !this.props.visible) {
return <span />;
}
let { craftCost, demands, factionOnlyTotal, totalChronCost } = calculateCrewDemands(crew, items);
return (
<Modal open={this.props.visible} onClose={() => this.props.onClosed()}>
<Modal.Header>{crew.name}'s expanded equipment recipe trees</Modal.Header>
<Modal.Content scrolling>
<p>
Faction-only items required <b>{factionOnlyTotal}</b>
</p>
<p>
Estimated chroniton cost{' '}
<span style={{ display: 'inline-block' }}>
<img src={`${process.env.GATSBY_ASSETS_URL}atlas/energy_icon.png`} height={14} />
</span>{' '}
<b>{totalChronCost}</b>
<Popup
wide
trigger={<Icon fitted name='help' />}
header={'How is this calculated?'}
content={
<div>
<p>This sums the estimated chroniton cost of each equipment and component in the tree.</p>
<p>It estimates an item's cost by running the formula below for each mission and choosing the cheapest:</p>
<p>
<code>
(6 - PIPS) * 1.8 * <i>mission cost</i>
</code>
</p>
<p>See code for details. Feedback is welcome!</p>
</div>
}
/>
</p>
<p>
Build cost{' '}
<span style={{ display: 'inline-block' }}>
<img src={`${process.env.GATSBY_ASSETS_URL}currency_sc_currency_0.png`} height={16} />
</span>{' '}
<b>{craftCost}</b>
</p>
<Grid columns={3} centered padded>
{demands.map((entry, idx) => (
<Grid.Column key={idx}>
<Popup
trigger={
<Header
style={{ display: 'flex', cursor: 'zoom-in' }}
icon={
<ItemDisplay
src={`${process.env.GATSBY_ASSETS_URL}${entry.equipment.imageUrl}`}
size={48}
maxRarity={entry.equipment.rarity}
rarity={entry.equipment.rarity}
/>
}
content={entry.equipment.name}
subheader={`Need ${entry.count} ${entry.factionOnly ? ' (FACTION)' : ''}`}
/>
}
header={CONFIG.RARITIES[entry.equipment.rarity].name + ' ' + entry.equipment.name}
content={<ItemSources item_sources={entry.equipment.item_sources} />}
on='click'
wide
/>
</Grid.Column>
))}
</Grid>
</Modal.Content>
</Modal>
);
}
Example #15
Source File: PermanentRoomModal.tsx From watchparty with MIT License | 5 votes |
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 #16
Source File: LoginModal.tsx From watchparty with MIT License | 5 votes |
render() {
const { closeLogin } = this.props;
return (
<Modal open={true} onClose={closeLogin as any}>
<Modal.Header>
{this.state.isCreateMode ? 'Create an account' : 'Login'}
</Modal.Header>
<Modal.Content>
{this.state.error && (
<Message color="red" header="Error" content={this.state.error} />
)}
<Form>
<Form.Field>
<label>Email</label>
<input
placeholder="Email"
value={this.state.email}
onChange={(e) => this.setState({ email: e.target.value })}
/>
</Form.Field>
<Form.Field>
<label>Password</label>
<input
type="password"
placeholder="Password"
value={this.state.password}
onChange={(e) => this.setState({ password: e.target.value })}
/>
</Form.Field>
{!this.state.isCreateMode && (
<div>
Don't have an account?{' '}
<button
type="button"
className="linkButton"
onClick={() => this.setState({ isCreateMode: true })}
>
Create one.
</button>{' '}
Forgot your password? Enter your email and{' '}
<button
type="button"
className="linkButton"
onClick={this.resetPassword}
>
reset it.
</button>
</div>
)}
{this.state.isCreateMode ? (
<Button
type="submit"
onClick={() =>
this.createAccount(this.state.email, this.state.password)
}
>
Create
</Button>
) : (
<Button
type="submit"
onClick={() =>
this.emailSignIn(this.state.email, this.state.password)
}
>
Login
</Button>
)}
</Form>
</Modal.Content>
</Modal>
);
}
Example #17
Source File: FileShareModal.tsx From watchparty with MIT License | 5 votes |
render() {
const { closeModal } = this.props;
return (
<Modal open={true} onClose={closeModal as any}>
<Modal.Header>Share A File</Modal.Header>
<Modal.Content image>
<Modal.Description>
<div>You're about to share a file from your device.</div>
<ul>
<li>This feature is only supported on Chrome and Edge.</li>
<li>
To test whether the file can be shared, you can try opening it
locally with your browser to see if it will play properly.
</li>
<li>
Certain codecs, such as HEVC, AC3, and H265 will not play in
Chrome (they may work in Edge)
</li>
<li>
There is a Chrome issue where the sharer needs to{' '}
<a
target="_blank"
rel="noreferrer"
href="https://www.howtogeek.com/412738/how-to-turn-hardware-acceleration-on-and-off-in-chrome/"
>
disable hardware acceleration
</a>{' '}
in order for others to receive video.
</li>
</ul>
<Button
onClick={() => {
this.props.startFileShare();
this.props.closeModal();
}}
>
Start Fileshare
</Button>
</Modal.Description>
</Modal.Content>
</Modal>
);
}
Example #18
Source File: missionslist.tsx From website with MIT License | 4 votes |
MissionsList = (props: MissionsListProps) => {
const { groupId, shuttlers, setShuttlers, activeShuttles } = props;
const [editMission, setEditMission] = React.useState(undefined);
const [state, dispatch] = React.useReducer(reducer, {
data: shuttlers.shuttles.filter(shuttle => shuttle.groupId === groupId),
column: null,
direction: null
});
const { data, column, direction } = state;
React.useEffect(() => {
dispatch({ type: 'UPDATE_DATA', data: shuttlers.shuttles.filter(shuttle => shuttle.groupId === groupId), column, direction });
}, [shuttlers]);
const CheckDropdown = () => {
if (data.length === 0) return (<></>);
const checkOptions = [];
const threeSeaters = [], fourSeaters = [];
data.forEach(shuttle => {
if (shuttle.seats.length <= 4)
fourSeaters.push(shuttle.id);
if (shuttle.seats.length === 3)
threeSeaters.push(shuttle.id);
});
if (threeSeaters.length > 0)
checkOptions.push({ key: 'three-seaters', text: `Select only 3-seaters (${threeSeaters.length})`, ids: threeSeaters });
if (fourSeaters.length > 0)
checkOptions.push({ key: 'four-seaters', text: `Select only 3- and 4- seaters (${fourSeaters.length})`, ids: fourSeaters });
if (activeShuttles?.length > 0) {
const openIds = activeShuttles.map(adventure => adventure.symbol);
checkOptions.push({ key: `open-adventures`, text: `Select only open in-game (${openIds.length})`, ids: openIds });
}
const factions = [];
data.forEach(shuttle => {
if (shuttle.faction > 0 && !factions.includes(shuttle.faction)) factions.push(shuttle.faction);
});
if (factions.length > 1) {
factions.forEach(factionId => {
const ids = data.filter(shuttle => shuttle.faction === factionId).map(shuttle => shuttle.id);
const faction = allFactions.find(af => af.id === factionId);
checkOptions.push({ key: `faction-${factionId}`, text: `Select only ${faction.name} (${ids.length})`, ids });
});
}
return (
<Dropdown
icon='check'
floating
>
<Dropdown.Menu>
<Dropdown.Item icon='check' text={`Select all (${data.length})`} onClick={() => checkMissions([])} />
{missionsSelected > 0 && (
<Dropdown.Item icon='x' text='Unselect all' onClick={() => checkMissions([], false)} />
)}
{checkOptions.length > 0 && <Dropdown.Divider />}
{checkOptions.map(option => (
<Dropdown.Item key={option.key} text={option.text} onClick={() => checkMissions(option.ids)} />
))}
</Dropdown.Menu>
</Dropdown>
);
};
const tableConfig = [
{ title: <CheckDropdown />, align: 'center' },
{ column: 'name', title: 'Mission' },
{ column: 'faction', title: 'Faction', align: 'center' },
{ column: 'seats.length', title: 'Seats', align: 'center' },
{ column: 'skills', title: 'Skills', span: 5 },
{ title: '' }
];
const MissionEditor = (props: { shuttle: Shuttle }) => {
const [shuttle, setShuttle] = React.useState(JSON.parse(JSON.stringify(props.shuttle)));
const factionOptions = allFactions.sort((a, b) => a.name.localeCompare(b.name)).map(faction => {
return { key: faction.id, value: faction.id, text: (<span style={{ whiteSpace: 'nowrap' }}>{faction.name}</span>) };
});
const EditorSeat = (props: { seat: ShuttleSeat, seatNum: number }) => {
const { seatNum, seat } = props;
const skillOptions = [
{ key: 'CMD', text: 'CMD', value: 'command_skill' },
{ key: 'DIP', text: 'DIP', value: 'diplomacy_skill' },
{ key: 'ENG', text: 'ENG', value: 'engineering_skill' },
{ key: 'MED', text: 'MED', value: 'medicine_skill' },
{ key: 'SCI', text: 'SCI', value: 'science_skill' },
{ key: 'SEC', text: 'SEC', value: 'security_skill' }
];
return (
<Grid textAlign='center' columns={3}>
<Grid.Column>
<Dropdown
direction='right'
compact
selection
options={skillOptions}
value={seat.skillA}
onChange={(e, { value }) => updateMissionSeat(seatNum, 'skillA', value)}
/>
</Grid.Column>
<Grid.Column>
<Button circular
disabled={seat.skillB == '' ? true : false}
onClick={() => updateMissionSeat(seatNum, 'operand', seat.operand == 'AND' ? 'OR' : 'AND')}
>
{seat.skillB == '' ? '' : seat.operand}
</Button>
</Grid.Column>
<Grid.Column>
<Dropdown
compact
selection
clearable
options={skillOptions}
value={seat.skillB}
onChange={(e, { value }) => updateMissionSeat(seatNum, 'skillB', value)}
/>
</Grid.Column>
</Grid>
);
};
return (
<Modal
open={true}
onClose={() => applyEdits()}
>
<Modal.Header>Edit Mission</Modal.Header>
<Modal.Content scrolling>
{renderContent()}
</Modal.Content>
<Modal.Actions>
<Button positive onClick={() => applyEdits()}>
Close
</Button>
</Modal.Actions>
</Modal>
);
function renderContent(): void {
return (
<React.Fragment>
<Grid columns={2} divided stackable>
<Grid.Column>
<div>
<Header as='h4'>Mission name</Header>
<Input style={{ marginTop: '-1em' }}
placeholder='Mission name...'
value={shuttle.name}
onChange={(e, { value }) => updateMissionName(value)}>
<input />
<Button icon onClick={() => updateMissionName('')} >
<Icon name='delete' />
</Button>
</Input>
</div>
<div style={{ marginTop: '1em' }}>
<Header as='h4'>Faction</Header>
<Dropdown
style={{ marginTop: '-1em' }}
selection
options={factionOptions}
value={shuttle.faction}
onChange={(e, { value }) => updateFaction(value)}
/>
</div>
</Grid.Column>
<Grid.Column>
<Header as='h4'>Seats</Header>
<p style={{ marginTop: '-1em' }}>Set each seat to the skills required. Add seats as necessary.</p>
<Table collapsing unstackable compact='very' size='small'>
<Table.Body>
{shuttle.seats.map((seat, seatNum) => (
<Table.Row key={seatNum}>
<Table.Cell textAlign='right'>{seatNum+1}</Table.Cell>
<Table.Cell textAlign='center'>
<EditorSeat seatNum={seatNum} seat={seat} />
</Table.Cell>
<Table.Cell textAlign='right'>
{shuttle.seats.length > 1 && <Button compact icon='trash' color='red' onClick={() => deleteMissionSeat(seatNum)} />}
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
<Button compact icon='plus square outline' content='Add Seat' onClick={() => addMissionSeat()} />
</Grid.Column>
</Grid>
<div style={{ marginTop: '1em' }}>
<Divider />
<p>If you no longer need this mission, you can delete it here. Note: missions will be automatically deleted after the event has concluded.</p>
<p><Button icon='trash' color='red' content='Delete Mission' onClick={() => deleteMission(shuttle.id)} /></p>
</div>
</React.Fragment>
);
}
function updateMissionName(newName: string): void {
shuttle.name = newName;
setShuttle({...shuttle});
}
function updateFaction(newFaction: number): void {
shuttle.faction = newFaction;
setShuttle({...shuttle});
}
function updateMissionSeat(seatNum: number, key: string, value: string): void {
shuttle.seats[seatNum][key] = value;
setShuttle({...shuttle});
}
function addMissionSeat(): void {
shuttle.seats.push(new ShuttleSeat());
setShuttle({...shuttle});
}
function deleteMissionSeat(seatNum: number): void {
shuttle.seats.splice(seatNum, 1);
setShuttle({...shuttle});
}
function applyEdits(): void {
if (shuttle.priority === 0) shuttle.priority = missionsSelected + 1;
const shuttleNum = shuttlers.shuttles.findIndex(s => s.id === shuttle.id);
shuttlers.shuttles[shuttleNum] = shuttle;
updateShuttlers();
setEditMission(undefined);
}
};
const missionsSelected = data.filter(shuttle => shuttle.priority > 0).length;
return (
<React.Fragment>
<div>Click all the missions that you want to run, then click 'Recommend Crew' to see the best seats for your crew.</div>
<Table celled striped selectable sortable singleLine>
<Table.Header>
<Table.Row>
{tableConfig.map((cell, idx) => (
<Table.HeaderCell key={idx}
sorted={column === cell.column ? direction : null}
onClick={() => dispatch({ type: 'CHANGE_SORT', column: cell.column, reverse: cell.reverse })}
colSpan={cell.span ?? 1}
textAlign={cell.align ?? 'left'}
>
{cell.title}
</Table.HeaderCell>
))}
</Table.Row>
</Table.Header>
<Table.Body>
{data.length === 0 && (
<Table.Row>
<Table.Cell colSpan={10} textAlign='center'>
No missions available.
</Table.Cell>
</Table.Row>
)}
{data.map(shuttle => (
<Table.Row key={shuttle.id} style={{ cursor: 'pointer' }}
onClick={() => toggleMissionStatus(shuttle.id)}
onDoubleClick={() => { toggleMissionStatus(shuttle.id); props.recommendShuttlers(); }}
>
<Table.Cell textAlign='center'>
{shuttle.priority > 0 && (<Icon color='green' name='check' />)}
</Table.Cell>
<Table.Cell>
<span style={{ fontSize: '1.1em' }}><b>{shuttle.name}</b></span>
</Table.Cell>
<Table.Cell textAlign='center'>
<ShuttleFactionView factionId={shuttle.faction} size={1.5} />
</Table.Cell>
<Table.Cell textAlign='center'>{shuttle.seats.length}</Table.Cell>
{[0, 1, 2, 3, 4].map(seatNum => (
<Table.Cell key={seatNum} textAlign='center'>
{shuttle.seats.length > seatNum && (
<SeatSkillView seat={shuttle.seats[seatNum]} />
)}
</Table.Cell>
))}
<Table.Cell textAlign='right'>
{!shuttle.readonly && (
<Button icon='edit' content='Edit' onClick={(e) => { setEditMission(shuttle); e.stopPropagation(); }}/>
)}
</Table.Cell>
</Table.Row>
))}
</Table.Body>
<Table.Footer>
<Table.Row>
<Table.HeaderCell colSpan={10} textAlign='right'>
{missionsSelected > 0 && (<Button compact icon='rocket' color='green' content='Recommend Crew' onClick={() => props.recommendShuttlers()} />)}
{missionsSelected === 0 && (<Button compact icon='rocket' content='Recommend Crew' />)}
</Table.HeaderCell>
</Table.Row>
</Table.Footer>
</Table>
{editMission && <MissionEditor shuttle={editMission} />}
<p>If the mission you want isn't listed here, click 'Create Mission' to input the mission parameters manually. Tip: open shuttle missions in-game before uploading your player data to DataCore so that this tool can import the missions automatically.</p>
<Button icon='plus square' content='Create Mission' onClick={() => createMission() } />
</React.Fragment>
);
function reducer(state, action): any {
switch (action.type) {
case 'UPDATE_DATA':
//const defaultColumn = action.data.filter(shuttle => shuttle.priority > 0).length ? 'priority' : 'name';
const updatedData = action.data.slice();
firstSort(updatedData, action.column ?? 'name', action.direction ?? 'ascending');
return {
column: action.column ?? 'name',
data: updatedData,
direction: action.direction ?? 'ascending'
};
case 'CHANGE_SORT':
if (!action.column) {
return {
column: state.column,
data: state.data,
direction: state.direction
};
}
if (state.column === action.column && action.column !== 'priority') {
return {
...state,
data: state.data.slice().reverse(),
direction: state.direction === 'ascending' ? 'descending' : 'ascending'
};
}
else {
const data = state.data.slice();
firstSort(data, action.column, action.reverse);
return {
column: action.column,
data: data,
direction: action.reverse ? 'descending' : 'ascending'
};
}
default:
throw new Error();
}
}
function firstSort(data: any[], column: string, reverse: boolean = false): any[] {
data.sort((a, b) => {
if (column === 'name') return a.name.localeCompare(b.name);
let aValue = column.split('.').reduce((prev, curr) => prev.hasOwnProperty(curr) ? prev[curr] : undefined, a);
let bValue = column.split('.').reduce((prev, curr) => prev.hasOwnProperty(curr) ? prev[curr] : undefined, b);
// Always show selected missions at the top when sorting by priority
if (column === 'priority') {
if (aValue === 0) aValue = 100;
if (bValue === 0) bValue = 100;
}
if (column === 'skills') {
aValue = a.seats.length;
bValue = b.seats.length;
}
// Tiebreaker goes to name ascending
if (aValue === bValue) return a.name.localeCompare(b.name);
if (reverse) bValue - aValue;
return aValue - bValue;
});
}
function checkMissions(shuttleIds: string[], checkState: boolean = true): void {
let priority = 0;
shuttlers.shuttles.forEach(shuttle => {
if (shuttleIds.length === 0)
shuttle.priority = checkState ? ++priority : 0;
else
shuttle.priority = checkState && shuttleIds.includes(shuttle.id) ? ++priority : 0;
});
updateShuttlers();
if (shuttleIds.length !== 0)
dispatch({ type: 'CHANGE_SORT', column: 'priority' });
}
function createMission(): void {
const shuttle = new Shuttle(groupId);
shuttle.seats.push(new ShuttleSeat());
shuttlers.shuttles.push(shuttle);
updateShuttlers();
setEditMission(shuttle);
}
function deleteMission(shuttleId: string): void {
const shuttleNum = shuttlers.shuttles.findIndex(shuttle => shuttle.id === shuttleId);
shuttlers.shuttles.splice(shuttleNum, 1);
updateShuttlers();
setEditMission(undefined);
}
function toggleMissionStatus(shuttleId: string): void {
const shuttle = shuttlers.shuttles.find(shuttle => shuttle.id === shuttleId);
shuttle.priority = shuttle.priority === 0 ? missionsSelected+1 : 0;
updateShuttlers();
}
function updateShuttlers(): void {
setShuttlers({...shuttlers});
}
}
Example #19
Source File: CreateTxModal.tsx From multi-sig-wallet with MIT License | 4 votes |
CreateTxModal: React.FC<Props> = ({ open, onClose }) => {
const {
state: { web3, account },
} = useWeb3Context();
const { pending, error, call } = useAsync<SubmitTxParams, any>(
async (params) => {
if (!web3) {
throw new Error("No web3");
}
await submitTx(web3, account, params);
}
);
const [inputs, setInputs] = useState({
to: "",
value: 0,
data: "",
});
function onChange(name: string, e: React.ChangeEvent<HTMLInputElement>) {
setInputs({
...inputs,
[name]: e.target.value,
});
}
async function onSubmit() {
if (pending) {
return;
}
const { error } = await call({
...inputs,
value: inputs.value.toString(),
});
if (!error) {
onClose();
}
}
return (
<Modal open={open} onClose={onClose}>
<Modal.Header>Create Transaction</Modal.Header>
<Modal.Content>
{error && <Message error>{error.message}</Message>}
<Form onSubmit={onSubmit}>
<Form.Field>
<label>To</label>
<Form.Input
type="text"
value={inputs.to}
onChange={(e) => onChange("to", e)}
/>
</Form.Field>
<Form.Field>
<label>Value</label>
<Form.Input
type="number"
min={0}
value={inputs.value}
onChange={(e) => onChange("value", e)}
/>
</Form.Field>
<Form.Field>
<label>Data</label>
<Form.Input
value={inputs.data}
onChange={(e) => onChange("data", e)}
/>
</Form.Field>
</Form>
</Modal.Content>
<Modal.Actions>
<Button onClick={onClose} disabled={pending}>
Cancel
</Button>
<Button
color="green"
onClick={onSubmit}
disabled={pending}
loading={pending}
>
Create
</Button>
</Modal.Actions>
</Modal>
);
}
Example #20
Source File: assignmentslist.tsx From website with MIT License | 4 votes |
AssignmentsList = (props: AssignmentsList) => {
const { shuttlers, setShuttlers, assigned, setAssigned, crewScores, updateCrewScores } = props;
const [shuttleScores, setShuttleScores] = React.useState([]);
const [editAssignment, setEditAssignment] = React.useState(undefined);
const [scoreLoadQueue, setScoreLoadQueue] = React.useState('');
React.useEffect(() => {
updateShuttleScores();
}, [assigned]);
const myCrew = props.crew;
const SeatAssignmentRow = (props: { shuttleId: string, seatNum: number, seat: ShuttleSeat }) => {
const { shuttleId, seatNum, seat } = props;
let assignedCrew;
const seated = assigned.find(seat => seat.shuttleId === shuttleId && seat.seatNum === seatNum);
if (seated) {
assignedCrew = myCrew.find(crew => crew.id === seated.assignedId && crew.symbol === seated.assignedSymbol);
if (!assignedCrew) assignedCrew = myCrew.find(crew => crew.symbol === seated.assignedSymbol);
}
const lockAttributes = {};
if (seated?.locked) lockAttributes.color = 'yellow';
return (
<Table.Row key={seatNum} style={{ cursor: 'pointer' }}
onClick={() => { if (seat.skillA) setEditAssignment({shuttleId, seatNum}); }}
>
<Table.Cell textAlign='center'>
<SeatSkillView seat={seat} />
</Table.Cell>
<Table.Cell textAlign={assignedCrew ? 'left' : 'right'}>
{assignedCrew && (<SeatCrewView crew={assignedCrew} />)}
{!assignedCrew && (<span style={{ color: 'gray' }}>(Open seat)</span>)}
</Table.Cell>
<Table.Cell>
{assignedCrew?.immortal > 0 && (<Icon name='snowflake' />)}
{assignedCrew?.prospect && (<Icon name='add user' />)}
</Table.Cell>
<Table.Cell textAlign='center'>
{assignedCrew && (
<Button.Group>
<Button compact icon='lock' {... lockAttributes}
onClick={(e) => { toggleAssignmentLock(shuttleId, seatNum); e.stopPropagation(); }} />
<Button compact icon='x'
onClick={(e) => { updateAssignment(shuttleId, seatNum); e.stopPropagation(); }} />
</Button.Group>
)}
</Table.Cell>
</Table.Row>
);
};
const SeatAssignmentPicker = () => {
const { shuttleId, seatNum } = editAssignment;
const [paginationPage, setPaginationPage] = React.useState(1);
const seat = shuttlers.shuttles.find(shuttle => shuttle.id === shuttleId).seats[seatNum];
const ssId = getSkillSetId(seat);
const scores = crewScores.skillsets[ssId];
if (!scores) {
if (scoreLoadQueue === '') {
setScoreLoadQueue(ssId);
updateCrewScores([seat], () => setScoreLoadQueue(''));
}
return (<></>);
}
const shuttle = shuttlers.shuttles.find(shuttle => shuttle.id === shuttleId);
return (
<Modal
open={true}
onClose={() => setEditAssignment(undefined)}
>
<Modal.Header>
{shuttle.name}
{shuttleScores[shuttleId] ?
<span style={{ fontSize: '.95em', fontWeight: 'normal', paddingLeft: '1em' }}>
({(shuttleScores[shuttleId].chance*100).toFixed(1)}% Chance)
</span>
: ''}
</Modal.Header>
<Modal.Content scrolling>
{scores && renderTable()}
</Modal.Content>
<Modal.Actions>
<Button icon='forward' content='Next Seat' onClick={() => cycleShuttleSeat()} />
<Button positive onClick={() => setEditAssignment(undefined)}>
Close
</Button>
</Modal.Actions>
</Modal>
);
function renderTable(): JSX.Element {
let assignedCrew;
const seated = assigned.find(seat => seat.shuttleId === shuttleId && seat.seatNum == seatNum);
if (seated) {
assignedCrew = myCrew.find(crew => crew.id === seated.assignedId && crew.symbol === seated.assignedSymbol);
if (!assignedCrew) assignedCrew = myCrew.find(crew => crew.symbol === seated.assignedSymbol);
}
// Pagination
const rowsPerPage = 10;
const totalPages = Math.ceil(scores.length / rowsPerPage);
const data = scores.slice(rowsPerPage * (paginationPage - 1), rowsPerPage * paginationPage).map(score => {
const scoreCrew = myCrew.find(crew => crew.id === score.id);
return {...scoreCrew, ...score}
});
return (
<React.Fragment>
<Table striped selectable singleLine compact='very'>
<Table.Header>
<Table.Row>
<Table.HeaderCell />
<Table.HeaderCell colSpan={2}>Best <span style={{ padding: '0 .5em' }}><SeatSkillView seat={seat} /></span> Crew</Table.HeaderCell>
<Table.HeaderCell textAlign='center'>Here<Popup trigger={<Icon name='help' />} content='Using this crew here will result in this net change to the success chance of this shuttle' /></Table.HeaderCell>
<Table.HeaderCell>Current Assignment</Table.HeaderCell>
<Table.HeaderCell textAlign='center'>There<Popup trigger={<Icon name='help' />} content='Removing this crew from their current assignment will leave an open seat on that shuttle, resulting in this success chance' /></Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{data.map((crew, idx) => renderRow(crew, idx, assignedCrew))}
</Table.Body>
<Table.Footer>
<Table.Row>
<Table.HeaderCell colSpan={6}>
<Pagination
totalPages={totalPages}
activePage={paginationPage}
onPageChange={(e, { activePage }) => setPaginationPage(activePage)}
/>
</Table.HeaderCell>
</Table.Row>
</Table.Footer>
</Table>
</React.Fragment>
);
}
function renderRow(crew: any, idx: number, assignedCrew: any): JSX.Element {
const currentSeat = assigned.find(seat => seat.assignedId === crew.id && seat.assignedSymbol === crew.symbol);
const currentShuttle = currentSeat ? shuttlers.shuttles.find(shuttle => shuttle.id === currentSeat.shuttleId) : undefined;
return (
<Table.Row key={idx} style={{ cursor: 'pointer' }}
onClick={() => {
if (!assignedCrew || crew.id !== assignedCrew.id)
updateAssignment(shuttleId, seatNum, crew, true);
setEditAssignment(undefined);
}}
>
<Table.Cell textAlign='center'>
{assignedCrew?.id === crew.id && (<Icon color='green' name='check' />)}
</Table.Cell>
<Table.Cell><SeatCrewView crew={crew} /></Table.Cell>
<Table.Cell>
{crew.immortal > 0 && (<Icon name='snowflake' />)}
{crew.prospect && (<Icon name='add user' />)}
</Table.Cell>
<Table.Cell textAlign='center'>
{renderScoreChange(shuttleId, seatNum, crew.score)}
</Table.Cell>
<Table.Cell>
{currentShuttle?.name}
{currentShuttle?.id === shuttleId && <span style={{ paddingLeft: '1em' }}><i>(This Shuttle)</i></span>}
{currentSeat?.locked && <span style={{ paddingLeft: '1em' }}><Icon name='lock' /></span>}
</Table.Cell>
<Table.Cell textAlign='center'>
{currentSeat && renderScoreChange(currentSeat.shuttleId, currentSeat.seatNum, 0)}
</Table.Cell>
</Table.Row>
);
}
function renderScoreChange(shuttleId: string, seatNum: number, replacementScore: number = 0): JSX.Element {
if (!shuttleScores[shuttleId]) return (<></>);
const newScores = [...shuttleScores[shuttleId].scores];
newScores[seatNum] = replacementScore;
const DIFFICULTY = 2000;
const dAvgSkill = newScores.reduce((a, b) => (a + b), 0)/newScores.length;
const dChance = 1/(1+Math.pow(Math.E, 3.5*(0.5-dAvgSkill/DIFFICULTY)));
const attributes = {};
if (replacementScore === 0) {
if (dChance*100 >= 90) attributes.style = { color: 'green', fontWeight: 'bold' };
return (<span {...attributes}>{Math.floor(dChance*100)}%</span>);
}
const dDelta = dChance - shuttleScores[shuttleId].chance;
if (dDelta > 0 && dChance*100 >= 90)
attributes.style = { color: 'green', fontWeight: 'bold' };
return (<span {...attributes}>{dDelta > 0 ? '+' : ''}{(dDelta*100).toFixed(1)}%</span>);
}
function cycleShuttleSeat(): void {
const nextAssignment = {
shuttleId: shuttleId,
seatNum: seatNum + 1 >= shuttle.seats.length ? 0 : seatNum + 1
};
setEditAssignment(nextAssignment);
}
};
const data = shuttlers.shuttles.slice()
.filter(shuttle => shuttle.groupId === props.groupId && shuttle.priority > 0)
.sort((a, b) => a.priority - b.priority);
return (
<React.Fragment>
<p>You can rearrange crew to balance shuttle chances as you see fit. Click a seat to change the crew assigned to it. Lock an assignment to keep that crew in that seat when requesting new recommendations.</p>
<Table celled striped compact='very'>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Mission</Table.HeaderCell>
<Table.HeaderCell textAlign='center'>Faction</Table.HeaderCell>
<Table.HeaderCell textAlign='center'>Seat Assignments</Table.HeaderCell>
<Table.HeaderCell textAlign='center'>Success Chance</Table.HeaderCell>
<Table.HeaderCell />
</Table.Row>
</Table.Header>
<Table.Body>
{data.length === 0 && (
<Table.Row>
<Table.Cell colSpan={6} textAlign='center'>
No missions selected.
</Table.Cell>
</Table.Row>
)}
{data.map(shuttle => (
<Table.Row key={shuttle.id}>
<Table.Cell><b>{shuttle.name}</b></Table.Cell>
<Table.Cell textAlign='center'>
<ShuttleFactionView factionId={shuttle.faction} size={3} />
</Table.Cell>
<Table.Cell>
<Table striped selectable singleLine compact='very' style={{ margin: '0 auto' }}>
<Table.Body>
{shuttle.seats.map((seat, seatNum) =>
<SeatAssignmentRow key={seatNum} shuttleId={shuttle.id} seatNum={seatNum} seat={seat} />
)}
</Table.Body>
</Table>
</Table.Cell>
<Table.Cell textAlign='center'>
{shuttleScores[shuttle.id]?.chance > 0 ? <b>{Math.floor(shuttleScores[shuttle.id].chance*100)}%</b> : <></>}
</Table.Cell>
<Table.Cell textAlign='right'>
<Button compact icon='ban' content='Dismiss' onClick={() => dismissShuttle(shuttle.id)} />
</Table.Cell>
</Table.Row>
))}
</Table.Body>
<Table.Footer>
<Table.Row>
<Table.HeaderCell colSpan={3}>
<Button compact icon='backward' content='Change Missions' onClick={() => props.setActiveStep('missions')} />
</Table.HeaderCell>
<Table.HeaderCell colSpan={3} textAlign='right'>
{data.length > 0 && (<Button compact icon='rocket' color='green' content='Recommend Crew' onClick={() => props.recommendShuttlers()} />)}
</Table.HeaderCell>
</Table.Row>
</Table.Footer>
</Table>
{editAssignment && (<SeatAssignmentPicker />)}
</React.Fragment>
);
function dismissShuttle(shuttleId: string): void {
shuttlers.shuttles.find(shuttle => shuttle.id === shuttleId).priority = 0;
setShuttlers({...shuttlers});
}
function updateAssignment(shuttleId: string, seatNum: number, assignedCrew: any, locked: boolean): void {
// Unassign crew from previously assigned seat, if necessary
if (assignedCrew) {
const current = assigned.find(seat => seat.assignedId === assignedCrew.id);
if (current) {
current.assignedId = -1;
current.assignedSymbol = '';
current.seatScore = 0;
current.locked = false;
}
}
const seated = assigned.find(seat => seat.shuttleId === shuttleId && seat.seatNum === seatNum);
if (assignedCrew && !seated) {
assigned.push({
shuttleId,
seatNum,
ssId: assignedCrew.ssId,
assignedId: assignedCrew.id,
assignedSymbol: assignedCrew.symbol,
seatScore: assignedCrew.score,
locked
});
}
else if (assignedCrew) {
seated.assignedId = assignedCrew.id;
seated.assignedSymbol = assignedCrew.symbol;
seated.seatScore = assignedCrew.score;
seated.locked = locked;
}
else {
seated.assignedId = -1;
seated.assignedSymbol = '';
seated.seatScore = 0;
seated.locked = false;
}
setAssigned([...assigned]);
}
function toggleAssignmentLock(shuttleId: string, seatNum: number): void {
const seated = assigned.find(seat => seat.shuttleId === shuttleId && seat.seatNum === seatNum);
seated.locked = !seated.locked;
setAssigned([...assigned]);
}
function updateShuttleScores(): void {
const DIFFICULTY = 2000;
const newScores = [];
assigned.forEach(seated => {
if (!newScores[seated.shuttleId]) {
const seatCount = shuttlers.shuttles.find(shuttle => shuttle.id === seated.shuttleId).seats.length;
newScores[seated.shuttleId] = { chance: 0, scores: Array(seatCount).fill(0) };
}
newScores[seated.shuttleId].scores[seated.seatNum] = seated.seatScore;
const dAvgSkill = newScores[seated.shuttleId].scores.reduce((a, b) => (a + b), 0)/newScores[seated.shuttleId].scores.length;
const dChance = 1/(1+Math.pow(Math.E, 3.5*(0.5-dAvgSkill/DIFFICULTY)));
newScores[seated.shuttleId].chance = dAvgSkill > 0 ? dChance : 0;
});
setShuttleScores(newScores);
}
}
Example #21
Source File: voyagecalculator.tsx From website with MIT License | 4 votes |
VoyageEditConfigModal = (props: VoyageEditConfigModalProps) => {
const { updateConfig } = props;
const [voyageConfig, setVoyageConfig] = React.useState(props.voyageConfig);
const [modalIsOpen, setModalIsOpen] = React.useState(false);
const [updateOnClose, setUpdateOnClose] = React.useState(false);
const [options, setOptions] = React.useState(undefined);
React.useEffect(() => {
if (!modalIsOpen && updateOnClose) {
updateConfig(voyageConfig);
setUpdateOnClose(false);
}
}, [modalIsOpen]);
const defaultSlots = [
{ symbol: 'captain_slot', name: 'First Officer', skill: 'command_skill', trait: '' },
{ symbol: 'first_officer', name: 'Helm Officer', skill: 'command_skill', trait: '' },
{ symbol: 'chief_communications_officer', name: 'Communications Officer', skill: 'diplomacy_skill', trait: '' },
{ symbol: 'communications_officer', name: 'Diplomat', skill: 'diplomacy_skill', trait: '' },
{ symbol: 'chief_security_officer', name: 'Chief Security Officer', skill: 'security_skill', trait: '' },
{ symbol: 'security_officer', name: 'Tactical Officer', skill: 'security_skill', trait: '' },
{ symbol: 'chief_engineering_officer', name: 'Chief Engineer', skill: 'engineering_skill', trait: '' },
{ symbol: 'engineering_officer', name: 'Engineer', skill: 'engineering_skill', trait: '' },
{ symbol: 'chief_science_officer', name: 'Chief Science Officer', skill: 'science_skill', trait: '' },
{ symbol: 'science_officer', name: 'Deputy Science Officer', skill: 'science_skill', trait: '' },
{ symbol: 'chief_medical_officer', name: 'Chief Medical Officer', skill: 'medicine_skill', trait: '' },
{ symbol: 'medical_officer', name: 'Ship\'s Counselor', skill: 'medicine_skill', trait: '' }
];
const crewSlots = voyageConfig.crew_slots ?? defaultSlots;
crewSlots.sort((s1, s2) => CONFIG.VOYAGE_CREW_SLOTS.indexOf(s1.symbol) - CONFIG.VOYAGE_CREW_SLOTS.indexOf(s2.symbol));
return (
<Modal
open={modalIsOpen}
onClose={() => setModalIsOpen(false)}
onOpen={() => setModalIsOpen(true)}
trigger={<Button size='small'><Icon name='edit' />Edit</Button>}
>
<Modal.Header>Edit Voyage</Modal.Header>
<Modal.Content scrolling>
{renderContent()}
</Modal.Content>
<Modal.Actions>
<Button positive onClick={() => setModalIsOpen(false)}>
Close
</Button>
</Modal.Actions>
</Modal>
);
function renderContent(): JSX.Element {
if (!modalIsOpen) return (<></>);
if (!options) {
// Renders a lot faster by using known voyage traits rather than calculate list from all possible traits
const knownShipTraits = ['andorian','battle_cruiser','borg','breen','cardassian','cloaking_device',
'dominion','emp','explorer','federation','ferengi','freighter','historic','hologram',
'klingon','malon','maquis','orion_syndicate','pioneer','reman','romulan','ruthless',
'scout','spore_drive','terran','tholian','transwarp','vulcan','warship','war_veteran','xindi'];
const knownCrewTraits = ['android','astrophysicist','bajoran','borg','brutal',
'cardassian','civilian','communicator','costumed','crafty','cultural_figure','cyberneticist',
'desperate','diplomat','doctor','duelist','exobiology','explorer','federation','ferengi',
'gambler','hero','hologram','human','hunter','innovator','inspiring','jury_rigger','klingon',
'marksman','maverick','pilot','prodigy','resourceful','romantic','romulan',
'saboteur','scoundrel','starfleet','survivalist','tactician','telepath','undercover_operative',
'veteran','villain','vulcan'];
const skillsList = [];
for (let skill in CONFIG.SKILLS) {
skillsList.push({
key: skill,
value: skill,
text: CONFIG.SKILLS[skill]
});
}
const shipTraitsList = knownShipTraits.map(trait => {
return {
key: trait,
value: trait,
text: allTraits.ship_trait_names[trait]
};
});
shipTraitsList.sort((a, b) => a.text.localeCompare(b.text));
const crewTraitsList = knownCrewTraits.map(trait => {
return {
key: trait,
value: trait,
text: allTraits.trait_names[trait]
};
});
crewTraitsList.sort((a, b) => a.text.localeCompare(b.text));
setOptions({ skills: skillsList, ships: shipTraitsList, traits: crewTraitsList });
return (<></>);
}
return (
<React.Fragment>
<Message>Editing this voyage will reset all existing recommendations and estimates.</Message>
<Form>
<Form.Group>
<Form.Select
label='Primary skill'
options={options.skills}
value={voyageConfig.skills.primary_skill ?? 'command_skill'}
onChange={(e, { value }) => setSkill('primary_skill', value)}
placeholder='Primary'
/>
<Form.Select
label='Secondary skill'
options={options.skills}
value={voyageConfig.skills.secondary_skill ?? 'science_skill'}
onChange={(e, { value }) => setSkill('secondary_skill', value)}
placeholder='Secondary'
/>
<Form.Select
search clearable
label='Ship trait'
options={options.ships}
value={voyageConfig.ship_trait}
onChange={(e, { value }) => setShipTrait(value)}
placeholder='Ship trait'
/>
</Form.Group>
</Form>
<Table compact striped>
<Table.Header>
<Table.Row>
<Table.HeaderCell textAlign='center'>Skill</Table.HeaderCell>
<Table.HeaderCell>Seat</Table.HeaderCell>
<Table.HeaderCell>Trait</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{crewSlots.map((seat, idx) => (
<Table.Row key={seat.symbol}>
{ idx % 2 == 0 ?
(
<Table.Cell rowSpan='2' textAlign='center'>
<img alt="{seat.skill}" src={`${process.env.GATSBY_ASSETS_URL}atlas/icon_${seat.skill}.png`} style={{ height: '2em' }} />
</Table.Cell>
)
: (<></>)
}
<Table.Cell>{seat.name}</Table.Cell>
<Table.Cell>
<Dropdown search selection clearable
options={options.traits}
value={seat.trait}
onChange={(e, { value }) => setSeatTrait(seat.symbol, value)}
placeholder='Trait'
/>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
</React.Fragment>
);
}
function setSkill(prime: string, value: string): void {
// Flip skill values if changing to value that's currently set as the other prime
if (prime == 'primary_skill' && value == voyageConfig.skills.secondary_skill)
voyageConfig.skills.secondary_skill = voyageConfig.skills.primary_skill;
else if (prime == 'secondary_skill' && value == voyageConfig.skills.primary_skill)
voyageConfig.skills.primary_skill = voyageConfig.skills.secondary_skill;
voyageConfig.skills[prime] = value;
setVoyageConfig({...voyageConfig});
setUpdateOnClose(true);
}
function setShipTrait(value: string): void {
voyageConfig.ship_trait = value;
setVoyageConfig({...voyageConfig});
setUpdateOnClose(true);
}
function setSeatTrait(seat: symbol, value: string): void {
voyageConfig.crew_slots.find(s => s.symbol === seat).trait = value;
setVoyageConfig({...voyageConfig});
setUpdateOnClose(true);
}
}
Example #22
Source File: crewchallenge.tsx From website with MIT License | 4 votes |
CustomRules = (props: CustomRulesProps) => {
const portalCrew = React.useContext(PortalCrewContext);
const [modalIsOpen, setModalIsOpen] = React.useState(false);
const [guesses, setGuesses] = React.useState(props.rules.guesses);
const [series, setSeries] = React.useState(props.rules.series);
const [rarities, setRarities] = React.useState(props.rules.rarities);
const [excludedCrew, setExcludedCrew] = React.useState([]);
React.useEffect(() => {
const excludes = portalCrew.filter(crew => !series.includes(crew.series) || !rarities.includes(crew.max_rarity)).map(crew => crew.symbol);
setExcludedCrew([...excludes]);
}, [series, rarities]);
const guessOptions = [];
for (let i = 1; i <= 20; i++) {
guessOptions.push(
{ key: i, value: i, text: i }
);
}
const seriesOptions = [
{ key: 'tos', value: 'tos', text: 'The Original Series' },
{ key: 'tas', value: 'tas', text: 'The Animated Series' },
{ key: 'tng', value: 'tng', text: 'The Next Generation' },
{ key: 'ds9', value: 'ds9', text: 'Deep Space Nine' },
{ key: 'voy', value: 'voy', text: 'Voyager' },
{ key: 'ent', value: 'ent', text: 'Enterprise' },
{ key: 'dsc', value: 'dsc', text: 'Discovery' },
{ key: 'pic', value: 'pic', text: 'Picard' },
{ key: 'low', value: 'low', text: 'Lower Decks' },
{ key: 'snw', value: 'snw', text: 'Strange New Worlds' },
{ key: 'original', value: 'original', text: 'Timelines Originals' }
];
const rarityOptions = [
{ key: '1*', value: 1, text: '1* Common' },
{ key: '2*', value: 2, text: '2* Uncommon' },
{ key: '3*', value: 3, text: '3* Rare' },
{ key: '4*', value: 4, text: '4* Super Rare' },
{ key: '5*', value: 5, text: '5* Legendary' }
];
const isDefault = guesses === DEFAULT_GUESSES && series.length === DEFAULT_SERIES.length && rarities.length === DEFAULT_RARITIES.length;
const isDirty = guesses !== props.rules.guesses || series.length !== props.rules.series.length || rarities.length !== props.rules.rarities.length;
const isValid = portalCrew.length - excludedCrew.length > 0;
return (
<Modal
open={modalIsOpen}
onClose={() => { revertRules(); setModalIsOpen(false); }}
onOpen={() => setModalIsOpen(true)}
trigger={renderTrigger()}
size='tiny'
>
<Modal.Header>
Custom rules
<span style={{ paddingLeft: '1em', fontSize: '.9em', fontWeight: 'normal' }}>
(Possible solutions: {portalCrew.length - excludedCrew.length})
</span>
</Modal.Header>
<Modal.Content>
<div>
Max guesses:{' '}
<Dropdown selection
options={guessOptions}
value={guesses}
onChange={(e, { value }) => setGuesses(value)}
/>
</div>
<div style={{ marginTop: '1em' }}>
Include crew by series:
<Dropdown selection multiple fluid clearable closeOnChange
placeholder='Select at least 1 series'
options={seriesOptions}
value={series}
onChange={(e, { value }) => setSeries(value)}
/>
</div>
<div style={{ marginTop: '1em' }}>
Include crew by rarity:
<Dropdown selection multiple fluid clearable closeOnChange
placeholder='Select at least 1 rarity'
options={rarityOptions}
value={rarities}
onChange={(e, { value }) => setRarities(value)}
/>
</div>
</Modal.Content>
<Modal.Actions>
{!isDefault && <Button content='Reset' onClick={() => resetRules()} />}
{isDirty && <Button positive={isValid ? true : undefined} content='New Practice Game' onClick={() => applyRules()} />}
{!isDirty && <Button content='Close' onClick={() => setModalIsOpen(false)} />}
</Modal.Actions>
</Modal>
);
function renderTrigger(): JSX.Element {
return (
<span style={{ paddingLeft: '1em' }}>
<Button compact>
{!isDefault && <span><Icon name='check' color='green' /> Use custom rules</span>}
{isDefault && <span>Use custom rules...</span>}
</Button>
</span>
);
}
function revertRules(): void {
setGuesses(props.rules.guesses);
setSeries(props.rules.series);
setRarities(props.rules.rarities);
}
function resetRules(): void {
setGuesses(DEFAULT_GUESSES);
setSeries(DEFAULT_SERIES);
setRarities(DEFAULT_RARITIES);
}
function applyRules(): void {
if (!isValid) return;
const newRules = new GameRules();
newRules.guesses = guesses;
newRules.excludedCrew = excludedCrew;
newRules.series = series;
newRules.rarities = rarities;
props.changeRules(newRules);
setModalIsOpen(false);
}
}
Example #23
Source File: crewchallenge.tsx From website with MIT License | 4 votes |
CrewPicker = (props: CrewPickerProps) => {
const { rules, guesses, handleSelect } = props;
const portalCrew = React.useContext(PortalCrewContext);
const [modalIsOpen, setModalIsOpen] = React.useState(false);
const [searchFilter, setSearchFilter] = React.useState('');
const [paginationPage, setPaginationPage] = React.useState(1);
const [selectedCrew, setSelectedCrew] = React.useState(undefined);
const [showHints, setShowHints] = React.useState(true);
const guessesLeft = rules.guesses - guesses.length;
const inputRef = React.createRef();
React.useEffect(() => {
if (modalIsOpen) inputRef.current.focus();
}, [modalIsOpen]);
return (
<Modal
open={modalIsOpen}
onClose={() => setModalIsOpen(false)}
onOpen={() => setModalIsOpen(true)}
trigger={renderButton()}
size='tiny'
centered={false}
closeIcon
>
<Modal.Header>
<Input ref={inputRef}
size='mini' fluid
iconPosition='left'
placeholder='Search for crew by name'
value={searchFilter}
onChange={(e, { value }) => { setSearchFilter(value); setPaginationPage(1); setSelectedCrew(undefined); }}>
<input />
<Icon name='search' />
<Button icon onClick={() => { setSearchFilter(''); setPaginationPage(1); setSelectedCrew(undefined); inputRef.current.focus(); }} >
<Icon name='delete' />
</Button>
</Input>
</Modal.Header>
<Modal.Content scrolling>
{renderGrid()}
</Modal.Content>
<Modal.Actions>
<Button content='Show hints' onClick={() => setShowHints(!showHints) } />
{selectedCrew && (
<Button color='blue'
content={`Guess ${selectedCrew.name}`}
onClick={() => confirmGuess(selectedCrew.symbol)} />
)}
{!selectedCrew && (
<Button content='Close' onClick={() => setModalIsOpen(false)} />
)}
</Modal.Actions>
</Modal>
);
function renderButton(): JSX.Element {
return (
<Button fluid size='big' color='blue'>
<Icon name='zoom-in' />
Guess Crew
<span style={{ fontSize: '.95em', fontWeight: 'normal', paddingLeft: '1em' }}>
({guessesLeft} guess{guessesLeft !== 1 ? 'es' : ''} remaining)
</span>
</Button>
);
}
function renderGrid(): JSX.Element {
if (!modalIsOpen) return (<></>);
let data = portalCrew.slice();
if (rules.excludedCrew.length > 0)
data = data.filter(crew => !rules.excludedCrew.includes(crew.symbol));
// Filtering
if (searchFilter !== '') {
const filter = (input: string) => input.toLowerCase().indexOf(searchFilter.toLowerCase()) >= 0;
data = data.filter(crew => filter(crew.name));
}
if (data.length === 0) return (
<Message>
<p>No crew names match your current search.</p>
<p>Only crew that are currently <b>available in the time portal</b> will be used as mystery crew and valid guesses.</p>
</Message>
);
// Pagination
const itemsPerPage = 24, itemsToShow = itemsPerPage*paginationPage;
return (
<div>
<Grid doubling columns={3} textAlign='center'>
{data.slice(0, itemsToShow).map(crew => (
<Grid.Column key={crew.symbol} style={{ cursor: 'pointer' }}
onClick={() => { if (!guesses.includes(crew.symbol)) setSelectedCrew(crew); }}
onDoubleClick={() => { if (!guesses.includes(crew.symbol)) confirmGuess(crew.symbol); }}
color={selectedCrew?.symbol === crew.symbol ? 'blue' : null}
>
<img width={48} height={48} src={`${process.env.GATSBY_ASSETS_URL}${crew.imageUrlPortrait}`} />
<div>
{guesses.includes(crew.symbol) && (<Icon name='x' color='red' />)}
{crew.name}
</div>
{!showHints && (
<div>({[crew.series.toUpperCase(), `${crew.max_rarity}*`].join(', ')})</div>
)}
</Grid.Column>
))}
</Grid>
{itemsToShow < data.length && (
<InView as='div' style={{ margin: '2em 0', textAlign: 'center' }}
onChange={(inView, entry) => { if (inView) setPaginationPage(prevState => prevState + 1); }}
>
<Icon loading name='spinner' /> Loading...
</InView>
)}
{itemsToShow >= data.length && (
<Message>Tip: Double-tap a crew to make your guess more quickly.</Message>
)}
</div>
);
}
function confirmGuess(symbol: string): void {
handleSelect(symbol);
setModalIsOpen(false);
setSelectedCrew(undefined);
}
}
Example #24
Source File: events.tsx From website with MIT License | 4 votes |
function EventsPage() {
const [eventsData, setEventsData] = React.useState<EventInstance[]>([]);
const [leaderboardData, setLeaderboardData] = React.useState(null);
const [loadingError, setLoadingError] = React.useState(null);
const [modalEventInstance, setModalEventInstance] = React.useState(null);
// load the events and leaderboard data once on component mount
React.useEffect(() => {
async function loadData() {
try {
const fetchEventResp = await fetch('/structured/event_instances.json')
const eventDataList = await fetchEventResp.json();
setEventsData(eventDataList.reverse());
const fetchLeaderboardResp = await fetch('/structured/event_leaderboards.json');
const leaderboardDataList = await fetchLeaderboardResp.json();
const keyedLeaderboard = {};
leaderboardDataList.forEach(entry => keyedLeaderboard[entry.instance_id] = entry);
setLeaderboardData(keyedLeaderboard);
}
catch (e) {
setLoadingError(e);
}
}
loadData();
}, []);
return (
<Layout>
<Container style={{ paddingTop: '4em', paddingBottom: '2em' }}>
<Header as='h2'>Events</Header>
{loadingError && (
<Message negative>
<Message.Header>Unable to load event information</Message.Header>
<pre>{loadingError.toString()}</pre>
</Message>
)}
<Grid stackable columns={3}>
{eventsData.map(eventInfo => (
<Grid.Column key={eventInfo.instance_id}>
<div
style={{ cursor: 'pointer' }}
onClick={() => setModalEventInstance(eventInfo)}
>
<Segment padded>
<Label attached="bottom">
{eventInfo.event_name}
</Label>
<LazyImage
src={`${process.env.GATSBY_ASSETS_URL}${eventInfo.image}`}
size="large"
onError={e => e.target.style.visibility = 'hidden'}
/>
</Segment>
</div>
</Grid.Column>
))}
</Grid>
{modalEventInstance !== null && (
<Modal
open
size="large"
onClose={() => setModalEventInstance(null)}
closeIcon
>
<Modal.Header>{modalEventInstance.event_name}</Modal.Header>
<Modal.Content scrolling>
<EventInfoModal
instanceId={modalEventInstance.instance_id}
image={modalEventInstance.image}
hasDetails={modalEventInstance.event_details}
leaderboard={leaderboardData[modalEventInstance.instance_id].leaderboard}
/>
</Modal.Content>
</Modal>
)}
</Container>
</Layout>
);
}
Example #25
Source File: playertools.tsx From website with MIT License | 4 votes |
PlayerToolsForm = (props: PlayerToolsFormProps) => {
const PLAYERLINK = 'https://stt.disruptorbeam.com/player?client_api=17';
const { setValidInput } = props;
const [inputPlayerData, setInputPlayerData] = React.useState(undefined);
const [fullInput, setFullInput] = React.useState('');
const [displayedInput, setDisplayedInput] = React.useState('');
const [errorMessage, setErrorMessage] = React.useState(undefined);
let inputUploadFile = null;
if (fullInput != "")
parseInput();
React.useEffect(() => {
if (inputPlayerData) {
setValidInput(inputPlayerData);
setInputPlayerData(undefined);
}
}, [inputPlayerData]);
return (
<Layout title='Player tools'>
<Header as='h2'>Player tools</Header>
<p>You can access some of your player data from the game's website and import it here to calculate optimal voyage lineups, identify unnecessary items, export your crew list as a CSV, or share your profile with other players, among other tools. This website cannot make direct requests to the game's servers due to security configurations and unclear terms of service interpretations, so there are a few manual steps required to import your data.</p>
<p>If you have multiple accounts, we recommend using your browser in InPrivate mode (Edge) or Incognito mode (Firefox / Chrome) to avoid caching your account credentials, making it easier to change accounts.</p>
<ul>
<li>
Open this page in your browser:{' '}
<a href={PLAYERLINK} target='_blank'>
https://stt.disruptorbeam.com/player
</a>
</li>
<li>
Log in if asked, then wait for the page to finish loading. It should start with:{' '}
<span style={{ fontFamily: 'monospace' }}>{'{"action":"update","player":'}</span> ...
</li>
<li>Select everything in the page (Ctrl+A) and copy it (Ctrl+C)</li>
<li>Paste it (Ctrl+V) in the text box below. Note that DataCore will intentionally display less data here to speed up the process</li>
<li>Click the 'Import data' button</li>
</ul>
<Form>
<TextArea
placeholder='Paste your player data here'
value={displayedInput}
onChange={(e, { value }) => setDisplayedInput(value)}
onPaste={(e) => { return onInputPaste(e) }}
/>
<input
type='file'
onChange={(e) => { handleFileUpload(e) }}
style={{ display: 'none' }}
ref={e => inputUploadFile = e}
/>
</Form>
<Button
onClick={() => parseInput()}
style={{ marginTop: '1em' }}
content='Import data'
icon='paste'
labelPosition='right'
/>
{errorMessage && (
<Message negative>
<Message.Header>Error</Message.Header>
<p>{errorMessage}</p>
</Message>
)}
<p style={{ marginTop: '2em' }}>To circumvent the long text copy limitations on mobile devices, download{' '}
<a href={PLAYERLINK} target='_blank'>
your player data
</a>
{' '}to your device, then click the 'Upload data file' button.
</p>
<p>
<Modal
trigger={<a href="#">Click here for detailed instructions for Apple iOS devices.</a>}
header='Player data upload on iOS'
content={<ul>
<li>Go to your player data using the link provided, logging in if asked.</li>
<li>Wait for the page to finish loading. It should start with:{' '}
<span style={{ fontFamily: 'monospace' }}>{'{"action":"update","player":'}</span> ...
</li>
<li>Press the share icon while viewing the page.</li>
<li>Tap 'options' and choose 'Web Archive', tap 'save to files', choose a location and save.</li>
<li>Come back to this page (DataCore.app player tools).</li>
<li>Tap the 'Upload data file' button.</li>
<li>Choose the file starting with 'player?client_api...' from where you saved it.</li>
</ul>}
/>
</p>
<Button
onClick={() => inputUploadFile.click()}
content='Upload data file'
icon='file'
labelPosition='right'
/>
</Layout>
);
function parseInput() {
let testInput = fullInput;
// Use inputted text if no pasted text detected
if (testInput == '') testInput = displayedInput;
try {
let testData = JSON.parse(testInput as string);
if (testData) {
// Test for playerData array glitch
if (Array.isArray(testData)) {
testData = {...testData[0]};
}
if (testData.player && testData.player.display_name) {
if (testData.player.character && testData.player.character.crew && (testData.player.character.crew.length > 0)) {
setInputPlayerData(testData);
setDisplayedInput('');
setErrorMessage(undefined);
} else {
setErrorMessage('Failed to parse player data from the text you pasted. Make sure you are logged in with the correct account.');
}
}
else {
setErrorMessage('Failed to parse player data from the text you pasted. Make sure the page is loaded correctly and you copied the entire contents!');
}
} else {
setErrorMessage('Failed to parse player data from the text you pasted. Make sure the page is loaded correctly and you copied the entire contents!');
}
} catch (err) {
if ((/Log in to CS Tools/).test(testInput)) {
setErrorMessage('You are not logged in! Open the player data link above and log in to the game as instructed. Then return to this DataCore page and repeat all the steps to import your data.');
}
else {
setErrorMessage(`Failed to read the data. Make sure the page is loaded correctly and you copied the entire contents! (${err})`);
}
}
setFullInput('');
}
function onInputPaste(event) {
let paste = event.clipboardData || window.clipboardData;
if (paste) {
let fullPaste = paste.getData('text');
setFullInput(fullPaste);
setDisplayedInput(`${fullPaste.substr(0, 300)} [ ... ] ${fullPaste.substr(-100)}\n/* Note that DataCore is intentionally displaying less data here to speed up the process */`);
event.preventDefault();
return false;
}
return true;
}
function handleFileUpload(event) {
// use FileReader to read file content in browser
const fReader = new FileReader();
fReader.onload = (e) => {
let data = e.target.result.toString();
// Handle Apple webarchive wrapping
if (data.match(/^bplist00/)) {
// Find where the JSON begins and ends, and extract just that from the larger string.
data = data.substring(data.indexOf('>{') + 1, data.lastIndexOf('}}') + 2);
}
setFullInput(data);
};
fReader.readAsText(event.target.files[0]);
}
}
Example #26
Source File: LobbyRoomList.tsx From FLECT_Amazon_Chime_Meeting with Apache License 2.0 | 4 votes |
render() {
const gs = this.props as GlobalState
const props = this.props as any
const appState = props.appState as AppState
const joinedMeetingIds = Object.keys(appState.joinedMeetings)
const meetings = gs.meetings.map((meeting:MeetingInfo)=>{
let joinButton
const currentMeetingId = meeting.meetingId
if(joinedMeetingIds.includes(currentMeetingId)){
joinButton = (
<Button basic color="red" floated='right'
onClick={()=>{
console.log("CLICK LEAVE", meeting.meetingId)
props._leaveMeeting(meeting.meetingId, appState.joinedMeetings[currentMeetingId].meetingSession.configuration.credentials!.attendeeId)
}}
>
leave
<Icon name='chevron left' />
</Button>
)
}else{
joinButton = (
<Button basic color="teal" floated='right'
onClick={()=>{
console.log("CLICK JOIN", meeting.meetingId)
props._joinMeeting(meeting.meetingId, gs)
}}>
join
<Icon name='chevron right' />
</Button>
)
}
return (
<Item>
{/* <Item.Image size='mini' src='/' /> */}
<Item.Content>
<Item.Header>
<Icon name="lock open" />
{meeting.meetingName}
</Item.Header>
<Item.Meta>
<div>
<b>Owner: </b>
{meeting.metaData.UserName}
</div>
<div>
<b>Open Date: </b>
<span>{new Date(Number(meeting.metaData.StartTime)).toLocaleDateString()}</span>
<span>{new Date(Number(meeting.metaData.StartTime)).toLocaleTimeString()}</span>
</div>
</Item.Meta>
<Item.Extra>
{joinButton}
</Item.Extra>
</Item.Content>
</Item>
)
})
return (
<div>
<div>
<Modal dimmer={'blurring'} size={'small'} open={this.state.open} onClose={this.close}>
<Modal.Header>Create New Meeting</Modal.Header>
<Modal.Content>
<Form>
<Form.Field>
<label>Room Name</label>
<input placeholder='name' ref={this.roomNameRef}/>
</Form.Field>
<Form.Field>
<Checkbox label='Use Passcode?(not implement)' checked={this.state.usePasscodeChecked}
onClick={()=>{this.setState({ usePasscodeChecked: !this.state.usePasscodeChecked })}}
/>
<label>Pass Code(not implement)</label>
<input placeholder='pass' ref={this.roomPassCodeRef}/>
</Form.Field>
<Form.Field>
<Checkbox label='Secret?(not implement)' checked={this.state.secretRoomCreateChecked}
onClick={()=>{this.setState({ secretRoomCreateChecked: !this.state.secretRoomCreateChecked })}}
/>
</Form.Field>
</Form>
</Modal.Content>
<Modal.Actions>
<Button negative onClick={this.close}>Cancel</Button>
<Button positive icon='checkmark' labelPosition='right' content='Create' onClick={this.createMeeting}/>
</Modal.Actions>
</Modal>
</div>
<div>
<Segment padded>
<Divider horizontal>
<Header as='h2' textAlign="left">
Lobby
</Header>
</Divider>
<Header as='h3' textAlign="left">
Actions
</Header>
<List link>
<List.Item as='a' active onClick={(e, d)=>{this.show()}}>
<Header as='h5' textAlign={'left'}>
<Icon name="chat" active />New Meeting!
</Header>
</List.Item>
<List.Item as='a' active onClick={()=>{props.refreshRoomList()}}>
<Header as='h5' textAlign={'left'}>
<Icon name="refresh" active />Refresh Meeting List
</Header>
</List.Item>
</List>
<Divider hidden />
<Header as='h3' textAlign="left">
Meetings
</Header>
<div>
<Item.Group >
{meetings}
</Item.Group>
</div>
</Segment>
</div>
</div>
)
}
Example #27
Source File: crewretrieval.tsx From website with MIT License | 4 votes |
PolestarProspectModal = (props: PolestarProspectModalProps) => {
const { ownedPolestars, allCrew, updateProspects } = props;
const [addedPolestars, setAddedPolestars] = React.useState(props.addedPolestars);
const [modalIsOpen, setModalIsOpen] = React.useState(false);
const [activeCrew, setActiveCrew] = React.useState('');
const [activeConstellation, setActiveConstellation] = React.useState('');
const [activePolestar, setActivePolestar] = React.useState('');
const [allKeystones, setAllKeystones] = React.useState(undefined);
const [control, setControl] = React.useState([]);
const [crewCrates, setCrewCrates] = React.useState(0);
const [ownedConstellations, setOwnedConstellations] = React.useState([]);
React.useEffect(() => {
if (allKeystones) {
// Chances assume you can't get rarity, skill constellations from scans
setCrewCrates(allKeystones.filter(k => k.type == 'crew_keystone_crate').length);
const owned = allKeystones.filter(k => (k.type == 'crew_keystone_crate' || k.type == 'keystone_crate') && k.quantity > 0)
.sort((a, b) => a.name.localeCompare(b.name));
setOwnedConstellations([...owned]);
}
}, [allKeystones]);
// Recalculate combos only when modal gets closed
React.useEffect(() => {
if (!modalIsOpen) {
updateProspects([...addedPolestars]);
}
}, [modalIsOpen]);
return (
<Modal
open={modalIsOpen}
onClose={() => setModalIsOpen(false)}
onOpen={() => setModalIsOpen(true)}
trigger={<Button><Icon name='add' />{addedPolestars.length}</Button>}
size='large'
>
<Modal.Header>Add Prospective Polestars</Modal.Header>
<Modal.Content scrolling>
{renderContent()}
</Modal.Content>
<Modal.Actions>
{activePolestar != '' && (<Button icon='backward' content='Return to polestars' onClick={() => setActivePolestar('')} />)}
<Button positive onClick={() => setModalIsOpen(false)}>Close</Button>
</Modal.Actions>
</Modal>
);
function renderContent(): JSX.Element {
if (!modalIsOpen) return (<></>);
if (!allKeystones) {
calculateKeystoneOdds();
calculateControl();
return (<></>);
}
if (activePolestar != '')
return renderPolestarDetail();
return renderPolestarFinder();
}
function calculateKeystoneOdds(): void {
const allkeystones = JSON.parse(JSON.stringify(props.allKeystones));
let totalCrates = 0, totalDrops = 0;
allkeystones.forEach(keystone => {
if (keystone.type == 'crew_keystone_crate') {
totalCrates++;
totalDrops += keystone.keystones.length;
}
});
allkeystones.filter(k => k.type == 'keystone').forEach(polestar => {
const crates = allkeystones.filter(k => (k.type == 'crew_keystone_crate' || k.type == 'keystone_crate') && k.keystones.includes(polestar.id));
const nochance = polestar.filter.type == 'rarity' || polestar.filter.type == 'skill' || crates.length == 0;
polestar.crate_count = nochance ? 0 : crates.length;
//polestar.scan_odds = nochance ? 0 : crates.length/totalDrops; // equal chance of dropping
polestar.scan_odds = nochance ? 0 : crates.reduce((prev, curr) => prev + (1/curr.keystones.length), 0)/totalCrates; // conditional probability
const owned = crates.filter(k => k.quantity > 0);
polestar.owned_crate_count = owned.reduce((prev, curr) => prev + curr.quantity, 0);
polestar.owned_best_odds = owned.length == 0 ? 0 : 1/owned.reduce((prev, curr) => Math.min(prev, curr.keystones.length), 100);
polestar.owned_total_odds = owned.length == 0 ? 0 : 1-owned.reduce((prev, curr) => prev*(((curr.keystones.length-1)/curr.keystones.length)**curr.quantity), 1);
if (polestar.filter.type === 'rarity')
polestar.crew_count = allCrew.filter(c => c.in_portal && c.max_rarity == polestar.filter.rarity).length;
else if (polestar.filter.type === 'skill')
polestar.crew_count = allCrew.filter(c => c.in_portal && c.base_skills[polestar.filter.skill]).length;
else if (polestar.filter.type === 'trait')
polestar.crew_count = allCrew.filter(c => c.in_portal && c.traits.some(trait => trait === polestar.filter.trait)).length;
});
setAllKeystones([...allkeystones]);
}
function calculateControl(): void {
// Control is a list of crew that you can't retrieve, which includes crew not in portal
const retrievable = getRetrievable(allCrew, ownedPolestars);
const unretrievable = allCrew.filter(pc => !retrievable.some(cc => cc === pc));
setControl([...unretrievable]);
}
function renderPolestarFinder(): JSX.Element {
const polestarTable: ITableConfigRow[] = [
{ width: 2, column: 'name', title: 'Polestar' },
{ width: 1, column: 'crew_count', title: 'Crew in Portal', reverse: true },
{ width: 1, column: 'crate_count', title: 'Constellation Chance', reverse: true },
{ width: 1, column: 'scan_odds', title: 'Scan Chance', reverse: true },
{ width: 1, column: 'owned_best_odds', title: 'Best Chance', reverse: true },
{ width: 1, column: 'quantity', title: 'Owned', reverse: true },
{ width: 1, column: 'loaned', title: 'Added', reverse: true }
];
const constellationList = ownedConstellations.map(c => {
return { key: c.symbol, value: c.symbol, text: c.name };
});
// !! Always filter polestars by crew_count to hide deprecated polestars !!
let data = allKeystones.filter(k => k.type == 'keystone' && k.crew_count > 0);
if (activeCrew != '') {
const crew = allCrew.find(c => c.symbol === activeCrew);
data = data.filter(k => (k.filter.type == 'trait' && crew.traits.includes(k.filter.trait))
|| (k.filter.type == 'rarity' && k.filter.rarity == crew.max_rarity)
|| (k.filter.type == 'skill' && k.filter.skill in crew.base_skills));
}
if (activeConstellation != '') {
const crewKeystones = allKeystones.find(k => k.symbol === activeConstellation).keystones;
data = data.filter(k => crewKeystones.includes(k.id));
}
data.forEach(p => {
p.loaned = addedPolestars.filter(added => added === p.symbol).length;
});
return (
<React.Fragment>
<CrewPicker crew={control} value={activeCrew} updateCrew={updateCrew} />
{activeCrew == '' && constellationList.length > 0 && (
<React.Fragment>
<span style={{ margin: '0 1em' }}>or</span>
<Dropdown
placeholder='Filter polestars by owned constellation'
style={{ minWidth: '20em' }}
selection
clearable
options={constellationList}
value={activeConstellation}
onChange={(e, { value }) => setAsActive('constellation', value) }
/>
</React.Fragment>
)}
{renderCrewMessage(data)}
{renderConstellationMessage(data)}
<div style={{ marginTop: '1em' }}>
<SearchableTable
data={data}
config={polestarTable}
renderTableRow={(polestar, idx) => renderPolestarRow(polestar, idx)}
filterRow={(polestar, filter) => filterText(polestar, filter)}
explanation={
<div>
<p>Search for polestars by name.</p>
</div>
}
/>
<p>
<i>Constellation Chance</i>: your chance of acquiring any constellation with the polestar from a successful scan.
<br /><i>Scan Chance</i>: your overall chance of acquiring the polestar from a successful scan.
<br /><i>Best Chance</i>: your best chance of acquiring the polestar from a constellation in your inventory.
</p>
</div>
</React.Fragment>
);
}
function filterText(polestar: any, filters: []): boolean {
if (filters.length == 0) return true;
const matchesFilter = (input: string, searchString: string) =>
input.toLowerCase().indexOf(searchString.toLowerCase()) >= 0;
let meetsAnyCondition = false;
for (let filter of filters) {
let meetsAllConditions = true;
if (filter.conditionArray.length === 0) {
// text search only
for (let segment of filter.textSegments) {
let segmentResult = matchesFilter(polestar.name, segment.text);
meetsAllConditions = meetsAllConditions && (segment.negated ? !segmentResult : segmentResult);
}
}
if (meetsAllConditions) {
meetsAnyCondition = true;
break;
}
}
return meetsAnyCondition;
}
function renderPolestarRow(polestar: any, idx: number): JSX.Element {
return (
<Table.Row key={polestar.symbol}
style={{ cursor: activePolestar != polestar.symbol ? 'zoom-in' : 'zoom-out' }}
onClick={() => setActivePolestar(activePolestar != polestar.symbol ? polestar.symbol : '')}
>
<Table.Cell>
<div
style={{
display: 'grid',
gridTemplateColumns: '30px auto',
gridTemplateAreas: `'icon stats'`,
gridGap: '1px'
}}
>
<div style={{ gridArea: 'icon' }}>
<img width={24} src={`${process.env.GATSBY_ASSETS_URL}${polestar.icon.file.substr(1).replace(/\//g, '_')}`} />
</div>
<div style={{ gridArea: 'stats' }}>
<span style={{ fontWeight: 'bolder', fontSize: '1.1em' }}>{polestar.short_name}</span>
</div>
</div>
</Table.Cell>
<Table.Cell textAlign='center'>{polestar.crew_count}</Table.Cell>
<Table.Cell textAlign='center'>{(polestar.crate_count/crewCrates*100).toFixed(1)}%</Table.Cell>
<Table.Cell textAlign='center'>{(polestar.scan_odds*100).toFixed(2)}%</Table.Cell>
<Table.Cell textAlign='center'>{(polestar.owned_best_odds*100).toFixed(1)}%</Table.Cell>
<Table.Cell textAlign='center'>{polestar.quantity}</Table.Cell>
<Table.Cell textAlign='center'>
<ProspectInventory polestar={polestar.symbol} loaned={polestar.loaned} updateProspect={updateProspect} />
</Table.Cell>
</Table.Row>
);
}
function renderCrewMessage(data: any[]): JSX.Element {
if (activeCrew == '') return (<></>);
const crew = allCrew.find(c => c.symbol === activeCrew);
if (!crew.in_portal)
return (<Message>{crew.name} is not available by crew retrieval.</Message>);
if (crew.unique_polestar_combos?.length == 0)
return (<Message>{crew.name} has no guaranteed retrieval options.</Message>);
const unownedPolestars = data.filter(p => p.quantity === 0);
if (unownedPolestars.length == 0)
return (<Message>You can already retrieve {crew.name} with the polestars in your inventory.</Message>);
crew.unique_polestar_combos.forEach(upc => {
const needs = unownedPolestars.filter(p => upc.some(trait => filterTraits(p, trait)));
needs.forEach(p => {
p.useful = p.useful ? p.useful + 1: 1;
if (needs.length == 1) p.useful_alone = true;
});
});
// "Useful" polestars are all unowned polestars that unlock retrievals by themselves (i.e. `useful_alone`)
// or other unowned polestars that together unlock retrievals WITHOUT also relying on a `useful_alone` polestar
const usefulPolestars = unownedPolestars.filter(p => p.useful_alone ||
crew.unique_polestar_combos.filter(upc => !upc.some(trait =>
unownedPolestars.filter(p => p.useful_alone).some(p => filterTraits(p, trait))
)).some(upc => upc.some(trait => filterTraits(p, trait))))
.sort((a, b) => b.useful - a.useful);
const showUsefulPolestars = () => {
if (usefulPolestars.length == 0)
return (<p>No unowned polestars will help you retrieve {crew.name}.</p>); // What case is this?
const usefulAlone = usefulPolestars.filter(p => p.useful_alone);
const usefulWithOthers = usefulPolestars.filter(p => !p.useful_alone); // Should either be 0 or 2+
return (
<p>
{usefulAlone.length > 0 && (<span>You need exactly one of the following polestars to retrieve {crew.name}: {renderPolestarsInline(usefulAlone)}</span>)}
{usefulAlone.length > 0 && usefulWithOthers.length > 0 && (<span><br />Or some combination of the following polestars: {renderPolestarsInline(usefulWithOthers)}</span>)}
{usefulAlone.length == 0 && (<span>You need some combination of the following polestars to retrieve {crew.name}: {renderPolestarsInline(usefulWithOthers)}</span>)}
</p>
);
};
// "Usable" constellations are owned constellations that include useful polestars
const showUsableConstellations = () => {
const usablePolestars = usefulPolestars.filter(p => p.owned_crate_count > 0);
if (usablePolestars.length == 0) return (<></>);
const constellations = ownedConstellations.filter(k => k.keystones.some(kId => usablePolestars.find(p => p.id === kId)));
if (constellations.length == 1)
return constellations.map(k => renderPolestarsFromConstellation(k, usablePolestars.filter(p => k.keystones.some(kId => kId === p.id))));
return usablePolestars.sort((a, b) => {
if (b.owned_total_odds == a.owned_total_odds)
return b.owned_best_odds - a.owned_best_odds;
return b.owned_total_odds - a.owned_total_odds;
}).map(p =>
renderConstellationsWithPolestar(p)
);
};
return (
<Message>
{showUsefulPolestars()}
{showUsableConstellations()}
</Message>
);
}
function renderPolestarsInline(polestars: any[]): JSX.Element[] {
return polestars.map((p, pdx) => (
<span key={pdx} onClick={() => setActivePolestar(p.symbol) }>
<b>{p.short_name}</b>{pdx < polestars.length-1 ? ',' : ''}
</span>
))
.reduce((prev, curr) => [prev, ' ', curr]);
}
function renderConstellationMessage(data: any[]): JSX.Element {
if (activeConstellation == '') return (<></>);
const constellation = allKeystones.find(k => k.symbol === activeConstellation);
const unownedPolestars = data.filter(p => p.quantity === 0);
if (unownedPolestars.length == 0)
return (<Message>You already own all polestars in the {constellation.name}.</Message>);
return (
<Message>
{renderPolestarsFromConstellation(constellation, unownedPolestars)}
</Message>
);
}
function renderPolestarDetail(): JSX.Element {
const polestar = allKeystones.find(k => k.symbol === activePolestar);
polestar.loaned = addedPolestars.filter(added => added === polestar.symbol).length;
return (
<div style={{ marginTop: '1em' }}>
<Table celled striped unstackable compact="very">
<Table.Header>
<Table.Row>
<Table.HeaderCell>Polestar</Table.HeaderCell>
<Table.HeaderCell textAlign='center'>Crew in Portal</Table.HeaderCell>
<Table.HeaderCell textAlign='center'>Constellation Chance</Table.HeaderCell>
<Table.HeaderCell textAlign='center'>Scan Chance</Table.HeaderCell>
<Table.HeaderCell textAlign='center'>Best Chance</Table.HeaderCell>
<Table.HeaderCell textAlign='center'>Owned</Table.HeaderCell>
<Table.HeaderCell textAlign='center'>Added</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{renderPolestarRow(polestar, 1)}
</Table.Body>
</Table>
{polestar.owned_crate_count > 0 && (<Message>{renderConstellationsWithPolestar(polestar)}</Message>)}
{renderNewRetrievals(polestar)}
</div>
);
}
function renderPolestarsFromConstellation(constellation: any, polestars: any[]): JSX.Element {
const clarify = activeCrew != '' ? 'a needed' : 'an unowned';
return (
<div key={constellation.symbol}>
Open the <b><span onClick={() => setAsActive('constellation', constellation.symbol) }>{constellation.name}</span></b>{` `}
for a <b>{(polestars.length/constellation.keystones.length*100).toFixed(1)}%</b> chance of acquiring {clarify} polestar:{` `}
<Grid centered padded stackable>
{
polestars.map((p, pdx) => (
<Grid.Column key={pdx} width={2} textAlign='center' onClick={() => setActivePolestar(p.symbol)}>
<img width={32} src={`${process.env.GATSBY_ASSETS_URL}${p.icon.file.substr(1).replace(/\//g, '_')}`} />
<br /><b>{p.short_name}</b><br /><small>({(1/constellation.keystones.length*100).toFixed(1)}%)</small>
</Grid.Column>
))
}
</Grid>
{activeCrew == '' && constellation.quantity > 1 && (<p>You own {constellation.quantity} of this constellation.</p>)}
</div>
);
}
function renderConstellationsWithPolestar(polestar: any): JSX.Element {
const constellations = [];
ownedConstellations.filter(k => k.keystones.includes(polestar.id))
.forEach(k => {
for (let i = 0; i < k.quantity; i++) {
const newName = k.quantity > 1 ? k.name + " #"+(i+1) : k.name;
constellations.push({...k, name: newName});
}
});
return (
<p key={polestar.symbol}>
Open{` `}
{
constellations.sort((a, b) => 1/b.keystones.length - 1/a.keystones.length).map((k, kdx) => (
<span key={kdx} onClick={() => setAsActive('constellation', k.symbol) }>
<b>{k.name}</b> ({(1/k.keystones.length*100).toFixed(1)}%){kdx < constellations.length-1 ? ' or ' : ''}
</span>
)).reduce((prev, curr) => [prev, ' ', curr])
}{` `}
for a chance of acquiring the <b><span onClick={() => setActivePolestar(polestar.symbol)}>{polestar.name}</span></b>
{constellations.length > 1 && (<span>; open all for a <b>{(polestar.owned_total_odds*100).toFixed(1)}%</b> chance</span>)}
</p>
);
}
function renderNewRetrievals(polestar: any): JSX.Element {
const ownedPlus = JSON.parse(JSON.stringify(ownedPolestars));
ownedPlus.push({...polestar, quantity: 1});
const newRetrievables = getRetrievable(control, ownedPlus).filter(c => c.in_portal);
if (newRetrievables.length == 0)
return (
<p>
{polestar.quantity > 0 ? `You own ${polestar.quantity} of the ${polestar.name}. ` : ''}
Acquiring{polestar.quantity > 0 ? ` more of ` : ` `}this polestar will not unlock guaranteed retrievals for any new crew.
</p>
);
return (
<React.Fragment>
<p>Acquire the <b>{polestar.name}</b> to unlock guaranteed retrievals for the following crew:</p>
<Grid centered padded stackable>
{newRetrievables.sort((a, b) => a.name.localeCompare(b.name)).map((crew, cdx) => (
<Grid.Column key={crew.symbol} width={2} textAlign='center' onClick={() => setAsActive('crew', crew.symbol) }>
<ItemDisplay
src={`${process.env.GATSBY_ASSETS_URL}${crew.imageUrlPortrait}`}
size={64}
maxRarity={crew.max_rarity}
rarity={crew.highest_owned_rarity}
/>
<div>{crew.name}</div>
</Grid.Column>
))}
</Grid>
</React.Fragment>
);
}
function getRetrievable(crewpool: any[], polestars: any[]): any[] {
return crewpool.filter(crew =>
crew.unique_polestar_combos?.some(upc =>
upc.every(trait => polestars.some(op => filterTraits(op, trait)))
));
}
function setAsActive(activeType: string, activeValue: string): void {
setActiveCrew(activeType == 'crew' ? activeValue : '');
setActiveConstellation(activeType == 'constellation' ? activeValue : '');
setActivePolestar('');
}
function updateCrew(symbol: string): void {
setAsActive('crew', symbol);
}
function updateProspect(polestar: string, increase: boolean): void {
if (polestar == '') return;
if (increase) {
addedPolestars.push(polestar);
}
else {
const prospectNum = addedPolestars.indexOf(polestar);
if (prospectNum >= 0) addedPolestars.splice(prospectNum, 1);
}
}
}
Example #28
Source File: crewretrieval.tsx From website with MIT License | 4 votes |
PolestarFilterModal = (props: PolestarFilterModalProps) => {
const { ownedPolestars, updateDisableds } = props;
const [modalIsOpen, setModalIsOpen] = React.useState(false);
const [disabledPolestars, setDisabledPolestars] = React.useState(props.disabledPolestars);
// Recalculate combos only when modal gets closed
React.useEffect(() => {
if (!modalIsOpen && JSON.stringify(disabledPolestars) != JSON.stringify(props.disabledPolestars)) {
updateDisableds([...disabledPolestars]);
}
}, [modalIsOpen]);
const rarityIds = [14502, 14504, 14506, 14507, 14509];
const skillIds = [14511, 14512, 14513, 14514, 14515, 14516];
const grouped = [
{
title: "Rarity",
polestars: [],
anyDisabled: false
},
{
title: "Skills",
polestars: [],
anyDisabled: false
},
{
title: "Traits",
polestars: [],
anyDisabled: false
},
];
ownedPolestars.forEach(p => {
let group = 2;
if (rarityIds.indexOf(p.id) !== -1) group = 0;
if (skillIds.indexOf(p.id) !== -1) group = 1;
grouped[group].polestars.push(p);
if (disabledPolestars.indexOf(p.id) !== -1) grouped[group].anyDisabled = true;
});
return (
<Modal
open={modalIsOpen}
onClose={() => setModalIsOpen(false)}
onOpen={() => setModalIsOpen(true)}
trigger={<Button><Icon name='filter' />{ownedPolestars.length-disabledPolestars.length} / {ownedPolestars.length}</Button>}
size='large'
>
<Modal.Header>Filter Owned Polestars</Modal.Header>
<Modal.Content scrolling>
<Grid columns={4} stackable padded>
{createFilterCheckboxes()}
</Grid>
</Modal.Content>
<Modal.Actions>
<Button positive onClick={() => setModalIsOpen(false)}>
Close
</Button>
</Modal.Actions>
</Modal>
);
function filterCheckbox(p: any): JSX.Element {
return (
<Grid.Column key={p.id}>
<Checkbox
toggle
id={`polestar_filter_id_${p.id}`}
label={`${p.short_name} (${p.quantity})`}
checked={disabledPolestars.indexOf(p.id)===-1}
onChange={(e) => checkOne(p.id, e.target.checked)}
/>
</Grid.Column>
)
}
function filterCheckboxGroupHeader(t: string): JSX.Element {
let group = grouped.find(group => group.title === t);
let groupLink = group ? (<Button style={{ marginLeft: '1em' }} size='mini' onClick={() => checkGroup(t, group.anyDisabled)}>{group.anyDisabled ? 'Check' : 'Uncheck'} All</Button>): (<></>);
return (
<Grid.Column largeScreen={16} mobile={4} key={t}>
<strong>{t}</strong> {groupLink}
</Grid.Column>
)
}
function createFilterCheckboxes(): JSX.Element[] {
const checkboxes = [];
grouped.map((group) => {
if(group.polestars.length > 0) {
checkboxes.push(filterCheckboxGroupHeader(group.title));
group.polestars.map((polestar) => {
checkboxes.push(filterCheckbox(polestar));
});
}
});
return checkboxes;
}
function checkOne(id: number, checked: boolean): void {
handleFilterChange(id, checked);
setDisabledPolestars([...disabledPolestars]);
}
function checkGroup(t: string, checkAll: boolean): void {
let group = grouped.find(group => group.title === t);
group.polestars.forEach(p => handleFilterChange(p.id, checkAll));
setDisabledPolestars([...disabledPolestars]);
}
function handleFilterChange(id: number, checked: boolean): void {
if(checked === true && disabledPolestars.indexOf(id) !== -1) {
disabledPolestars.splice(disabledPolestars.indexOf(id), 1);
}
if(checked === false && disabledPolestars.indexOf(id) === -1) {
disabledPolestars.push(id);
}
}
}
Example #29
Source File: ProfileWidgetPlus.tsx From communitymap-ui with Apache License 2.0 | 4 votes |
ProfileWidgetPlus: React.FC = () => {
const user = useAuth();
const { dialogs } = useMyDirectMessages();
const unreadDialogs =
dialogs?.filter((dlg) => dlg.lastMsgId !== dlg.lastReadBy[user?.uid || ''])
.length || 0;
const [login, setLogin] = useState(false);
useEffect(() => {
if (user && login) {
setLogin(false);
}
}, [user, login]);
const [showProfile, setShowProfile] = useState(false);
const signOut = () => getFirebaseApp().auth().signOut();
return (
<div id="profile-widget">
{login && <Login title="" onClose={() => setLogin(false)} />}
{showProfile && !!user && (
<Modal open closeIcon onClose={() => setShowProfile(false)}>
<Modal.Header>Your profile</Modal.Header>
<Modal.Content>
<EditUserProfile user={user} />
</Modal.Content>
</Modal>
)}
{user ? (
<Dropdown
trigger={
<Button className="profile-button" icon size="large">
<Icon.Group>
<Icon name="user outline" />
{unreadDialogs > 0 && (
<Icon
corner="top right"
className="has-unread-messages"
name="mail"
color="red"
/>
)}
</Icon.Group>
</Button>
}
pointing="top right"
icon={null}
>
<Dropdown.Menu>
<Dropdown.Item disabled>{user.email}</Dropdown.Item>
<Dropdown.Divider />
<Dropdown.Item onClick={() => setShowProfile(true)}>
<Icon name="user" />
Profile
</Dropdown.Item>
<Dropdown.Item as={Link} to="/my-messages">
<Icon name="mail" />
Messages
{unreadDialogs > 0 && (
<Label className="user-menu-label" color="blue">
{unreadDialogs}
</Label>
)}
</Dropdown.Item>
<Dropdown.Divider />
<Dropdown.Item as={Link} to="/terms">
Terms of Service
</Dropdown.Item>
<Dropdown.Item as={Link} to="/privacy">
Privacy Policy
</Dropdown.Item>
<Dropdown.Divider />
<Dropdown.Item onClick={signOut}>
<Icon name="log out" />
Log out
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
) : (
// <Button primary size="large" onClick={() => setLogin(true)}>
// Sign in
// </Button>
<Dropdown
trigger={
<Button className="profile-button" icon size="large">
<Icon name="bars" />
</Button>
}
pointing="top right"
icon={null}
>
<Dropdown.Menu>
<Dropdown.Item as={Link} to="/terms">
Terms of Service
</Dropdown.Item>
<Dropdown.Item as={Link} to="/privacy">
Privacy Policy
</Dropdown.Item>
<Dropdown.Divider />
<Dropdown.Item onClick={() => setLogin(true)}>
<Icon name="sign in" />
Sign in
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
)}
</div>
);
}