React bootstrap upgrade to v1, migrate to react-beautiful-dnd (#1639)
* Upgrade packages, fix errors, still WIP * WIP * Bootstrap v4 upgrade * Switch to react-beautiful-dnd * Fixed some issues * Added to generate signed Mac build for UX * PR feedback * Missed one * PR feedback
This commit is contained in:
@@ -43,7 +43,7 @@ export default function AutoSaveIndicator(props: Props) {
|
||||
<Alert
|
||||
className={className}
|
||||
{...rest}
|
||||
bsStyle={savingState === 'error' ? 'danger' : 'info'}
|
||||
variant={savingState === 'error' ? 'danger' : 'info'}
|
||||
>
|
||||
{message}
|
||||
</Alert>
|
||||
|
@@ -14,15 +14,15 @@ storiesOf('Button', module).
|
||||
<Button onClick={action('clicked default')}>{'Default'}</Button>
|
||||
<Button
|
||||
onClick={action('clicked primary')}
|
||||
bsStyle='primary'
|
||||
variant='primary'
|
||||
>{'Primary'}</Button>
|
||||
<Button
|
||||
onClick={action('clicked danger')}
|
||||
bsStyle='danger'
|
||||
variant='danger'
|
||||
>{'Danger'}</Button>
|
||||
<Button
|
||||
onClick={action('clicked link')}
|
||||
bsStyle='link'
|
||||
variant='link'
|
||||
>{'Link'}</Button>
|
||||
</ButtonToolbar>
|
||||
));
|
||||
|
@@ -11,8 +11,8 @@ type Props = {
|
||||
acceptLabel: string;
|
||||
cancelLabel: string;
|
||||
onHide: () => void;
|
||||
onAccept: React.MouseEventHandler<Button>;
|
||||
onCancel: React.MouseEventHandler<Button>;
|
||||
onAccept: React.MouseEventHandler<HTMLButtonElement>;
|
||||
onCancel: React.MouseEventHandler<HTMLButtonElement>;
|
||||
};
|
||||
|
||||
export default function DestructiveConfirmationModal(props: Props) {
|
||||
@@ -36,11 +36,11 @@ export default function DestructiveConfirmationModal(props: Props) {
|
||||
{body}
|
||||
<Modal.Footer>
|
||||
<Button
|
||||
bsStyle='link'
|
||||
variant='link'
|
||||
onClick={onCancel}
|
||||
>{cancelLabel}</Button>
|
||||
<Button
|
||||
bsStyle='danger'
|
||||
variant='danger'
|
||||
onClick={onAccept}
|
||||
>{acceptLabel}</Button>
|
||||
</Modal.Footer>
|
||||
|
@@ -5,7 +5,7 @@
|
||||
// ErrorCode: https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h
|
||||
|
||||
import React from 'react';
|
||||
import {Grid, Row, Col} from 'react-bootstrap';
|
||||
import {Container, Row, Col} from 'react-bootstrap';
|
||||
|
||||
type Props = {
|
||||
errorInfo?: string;
|
||||
@@ -22,9 +22,8 @@ export default function ErrorView(props: Props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid
|
||||
<Container
|
||||
id={props.id}
|
||||
bsClass={classNames.join(' ')}
|
||||
>
|
||||
<div className='ErrorView-table'>
|
||||
<div className='ErrorView-cell'>
|
||||
@@ -82,6 +81,6 @@ export default function ErrorView(props: Props) {
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
@@ -34,8 +34,8 @@ export default class ExtraBar extends React.PureComponent<Props> {
|
||||
onClick={this.handleBack}
|
||||
>
|
||||
<Button
|
||||
bsStyle={'link'}
|
||||
bsSize={'xsmall'}
|
||||
variant={'link'}
|
||||
size={'sm'}
|
||||
>
|
||||
<span className={'backIcon fa fa-1x fa-angle-left'}/>
|
||||
<span className={'backLabel'}>
|
||||
|
@@ -3,10 +3,10 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {Fragment} from 'react';
|
||||
import {Grid, Row} from 'react-bootstrap';
|
||||
import {Container, Row} from 'react-bootstrap';
|
||||
import {DropResult} from 'react-beautiful-dnd';
|
||||
import DotsVerticalIcon from 'mdi-react/DotsVerticalIcon';
|
||||
import {IpcRendererEvent} from 'electron/renderer';
|
||||
import {DropResult} from 'react-smooth-dnd';
|
||||
|
||||
import {Team} from 'types/config';
|
||||
|
||||
@@ -60,7 +60,7 @@ enum Status {
|
||||
type Props = {
|
||||
teams: Team[];
|
||||
showAddServerButton: boolean;
|
||||
moveTabs: (originalOrder: number, newOrder: number) => Promise<number | undefined>;
|
||||
moveTabs: (originalOrder: number, newOrder: number) => number | undefined;
|
||||
openMenu: () => void;
|
||||
darkMode: boolean;
|
||||
appName: string;
|
||||
@@ -256,18 +256,17 @@ export default class MainPage extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
handleDragAndDrop = async (dropResult: DropResult) => {
|
||||
const {removedIndex, addedIndex} = dropResult;
|
||||
if (removedIndex === null || addedIndex === null) {
|
||||
const removedIndex = dropResult.source.index;
|
||||
const addedIndex = dropResult.destination?.index;
|
||||
if (addedIndex === undefined || removedIndex === addedIndex) {
|
||||
return;
|
||||
}
|
||||
if (removedIndex !== addedIndex) {
|
||||
const teamIndex = await this.props.moveTabs(removedIndex, addedIndex < this.props.teams.length ? addedIndex : this.props.teams.length - 1);
|
||||
if (!teamIndex) {
|
||||
return;
|
||||
}
|
||||
const name = this.props.teams[teamIndex].name;
|
||||
this.handleSelect(name, teamIndex);
|
||||
const teamIndex = this.props.moveTabs(removedIndex, addedIndex < this.props.teams.length ? addedIndex : this.props.teams.length - 1);
|
||||
if (!teamIndex) {
|
||||
return;
|
||||
}
|
||||
const name = this.props.teams[teamIndex].name;
|
||||
this.handleSelect(name, teamIndex);
|
||||
}
|
||||
|
||||
handleClose = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
@@ -471,10 +470,10 @@ export default class MainPage extends React.PureComponent<Props, State> {
|
||||
className='MainPage'
|
||||
onClick={this.focusOnWebView}
|
||||
>
|
||||
<Grid fluid={true}>
|
||||
<Container fluid={true}>
|
||||
{topRow}
|
||||
{viewsRow}
|
||||
</Grid>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {Modal, Button, FormGroup, FormControl, ControlLabel, HelpBlock} from 'react-bootstrap';
|
||||
import {Modal, Button, FormGroup, FormControl, FormLabel, FormText} from 'react-bootstrap';
|
||||
|
||||
import {TeamWithIndex} from 'types/config';
|
||||
|
||||
@@ -69,7 +69,7 @@ export default class NewTeamModal extends React.PureComponent<Props, State> {
|
||||
return this.getTeamNameValidationError() === null ? null : 'error';
|
||||
}
|
||||
|
||||
handleTeamNameChange = (e: React.ChangeEvent<FormControl & HTMLInputElement>) => {
|
||||
handleTeamNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({
|
||||
teamName: e.target.value,
|
||||
});
|
||||
@@ -95,7 +95,7 @@ export default class NewTeamModal extends React.PureComponent<Props, State> {
|
||||
return this.getTeamUrlValidationError() === null ? null : 'error';
|
||||
}
|
||||
|
||||
handleTeamUrlChange = (e: React.ChangeEvent<FormControl & HTMLInputElement>) => {
|
||||
handleTeamUrlChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({
|
||||
teamUrl: e.target.value,
|
||||
});
|
||||
@@ -165,7 +165,7 @@ export default class NewTeamModal extends React.PureComponent<Props, State> {
|
||||
onEntered={() => this.teamNameInputRef?.focus()}
|
||||
onHide={this.props.onClose}
|
||||
restoreFocus={this.props.restoreFocus}
|
||||
onKeyDown={(e) => {
|
||||
onKeyDown={(e: React.KeyboardEvent) => {
|
||||
switch (e.key) {
|
||||
case 'Enter':
|
||||
this.save();
|
||||
@@ -186,47 +186,46 @@ export default class NewTeamModal extends React.PureComponent<Props, State> {
|
||||
|
||||
<Modal.Body>
|
||||
<form>
|
||||
<FormGroup
|
||||
validationState={this.getTeamNameValidationState()}
|
||||
>
|
||||
<ControlLabel>{'Server Display Name'}</ControlLabel>
|
||||
<FormGroup>
|
||||
<FormLabel>{'Server Display Name'}</FormLabel>
|
||||
<FormControl
|
||||
id='teamNameInput'
|
||||
type='text'
|
||||
value={this.state.teamName}
|
||||
placeholder='Server Name'
|
||||
onChange={this.handleTeamNameChange}
|
||||
inputRef={(ref) => {
|
||||
ref={(ref: HTMLInputElement) => {
|
||||
this.teamNameInputRef = ref;
|
||||
if (this.props.setInputRef) {
|
||||
this.props.setInputRef(ref);
|
||||
}
|
||||
}}
|
||||
onClick={(e) => {
|
||||
onClick={(e: React.MouseEvent<HTMLInputElement>) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
autoFocus={true}
|
||||
isInvalid={Boolean(this.getTeamNameValidationState())}
|
||||
/>
|
||||
<FormControl.Feedback/>
|
||||
<HelpBlock>{'The name of the server displayed on your desktop app tab bar.'}</HelpBlock>
|
||||
<FormText>{'The name of the server displayed on your desktop app tab bar.'}</FormText>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
className='NewTeamModal-noBottomSpace'
|
||||
validationState={this.getTeamUrlValidationState()}
|
||||
>
|
||||
<ControlLabel>{'Server URL'}</ControlLabel>
|
||||
<FormLabel>{'Server URL'}</FormLabel>
|
||||
<FormControl
|
||||
id='teamUrlInput'
|
||||
type='text'
|
||||
value={this.state.teamUrl}
|
||||
placeholder='https://example.com'
|
||||
onChange={this.handleTeamUrlChange}
|
||||
onClick={(e) => {
|
||||
onClick={(e: React.MouseEvent<HTMLInputElement>) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
isInvalid={Boolean(this.getTeamUrlValidationState())}
|
||||
/>
|
||||
<FormControl.Feedback/>
|
||||
<HelpBlock className='NewTeamModal-noBottomSpace'>{'The URL of your Mattermost server. Must start with http:// or https://.'}</HelpBlock>
|
||||
<FormText className='NewTeamModal-noBottomSpace'>{'The URL of your Mattermost server. Must start with http:// or https://.'}</FormText>
|
||||
</FormGroup>
|
||||
</form>
|
||||
</Modal.Body>
|
||||
@@ -241,12 +240,13 @@ export default class NewTeamModal extends React.PureComponent<Props, State> {
|
||||
<Button
|
||||
id='cancelNewServerModal'
|
||||
onClick={this.props.onClose}
|
||||
variant='link'
|
||||
>{'Cancel'}</Button>
|
||||
<Button
|
||||
id='saveNewServerModal'
|
||||
onClick={this.save}
|
||||
disabled={!this.validateForm()}
|
||||
bsStyle='primary'
|
||||
variant='primary'
|
||||
>{this.getSaveButtonLabel()}</Button>
|
||||
</Modal.Footer>
|
||||
|
||||
|
@@ -3,7 +3,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {Button, Modal} from 'react-bootstrap';
|
||||
import {Modal} from 'react-bootstrap';
|
||||
|
||||
import DestructiveConfirmationModal from './DestructiveConfirmModal';
|
||||
|
||||
@@ -11,8 +11,8 @@ type Props = {
|
||||
show: boolean;
|
||||
serverName: string;
|
||||
onHide: () => void;
|
||||
onAccept: React.MouseEventHandler<Button>;
|
||||
onCancel: React.MouseEventHandler<Button>;
|
||||
onAccept: React.MouseEventHandler<HTMLButtonElement>;
|
||||
onCancel: React.MouseEventHandler<HTMLButtonElement>;
|
||||
}
|
||||
|
||||
export default function RemoveServerModal(props: Props) {
|
||||
|
@@ -7,7 +7,7 @@
|
||||
import 'renderer/css/settings.css';
|
||||
|
||||
import React from 'react';
|
||||
import {Checkbox, Col, FormGroup, Grid, HelpBlock, Navbar, Radio, Row, Button} from 'react-bootstrap';
|
||||
import {FormCheck, Col, FormGroup, FormText, Container, Row, Button} from 'react-bootstrap';
|
||||
|
||||
import {debounce} from 'underscore';
|
||||
|
||||
@@ -52,16 +52,16 @@ function backToIndex(serverName: string) {
|
||||
}
|
||||
|
||||
export default class SettingsPage extends React.PureComponent<Record<string, never>, State> {
|
||||
trayIconThemeRef: React.RefObject<FormGroup>;
|
||||
trayIconThemeRef: React.RefObject<HTMLDivElement>;
|
||||
downloadLocationRef: React.RefObject<HTMLInputElement>;
|
||||
showTrayIconRef: React.RefObject<Checkbox>;
|
||||
autostartRef: React.RefObject<Checkbox>;
|
||||
minimizeToTrayRef: React.RefObject<Checkbox>;
|
||||
flashWindowRef: React.RefObject<Checkbox>;
|
||||
bounceIconRef: React.RefObject<Checkbox>;
|
||||
showUnreadBadgeRef: React.RefObject<Checkbox>;
|
||||
useSpellCheckerRef: React.RefObject<Checkbox>;
|
||||
enableHardwareAccelerationRef: React.RefObject<Checkbox>;
|
||||
showTrayIconRef: React.RefObject<HTMLInputElement>;
|
||||
autostartRef: React.RefObject<HTMLInputElement>;
|
||||
minimizeToTrayRef: React.RefObject<HTMLInputElement>;
|
||||
flashWindowRef: React.RefObject<HTMLInputElement>;
|
||||
bounceIconRef: React.RefObject<HTMLInputElement>;
|
||||
showUnreadBadgeRef: React.RefObject<HTMLInputElement>;
|
||||
useSpellCheckerRef: React.RefObject<HTMLInputElement>;
|
||||
enableHardwareAccelerationRef: React.RefObject<HTMLInputElement>;
|
||||
|
||||
saveQueue: SaveQueueItem[];
|
||||
|
||||
@@ -186,7 +186,7 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
}
|
||||
|
||||
handleChangeShowTrayIcon = () => {
|
||||
const shouldShowTrayIcon = !this.showTrayIconRef.current?.props.checked;
|
||||
const shouldShowTrayIcon = this.showTrayIconRef.current?.checked;
|
||||
window.timers.setImmediate(this.saveSetting, CONFIG_TYPE_APP_OPTIONS, {key: 'showTrayIcon', data: shouldShowTrayIcon});
|
||||
this.setState({
|
||||
showTrayIcon: shouldShowTrayIcon,
|
||||
@@ -207,14 +207,14 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
}
|
||||
|
||||
handleChangeAutoStart = () => {
|
||||
window.timers.setImmediate(this.saveSetting, CONFIG_TYPE_APP_OPTIONS, {key: 'autostart', data: !this.autostartRef.current?.props.checked});
|
||||
window.timers.setImmediate(this.saveSetting, CONFIG_TYPE_APP_OPTIONS, {key: 'autostart', data: this.autostartRef.current?.checked});
|
||||
this.setState({
|
||||
autostart: !this.autostartRef.current?.props.checked,
|
||||
autostart: this.autostartRef.current?.checked,
|
||||
});
|
||||
}
|
||||
|
||||
handleChangeMinimizeToTray = () => {
|
||||
const shouldMinimizeToTray = this.state.showTrayIcon && !this.minimizeToTrayRef.current?.props.checked;
|
||||
const shouldMinimizeToTray = this.state.showTrayIcon && !this.minimizeToTrayRef.current?.checked;
|
||||
|
||||
window.timers.setImmediate(this.saveSetting, CONFIG_TYPE_APP_OPTIONS, {key: 'minimizeToTray', data: shouldMinimizeToTray});
|
||||
this.setState({
|
||||
@@ -240,13 +240,13 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
key: 'notifications',
|
||||
data: {
|
||||
...this.state.notifications,
|
||||
flashWindow: this.flashWindowRef.current?.props.checked ? 0 : 2,
|
||||
flashWindow: this.flashWindowRef.current?.checked ? 0 : 2,
|
||||
},
|
||||
});
|
||||
this.setState({
|
||||
notifications: {
|
||||
...this.state.notifications,
|
||||
flashWindow: this.flashWindowRef.current?.props.checked ? 0 : 2,
|
||||
flashWindow: this.flashWindowRef.current?.checked ? 0 : 2,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -256,18 +256,18 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
key: 'notifications',
|
||||
data: {
|
||||
...this.state.notifications,
|
||||
bounceIcon: !this.bounceIconRef.current?.props.checked,
|
||||
bounceIcon: this.bounceIconRef.current?.checked,
|
||||
},
|
||||
});
|
||||
this.setState({
|
||||
notifications: {
|
||||
...this.state.notifications,
|
||||
bounceIcon: !this.bounceIconRef.current?.props.checked,
|
||||
bounceIcon: this.bounceIconRef.current?.checked,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
handleBounceIconType = (event: React.ChangeEvent<Radio & HTMLInputElement>) => {
|
||||
handleBounceIconType = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
window.timers.setImmediate(this.saveSetting, CONFIG_TYPE_APP_OPTIONS, {
|
||||
key: 'notifications',
|
||||
data: {
|
||||
@@ -284,23 +284,23 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
}
|
||||
|
||||
handleShowUnreadBadge = () => {
|
||||
window.timers.setImmediate(this.saveSetting, CONFIG_TYPE_APP_OPTIONS, {key: 'showUnreadBadge', data: !this.showUnreadBadgeRef.current?.props.checked});
|
||||
window.timers.setImmediate(this.saveSetting, CONFIG_TYPE_APP_OPTIONS, {key: 'showUnreadBadge', data: this.showUnreadBadgeRef.current?.checked});
|
||||
this.setState({
|
||||
showUnreadBadge: !this.showUnreadBadgeRef.current?.props.checked,
|
||||
showUnreadBadge: this.showUnreadBadgeRef.current?.checked,
|
||||
});
|
||||
}
|
||||
|
||||
handleChangeUseSpellChecker = () => {
|
||||
window.timers.setImmediate(this.saveSetting, CONFIG_TYPE_APP_OPTIONS, {key: 'useSpellChecker', data: !this.useSpellCheckerRef.current?.props.checked});
|
||||
window.timers.setImmediate(this.saveSetting, CONFIG_TYPE_APP_OPTIONS, {key: 'useSpellChecker', data: this.useSpellCheckerRef.current?.checked});
|
||||
this.setState({
|
||||
useSpellChecker: !this.useSpellCheckerRef.current?.props.checked,
|
||||
useSpellChecker: this.useSpellCheckerRef.current?.checked,
|
||||
});
|
||||
}
|
||||
|
||||
handleChangeEnableHardwareAcceleration = () => {
|
||||
window.timers.setImmediate(this.saveSetting, CONFIG_TYPE_APP_OPTIONS, {key: 'enableHardwareAcceleration', data: !this.enableHardwareAccelerationRef.current?.props.checked});
|
||||
window.timers.setImmediate(this.saveSetting, CONFIG_TYPE_APP_OPTIONS, {key: 'enableHardwareAcceleration', data: this.enableHardwareAccelerationRef.current?.checked});
|
||||
this.setState({
|
||||
enableHardwareAcceleration: !this.enableHardwareAccelerationRef.current?.props.checked,
|
||||
enableHardwareAcceleration: this.enableHardwareAccelerationRef.current?.checked,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -350,10 +350,6 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
|
||||
render() {
|
||||
const settingsPage = {
|
||||
navbar: {
|
||||
backgroundColor: '#fff',
|
||||
position: 'relative' as const,
|
||||
},
|
||||
close: {
|
||||
textDecoration: 'none',
|
||||
position: 'absolute',
|
||||
@@ -424,7 +420,6 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
const serversRow = (
|
||||
<Row>
|
||||
<Col
|
||||
md={10}
|
||||
xs={8}
|
||||
>
|
||||
<h2 style={settingsPage.sectionHeading}>{'Server Management'}</h2>
|
||||
@@ -437,7 +432,6 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
</div>
|
||||
</Col>
|
||||
<Col
|
||||
md={2}
|
||||
xs={4}
|
||||
>
|
||||
<p className='text-right'>
|
||||
@@ -468,66 +462,74 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
// MacOS has an option in the Dock, to set the app to autostart, so we choose to not support this option for OSX
|
||||
if (window.process.platform === 'win32' || window.process.platform === 'linux') {
|
||||
options.push(
|
||||
<Checkbox
|
||||
key='inputAutoStart'
|
||||
id='inputAutoStart'
|
||||
ref={this.autostartRef}
|
||||
checked={this.state.autostart}
|
||||
onChange={this.handleChangeAutoStart}
|
||||
>
|
||||
<FormCheck>
|
||||
<FormCheck.Input
|
||||
type='checkbox'
|
||||
key='inputAutoStart'
|
||||
id='inputAutoStart'
|
||||
ref={this.autostartRef}
|
||||
checked={this.state.autostart}
|
||||
onChange={this.handleChangeAutoStart}
|
||||
/>
|
||||
{'Start app on login'}
|
||||
<HelpBlock>
|
||||
<FormText>
|
||||
{'If enabled, the app starts automatically when you log in to your machine.'}
|
||||
</HelpBlock>
|
||||
</Checkbox>);
|
||||
</FormText>
|
||||
</FormCheck>);
|
||||
}
|
||||
|
||||
options.push(
|
||||
<Checkbox
|
||||
key='inputSpellChecker'
|
||||
id='inputSpellChecker'
|
||||
ref={this.useSpellCheckerRef}
|
||||
checked={this.state.useSpellChecker}
|
||||
onChange={this.handleChangeUseSpellChecker}
|
||||
>
|
||||
<FormCheck>
|
||||
<FormCheck.Input
|
||||
type='checkbox'
|
||||
key='inputSpellChecker'
|
||||
id='inputSpellChecker'
|
||||
ref={this.useSpellCheckerRef}
|
||||
checked={this.state.useSpellChecker}
|
||||
onChange={this.handleChangeUseSpellChecker}
|
||||
/>
|
||||
{'Check spelling'}
|
||||
<HelpBlock>
|
||||
<FormText>
|
||||
{'Highlight misspelled words in your messages based on your system language configuration. '}
|
||||
{'Setting takes effect after restarting the app.'}
|
||||
</HelpBlock>
|
||||
</Checkbox>);
|
||||
</FormText>
|
||||
</FormCheck>);
|
||||
|
||||
if (window.process.platform === 'darwin' || window.process.platform === 'win32') {
|
||||
const TASKBAR = window.process.platform === 'win32' ? 'taskbar' : 'Dock';
|
||||
options.push(
|
||||
<Checkbox
|
||||
key='inputShowUnreadBadge'
|
||||
id='inputShowUnreadBadge'
|
||||
ref={this.showUnreadBadgeRef}
|
||||
checked={this.state.showUnreadBadge}
|
||||
onChange={this.handleShowUnreadBadge}
|
||||
>
|
||||
<FormCheck>
|
||||
<FormCheck.Input
|
||||
type='checkbox'
|
||||
key='inputShowUnreadBadge'
|
||||
id='inputShowUnreadBadge'
|
||||
ref={this.showUnreadBadgeRef}
|
||||
checked={this.state.showUnreadBadge}
|
||||
onChange={this.handleShowUnreadBadge}
|
||||
/>
|
||||
{`Show red badge on ${TASKBAR} icon to indicate unread messages`}
|
||||
<HelpBlock>
|
||||
<FormText>
|
||||
{`Regardless of this setting, mentions are always indicated with a red badge and item count on the ${TASKBAR} icon.`}
|
||||
</HelpBlock>
|
||||
</Checkbox>);
|
||||
</FormText>
|
||||
</FormCheck>);
|
||||
}
|
||||
|
||||
if (window.process.platform === 'win32' || window.process.platform === 'linux') {
|
||||
options.push(
|
||||
<Checkbox
|
||||
key='flashWindow'
|
||||
id='inputflashWindow'
|
||||
ref={this.flashWindowRef}
|
||||
checked={!this.state.notifications || this.state.notifications.flashWindow === 2}
|
||||
onChange={this.handleFlashWindow}
|
||||
>
|
||||
<FormCheck>
|
||||
<FormCheck.Input
|
||||
type='checkbox'
|
||||
key='flashWindow'
|
||||
id='inputflashWindow'
|
||||
ref={this.flashWindowRef}
|
||||
checked={!this.state.notifications || this.state.notifications.flashWindow === 2}
|
||||
onChange={this.handleFlashWindow}
|
||||
/>
|
||||
{'Flash app window and taskbar icon when a new message is received'}
|
||||
<HelpBlock>
|
||||
<FormText>
|
||||
{'If enabled, app window and taskbar icon flash for a few seconds when a new message is received.'}
|
||||
</HelpBlock>
|
||||
</Checkbox>);
|
||||
</FormText>
|
||||
</FormCheck>);
|
||||
}
|
||||
|
||||
if (window.process.platform === 'darwin') {
|
||||
@@ -535,7 +537,8 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
<FormGroup
|
||||
key='OptionsForm'
|
||||
>
|
||||
<Checkbox
|
||||
<FormCheck
|
||||
type='checkbox'
|
||||
inline={true}
|
||||
key='bounceIcon'
|
||||
id='inputBounceIcon'
|
||||
@@ -543,10 +546,10 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
checked={this.state.notifications ? this.state.notifications.bounceIcon : false}
|
||||
onChange={this.handleBounceIcon}
|
||||
style={{marginRight: '10px'}}
|
||||
>
|
||||
{'Bounce the Dock icon'}
|
||||
</Checkbox>
|
||||
<Radio
|
||||
label='Bounce the Dock icon'
|
||||
/>
|
||||
<FormCheck
|
||||
type='radio'
|
||||
inline={true}
|
||||
name='bounceIconType'
|
||||
value='informational'
|
||||
@@ -557,43 +560,44 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
this.state.notifications.bounceIconType === 'informational'
|
||||
}
|
||||
onChange={this.handleBounceIconType}
|
||||
>
|
||||
{'once'}
|
||||
</Radio>
|
||||
label='once'
|
||||
/>
|
||||
{' '}
|
||||
<Radio
|
||||
<FormCheck
|
||||
type='radio'
|
||||
inline={true}
|
||||
name='bounceIconType'
|
||||
value='critical'
|
||||
disabled={!this.state.notifications || !this.state.notifications.bounceIcon}
|
||||
defaultChecked={this.state.notifications && this.state.notifications.bounceIconType === 'critical'}
|
||||
onChange={this.handleBounceIconType}
|
||||
>
|
||||
{'until I open the app'}
|
||||
</Radio>
|
||||
<HelpBlock
|
||||
label={'until I open the app'}
|
||||
/>
|
||||
<FormText
|
||||
style={{marginLeft: '20px'}}
|
||||
>
|
||||
{'If enabled, the Dock icon bounces once or until the user opens the app when a new notification is received.'}
|
||||
</HelpBlock>
|
||||
</FormText>
|
||||
</FormGroup>,
|
||||
);
|
||||
}
|
||||
|
||||
if (window.process.platform === 'darwin' || window.process.platform === 'linux') {
|
||||
options.push(
|
||||
<Checkbox
|
||||
key='inputShowTrayIcon'
|
||||
id='inputShowTrayIcon'
|
||||
ref={this.showTrayIconRef}
|
||||
checked={this.state.showTrayIcon}
|
||||
onChange={this.handleChangeShowTrayIcon}
|
||||
>
|
||||
<FormCheck>
|
||||
<FormCheck.Input
|
||||
type='checkbox'
|
||||
key='inputShowTrayIcon'
|
||||
id='inputShowTrayIcon'
|
||||
ref={this.showTrayIconRef}
|
||||
checked={this.state.showTrayIcon}
|
||||
onChange={this.handleChangeShowTrayIcon}
|
||||
/>
|
||||
{window.process.platform === 'darwin' ? `Show ${this.state.appName} icon in the menu bar` : 'Show icon in the notification area'}
|
||||
<HelpBlock>
|
||||
<FormText>
|
||||
{'Setting takes effect after restarting the app.'}
|
||||
</HelpBlock>
|
||||
</Checkbox>);
|
||||
</FormText>
|
||||
</FormCheck>);
|
||||
}
|
||||
|
||||
if (window.process.platform === 'linux') {
|
||||
@@ -604,30 +608,33 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
style={{marginLeft: '20px'}}
|
||||
>
|
||||
{'Icon theme: '}
|
||||
<Radio
|
||||
<FormCheck
|
||||
type='radio'
|
||||
inline={true}
|
||||
name='trayIconTheme'
|
||||
value='light'
|
||||
defaultChecked={this.state.trayIconTheme === 'light' || !this.state.trayIconTheme}
|
||||
onChange={() => this.handleChangeTrayIconTheme('light')}
|
||||
>
|
||||
{'Light'}
|
||||
</Radio>
|
||||
label={'Light'}
|
||||
/>
|
||||
{' '}
|
||||
<Radio
|
||||
<FormCheck
|
||||
type='radio'
|
||||
inline={true}
|
||||
name='trayIconTheme'
|
||||
value='dark'
|
||||
defaultChecked={this.state.trayIconTheme === 'dark'}
|
||||
onChange={() => this.handleChangeTrayIconTheme('dark')}
|
||||
>{'Dark'}</Radio>
|
||||
label={'Dark'}
|
||||
/>
|
||||
</FormGroup>,
|
||||
);
|
||||
}
|
||||
|
||||
if (window.process.platform === 'linux') {
|
||||
options.push(
|
||||
<Checkbox
|
||||
<FormCheck
|
||||
type='radio'
|
||||
key='inputMinimizeToTray'
|
||||
id='inputMinimizeToTray'
|
||||
ref={this.minimizeToTrayRef}
|
||||
@@ -636,27 +643,29 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
onChange={this.handleChangeMinimizeToTray}
|
||||
>
|
||||
{'Leave app running in notification area when application window is closed'}
|
||||
<HelpBlock>
|
||||
<FormText>
|
||||
{'If enabled, the app stays running in the notification area after app window is closed.'}
|
||||
{this.state.trayWasVisible || !this.state.showTrayIcon ? '' : ' Setting takes effect after restarting the app.'}
|
||||
</HelpBlock>
|
||||
</Checkbox>);
|
||||
</FormText>
|
||||
</FormCheck>);
|
||||
}
|
||||
|
||||
options.push(
|
||||
<Checkbox
|
||||
key='inputEnableHardwareAcceleration'
|
||||
id='inputEnableHardwareAcceleration'
|
||||
ref={this.enableHardwareAccelerationRef}
|
||||
checked={this.state.enableHardwareAcceleration}
|
||||
onChange={this.handleChangeEnableHardwareAcceleration}
|
||||
>
|
||||
<FormCheck>
|
||||
<FormCheck.Input
|
||||
type='checkbox'
|
||||
key='inputEnableHardwareAcceleration'
|
||||
id='inputEnableHardwareAcceleration'
|
||||
ref={this.enableHardwareAccelerationRef}
|
||||
checked={this.state.enableHardwareAcceleration}
|
||||
onChange={this.handleChangeEnableHardwareAcceleration}
|
||||
/>
|
||||
{'Use GPU hardware acceleration'}
|
||||
<HelpBlock>
|
||||
<FormText>
|
||||
{'If enabled, Mattermost UI is rendered more efficiently but can lead to decreased stability for some systems.'}
|
||||
{' Setting takes effect after restarting the app.'}
|
||||
</HelpBlock>
|
||||
</Checkbox>,
|
||||
</FormText>
|
||||
</FormCheck>,
|
||||
);
|
||||
|
||||
options.push(
|
||||
@@ -679,9 +688,9 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
>
|
||||
<span>{'Change'}</span>
|
||||
</Button>
|
||||
<HelpBlock>
|
||||
<FormText>
|
||||
{'Specify the folder where files will download.'}
|
||||
</HelpBlock>
|
||||
</FormText>
|
||||
</div>,
|
||||
);
|
||||
|
||||
@@ -734,19 +743,15 @@ export default class SettingsPage extends React.PureComponent<Record<string, nev
|
||||
margin: '0 -15px',
|
||||
}}
|
||||
>
|
||||
<Navbar
|
||||
className='navbar-fixed-top'
|
||||
style={settingsPage.navbar}
|
||||
>
|
||||
<div style={{position: 'relative'}}>
|
||||
<h1 style={settingsPage.heading}>{'Settings'}</h1>
|
||||
</div>
|
||||
</Navbar>
|
||||
<Grid
|
||||
<div style={{position: 'relative'}}>
|
||||
<h1 style={settingsPage.heading}>{'Settings'}</h1>
|
||||
<hr/>
|
||||
</div>
|
||||
<Container
|
||||
className='settingsPage'
|
||||
>
|
||||
{waitForIpc}
|
||||
</Grid>
|
||||
</Container>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@@ -3,9 +3,10 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {Nav, NavItem} from 'react-bootstrap';
|
||||
import {Container, Draggable, OnDropCallback} from 'react-smooth-dnd';
|
||||
import {Nav, NavItem, NavLink} from 'react-bootstrap';
|
||||
import {DragDropContext, Draggable, DraggingStyle, Droppable, DropResult, NotDraggingStyle} from 'react-beautiful-dnd';
|
||||
import PlusIcon from 'mdi-react/PlusIcon';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {Team} from 'types/config';
|
||||
|
||||
@@ -22,7 +23,7 @@ type Props = {
|
||||
mentionCounts: Record<string, number>;
|
||||
showAddServerButton: boolean;
|
||||
onAddServer: () => void;
|
||||
onDrop: OnDropCallback;
|
||||
onDrop: (result: DropResult) => void;
|
||||
tabsDisabled?: boolean;
|
||||
};
|
||||
|
||||
@@ -30,9 +31,18 @@ type State = {
|
||||
hasGPOTeams: boolean;
|
||||
};
|
||||
|
||||
export default class TabBar extends React.PureComponent<Props, State> { // need "this"
|
||||
container?: React.RefObject<Container>;
|
||||
function getStyle(style?: DraggingStyle | NotDraggingStyle) {
|
||||
if (style?.transform) {
|
||||
const axisLockX = `${style.transform.slice(0, style.transform.indexOf(','))}, 0px)`;
|
||||
return {
|
||||
...style,
|
||||
transform: axisLockX,
|
||||
};
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
export default class TabBar extends React.PureComponent<Props, State> { // need "this"
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -48,7 +58,7 @@ export default class TabBar extends React.PureComponent<Props, State> { // need
|
||||
|
||||
render() {
|
||||
const orderedTabs = this.props.teams.concat().sort((a, b) => a.order - b.order);
|
||||
const tabs = orderedTabs.map((team) => {
|
||||
const tabs = orderedTabs.map((team, orderedIndex) => {
|
||||
const index = this.props.teams.indexOf(team);
|
||||
|
||||
const sessionExpired = this.props.sessionsExpired[index];
|
||||
@@ -76,83 +86,106 @@ export default class TabBar extends React.PureComponent<Props, State> { // need
|
||||
);
|
||||
}
|
||||
|
||||
const id = `teamTabItem${index}`;
|
||||
const navItem = () => (
|
||||
<NavItem
|
||||
key={index}
|
||||
id={id}
|
||||
eventKey={index}
|
||||
draggable={false}
|
||||
ref={id}
|
||||
active={this.props.activeKey === index}
|
||||
onMouseDown={() => {
|
||||
this.props.onSelect(team.name, index);
|
||||
}}
|
||||
onSelect={() => {
|
||||
this.props.onSelect(team.name, index);
|
||||
}}
|
||||
title={team.name}
|
||||
disabled={this.props.tabsDisabled}
|
||||
>
|
||||
<div className='TabBar-tabSeperator'>
|
||||
<span>
|
||||
{team.name}
|
||||
</span>
|
||||
{ badgeDiv }
|
||||
</div>
|
||||
</NavItem>
|
||||
);
|
||||
|
||||
return (
|
||||
<Draggable
|
||||
key={id}
|
||||
render={navItem}
|
||||
className='teamTabItem'
|
||||
/>);
|
||||
key={index}
|
||||
draggableId={`teamTabItem${index}`}
|
||||
index={orderedIndex}
|
||||
>
|
||||
{(provided, snapshot) => (
|
||||
<NavItem
|
||||
ref={provided.innerRef}
|
||||
as='li'
|
||||
id={`teamTabItem${index}`}
|
||||
draggable={false}
|
||||
title={team.name}
|
||||
className={classNames('teamTabItem', {
|
||||
active: this.props.activeKey === index,
|
||||
dragging: snapshot.isDragging,
|
||||
})}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
style={getStyle(provided.draggableProps.style)}
|
||||
>
|
||||
<NavLink
|
||||
eventKey={index}
|
||||
draggable={false}
|
||||
active={this.props.activeKey === index}
|
||||
disabled={this.props.tabsDisabled}
|
||||
onSelect={() => {
|
||||
this.props.onSelect(team.name, index);
|
||||
}}
|
||||
>
|
||||
<div className='TabBar-tabSeperator'>
|
||||
<span>
|
||||
{team.name}
|
||||
</span>
|
||||
{ badgeDiv }
|
||||
</div>
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
});
|
||||
if (this.props.showAddServerButton === true) {
|
||||
tabs.push(
|
||||
<NavItem
|
||||
className='TabBar-addServerButton'
|
||||
key='addServerButton'
|
||||
id='addServerButton'
|
||||
eventKey='addServerButton'
|
||||
draggable={false}
|
||||
title='Add new server'
|
||||
onSelect={() => {
|
||||
this.props.onAddServer();
|
||||
}}
|
||||
disabled={this.props.tabsDisabled}
|
||||
<Draggable
|
||||
draggableId={'TabBar-addServerButton'}
|
||||
index={this.props.teams.length}
|
||||
isDragDisabled={true}
|
||||
>
|
||||
<div className='TabBar-tabSeperator'>
|
||||
<PlusIcon size={20}/>
|
||||
</div>
|
||||
</NavItem>,
|
||||
{(provided) => (
|
||||
<NavItem
|
||||
ref={provided.innerRef}
|
||||
as='li'
|
||||
className='TabBar-addServerButton'
|
||||
key='addServerButton'
|
||||
id='addServerButton'
|
||||
draggable={false}
|
||||
title='Add new server'
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
>
|
||||
<NavLink
|
||||
eventKey='addServerButton'
|
||||
draggable={false}
|
||||
disabled={this.props.tabsDisabled}
|
||||
onSelect={() => {
|
||||
this.props.onAddServer();
|
||||
}}
|
||||
>
|
||||
<div className='TabBar-tabSeperator'>
|
||||
<PlusIcon size={20}/>
|
||||
</div>
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
)}
|
||||
</Draggable>,
|
||||
);
|
||||
}
|
||||
|
||||
const navContainer = (ref: React.RefObject<Nav>) => (
|
||||
<Nav
|
||||
ref={ref}
|
||||
className={`smooth-dnd-container TabBar${this.props.isDarkMode ? ' darkMode' : ''}`}
|
||||
id={this.props.id}
|
||||
bsStyle='tabs'
|
||||
>
|
||||
{ tabs }
|
||||
</Nav>
|
||||
);
|
||||
return (
|
||||
<Container
|
||||
ref={this.container}
|
||||
render={navContainer}
|
||||
orientation='horizontal'
|
||||
lockAxis={'x'}
|
||||
onDrop={this.props.onDrop}
|
||||
animationDuration={300}
|
||||
shouldAcceptDrop={() => {
|
||||
return !this.state.hasGPOTeams && !this.props.tabsDisabled;
|
||||
}}
|
||||
/>
|
||||
<DragDropContext onDragEnd={this.props.onDrop}>
|
||||
<Droppable
|
||||
isDropDisabled={this.state.hasGPOTeams || this.props.tabsDisabled}
|
||||
droppableId='tabBar'
|
||||
direction='horizontal'
|
||||
>
|
||||
{(provided) => (
|
||||
<Nav
|
||||
ref={provided.innerRef}
|
||||
className={`TabBar${this.props.isDarkMode ? ' darkMode' : ''}`}
|
||||
id={this.props.id}
|
||||
variant='tabs'
|
||||
{...provided.droppableProps}
|
||||
>
|
||||
{tabs}
|
||||
{provided.placeholder}
|
||||
</Nav>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ export default class TeamListItem extends React.PureComponent<Props> {
|
||||
{ this.props.url }
|
||||
</p>
|
||||
</div>
|
||||
<div className='pull-right'>
|
||||
<div>
|
||||
<a
|
||||
href='#'
|
||||
onClick={this.handleTeamEditing}
|
||||
|
@@ -7,22 +7,22 @@ import {Button, Navbar, ProgressBar} from 'react-bootstrap';
|
||||
|
||||
type InstallButtonProps = {
|
||||
notifyOnly?: boolean;
|
||||
onClickInstall?: React.MouseEventHandler<Button>;
|
||||
onClickDownload?: React.MouseEventHandler<Button>;
|
||||
onClickInstall?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
onClickDownload?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
};
|
||||
|
||||
function InstallButton(props: InstallButtonProps) {
|
||||
if (props.notifyOnly) {
|
||||
return (
|
||||
<Button
|
||||
bsStyle='primary'
|
||||
variant='primary'
|
||||
onClick={props.onClickDownload}
|
||||
>{'Download Update'}</Button>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
bsStyle='primary'
|
||||
variant='primary'
|
||||
onClick={props.onClickInstall}
|
||||
>{'Install Update'}</Button>
|
||||
);
|
||||
@@ -33,12 +33,12 @@ type UpdaterPageProps = {
|
||||
notifyOnly?: boolean;
|
||||
isDownloading?: boolean;
|
||||
progress?: number;
|
||||
onClickInstall?: React.MouseEventHandler<Button>;
|
||||
onClickDownload?: React.MouseEventHandler<Button>;
|
||||
onClickInstall?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
onClickDownload?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
onClickReleaseNotes?: React.MouseEventHandler<HTMLAnchorElement>;
|
||||
onClickRemind?: React.MouseEventHandler<Button>;
|
||||
onClickSkip?: React.MouseEventHandler<Button>;
|
||||
onClickCancel?: React.MouseEventHandler<Button>;
|
||||
onClickRemind?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
onClickSkip?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
onClickCancel?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
};
|
||||
|
||||
function UpdaterPage(props: UpdaterPageProps) {
|
||||
@@ -47,11 +47,10 @@ function UpdaterPage(props: UpdaterPageProps) {
|
||||
footer = (
|
||||
<Navbar
|
||||
className='UpdaterPage-footer'
|
||||
fixedBottom={true}
|
||||
fluid={true}
|
||||
fixed='bottom'
|
||||
>
|
||||
<ProgressBar
|
||||
active={true}
|
||||
animated={true}
|
||||
now={props.progress}
|
||||
label={`${props.progress}%`}
|
||||
/>
|
||||
@@ -66,17 +65,16 @@ function UpdaterPage(props: UpdaterPageProps) {
|
||||
footer = (
|
||||
<Navbar
|
||||
className='UpdaterPage-footer'
|
||||
fixedBottom={true}
|
||||
fluid={true}
|
||||
fixed='bottom'
|
||||
>
|
||||
<Button
|
||||
className='UpdaterPage-skipButton'
|
||||
bsStyle='link'
|
||||
variant='link'
|
||||
onClick={props.onClickSkip}
|
||||
>{'Skip this version'}</Button>
|
||||
<div className='pull-right'>
|
||||
<Button
|
||||
bsStyle='link'
|
||||
variant='link'
|
||||
onClick={props.onClickRemind}
|
||||
>{'Remind me in 2 days'}</Button>
|
||||
<InstallButton
|
||||
@@ -91,7 +89,7 @@ function UpdaterPage(props: UpdaterPageProps) {
|
||||
|
||||
return (
|
||||
<div className='UpdaterPage'>
|
||||
<Navbar fluid={true} >
|
||||
<Navbar>
|
||||
<h1 className='UpdaterPage-heading'>{'New update is available'}</h1>
|
||||
</Navbar>
|
||||
<div className='container-fluid'>
|
||||
|
@@ -105,6 +105,7 @@ export default class ShowCertificateModal extends React.PureComponent<Props, Sta
|
||||
<Row>
|
||||
<Col>
|
||||
<Button
|
||||
variant='primary'
|
||||
onClick={this.handleOk}
|
||||
className={'primary'}
|
||||
>{'Close'}</Button>
|
||||
|
Reference in New Issue
Block a user