Merge pull request #726 from vincentbavitz/brand-redesign
Password [set, change, remove] and ContextMenu addition to Main View.pull/737/head
commit
adae5b6bf0
@ -0,0 +1,268 @@
|
||||
.slider {
|
||||
&-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 20px 10px;
|
||||
}
|
||||
|
||||
&-info {
|
||||
display: block;
|
||||
margin-left: 20px;
|
||||
text-align: center;
|
||||
|
||||
p {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rc-slider {
|
||||
position: relative;
|
||||
height: 14px;
|
||||
padding: 5px 0;
|
||||
width: 100%;
|
||||
border-radius: 6px;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.rc-slider * {
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.rc-slider-rail {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
background-color: $session-shade-6;
|
||||
height: 8px;
|
||||
border-radius: 6px;
|
||||
margin: 0px 0px 0px -1px;
|
||||
}
|
||||
.rc-slider-handle {
|
||||
transition: $session-transition-duration;
|
||||
position: absolute;
|
||||
width: 6px;
|
||||
height: 25px;
|
||||
cursor: pointer;
|
||||
cursor: -webkit-grab;
|
||||
margin-top: -9.5px;
|
||||
margin-left: -2.3px;
|
||||
cursor: grab;
|
||||
border-radius: 4px;
|
||||
/* border: solid 2px rgba(0, 247, 130, 0.8); */
|
||||
background-color: rgba(0, 247, 130, 1);
|
||||
-ms-touch-action: pan-x;
|
||||
touch-action: pan-x;
|
||||
}
|
||||
.rc-slider-handle:focus {
|
||||
border-color: $session-color-green;
|
||||
box-shadow: 0 0 0 5px rgba($session-color-white, 0.2);
|
||||
outline: none;
|
||||
}
|
||||
.rc-slider-handle-click-focused:focus {
|
||||
border-color: rgba($session-color-white, 0.2);
|
||||
box-shadow: unset;
|
||||
}
|
||||
.rc-slider-handle:hover {
|
||||
border-color: $session-color-green;
|
||||
}
|
||||
.rc-slider-handle:active {
|
||||
border-color: $session-color-green;
|
||||
box-shadow: 0 0 5px rgba($session-color-white, 0.2);
|
||||
cursor: -webkit-grabbing;
|
||||
cursor: grabbing;
|
||||
}
|
||||
.rc-slider-mark {
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
}
|
||||
.rc-slider-mark-text {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
color: #999;
|
||||
}
|
||||
.rc-slider-mark-text-active {
|
||||
color: #666;
|
||||
}
|
||||
.rc-slider-step {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
top: -4px;
|
||||
background: transparent;
|
||||
}
|
||||
.rc-slider-dot {
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
margin-left: -4px;
|
||||
width: 3px;
|
||||
height: 6px;
|
||||
background-color: $session-shade-6;
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.rc-slider-dot:first-child {
|
||||
margin-left: -2px;
|
||||
}
|
||||
.rc-slider-dot:last-child {
|
||||
margin-right: -2px;
|
||||
}
|
||||
.rc-slider-dot-active {
|
||||
border-color: #96dbfa;
|
||||
}
|
||||
.rc-slider-dot-reverse {
|
||||
margin-left: 0;
|
||||
margin-right: -4px;
|
||||
}
|
||||
.rc-slider-disabled {
|
||||
background-color: #e9e9e9;
|
||||
}
|
||||
.rc-slider-disabled .rc-slider-handle,
|
||||
.rc-slider-disabled .rc-slider-dot {
|
||||
border-color: #ccc;
|
||||
box-shadow: none;
|
||||
background-color: #fff;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.rc-slider-disabled .rc-slider-mark-text,
|
||||
.rc-slider-disabled .rc-slider-dot {
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
.rc-slider-vertical {
|
||||
width: 14px;
|
||||
height: 100%;
|
||||
padding: 0 5px;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-rail {
|
||||
height: 100%;
|
||||
width: 4px;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-handle {
|
||||
margin-left: -5px;
|
||||
-ms-touch-action: pan-y;
|
||||
touch-action: pan-y;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-mark {
|
||||
top: 0;
|
||||
left: 18px;
|
||||
height: 100%;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-step {
|
||||
height: 100%;
|
||||
width: 4px;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-dot {
|
||||
left: 2px;
|
||||
margin-bottom: -4px;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-dot:first-child {
|
||||
margin-bottom: -4px;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-dot:last-child {
|
||||
margin-bottom: -4px;
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-enter,
|
||||
.rc-slider-tooltip-zoom-down-appear {
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
display: block !important;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-leave {
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
display: block !important;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-enter.rc-slider-tooltip-zoom-down-enter-active,
|
||||
.rc-slider-tooltip-zoom-down-appear.rc-slider-tooltip-zoom-down-appear-active {
|
||||
animation-name: rcSliderTooltipZoomDownIn;
|
||||
animation-play-state: running;
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-leave.rc-slider-tooltip-zoom-down-leave-active {
|
||||
animation-name: rcSliderTooltipZoomDownOut;
|
||||
animation-play-state: running;
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-enter,
|
||||
.rc-slider-tooltip-zoom-down-appear {
|
||||
transform: scale(0, 0);
|
||||
animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-leave {
|
||||
animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
|
||||
}
|
||||
@keyframes rcSliderTooltipZoomDownIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform-origin: 50% 100%;
|
||||
transform: scale(0, 0);
|
||||
}
|
||||
100% {
|
||||
transform-origin: 50% 100%;
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
}
|
||||
@keyframes rcSliderTooltipZoomDownOut {
|
||||
0% {
|
||||
transform-origin: 50% 100%;
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform-origin: 50% 100%;
|
||||
transform: scale(0, 0);
|
||||
}
|
||||
}
|
||||
.rc-slider-tooltip {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
top: -9999px;
|
||||
visibility: visible;
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.rc-slider-tooltip * {
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.rc-slider-tooltip-hidden {
|
||||
display: none;
|
||||
}
|
||||
.rc-slider-tooltip-placement-top {
|
||||
padding: 4px 0 8px 0;
|
||||
}
|
||||
.rc-slider-tooltip-inner {
|
||||
padding: 6px 2px;
|
||||
min-width: 24px;
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
background-color: #6c6c6c;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 0 4px #d9d9d9;
|
||||
}
|
||||
.rc-slider-tooltip-arrow {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
}
|
||||
.rc-slider-tooltip-placement-top .rc-slider-tooltip-arrow {
|
||||
bottom: 4px;
|
||||
left: 50%;
|
||||
margin-left: -4px;
|
||||
border-width: 4px 4px 0;
|
||||
border-top-color: #6c6c6c;
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
import React from 'react';
|
||||
|
||||
import { SessionModal } from './SessionModal';
|
||||
import { SessionButton, SessionButtonColor } from './SessionButton';
|
||||
|
||||
export enum PasswordAction {
|
||||
Set = 'set',
|
||||
Change = 'change',
|
||||
Remove = 'remove',
|
||||
}
|
||||
|
||||
interface Props {
|
||||
action: PasswordAction;
|
||||
onOk: any;
|
||||
onClose: any;
|
||||
}
|
||||
|
||||
interface State {
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
export class SessionPasswordModal extends React.Component<Props, State> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
error: null,
|
||||
};
|
||||
|
||||
this.showError = this.showError.bind(this);
|
||||
|
||||
this.setPassword = this.setPassword.bind(this);
|
||||
this.closeDialog = this.closeDialog.bind(this);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { action, onOk } = this.props;
|
||||
const placeholders =
|
||||
this.props.action === PasswordAction.Change
|
||||
? [window.i18n('typeInOldPassword'), window.i18n('enterPassword')]
|
||||
: [window.i18n('enterPassword'), window.i18n('confirmPassword')];
|
||||
|
||||
const confirmButtonColor =
|
||||
this.props.action === PasswordAction.Remove
|
||||
? SessionButtonColor.Danger
|
||||
: SessionButtonColor.Primary;
|
||||
|
||||
return (
|
||||
<SessionModal
|
||||
title={window.i18n(`${action}Password`)}
|
||||
onOk={() => null}
|
||||
onClose={this.closeDialog}
|
||||
>
|
||||
<div className="spacer-sm" />
|
||||
|
||||
<div className="session-modal__input-group">
|
||||
<input
|
||||
type="password"
|
||||
id="password-modal-input"
|
||||
placeholder={placeholders[0]}
|
||||
/>
|
||||
{action !== PasswordAction.Remove && (
|
||||
<input
|
||||
type="password"
|
||||
id="password-modal-input-confirm"
|
||||
placeholder={placeholders[1]}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="spacer-sm" />
|
||||
{this.showError()}
|
||||
|
||||
<div className="session-modal__button-group">
|
||||
<SessionButton
|
||||
text={window.i18n('ok')}
|
||||
buttonColor={confirmButtonColor}
|
||||
onClick={async () => this.setPassword(onOk)}
|
||||
/>
|
||||
|
||||
<SessionButton
|
||||
text={window.i18n('cancel')}
|
||||
onClick={this.closeDialog}
|
||||
/>
|
||||
</div>
|
||||
</SessionModal>
|
||||
);
|
||||
}
|
||||
|
||||
public async validatePasswordHash(password: string | null) {
|
||||
// Check if the password matches the hash we have stored
|
||||
const hash = await window.Signal.Data.getPasswordHash();
|
||||
if (hash && !window.passwordUtil.matchesHash(password, hash)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private showError() {
|
||||
const message = this.state.error;
|
||||
|
||||
return (
|
||||
<>
|
||||
{message && (
|
||||
<>
|
||||
<div className="session-label warning">{message}</div>
|
||||
<div className="spacer-lg" />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
private async setPassword(onSuccess: any) {
|
||||
const enteredPassword = String($('#password-modal-input').val());
|
||||
const enteredPasswordConfirm = String(
|
||||
$('#password-modal-input-confirm').val()
|
||||
);
|
||||
|
||||
// Check passwords enntered
|
||||
if (
|
||||
enteredPassword.length === 0 ||
|
||||
(this.props.action === PasswordAction.Change &&
|
||||
enteredPasswordConfirm.length === 0)
|
||||
) {
|
||||
this.setState({
|
||||
error: window.i18n('noGivenPassword'),
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Passwords match or remove password successful
|
||||
const newPassword =
|
||||
this.props.action === PasswordAction.Remove
|
||||
? null
|
||||
: enteredPasswordConfirm;
|
||||
const oldPassword =
|
||||
this.props.action === PasswordAction.Set ? null : enteredPassword;
|
||||
|
||||
// Check if password match, when setting, changing or removing
|
||||
const valid =
|
||||
this.props.action !== PasswordAction.Set
|
||||
? !!await this.validatePasswordHash(oldPassword)
|
||||
: enteredPassword === enteredPasswordConfirm;
|
||||
|
||||
if (!valid) {
|
||||
this.setState({
|
||||
error: window.i18n(`${this.props.action}PasswordInvalid`),
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await window.setPassword(newPassword, oldPassword);
|
||||
|
||||
const toastParams = {
|
||||
title: window.i18n(`${this.props.action}PasswordTitle`),
|
||||
description: window.i18n(`${this.props.action}PasswordToastDescription`),
|
||||
type: this.props.action !== PasswordAction.Remove ? 'success' : 'warning',
|
||||
icon: this.props.action !== PasswordAction.Remove ? 'lock' : undefined,
|
||||
};
|
||||
|
||||
window.pushToast({
|
||||
id: 'set-password-success-toast',
|
||||
...toastParams,
|
||||
});
|
||||
|
||||
onSuccess(this.props.action);
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
private closeDialog() {
|
||||
window.removeEventListener('keyup', this.onEnter);
|
||||
|
||||
if (this.props.onClose) {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue