🧹 Merge pull request #31 from linagora/removing-js-files

🧹 Removing js files
This commit is contained in:
Romaric Mourgues
2023-04-24 15:47:20 +02:00
committed by GitHub
94 changed files with 57 additions and 7660 deletions
@@ -1,55 +0,0 @@
/* eslint-disable react/prop-types */
/* eslint-disable react/jsx-key */
import React, { Component } from 'react';
import './color-picker.scss';
import CheckIcon from '@material-ui/icons/CheckOutlined';
export default class ColorPicker extends Component {
static colors = [
'#D50000',
'#E67C73',
'#F4511E',
'#F6BF26',
'#33B679',
'#0B8043',
'#039BE5',
'#3F51B5',
'#7986CB',
'#8E24AA',
'#801515',
'#616161',
];
/*
props = {
value : default color
onChange : called when a color is selected
}
*/
constructor() {
super();
this.colors = ColorPicker.colors;
}
render() {
var showed_selected = this.colors.indexOf(this.props.value) >= 0;
var colors = this.colors;
if (!showed_selected) {
colors = this.colors.concat([this.props.value]);
}
return (
<div className="colorPicker" ref={this.props.refDom}>
{colors.map(color => {
return (
<div
className="color"
style={{ backgroundColor: color }}
onClick={() => this.props.onChange(color)}
>
{this.props.value == color && <CheckIcon className="m-icon-small" />}
</div>
);
})}
</div>
);
}
}
@@ -1,18 +0,0 @@
.colorPicker {
.color {
width: 16px;
height: 16px;
border-radius: var(--border-radius-large);
display: inline-block;
margin: 10px;
position: relative;
cursor: pointer;
.m-icon-small {
color: #fff;
width: 16px;
height: 16px;
position: absolute;
}
}
}
@@ -1,139 +0,0 @@
/* eslint-disable react/prop-types */
/* eslint-disable react/no-direct-mutation-state */
import React, { Component } from 'react';
import CloseIcon from '@material-ui/icons/CloseOutlined';
import AddIcon from '@material-ui/icons/AddOutlined';
import GearIcon from '@material-ui/icons/BuildOutlined';
import Input from 'components/inputs/input.jsx';
import './connectors-list-manager.scss';
import Languages from '@features/global/services/languages-service';
import WorkspacesApps from '@deprecated/workspaces/workspaces_apps.jsx';
import { getCompanyApplication as getApplication } from '@features/applications/state/company-applications';
export default class ConnectorsListManager extends Component {
constructor(props) {
super();
this.props = props;
this.state = {
connectors_ids: props.current.map(item => item.id || ''),
filtered: [],
input: '',
};
}
filter(text) {
this.state.input = text;
var list = this.props.list;
var res = list
.filter(el => {
return el.identity.name.toLocaleLowerCase().indexOf(text.toLocaleLowerCase()) >= 0;
})
.map(el => el);
this.setState({ filtered: res });
}
componentDidMount() {
this.filter('');
}
renderLine(item, added) {
var id = item.id || item;
item = getApplication(id);
var text = '';
var button = '';
text = (
<div className="text" style={{ display: 'flex', alignItems: 'center' }}>
{WorkspacesApps.getAppIconComponent(item)}
{item.identity.name}
</div>
);
if (added) {
button = (
<div className="more">
<GearIcon
className="m-icon-small config"
onClick={() => {
this.props.onConfig(item);
}}
/>
<CloseIcon
className="m-icon-small remove"
onClick={() => {
this.state.connectors_ids = this.state.connectors_ids.filter(id =>
typeof item == 'string' ? item != id : item.id != id,
);
this.setState({});
this.props.onChange(this.state.connectors_ids);
}}
/>
</div>
);
} else {
button = (
<div className="more">
<AddIcon
className="m-icon-small add"
onClick={() => {
this.state.connectors_ids.push(item.id || this.state.input.toLocaleLowerCase());
this.setState({ input: '' });
this.props.onChange(this.state.connectors_ids);
}}
/>
</div>
);
}
return (
<div className="menu no-background">
{text}
{button}
</div>
);
}
render() {
return (
<div className="connectorsListManager">
<div className="menu-title no-separator">
<div className="text">
{Languages.t('scenes.apps.tasks.connectors_menu', [], 'Connecteurs')}
</div>
</div>
{this.state.connectors_ids.length == 0 && (
<div className="menu-text">
{Languages.t('scenes.apps.tasks.no_connector', [], 'Aucun connecteurs.')}
</div>
)}
{this.state.connectors_ids.map(id => {
return this.renderLine(id, true);
})}
<div className="menu-title no-separator">
<div className="text">
{Languages.t('components.connectorslistmanager.add_connectors')}
</div>
</div>
<div className="menu-buttons" style={{ paddingTop: 2, paddingBottom: 2 }}>
<div className="text">
<Input
type="text"
className="small full_width bottom-margin"
placeholder={Languages.t('components.listmanager.filter', [], 'Filtrer')}
style={{ margin: 0 }}
onChange={evt => this.filter(evt.target.value)}
/>
</div>
</div>
{this.state.filtered.slice(0, 5).map(item => {
if (this.state.connectors_ids.indexOf(item.id) >= 0) {
return '';
}
return this.renderLine(item, false);
})}
</div>
);
}
}
@@ -1,49 +0,0 @@
.connectorsListManager {
margin: 0px -16px;
margin-top: -8px;
.menu-app-icon {
width: 18px;
height: 18px;
display: inline-block;
background-position: center;
background-size: contain;
border-radius: var(--border-radius-base);
vertical-align: middle;
margin-right: 8px;
}
.emoji-container {
margin-right: 5px;
top: 1px;
position: relative;
}
.menu-buttons {
padding: 0px 8px;
margin: 0px 8px;
}
.more {
width: auto !important;
margin-right: -5px;
.m-icon-small {
margin: 3px;
}
&:hover {
.m-icon-small {
&.remove:hover {
color: var(--red);
}
&.config:hover {
color: var(--primary);
}
&.add {
color: var(--primary);
}
}
}
}
}
@@ -1,74 +0,0 @@
import React, { Component } from 'react';
import InfiniteMessages from './infinite-messages.js';
export default class InfiniteMessagesTester extends Component {
constructor() {
super();
///////// Simulate messages
this.messages = [];
this.i = 0;
for (this.i; this.i < 10000; this.i++) {
this.messages.push({
content: 'message ' + this.i,
height: 40 + Math.random() * 100,
id: this.i,
});
}
setInterval(() => {
this.messages.push({
content: 'message ' + this.i,
height: 40 + Math.random() * 100,
id: this.i,
});
this.i++;
this.setState({});
}, 1000);
///////////
this.infinieMessages = null;
}
getMessages(offset, limit, callback) {
if (!offset) {
setTimeout(() => {
callback(this.messages.slice(this.messages.length - limit, this.messages.length));
}, 10);
} else {
var offsetPos = 0;
this.messages.every(m => {
if (m.id >= offset) {
return false;
}
offsetPos++;
return true;
});
setTimeout(() => {
if (limit > 0) {
callback(this.messages.slice(offsetPos - limit, offsetPos));
} else {
callback(this.messages.slice(offsetPos + 1, offsetPos - limit));
}
}, 10);
}
}
render() {
return (
<div className="infinite_messages_tester">
<InfiniteMessages
ref={node => (this.infinieMessages = node)}
getMessages={(offset, limit, callback) => this.getMessages(offset, limit, callback)}
messages={this.messages}
offsetKey={'id'}
top={''}
renderMessage={(message, oldmessage, measure) => (
<div className="message" style={{ height: message.height }}>
{message.content}
</div>
)}
/>
</div>
);
}
}
@@ -1,472 +0,0 @@
/* eslint-disable react/no-direct-mutation-state */
import React, { Component } from 'react';
import Button from 'components/buttons/button.jsx';
import './infinite-messages.scss';
export default class InfiniteMessages extends Component {
constructor(props) {
super(props);
window.test = this;
this.init(props);
this.onScroll = this.onScroll.bind(this);
this.onUpdate = this.onUpdate.bind(this);
}
init(props) {
props = props || this.props;
this.on_load_min = {};
if (props.messages.length > 60) {
this.on_load_min = props.messages.slice(props.messages.length - 60, props.messages.length)[0];
} else {
this.on_load_min = props.messages[0] || {};
}
this.state = {
messages: props.messages.filter(m => this.compare(m, this.on_load_min) >= 0),
};
this.shouldAutoScroll = true;
this.messages_min = this.state.messages[0] || {};
this.messages_med = this.state.messages[this.state.messages.length - 1] || {};
this.messages_max = this.state.messages[this.state.messages.length - 1] || {};
this.bloc_top_height = 0;
this.did_update_messages_data = {};
this.dom_messages = {};
this.max_known = this.messages_max;
}
componentDidMount() {
this.did_update_messages = true;
this.did_update_messages_data = {
direction: 'bottom_auto_add',
bloc_top_height: this.dom_visualized_messages_top.clientHeight,
bloc_bottom_height: this.dom_visualized_messages_bottom.clientHeight,
scroll_top: this.dom_infinite_messages.scrollTop,
scroll_height: this.dom_infinite_messages.scrollHeight,
};
// this.dom_infinite_messages.addEventListener("scroll", this.onScroll);
window.addEventListener('resize', this.onUpdate);
this.onUpdate();
}
componentWillUnmount() {
window.removeEventListener('resize', this.onUpdate);
// this.dom_infinite_messages.removeEventListener("scroll", this.onScroll);
}
UNSAFE_componentWillUpdate(nextProps, nextState) {
this.scroll_distance_from_bottom =
this.dom_infinite_messages.scrollHeight -
this.dom_infinite_messages.clientHeight -
this.dom_infinite_messages.scrollTop;
if (nextProps.messages.length) {
if (nextProps.messages[nextProps.messages.length - 1].id) {
if (this.messages_med.id == this.max_known.id) {
this.messages_med = nextProps.messages[nextProps.messages.length - 1];
this.messages_max = nextProps.messages[nextProps.messages.length - 1];
}
this.max_known = nextProps.messages[nextProps.messages.length - 1];
}
}
if (this.max_known.id == this.messages_med.id) {
nextState.messages = nextProps.messages.filter(m => {
return !m.id || this.compare(m, this.on_load_min) >= 0;
});
this.did_update_messages = true;
this.did_update_messages_data = {
direction: 'bottom_auto_add',
bloc_top_height: this.dom_visualized_messages_top.clientHeight,
bloc_bottom_height: this.dom_visualized_messages_bottom.clientHeight,
scroll_top: this.dom_infinite_messages.scrollTop,
scroll_height: this.dom_infinite_messages.scrollHeight,
};
}
}
componentDidUpdate() {
this.onUpdate();
}
onUpdate() {
this.did_auto_scroll = new Date();
this.scrollToBottom();
var dom_fake_separator_top_visible = Math.max(
0,
this.dom_infinite_messages.clientHeight - this.dom_visualized_messages_top.clientHeight,
);
if (dom_fake_separator_top_visible == 0) {
this.dom_fake_separator_top.style.display = 'none';
} else {
this.dom_fake_separator_top.style.display = 'block';
}
if (this.did_update_messages) {
this.did_update_messages = false;
this.bloc_top_height = this.dom_visualized_messages_top.clientHeight;
this.bloc_bottom_height = this.dom_visualized_messages_bottom.clientHeight;
if (this.did_update_messages_data.direction == 'bottom') {
this.scrollToPosition(
this.dom_anything_on_top.clientHeight +
(this.did_update_messages_data.bloc_bottom_height -
(this.did_update_messages_data.scroll_height -
this.did_update_messages_data.scroll_top)),
);
} else if (this.did_update_messages_data.direction == 'bottom_auto_add') {
//Nothing
} else {
this.scrollToPosition(this.bloc_top_height + this.did_update_messages_data.scroll_top);
}
this.getVisibleMessages();
}
}
onScroll(evt) {
if (new Date() - this.did_auto_scroll < 100) {
return;
}
if (
this.dom_infinite_messages.scrollHeight != this.last_scrollHeight &&
this.shouldAutoScroll
) {
this.last_scrollHeight = this.dom_infinite_messages.scrollHeight;
this.scrollToBottom();
} else {
var shouldAutoScroll = this.shouldAutoScroll;
this.shouldAutoScroll = false;
if (this.props.onAutoScrollChange && shouldAutoScroll) this.props.onAutoScrollChange(false);
}
if (this.dom_infinite_messages.scrollTop <= this.dom_infinite_messages.scrollHeight / 8) {
this.onScrollTop();
}
var dist_from_bottom =
this.dom_infinite_messages.scrollHeight -
this.dom_infinite_messages.scrollTop -
this.dom_infinite_messages.clientHeight;
if (dist_from_bottom <= Math.max(10, this.dom_infinite_messages.scrollHeight / 8)) {
if (
dist_from_bottom <= 10 &&
(this.shouldAutoScroll ||
this.max_known.id == this.state.messages[this.state.messages.length - 1].id)
) {
// eslint-disable-next-line no-redeclare
var shouldAutoScroll = this.shouldAutoScroll;
this.shouldAutoScroll = true;
if (this.props.onAutoScrollChange && !shouldAutoScroll) this.props.onAutoScrollChange(true);
} else if (dist_from_bottom <= 10 || this.max_known.id != this.messages_med.id) {
this.onScrollBottom();
}
}
this.getVisibleMessages();
}
getVisibleMessages() {
if (
this.last_get &&
Math.abs(this.last_get.scroll_top - this.dom_infinite_messages.scrollTop) < 40
) {
return [this.last_get.first];
}
var first = null;
var frame = window.getBoundingClientRect(this.dom_infinite_messages);
frame.x = frame.x || frame.left;
frame.y = frame.y || frame.top;
frame.scrollTop = this.dom_infinite_messages.scrollTop;
Object.keys(this.dom_messages).every(i => {
var item = this.dom_messages[i];
if (item.node && item.scrollTop === undefined) {
item.scrollTop = window.getBoundingClientRect(item.node).top + frame.scrollTop - frame.y;
}
if (item.node) {
if (!first && item.scrollTop > frame.scrollTop) {
first = item.message;
return false;
}
}
return true;
});
var first_date = new Date((first || {}).creation_date * 1000).setHours(0, 0, 0, 0);
if (first_date != (this.last_get || {}).first_date) {
if (this.props.onFirstDateChange) this.props.onFirstDateChange(first_date);
}
this.last_get = {
scroll_top: frame.scrollTop,
first: first,
first_date: first_date,
};
return [this.last_get.first];
}
scrollToBottom(force) {
if (this.shouldAutoScroll || force) {
//Make sure we display the last bloc !
if (
this.messages_med.id != this.messages_min.id &&
this.messages_med.id != this.messages_max.id
) {
this.init();
this.did_update_messages = true;
this.did_update_messages_data = {
direction: 'bottom_auto_add',
bloc_top_height: this.dom_visualized_messages_top.clientHeight,
bloc_bottom_height: this.dom_visualized_messages_bottom.clientHeight,
scroll_top: this.dom_infinite_messages.scrollTop,
scroll_height: this.dom_infinite_messages.scrollHeight,
};
this.onUpdate();
}
var old_position = this.dom_infinite_messages.scrollTop;
this.dom_infinite_messages.scrollTop = this.dom_infinite_messages.scrollHeight;
if (old_position != this.dom_infinite_messages.scrollTop) {
this.did_auto_scroll = new Date();
}
var shouldAutoScroll = this.shouldAutoScroll;
this.shouldAutoScroll = true;
if (this.props.onAutoScrollChange && !shouldAutoScroll) this.props.onAutoScrollChange(true);
}
}
scrollToPosition(scrollTop) {
var old_position = this.dom_infinite_messages.scrollTop;
this.dom_infinite_messages.scrollTop = scrollTop;
this.did_auto_scroll = new Date();
}
onScrollTop() {
if (this.is_getting_top_messages) {
return;
}
if (this.no_more_before && this.messages_min.id == this.no_more_before.id) {
return;
}
this.number_of_messages_to_load = 2 * (this.dom_infinite_messages.clientHeight / 20);
this.is_getting_top_messages = true;
this.props.getMessages(this.messages_min.id, parseInt(this.number_of_messages_to_load), res => {
if (res.length == 0) {
this.is_getting_top_messages = false;
this.no_more_before = this.messages_min;
return;
}
var top_list = this.state.messages.filter(
m => this.compare(m, this.messages_min) >= 0 && this.compare(m, this.messages_med) <= 0,
);
top_list = res.concat(top_list);
this.messages_med = res[res.length - 1];
this.messages_min = res[0];
this.messages_max = top_list[top_list.length - 1];
this.did_update_messages = true;
this.did_update_messages_data = {
direction: 'top',
bloc_top_height: this.dom_visualized_messages_top.clientHeight,
bloc_bottom_height: this.dom_visualized_messages_bottom.clientHeight,
scroll_top: this.dom_infinite_messages.scrollTop,
scroll_height: this.dom_infinite_messages.scrollHeight,
};
this.dom_messages = {};
this.setState({ messages: top_list });
this.is_getting_top_messages = false;
});
}
onScrollBottom() {
if (this.is_getting_bottom_messages) {
return;
}
this.number_of_messages_to_load = 2 * (this.dom_infinite_messages.clientHeight / 20);
this.is_getting_bottom_messages = true;
this.props.getMessages(
this.messages_max.id,
-parseInt(this.number_of_messages_to_load),
res => {
if (res.length == 0) {
this.is_getting_bottom_messages = false;
var shouldAutoScroll = this.shouldAutoScroll;
this.shouldAutoScroll = true;
if (this.props.onAutoScrollChange && !shouldAutoScroll)
this.props.onAutoScrollChange(true);
this.max_known = this.state.messages[this.state.messages.length - 1];
this.messages_min = this.state.messages[0];
this.messages_max = this.state.messages[this.state.messages.length - 1];
this.messages_med = this.messages_max;
return;
}
if (this.compare(res[res.length - 1], this.on_load_min) >= 0) {
var top_list = this.state.messages.filter(m => this.compare(m, res[res.length - 1]) >= 0);
top_list = top_list.concat(res);
this.state.messages = this.props.messages.filter(
m => this.compare(m, this.on_load_min) > 0,
);
this.max_known = this.state.messages[this.state.messages.length - 1];
this.messages_min = this.state.messages[0];
this.messages_max = this.state.messages[this.state.messages.length - 1];
this.messages_med = this.messages_max;
this.did_update_messages = true;
this.did_update_messages_data = {
direction: 'bottom_auto_add',
bloc_top_height: this.dom_visualized_messages_top.clientHeight,
bloc_bottom_height: this.dom_visualized_messages_bottom.clientHeight,
scroll_top: this.dom_infinite_messages.scrollTop,
scroll_height: this.dom_infinite_messages.scrollHeight,
};
this.dom_messages = {};
this.setState({ messages: this.state.messages });
} else {
// eslint-disable-next-line no-redeclare
var top_list = this.state.messages.filter(
m => this.compare(m, this.messages_med) > 0 && this.compare(m, this.messages_max) <= 0,
);
top_list = top_list.concat(res);
this.messages_min = top_list[0];
this.messages_med = this.messages_max;
this.messages_max = top_list[top_list.length - 1];
this.did_update_messages = true;
this.did_update_messages_data = {
direction: 'bottom',
bloc_top_height: this.dom_visualized_messages_top.clientHeight,
bloc_bottom_height: this.dom_visualized_messages_bottom.clientHeight,
scroll_top: this.dom_infinite_messages.scrollTop,
scroll_height: this.dom_infinite_messages.scrollHeight,
};
this.dom_messages = {};
this.setState({ messages: top_list });
}
this.is_getting_bottom_messages = false;
},
);
}
measure(message) {
if (this.dom_infinite_messages) {
this.scrollToBottom();
}
}
//Returns a-b as int
compare(a, b) {
if (this.props.compare) {
return this.props.compare(a, b);
}
return a.id - b.id;
}
render() {
var previous_message = null;
return (
<div className="infinite_messages" ref={node => (this.dom_infinite_messages = node)}>
<div ref={node => (this.dom_anything_on_top = node)}>
{this.props.top || ''}
{!this.no_more_before && (
<div style={{ textAlign: 'center' }}>
<Button
className="small primary"
onClick={() => {
this.onScrollTop();
}}
>
Load more messages
</Button>
</div>
)}
</div>
<div className="fake_separator_top" ref={node => (this.dom_fake_separator_top = node)} />
<div
className="visualized_messages top"
ref={node => (this.dom_visualized_messages_top = node)}
>
{this.state.messages.map((message, _i) => {
if (this.compare(message, this.messages_med) <= 0) {
var tmp = previous_message;
previous_message = message;
var i = _i;
return (
<div
key={message.id}
ref={node =>
(this.dom_messages[i] = {
node: node,
message: message,
scrollTop: (this.dom_messages[i] || {}).scrollTop,
})
}
>
{this.props.renderMessage(message, tmp, () => this.measure(message))}
</div>
);
}
})}
</div>
<div
className="visualized_messages"
ref={node => (this.dom_visualized_messages_bottom = node)}
>
{this.state.messages.map((message, _i) => {
if (this.compare(message, this.messages_med) > 0) {
var tmp = previous_message;
previous_message = message;
var i = _i;
return (
<div
key={message.id}
ref={node =>
(this.dom_messages[i] = {
node: node,
message: message,
scrollTop: (this.dom_messages[i] || {}).scrollTop,
})
}
>
{this.props.renderMessage(message, tmp, () => this.measure(message))}
</div>
);
}
})}
</div>
</div>
);
}
}
@@ -1,31 +0,0 @@
.infinite_messages {
overflow: auto;
flex: 1;
display: flex;
flex-direction: column;
.message {
box-sizing: border-box;
}
.fake_separator_top {
background: transparent;
flex: 1;
}
}
.infinite_messages_tester {
height: 90vh;
display: flex;
width: 100%;
.message {
box-sizing: border-box;
border: 1px solid #f00;
border-bottom: 5px solid #0f0;
}
.visualized_messages.top {
.message {
background: #fee;
}
}
}
@@ -1,28 +0,0 @@
import React, { Component } from 'react';
import './inline-tag-picker.scss';
export default class InlineTagPicker extends Component {
render() {
return this.props.available.map(item => {
return (
<div
key={item}
className={
'inline-tag small-top-margin ' + (this.props.value.includes(item) ? 'selected' : '')
}
onClick={() => {
let array = (this.props.value || []).map(a => a);
if (this.props.value.includes(item)) {
array = this.props.value.filter(val => item !== val);
} else {
array.push(item);
}
this.props.onChange(array);
}}
>
{item}
</div>
);
});
}
}
@@ -1,23 +0,0 @@
.inline-tag {
cursor: pointer;
background: var(--grey-light);
border-radius: var(--border-radius-base);
height: 20px;
color: var(--grey-dark);
font-size: 12px;
padding: 0 5px;
line-height: 18px;
box-sizing: border-box;
width: auto;
display: inline-block;
margin-right: 6px;
font-weight: normal;
&.selected {
background: var(--green);
color: var(--white);
}
&:hover {
transition: 0.3s;
opacity: 0.9;
}
}
@@ -1,59 +0,0 @@
/* eslint-disable react/prop-types */
import React, { Component } from 'react';
import Languages from '@features/global/services/languages-service';
import Tooltip from 'components/tooltip/tooltip.jsx';
import InputWithButton from 'components/inputs/input-with-button.jsx';
export default class InputWithClipBoard extends Component {
/*
props = {
value : "",
disabled : true|false
}
*/
constructor(props) {
super(props);
this.props = props;
this.state = {
i18n: Languages,
};
Languages.addListener(this);
this.inputElement = false;
}
componentWillUnmount() {
Languages.removeListener(this);
}
selectAll() {
this.inputElement.focus();
this.inputElement.select();
}
copy() {
this.inputElement.disabled = false;
this.inputElement.focus();
this.inputElement.select();
document.execCommand('copy');
this.inputElement.disabled = this.props.disabled;
this.tooltip.openWithTimeOut(2);
}
render() {
return (
<Tooltip
ref={obj => (this.tooltip = obj)}
overable={false}
tooltip={Languages.t('components.input.copied', [], 'Copié')}
>
<InputWithButton
refInput={obj => (this.inputElement = obj)}
btnAction={() => this.copy()}
icon="copy"
hideBtn={this.props.hideBtn}
value={this.props.value}
disabled={this.props.disabled}
/>
</Tooltip>
);
}
}
@@ -1,95 +0,0 @@
/* eslint-disable react/prop-types */
import React, { Component } from 'react';
import './inputs.scss';
import CheckIcon from '@material-ui/icons/CheckOutlined';
export default class Checkbox extends Component {
constructor() {
super();
}
renderSwitch() {
var className = this.props.className || '';
if (this.props.big) {
className += ' big ';
}
if (this.props.medium) {
className += ' medium ';
}
if (this.props.small) {
className += ' small ';
}
if (
className.indexOf('medium') === className.indexOf('small') &&
className.indexOf('big') === className.indexOf('small') &&
className.indexOf('big') < 0
) {
className += ' medium';
}
return (
<div
className={
' flex justify-center items-center w-6 h-6 border-2 rounded-full text-white ' +
(this.props.value
? 'border-blue-500 bg-blue-500 hover:border-blue-600 hover:bg-blue-600'
: 'border-zinc-300 hover:border-blue-300') +
' ' +
(this.props.disabled ? 'opacity-50' : 'cursor-pointer') +
' ' +
(className || '')
}
onClick={() => {
if (!this.props.label && !this.props.disabled) {
this.props.onChange(!this.props.value);
}
}}
>
<div className="state">
<CheckIcon className="m-icon-small" />
</div>
</div>
);
}
render() {
var parentClassName = '';
if (this.props.big) {
parentClassName += ' big ';
}
if (this.props.medium) {
parentClassName += ' medium ';
}
if (this.props.small) {
parentClassName += ' small ';
}
if (
parentClassName.indexOf('medium') === parentClassName.indexOf('small') &&
parentClassName.indexOf('big') === parentClassName.indexOf('small') &&
parentClassName.indexOf('big') < 0
) {
parentClassName += ' medium';
}
if (this.props.label) {
return (
<div
className={'checkbox_with_label ' + (this.props.value ? 'on ' : 'off ') + parentClassName}
onClick={() => {
if (!this.props.disabled) {
this.props.onChange(!this.props.value);
}
}}
>
{this.renderSwitch()}
<div className="label">{this.props.label}</div>
</div>
);
} else {
return this.renderSwitch();
}
}
}
@@ -1,46 +0,0 @@
/* eslint-disable react/prop-types */
import React, { Component } from 'react';
import Input from './input.jsx';
import Icon from '@components/icon/icon.jsx';
import './inputs.scss';
export default class InputEnter extends Component {
constructor() {
super();
}
render() {
var parentClassName = '';
if (this.props.big) {
parentClassName += ' big ';
}
if (this.props.medium) {
parentClassName += ' medium ';
}
if (this.props.small) {
parentClassName += ' small ';
}
if (
parentClassName.indexOf('medium') === parentClassName.indexOf('small') &&
parentClassName.indexOf('big') === parentClassName.indexOf('small') &&
parentClassName.indexOf('big') < 0
) {
parentClassName += ' medium';
}
return (
<div className={'input_icon input_enter ' + parentClassName}>
<Input
onEchap={this.props.onEchap}
onEnter={this.props.onEnter}
autoFocus={this.props.autoFocus}
onKeyDown={this.props.onKeyDown}
{...this.props}
/>
<Icon type={'enter'} />
</div>
);
}
}
@@ -1,47 +0,0 @@
/* eslint-disable react/prop-types */
import React, { Component } from 'react';
import Input from './input.jsx';
import Icon from '@components/icon/icon.jsx';
import './inputs.scss';
export default class InputIcon extends Component {
constructor() {
super();
}
render() {
var parentClassName = '';
if (this.props.big) {
parentClassName += ' big ';
}
if (this.props.medium) {
parentClassName += ' medium ';
}
if (this.props.small) {
parentClassName += ' small ';
}
if (
parentClassName.indexOf('medium') === parentClassName.indexOf('small') &&
parentClassName.indexOf('big') === parentClassName.indexOf('small') &&
parentClassName.indexOf('big') < 0
) {
parentClassName += ' medium';
}
return (
<div className={'input_icon ' + parentClassName}>
<Icon type={this.props.icon} />
<Input
onEchap={this.props.onEchap}
onEnter={this.props.onEnter}
autoFocus={this.props.autoFocus}
onKeyDown={this.props.onKeyDown}
ref={this.refInput}
{...this.props}
/>
</div>
);
}
}
@@ -1,54 +0,0 @@
/* eslint-disable react/prop-types */
import React, { Component } from 'react';
import Languages from '@features/global/services/languages-service';
import './input-with-button.scss';
import Icon from '@components/icon/icon.jsx';
import Input from './input.jsx';
export default class InputWithButton extends Component {
/*
props = {
value : "",
disabled : true|false
}
*/
constructor(props) {
super(props);
this.props = props;
this.state = {
i18n: Languages,
};
Languages.addListener(this);
this.inputElement = false;
}
componentWillUnmount() {
Languages.removeListener(this);
}
render() {
return (
<div className="inputWithButton">
<Input
className="medium full_width"
refInput={obj => {
this.inputElement = obj;
this.props.refInput && this.props.refInput();
}}
{...this.props}
/>
{!this.props.hideBtn && (
<div
className={'button button-icon ' + (this.props.color ? this.props.color : '')}
onClick={() => this.props.btnAction()}
>
<Icon type={this.props.icon} />
</div>
)}
</div>
);
}
}
@@ -1,29 +0,0 @@
.inputWithButton {
display: flex;
position: relative;
input.input {
padding-right: 50px;
}
.button-icon {
position: absolute;
height: 40px;
right: 0px;
box-sizing: border-box;
padding: 10px;
border-radius: 0px 4px 4px 0px;
border-left: solid 1px var(--grey-light);
font-size: 16px;
cursor: pointer;
}
.danger {
background-color: var(--grey-background);
color: var(--grey-dark);
}
.danger:hover {
transition: 0.5s;
background-color: var(--grey-light);
color: var(--red);
}
}
@@ -1,129 +0,0 @@
/* eslint-disable react/prop-types */
import React, { Component } from 'react';
import MenusManager from '@components/menus/menus-manager.jsx';
import ColorPicker from 'components/color-picker/color-picker.jsx';
import Input from 'components/inputs/input.jsx';
import './inputs.scss';
export default class InputWithColor extends Component {
constructor() {
super();
}
outsideMenuListener() {
this.closeColorPicker();
}
componentWillUnmount() {
document.removeEventListener('click', this.outsideClickListener);
}
componentDidMount() {
if (this.props.focusOnDidMount && this.input) {
this.input.focus();
}
this.outsideClickListener = event => {
if (
this.colorPickerIsOpen &&
this.colorpicker_dom &&
!this.colorpicker_dom.contains(event.target) &&
document.contains(event.target)
) {
this.outsideMenuListener();
}
};
this.outsideClickListener = this.outsideClickListener.bind(this);
document.addEventListener('click', this.outsideClickListener);
}
closeColorPicker() {
if (!this.colorPickerIsOpen) {
return;
}
if (this.props.menu_level !== undefined) {
MenusManager.closeSubMenu(this.props.menu_level);
} else {
MenusManager.closeMenu();
}
this.colorPickerIsOpen = false;
}
openColorPicker() {
if (this.colorPickerIsOpen) {
return;
}
var menu = [
{
type: 'react-element',
reactElement: () => {
return (
<ColorPicker
refDom={node => (this.colorpicker_dom = node)}
value={this.props.value[0]}
onChange={color => this.selectColor(color)}
/>
);
},
},
];
var elementRect = window.getBoundingClientRect(this.color_dom);
elementRect.x = elementRect.x || elementRect.left;
elementRect.y = elementRect.y || elementRect.top;
if (this.props.menu_level !== undefined) {
MenusManager.openSubMenu(menu, elementRect, this.props.menu_level, 'bottom');
} else {
MenusManager.openMenu(menu, elementRect, 'bottom');
}
setTimeout(() => {
this.colorPickerIsOpen = true;
}, 200);
}
selectColor(color) {
this.closeColorPicker();
var value = [color, this.props.value[1]];
this.onChange(value);
}
onChange(value) {
if (this.props.onChange) {
this.props.onChange(value);
}
}
render() {
if (!this.props.value[0]) {
this.onChange([
ColorPicker.colors[parseInt(Math.random() * ColorPicker.colors.length)],
this.props.value[1],
]);
}
return (
<div className={'input_with_color ' + this.props.className}>
<div
className="color"
ref={node => (this.color_dom = node)}
onClick={() => {
this.openColorPicker();
}}
>
<div className="acolor" style={{ backgroundColor: this.props.value[0] }} />
</div>
<div className="right_input">
<Input
className="full_width medium"
refInput={obj => (this.input = obj)}
type="text"
placeholder={this.props.placeholder}
value={this.props.value[1]}
onKeyDown={e => {
if (e.keyCode == 13 && this.props.onEnter) {
this.props.onEnter();
}
}}
onChange={evt => {
if (this.onChange) this.onChange([this.props.value[0], evt.target.value]);
}}
/>
</div>
</div>
);
}
}
@@ -1,22 +0,0 @@
/* eslint-disable react/prop-types */
import React, { Component } from 'react';
import PlusIcon from '@material-ui/icons/AddOutlined';
export default class Rounded extends Component {
constructor() {
super();
}
render() {
return (
<div
style={this.props.style}
className={'rounded-btn ' + this.props.className}
onClick={this.props.onClick}
>
{this.props.text ? this.props.text + ' ' : ''}
<PlusIcon className="m-icon-small" />
</div>
);
}
}
@@ -1,84 +0,0 @@
import React from 'react';
import { Switch } from 'antd';
import './inputs.scss';
export default (props: {
big?: boolean;
medium?: boolean;
small?: boolean;
loading?: boolean;
className?: string;
label?: string;
checked?: boolean;
disabled?: boolean;
onChange: (value: boolean) => void;
}) => {
const renderSwitch = () => {
let className = props.className || '';
if (props.big) {
className += ' big ';
}
if (props.medium) {
className += ' medium ';
}
if (props.small) {
className += ' small ';
}
if (
className.indexOf('medium') === className.indexOf('small') &&
className.indexOf('big') === className.indexOf('small') &&
className.indexOf('big') < 0
) {
className += ' medium';
}
return (
<Switch
checked={props.checked}
onChange={(value: boolean) => {
props.onChange(value);
}}
className={className}
loading={props.loading}
disabled={props.disabled}
/>
);
};
let parentClassName = '';
if (props.big) {
parentClassName += ' big ';
}
if (props.medium) {
parentClassName += ' medium ';
}
if (props.small) {
parentClassName += ' small ';
}
if (
parentClassName.indexOf('medium') === parentClassName.indexOf('small') &&
parentClassName.indexOf('big') === parentClassName.indexOf('small') &&
parentClassName.indexOf('big') < 0
) {
parentClassName += ' medium';
}
if (props.label) {
return (
<div
className={
'switch_with_label ' + (props.disabled ? 'disabled' : '') + ' ' + parentClassName
}
>
{renderSwitch()}
<div className="label">{props.label}</div>
</div>
);
} else {
return renderSwitch();
}
};
@@ -1,100 +0,0 @@
import React, { Component } from 'react';
import './interactive-login-background.scss';
export default class InteractiveLoginBackground extends React.Component {
constructor(props) {
super();
this.lFollowX = 0;
this.lFollowY = 0;
this.x = 0;
this.y = 0;
this.friction = 1 / 30;
this.objects = [];
}
addObject() {
var div = document.createElement('div');
var size = Math.pow(0.1 + Math.random(), 2) * 10;
var left = Math.random() * 100 + 'vw';
div.style.width = size + 'px';
div.style.height = size + 'px';
var duration = 30 + Math.random() * 15;
var time_class = 'a';
if (duration > 40) {
time_class = 'b';
} else if (duration >= 35) {
time_class = 'c';
}
div.style.animationDelay = '-' + Math.random() * 45 + 's';
div.style.animationDuration = duration + 's';
div.style.left = left;
div.classList.add('animated_shape');
if (Math.random() > 0.5) {
div.classList.add('circle');
}
div.classList.add('speed-' + time_class);
this.background.appendChild(div);
this.objects.push(div);
}
mousemove(evt) {
this.lFollowX = evt.clientX / window.innerWidth;
this.lFollowY = evt.clientY / window.innerHeight;
}
moveBackground() {
if (!this.mounted) {
return;
}
this.x += (this.lFollowX - this.x) * this.friction;
this.y += (this.lFollowY - this.y) * this.friction;
this.objects.forEach(div => {
var fa, fb;
if (div.classList.contains('speed-a')) {
fa = 50;
fb = 15;
}
if (div.classList.contains('speed-b')) {
fa = 40;
fb = 10;
}
if (div.classList.contains('speed-c')) {
fa = 30;
fb = 5;
}
div.style.marginLeft = (this.x - 0.5) * fa;
div.style.marginTop = (this.y - 0.5) * fb;
});
window.requestAnimationFrame(this.moveBackground);
}
componentDidMount() {
for (var i = 0; i < 50; i++) {
this.addObject();
}
this.mousemove = this.mousemove.bind(this);
this.moveBackground = this.moveBackground.bind(this);
this.mounted = true;
this.moveBackground();
document.addEventListener('mousemove', this.mousemove);
}
componentWillUnmount() {
this.mounted = false;
document.removeEventListener('mousemove', this.mousemove);
}
render() {
return <div ref={node => (this.background = node)} className="animated_background" />;
}
}
@@ -1,42 +0,0 @@
.animated_background {
width: 100%;
height: 60vh;
position: relative;
background: var(--primary);
.animated_shape {
background: #fff;
top: calc(100% + 50px);
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-name: animated_background_shape_animation;
position: absolute;
border-radius: var(--border-radius-base);
}
.animated_shape.circle {
border-radius: 100%;
animation-name: animated_background_shape_animation_circle;
}
}
@keyframes animated_background_shape_animation {
0% {
transform: translateY(0);
opacity: 0.7;
}
100% {
transform: translateY(-100vh) rotate(600deg);
opacity: 0;
}
}
@keyframes animated_background_shape_animation_circle {
0% {
transform: translateY(0);
opacity: 0.7;
}
100% {
transform: translateY(-100vh);
opacity: 0;
}
}
@@ -1,21 +0,0 @@
.locked-history-banner {
.title-container {
display: flex;
align-items: center;
height: 24px;
margin: 16px 0 8px 0px;
.title {
line-height: 16px;
margin: 0;
margin-left: 8px;
font-size: 16px;
}
}
.description {
height: 30px;
text-align: center;
font-size: 12px;
}
}
@@ -1,46 +0,0 @@
import React from 'react';
import { Typography, Button } from 'antd';
import Banner from '@components/banner/banner';
import Emojione from '@components/emojione/emojione';
import Languages from '@features/global/services/languages-service';
import './locked-history-banner.scss';
import consoleService from '@features/console/services/console-service';
import useRouterCompany from '@features/router/hooks/use-router-company';
const { Title, Text } = Typography;
const LockedHistoryBanner = (): JSX.Element => {
const companyId = useRouterCompany();
const onClickBtn = () =>
window.open(consoleService.getCompanySubscriptionUrl(companyId), 'blank');
return (
<Banner
type="ghost"
height={135}
className="locked-history-banner"
contentColumnStyle={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
height: '100%',
width: '380px',
}}
>
<div className="title-container">
<Emojione type=":rocket:" s64 />
<Title level={5} className="title">
{Languages.t('components.locked_features.locked_history_banner.title')}
</Title>
</div>
<Text type="secondary" className="description">
{Languages.t('components.locked_features.locked_history_banner.description')}
</Text>
<Button type="primary" size="middle" onClick={onClickBtn} style={{ margin: '16px 0 16px 0' }}>
{Languages.t('components.locked_features.locked_history_banner.button')}
</Button>
</Banner>
);
};
export default LockedHistoryBanner;
@@ -1,72 +0,0 @@
import React from 'react';
import ObjectModal from '@components/object-modal/object-modal';
import { Button, Row, Typography } from 'antd';
import ModalManager from '@components/modal/modal-manager';
import Languages from '@features/global/services/languages-service';
import consoleService from '@features/console/services/console-service';
import useRouterCompany from '@features/router/hooks/use-router-company';
const { Title, Text } = Typography;
export default () => {
const companyId = useRouterCompany();
return (
<ObjectModal
titleCenter
titleLevel={2}
title={Languages.t('components.locked_features.locked_workspace_popup.title')}
titleTypographyStyle={{ textAlign: 'center', marginBottom: 32 }}
style={{ padding: 32 }}
contentStyle={{ display: 'flex', flexDirection: 'column', justifyContent: 'center' }}
footer={
<div
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginTop: 32 }}
>
<Button
type="primary"
size="large"
style={{ marginTop: 8 }}
onClick={() =>
window.open(consoleService.getCompanySubscriptionUrl(companyId), 'blank')
}
>
{Languages.t('components.locked_features.locked_guests_popup.learn_more_button')}
</Button>
<Text style={{ margin: '16px 0 ' }} strong>
{Languages.t('components.locked_features.locked_guests_popup.or')}
</Text>
<Button
type="ghost"
style={{ height: 36, width: 163 }}
onClick={() => ModalManager.closeAll()}
>
{Languages.t('components.locked_features.locked_guests_popup.skip_for_now_button')}
</Button>
</div>
}
hideFooterDivider
footerAlign="center"
footerStyle={{ marginBottom: 16 }}
>
<Row justify="center" align="middle" className="bottom-margin">
<Title
level={3}
children={Languages.t('components.locked_features.locked_workspace_popup.subtitle')}
style={{
textAlign: 'center',
margin: 0,
}}
/>
</Row>
<Row justify="center">
<Text
style={{
textAlign: 'center',
width: '404px',
}}
children={Languages.t('components.locked_features.locked_workspace_popup.text')}
/>
</Row>
</ObjectModal>
);
};
@@ -1,118 +0,0 @@
import React, { Component } from 'react';
import Icon from '@components/icon/icon.jsx';
import PerfectScrollbar from 'react-perfect-scrollbar';
import Tabs from 'components/tabs/tabs.jsx';
import './deprecated-object-modal.scss';
import { Divider } from 'antd';
export class ObjectModalTitle extends Component {
render() {
return (
<div className={'modal_title ' + this.props.className} style={this.props.style}>
{this.props.children}
</div>
);
}
}
//ObjectModalSeparator
export class ObjectModalSeparator extends Component {
render() {
return <div className="separator"></div>;
}
}
//ObjectModalSectionTitle
export class ObjectModalSectionTitle extends Component {
render() {
return (
<div
style={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: this.props.smallMargin ? '16px' : '',
}}
>
<b>{this.props.title || this.props.children}</b>
<div>{this.props.action}</div>
</div>
);
}
}
// ObjectModalFormTitle
export class ObjectModalFormTitle extends Component {
render() {
return (
<div className={'section_title ' + this.props.className} style={this.props.style}>
{this.props.icon && <Icon type={this.props.icon} className="m-icon-small" />}
<span>{this.props.name}</span>
</div>
);
}
}
export class ObjectModal extends Component {
constructor(props) {
super();
this.state = {};
}
render() {
return (
<div className={'object_modal ' + this.props.className}>
<div className="top_right_buttons">
{this.props.onEdit && (
<div className="square_button" onClick={this.props.onEdit}>
<Icon type="edit-alt" className="m-icon-small" />
</div>
)}
{this.props.onClose && (
<div className="square_button" onClick={this.props.onClose}>
<Icon type="times" className="m-icon-small" />
</div>
)}
</div>
<div className={!this.props.noCloseButton ? 'title' : ''}>{this.props.title}</div>
{this.props.tabs && (
<Tabs
tabs={this.props.tabs.map(item => {
return {
title: item.title || '',
render: (
<div className="body">
<PerfectScrollbar options={{ suppressScrollX: true }} component="div">
{item.render}
</PerfectScrollbar>
</div>
),
};
})}
/>
)}
{!this.props.tabs && (
<div className="body">
{!!this.props.noScrollBar && (
<div className="child-with-margin">{this.props.children}</div>
)}
{!this.props.noScrollBar && (
<PerfectScrollbar options={{ suppressScrollX: true }} component="div">
{this.props.children}
</PerfectScrollbar>
)}
</div>
)}
{this.props.footer && <div className="separator" style={{ marginTop: 0 }} />}
{this.props.footer && (
<div className="footer bottom-margin">
<Divider />
<div className="x-margin">{this.props.footer}</div>
</div>
)}
</div>
);
}
}
@@ -1,64 +0,0 @@
.object_modal {
position: relative;
display: flex;
flex-direction: column;
margin-bottom: 24px;
& > .title {
padding-right: 56px;
margin: 16px 0 11px 16px;
}
& > .top_right_buttons {
position: absolute;
top: 16px;
right: 16px;
left: 16px;
text-align: right;
.square_button {
vertical-align: top;
display: inline-block;
width: 24px;
height: 24px;
line-height: 24px;
border-radius: var(--border-radius-base);
margin-left: 8px;
background: var(--grey-background);
cursor: pointer;
&:hover {
background: var(--primary-background);
}
}
}
& > .body {
margin: 0 0;
.scrollbar-container,
.child-with-margin {
padding: 16px;
max-height: calc(80vh - 200px);
}
}
.modal_title {
font-size: 24px;
color: var(--black);
}
}
.section_title {
color: var(--grey-dark);
font-size: 14px;
font-weight: 500;
margin-bottom: 8px;
margin-top: 24px;
i {
margin-right: 4px;
&:before {
margin-left: 0px;
}
}
}
@@ -1,87 +0,0 @@
import React, { Component } from 'react';
import Number from '@features/global/utils/Numbers';
import AttributesManager from './attributes-manager.js';
import './parameters.scss';
import Languages from '@features/global/services/languages-service';
export default class Attribute extends React.Component {
constructor(props) {
super();
this.id = Number.unid();
this.state = {
parameters_attributes: AttributesManager,
i18n: Languages,
};
this.timeout = setTimeout('');
Languages.addListener(this);
AttributesManager.addListener(this);
}
componentDidMount() {
if (this.props.autoOpen) {
AttributesManager.toggle(this.id);
}
}
componentWillUnmount() {
clearTimeout(this.timeout);
Languages.removeListener(this);
AttributesManager.removeListener(this);
}
componentDidUpdate() {
var open = this.state.parameters_attributes.open == this.id;
if (open && !this.was_open) {
this.value_node.style.maxHeight = 'none';
this.value_height = window.getBoundingClientRect(this.value_node).height;
this.value_node.style.maxHeight = '0px';
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.value_node.style.maxHeight = this.value_height + 'px';
this.timeout = setTimeout(() => {
this.value_node.style.maxHeight = 'none';
this.value_height = window.getBoundingClientRect(this.value_node).height;
}, 200);
}, 50);
if (this.props.focusOnOpen) {
this.props.focusOnOpen.focus();
}
} else if (!open && this.was_open) {
clearTimeout(this.timeout);
this.value_node.style.maxHeight = this.value_height + 'px';
this.timeout = setTimeout(() => {
this.value_node.style.maxHeight = '0px';
}, 50);
}
this.was_open = open;
}
render() {
return (
<div
className={
'parameters_attribute ' + (this.state.parameters_attributes.open == this.id ? 'open' : '')
}
>
<div className="label" onClick={() => AttributesManager.toggle(this.id)}>
{!this.props.autoOpen && (
<a href="#" className="modify">
{this.state.parameters_attributes.open == this.id
? this.state.i18n.t('general.close')
: this.state.i18n.t('general.open')}
</a>
)}
<div className="label">{this.props.label}</div>
<div className="description">{this.props.description}</div>
</div>
<div ref={node => (this.value_node = node)} className="value">
{this.props.children}
</div>
</div>
);
}
}
@@ -0,0 +1,14 @@
import { ReactNode } from 'react';
import './parameters.scss';
export default (props: { label: string; description: string; children: ReactNode }) => {
return (
<div className={'parameters_attribute open block'}>
<div className="label">
<div className="label">{props.label}</div>
<div className="description">{props.description}</div>
</div>
<div className="value">{props.children}</div>
</div>
);
};
@@ -1,26 +0,0 @@
import React, { Component } from 'react';
import Observable from '@deprecated/CollectionsV1/observable.js';
/*
Menus manager service, choose where to generate menu
*/
class AttributesManager extends Observable {
constructor() {
super();
this.setObservableName('parameters_attributes');
this.open = '';
}
toggle(id) {
if (this.open == id) {
this.open = '';
} else {
this.open = id;
}
this.notify();
}
}
const service = new AttributesManager();
export default service;
@@ -47,7 +47,6 @@
& > .value {
font-size: 14px;
max-height: 0;
transition: max-height 0.2s, opacity 0.2s;
overflow: hidden;
opacity: 0;
@@ -1,262 +0,0 @@
/* eslint-disable react/jsx-key */
import React, { Component } from 'react';
import AutoComplete from 'components/auto-complete/auto-complete';
import Icon from '@components/icon/icon.jsx';
import Languages from '@features/global/services/languages-service';
import './picker.scss';
export default class Picker extends React.Component {
/*
placeholder : string,
search : Array of search function,
renderItem(item) : how to render item
renderItemChoosen(item) : how to add item in input
renderItemSimply(item) : what user will type on input and what we have to replace
onChangeSuggested(list)
onChange(list) :
canCreate
onCreate
disableNavigationKey :
*/
constructor(props) {
super(props);
this.state = {
currentSelected: props.value || [],
inputValue: '',
currentList: [],
selected: [],
};
this.alreadyBackspace = false;
}
componentWillUnmount() {
document.removeEventListener('click', this.outsideClickListener);
}
componentDidMount() {
if (!this.props.inline) {
this.focus();
}
var element = this.container;
this.outsideClickListener = event => {
if (!element.contains(event.target) && document.contains(event.target)) {
this.setState({ focused: false });
}
};
this.outsideClickListener = this.outsideClickListener.bind(this);
document.addEventListener('click', this.outsideClickListener);
}
UNSAFE_componentWillUpdate(nextProps, nextState) {
if (nextProps.value != this.props.value) {
nextState.currentSelected = nextProps.value;
}
return true;
}
onSelect(item) {
this.alreadyBackspace = true;
if (item && (item.id === undefined || item.id < 0) && item.front_id === undefined) {
this.onCreate(item.textSearched);
return;
}
var newList = this.state.currentSelected;
for (var i = 0; i < newList.length; i++) {
if (newList[i].id == item.id) {
this.setState({ inputValue: '' });
return false;
}
}
newList.push(item);
this.setState({ currentSelected: newList });
this.setState({ inputValue: '' });
if (this.props.onChange) {
this.props.onChange(newList);
}
this.focus();
}
onRemove(item) {
var newList = this.state.currentSelected;
var index = newList.indexOf(item);
if (index >= 0) {
newList.splice(index, 1);
this.setState({ currentSelected: newList });
if (this.props.onChange) {
this.props.onChange(newList);
}
}
this.focus();
}
onBackspace() {
if (this.alreadyBackspace) {
// if already typed on backspace once
var newList = this.state.currentSelected;
newList.splice(newList.length - 1, 1);
this.setState({ currentSelected: newList });
} else {
this.alreadyBackspace = true;
}
}
onCreate(text) {
if (this.props.onCreate) {
var item = this.props.onCreate(text, item => {
this.onSelect(item);
});
}
}
focus() {
if (this.autocomplete_node) {
this.autocomplete_node.focus();
}
}
render() {
var that = this;
var objects = [
<div className="picker">
<div className={'pickerInput ' + (this.props.readOnly ? 'readOnly ' : '')}>
{this.state.currentSelected.map(item => {
return this.props.renderItemChoosen(item);
})}
{!this.props.readOnly && (
<AutoComplete
search={[
(text, cb) => {
text = this.state.inputValue;
this.props.search(text, list => {
if (that.props.canCreate && text.length > 0) {
list.push({ id: -1, autocomplete_id: list.length, textSearched: text });
}
cb(list);
});
},
]}
renderItemChoosen={[
item => {
if (item.id < 0 && this.props.canCreate) {
return item.textSearched;
} else {
return this.props.renderItemSimply(item);
}
},
]}
max={[this.props.max || 10]}
renderItem={[() => {}]}
regexHooked={[/(.*)/]}
placeholder={
this.state.currentSelected.length
? ''
: this.props.placeholder ||
Languages.t('scenes.apps.drive.left.search', [], 'Search')
}
onChange={evt => {
this.alreadyBackspace = false;
this.setState({ inputValue: evt.target.value });
}}
onSelect={item => {
this.onSelect(item);
}}
value={this.state.inputValue}
onChangeCurrentList={(list, selected) => {
this.setState({ currentList: list, selected: selected });
if (this.props.onChangeSuggested) {
this.props.onChangeSuggested(list);
}
}}
hideResult={true}
onBackspace={() => {
this.onBackspace();
}}
disableNavigationKey={this.props.disableNavigationKey}
showResultsOnInit={true}
onHide={() => this.setState({ focused: false })}
ref={node => {
this.autocomplete_node = node;
}}
/>
)}
</div>
</div>,
];
if (!this.props.readOnly) {
if (!this.props.inline) {
objects.push(<div className="menu-text">{!!this.props.title && this.props.title}</div>);
}
var results = [];
if (this.state.currentList.length > 0) {
results = this.state.currentList.map((item, index) => {
if (item.id < 0) {
return (
<div
key={'create'}
className={
'menu ' +
(!this.props.disableNavigationKey && item.autocomplete_id == this.state.selected
? 'is_selected'
: '')
}
onClick={evt => {
evt.stopPropagation();
evt.preventDefault();
this.onSelect(item);
}}
>
<div className="text">
<Icon type="plus" />
Create {this.props.renderItem(item.textSearched)}
</div>
</div>
);
} else {
return (
<div
key={item.id}
className={
'menu ' +
(!this.props.disableNavigationKey && item.autocomplete_id == this.state.selected
? 'is_selected'
: '')
}
onClick={evt => {
evt.stopPropagation();
evt.preventDefault();
this.onSelect(item);
}}
>
<div className="text">{this.props.renderItem(item)}</div>
</div>
);
}
});
}
if (this.props.inline) {
results = (
<div className={'dropmenu inline ' + (this.state.focused ? 'fade_in ' : '')}>
{results}
</div>
);
}
objects = objects.concat(results);
}
objects = (
<div
onClick={() => {
this.focus();
this.setState({ focused: true });
}}
ref={node => (this.container = node)}
className={
'picker_container ' +
(this.props.readOnly ? 'readOnly ' : '') +
(this.props.inline ? 'inline ' : '') +
(this.state.focused ? 'focused ' : '') +
this.props.className
}
>
{objects}
</div>
);
return objects;
}
}
@@ -1,55 +0,0 @@
.picker_container {
position: relative;
border-radius: var(--border-radius-large);
&.inline {
transition: box-shadow 0.2s;
.dropmenu {
opacity: 0;
transition: opacity 0.2s;
pointer-events: none;
}
&.focused {
&.readOnly {
box-shadow: none !important;
}
.autocomplete {
box-shadow: none !important;
}
.dropmenu {
opacity: 1;
pointer-events: all;
}
}
}
}
.picker {
.pickerInput {
border-radius: var(--border-radius-base);
display: flex;
flex-wrap: wrap;
position: relative;
background: #f2f2f2;
&.readOnly {
margin: 0;
display: inline;
padding: 0;
background: transparent;
}
.autocomplete {
flex: 1;
min-width: 50px;
position: unset;
input {
background: inherit;
margin-top: 0px;
margin-bottom: 0px;
}
}
}
}
@@ -1,199 +0,0 @@
/* eslint-disable react/no-direct-mutation-state */
import React, { Component } from 'react';
import Button from 'components/buttons/button.jsx';
import Input from 'components/inputs/input.jsx';
import Icon from '@components/icon/icon.jsx';
import Select from 'components/select/select.jsx';
import './reminder-selector.scss';
import Languages from '@features/global/services/languages-service';
export default class ReminderSelector extends React.Component {
constructor(props) {
super(props);
this.state = {
reminders: [],
};
this.old_value = JSON.stringify(props.reminders || []);
}
componentDidMount() {
this.updateFromProps(this.props);
}
shouldComponentUpdate(nextProps, nextState) {
if (this.old_value != JSON.stringify(nextProps.reminders)) {
this.old_value = JSON.stringify(nextProps.reminders);
this.updateFromProps(nextProps);
}
return true;
}
remove(i) {
delete this.state.reminders[i];
this.update();
}
add() {
this.state.reminders.push({
mode: 'push',
delay_unit: 'minutes',
delay_value: '30',
});
this.update();
}
update() {
var ret = [];
this.state.reminders.forEach(item => {
if (item.delay_unit == 'minutes' && item.delay_value > 59) {
item.delay_value = 30;
}
if (item.delay_unit == 'hours' && item.delay_value > 23) {
item.delay_value = 1;
}
if (item.delay_unit == 'days' && item.delay_value > 13) {
item.delay_value = 1;
}
if (item.delay_unit == 'weeks' && item.delay_value < 2) {
item.delay_value = 2;
}
ret.push({
mode: item.mode,
delay:
parseInt(item.delay_value) *
60 *
(item.delay_unit == 'hours' ? 60 : 1) *
(item.delay_unit == 'days' ? 60 * 24 : 1) *
(item.delay_unit == 'weeks' ? 60 * 24 * 7 : 1),
});
});
if (this.props.onChange) {
this.props.onChange(ret);
}
this.setState({});
}
updateFromProps(nextProps) {
var props = nextProps || this.props;
var reminders = [];
(props.reminders || []).forEach(item => {
var delay_value = parseInt(item.delay / 60);
var delay_unit = 'minutes';
if (item.delay > 59 * 60) {
delay_value = parseInt(item.delay / (60 * 60));
delay_unit = 'hours';
}
if (item.delay > 23 * 60 * 60) {
delay_value = parseInt(item.delay / (60 * 60 * 24));
delay_unit = 'days';
}
if (item.delay > 13 * 24 * 60 * 60) {
delay_value = parseInt(item.delay / (60 * 60 * 24 * 7));
delay_unit = 'weeks';
}
reminders.push({
mode: item.mode,
delay_value: delay_value,
delay_unit: delay_unit,
});
});
this.setState({ reminders: reminders });
}
render() {
return (
<div className="reminderSelector">
{this.state.reminders.map((reminder, i) => {
return (
<div className="bottom-margin" key={`reminder-${i}`}>
<Select
className="small small-right-margin"
value={reminder['mode'] || 'push'}
style={{ width: 'auto' }}
onChange={value => {
this.state.reminders[i].mode = value;
this.update();
}}
options={[
{
text: Languages.t('components.reminder.notification', [], 'Notification'),
value: 'push',
},
{
text: Languages.t('components.reminder.by_email', [], 'E-Mail'),
value: 'mail',
},
]}
/>
<Input
className="small small-right-margin"
type="number"
min="0"
max="60"
step="1"
value={reminder['delay_value'] || '0'}
onChange={evt => {
this.state.reminders[i].delay_value = evt.target.value;
if (!evt.target.value) {
return;
}
this.update();
}}
style={{ width: 60 }}
/>
<Select
className="small small-right-margin"
value={reminder['delay_unit'] || 'minutes'}
style={{ width: 'auto', marginLeft: 10 }}
onChange={value => {
this.state.reminders[i].delay_unit = value;
this.update();
}}
options={[
{
text: Languages.t('components.reminder.minutes_bef', [], 'minutes avant'),
value: 'minutes',
},
{
text: Languages.t('components.reminder.hours_bef', [], 'heures avant'),
value: 'hours',
},
{
text: Languages.t('components.reminder.days_bef', [], 'jours avant'),
value: 'days',
},
{
text: Languages.t('components.reminder.weeks_bef', [], 'semaines avant'),
value: 'weeks',
},
]}
/>
<Icon
type="trash"
className="remove_icon"
style={{ verticalAlign: 'middle' }}
onClick={() => this.remove(i)}
/>
</div>
);
})}
<Button small className="button secondary-text" onClick={() => this.add()}>
<Icon type="plus" className="m-icon-small" />{' '}
{Languages.t('scenes.apps.calendar.modals.reminder_add', [], 'Ajouter un rappel')}
</Button>
</div>
);
}
}
@@ -1,8 +0,0 @@
.reminderSelector {
.remove_icon {
cursor: pointer;
&:hover {
color: var(--red);
}
}
}
@@ -1,98 +0,0 @@
rm .table {
overflow: hidden;
border-radius: var(--border-radius-base);
margin-bottom: 16px;
border: 1px solid var(--grey-background);
font-weight: 500;
&.unfocused {
box-shadow: none;
}
&.loading {
opacity: 0.5;
.tr .item .line {
pointer-events: none;
border-radius: var(--border-radius-large);
height: 16px;
margin-top: 8px;
width: 200px;
animation-duration: 1s;
animation-fill-mode: forwards;
animation-iteration-count: infinite;
animation-name: placeHolderShimmer;
animation-timing-function: linear;
background-repeat: repeat !important;
background-image: linear-gradient(
to right,
var(--grey-light) 0px,
#f2f2f2 120px,
var(--grey-light) 234px
) !important;
& > * {
opacity: 0 !important;
}
}
}
.headerTable {
background: var(--grey-background);
font-weight: 700;
font-size: 14px;
display: flex;
padding: 8px;
border-bottom: 1px solid var(--grey-background);
}
.footerTable {
text-align: center;
line-height: 32px;
font-size: 14px;
padding: 5px 10px;
border-top: 1px solid var(--grey-background);
border-bottom: 1px solid var(--grey-background);
}
.contentTable {
.tr {
display: flex;
border-bottom: 1px solid var(--grey-background);
padding: 8px;
font-size: 12px;
&:hover {
background: var(--grey-background);
}
&:last-child {
border-bottom: none;
}
& > div {
position: relative;
min-height: 32px;
& > .absolute_position {
width: 100%;
position: absolute;
height: 100%;
display: flex;
}
}
}
.fix_text_padding_medium {
padding: 8px 0;
display: inline-block;
line-height: 18px;
}
.text-complete-width {
overflow: hidden;
flex: 1;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
@@ -1,243 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable jsx-a11y/anchor-is-valid */
import React, { Component } from 'react';
import Languages from '@features/global/services/languages-service';
import Button from 'components/buttons/button.jsx';
import InputIcon from 'components/inputs/input-icon.jsx';
import './table.scss';
type Props = {
column?: any;
noHeader: boolean;
onAdd: () => void;
onRequestMore: (refresh: boolean) => Promise<any[]>;
onSearch: (query: string, maxResults: number, callback: (res: any[]) => void) => Promise<any[]>;
updatedData: (data: any) => any;
addText?: string;
resultsPerPage?: number;
unFocused: boolean;
};
type State = {
data: null | any[];
searchResults: any[];
loading: boolean;
has_more: boolean;
page: number;
searching: boolean;
};
export default class Table extends Component<Props, State> {
searchFieldValue = '';
searchRunning = false;
searchRunningTimeout: number | null = null;
constructor(props: Props) {
super(props);
this.state = {
data: null,
searchResults: [],
loading: true,
has_more: true,
page: 0,
searching: false,
};
Languages.addListener(this);
}
componentWillUnmount() {
Languages.removeListener(this);
}
componentDidMount() {
if (this.state.data === null) {
this.requestMore(true);
}
}
search() {
if (this.searchFieldValue?.length <= 2) {
return false;
}
if (this.searchRunning) {
clearTimeout(this.searchRunningTimeout as number);
this.searchRunningTimeout = window.setTimeout(() => {
this.search();
}, 1000);
return;
}
this.setState({
loading: true,
});
this.searchRunning = true;
this.props.onSearch(this.searchFieldValue, 10, (data: any[]) => {
this.searchRunning = false;
this.setState({
loading: false,
searchResults: data,
});
});
}
renderItem(col: any, item: any) {
if (col.render) {
return col.render(item);
} else {
return <div className="">{col.dataIndex ? item[col.dataIndex] : ''}</div>;
}
}
requestMore(refresh = false) {
this.setState({ loading: true });
this.props.onRequestMore(refresh).then(res => {
const hasMore = res.length !== (this.state.data || []).length;
this.setState({ loading: false, data: res, has_more: hasMore });
});
}
setPosition() {
if (!!this.props.onSearch && !this.props.onAdd) return 'flex-end';
else return 'space-between';
}
nextPage() {
this.setState({ page: this.state.page + 1 });
if (this.getPageData().length < (this.state.page + 1) * this.getResultsPerPage()) {
this.requestMore();
}
}
getPageData() {
const from = this.state.page * this.getResultsPerPage();
return (this.state.data || []).slice(0, from + this.getResultsPerPage());
}
getResultsPerPage(): number {
return this.props.resultsPerPage || 50;
}
render() {
let page_data = this.state.searching ? this.state.searchResults : this.getPageData();
page_data = page_data
.map(data => {
data = this.props.updatedData ? this.props.updatedData(data) : data;
if (!data) {
return false;
}
return data;
})
.filter(i => i);
return (
<div>
<div
className="small-y-margin full-width"
style={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: this.setPosition(),
}}
>
{this.props.onAdd && (
<Button
className="medium"
value={this.props.addText || 'Add'}
onClick={this.props.onAdd}
/>
)}
{!!this.props.onSearch && (
<div>
<InputIcon
icon="search"
small
placeholder={Languages.t('components.listmanager.filter', [], 'Search')}
onChange={(event: any) => {
const q = event.target.value;
this.searchFieldValue = q;
this.setState({ searching: q.length > 0 });
this.search();
}}
/>
</div>
)}
</div>
<div
className={
'table ' +
(this.props.unFocused ? 'unfocused ' : '') +
(this.state.loading ? 'loading ' : '')
}
>
{!this.props.noHeader && (
<div className="headerTable">
{this.props.column.map((col: any, i: number) => {
return (
<div
key={i}
className="headerItem"
style={{
width: col.width || 'inherit',
flex: col.width ? 'inherit' : '1',
textAlign: (col.titleStyle || {}).textAlign,
}}
>
{col.title}
</div>
);
})}
</div>
)}
<div className="contentTable">
{(page_data || []).length === 0 && !this.state.loading && (
<div className="tr" style={{ justifyContent: 'center', display: 'flex' }}>
<div className="item" style={{ lineHeight: '32px' }}>
{Languages.t('components.user_picker.modal_no_result')}
</div>
</div>
)}
{(page_data || []).length === 0 &&
!!this.state.loading &&
Array(this.state.searching ? 1 : this.getResultsPerPage()).map((_, i: number) => {
return (
<div key={i} className="tr">
<div className="item">
<div className="line"></div>
</div>
</div>
);
})}
{(page_data || []).map((data: any, i: number) => {
return (
<div key={i} className="tr">
{this.props.column.map((col: any, j: number) => {
return (
<div
key={j}
className="item"
style={{ width: col.width || 'inherit', flex: col.width ? 'inherit' : '1' }}
>
{this.renderItem(col, data)}
</div>
);
})}
</div>
);
})}
</div>
{!this.state.searching &&
!!this.state.has_more &&
page_data.length >= this.getResultsPerPage() && (
<div className="footerTable">
<a href="#" onClick={() => this.nextPage()}>
{Languages.t('components.searchpopup.load_more', [], 'Load more results')}
</a>
</div>
)}
</div>
</div>
);
}
}
@@ -1,69 +0,0 @@
/* eslint-disable react/jsx-key */
import React, { Component } from 'react';
import Languages from '@features/global/services/languages-service';
import PerfectScrollbar from 'react-perfect-scrollbar';
import './tabs.scss';
export default class Tabs extends Component {
constructor(props) {
super(props);
this.state = {
i18n: Languages,
tab: props.selected,
};
Languages.addListener(this);
}
componentWillUnmount() {
Languages.removeListener(this);
}
select(i) {
this.setState({ tab: i });
this.props.onChange && this.props.onChange(i);
}
UNSAFE_componentWillUpdate(nextProps, nextState) {
if (nextProps.selected && JSON.stringify(nextProps.selected) != this.oldSelected) {
nextState.tab = nextProps.selected;
this.oldSelected = JSON.stringify(nextProps.selected);
}
}
render() {
var selected = this.state.tab || Object.keys(this.props.tabs || {})[0];
if (!this.props.tabs[selected]) {
selected = Object.keys(this.props.tabs || {})[0];
}
return (
<div className={'component_tabs ' + (this.props.fullBody ? 'full-body ' : '')}>
<PerfectScrollbar
options={{ suppressScrollY: true }}
component="div"
className="component_tabs_tabs"
>
{Object.keys(this.props.tabs || {}).map(i => {
var item = this.props.tabs[i];
return (
<div
style={item.titleStyle || {}}
className={
'component_tabs_tab ' +
(i == selected ? 'selected ' : '') +
(item.titleClassName || '') +
' '
}
onClick={() => this.select(i)}
>
{typeof item.title == 'function' ? item.title() : item.title}
</div>
);
})}
</PerfectScrollbar>
<div className="body ">
{typeof this.props.tabs[selected].render == 'function'
? this.props.tabs[selected].render()
: this.props.tabs[selected].render}
</div>
</div>
);
}
}
@@ -1,58 +0,0 @@
.component_tabs {
position: relative;
overflow: hidden;
&::before {
content: '';
position: relative;
right: 0px;
left: auto;
top: 0px;
height: 100%;
width: 50px;
z-index: 1;
pointer-events: none;
background: linear-gradient(to right, #ffffff00 0%, #ffffffff 100%);
}
& > .component_tabs_tabs {
border-bottom: 1px solid var(--grey-background);
padding: 0 16px;
white-space: nowrap;
max-width: 100%;
box-sizing: border-box;
.component_tabs_tab {
height: 40px;
box-sizing: border-box;
border-radius: 0;
display: inline-block;
padding: 8px 16px;
font-weight: 500;
color: var(--grey-dark);
font-size: 14px;
cursor: pointer;
&:hover {
background: transparent;
color: var(--black);
}
&.selected {
background: transparent;
border-bottom: 4px solid var(--primary);
color: var(--black);
}
}
}
& > .body {
padding: 16px;
}
&.full-body {
& > .body,
& > .component_tabs_tabs {
padding: 0px;
}
}
}
@@ -1,196 +0,0 @@
/* eslint-disable react/jsx-key */
/* eslint-disable react/no-direct-mutation-state */
import React, { Component } from 'react';
import Icon from '@components/icon/icon.jsx';
import Button from 'components/buttons/button.jsx';
import './tag-picker.scss';
import MenusManager from '@components/menus/menus-manager.jsx';
import TagSelector from './tag-selector.jsx';
import Workspaces from '@deprecated/workspaces/workspaces.jsx';
import Collections from '@deprecated/CollectionsV1/Collections/Collections.js';
import Languages from '@features/global/services/languages-service';
export default class TagPicker extends React.Component {
constructor(props) {
super();
this.props = props;
this.state = {
currentSelected: [],
inputValue: '',
currentList: [],
selected: [],
value: this.props.value || [],
list: props.data || [
{ id: 1, name: Languages.t('scenes.apps.tasks.task_status.todo', [], 'To do') },
{ id: 2, name: Languages.t('scenes.apps.tasks.task_status.current', [], 'Doing') },
{ id: 3, name: Languages.t('scenes.apps.tasks.task_status.done', [], 'Done') },
],
};
}
UNSAFE_componentWillUpdate(nextProps, nextState) {
if (JSON.stringify(nextProps.value) != JSON.stringify(this.props.value)) {
this.state.value = nextProps.value;
}
}
UNSAFE_componentWillMount() {
if (!this.props.data) {
Collections.get('tags').addListener(this);
Collections.get('tags').addSource(
{
http_base_url: 'globalsearch/tags',
http_options: {
workspace_id: Workspaces.currentWorkspaceId,
},
websockets: [{ uri: 'tags/' + Workspaces.currentWorkspaceId, options: { type: 'tags' } }],
},
Workspaces.currentWorkspaceId,
);
}
}
componentWillUnmount() {
if (!this.props.data) {
Collections.get('tags').removeListener(this);
}
}
addTags(evt) {
var menu = [
{
type: 'react-element',
reactElement: level => {
return (
<TagSelector
level={level}
data={
this.props.data
? this.props.data.map(a => {
if (typeof a == 'string') {
return { name: a, id: a, color: '#888' };
} else {
return a;
}
})
: false
}
canCreate={this.props.canCreate}
disabledTags={
this.state.value.map(item => {
return typeof item == 'string' ? item : item.id;
}) || []
}
onChange={value => {
value.forEach(tag => {
if (
this.state.value
.map(item => {
return typeof item == 'string' ? item : item.id;
})
.indexOf(tag.id || tag.name || tag) < 0
) {
this.state.value.push(tag.id || tag.name || tag);
}
});
if (!this.props.saveButton) this.props.onChange(this.state.value);
this.setState({});
if (this.props.menu_level !== undefined) {
MenusManager.closeSubMenu(this.props.menu_level);
} else {
MenusManager.closeMenu();
}
}}
/>
);
},
},
];
if (this.props.menu_level !== undefined) {
MenusManager.openSubMenu(
menu,
{ x: evt.clientX, y: evt.clientY },
this.props.menu_level,
'right',
);
} else {
MenusManager.openMenu(menu, { x: evt.clientX, y: evt.clientY }, 'right');
}
}
render() {
var value = this.state.value || [];
var tag_list = value.map(tag_id => {
var tag = false;
if (this.props.data) {
if (typeof tag_id == 'string') {
tag = { name: tag_id, id: tag_id, color: '#888' };
} else {
tag = tag_id;
tag.id = tag.id || tag.name;
tag.color = tag.color || '#888';
}
} else {
tag = Collections.get('tags').find(tag_id);
}
if (!tag) {
return '';
}
var name = tag.name;
return (
<div
className={
'tag ' + (this.props.inline ? 'inline-tag ' + (this.props.className || '') : '')
}
style={{ backgroundColor: tag.color }}
>
{name}{' '}
{!this.props.readOnly && (
<Icon
className="remove"
type="times"
onClick={() => {
this.state.value.splice(this.state.value.indexOf(tag.id), 1);
if (!this.props.saveButton) this.props.onChange(this.state.value);
this.setState({});
}}
/>
)}
</div>
);
});
if (this.props.inline) {
return tag_list;
}
return (
<div className={'tagPicker ' + (this.props.className || '')}>
{!this.props.readOnly && value.length == 0 && (
<div className="tag notag">{Languages.t('components.tagpicker.notag', [], 'No tag')}</div>
)}
{tag_list}
{!this.props.readOnly && (
<Button
className="small secondary-text"
onClick={evt => {
this.addTags(evt);
}}
>
<Icon type="plus" className="m-icon-small" /> {Languages.t('general.add', [], 'Add')}
</Button>
)}
{this.props.saveButton && (
<div className="full_width" style={{ textAlign: 'right' }}>
<Button
className="small primary"
value={Languages.t('general.save')}
onClick={() => {
this.props.onChange(this.state.value);
}}
/>
</div>
)}
</div>
);
}
}
@@ -1,104 +0,0 @@
.tagPicker .tag,
.inline-tag {
height: 22px;
border-radius: var(--border-radius-base);
background: var(--grey-dark);
color: #fff;
line-height: 22px;
vertical-align: top;
margin-bottom: 8px;
margin-right: 8px;
font-size: 12px;
font-weight: bold;
padding: 0 4px;
padding-left: 6px;
&.notag {
background: var(--grey-background);
color: var(--grey-dark);
}
.remove {
opacity: 0.5;
cursor: pointer;
&:hover {
opacity: 1;
}
}
.remove:before {
margin: 0px;
}
&.disabled {
opacity: 0.5;
}
}
.tagPicker {
.tag_selectable {
display: flex;
.tag_part {
flex: 1;
}
.edit {
}
}
.secondary-text {
height: 22px;
line-height: 22px;
}
&.disabled {
opacity: 0.5;
pointer-events: none;
}
.pickerInput {
padding-left: 5px;
&.readOnly {
padding-left: 0px;
}
}
.itemSelected {
display: inline-block;
.close {
font-size: 8px;
margin-left: 5px;
opacity: 0.4;
display: inline-block;
margin-right: -1px;
bottom: 0px;
position: relative;
}
.close:hover {
opacity: 0.8;
cursor: pointer;
}
}
.itemContoured {
padding: 1px 6px;
display: inline-block;
background-color: var(--primary);
box-sizing: border-box;
color: #fff;
border-radius: var(--border-radius-base);
margin: 5px 0px;
margin-right: 5px;
height: 18px;
line-height: 16px;
font-size: 12px;
}
.menu {
.itemContoured {
margin: 3px 0;
}
}
}
.inline-tag {
display: inline-block;
margin-bottom: 0px;
vertical-align: middle;
}
@@ -1,220 +0,0 @@
/* eslint-disable react/no-direct-mutation-state */
import React, { Component } from 'react';
import Picker from 'components/picker/picker.jsx';
import Button from 'components/buttons/button.jsx';
import Workspaces from '@deprecated/workspaces/workspaces.jsx';
import Collections from '@deprecated/CollectionsV1/Collections/Collections.js';
import Icon from '@components/icon/icon.jsx';
import ColorPicker from 'components/color-picker/color-picker.jsx';
import Strings from '@features/global/utils/strings';
import InputWithColor from 'components/inputs/input-with-color.jsx';
import AlertManager from '@features/global/services/alert-manager-service';
import Languages from '@features/global/services/languages-service';
import MenusManager from '@components/menus/menus-manager.jsx';
class TagEditor extends React.Component {
constructor(props) {
super();
this.props = props;
this.state = {
edited_tag_name: '',
edited_tag_color: '',
};
}
UNSAFE_componentWillMount() {
var tag = this.props.tag;
this.state.edited_tag_color = tag.color;
this.state.edited_tag_name = tag.name;
}
saveTag(tag) {
tag.color = this.state.edited_tag_color || tag.color;
tag.name = this.state.edited_tag_name || tag.name;
Collections.get('tags').save(tag, Workspaces.currentWorkspaceId);
MenusManager.closeSubMenu(this.props.level - 1);
}
render() {
var tag = this.props.tag;
return (
<div>
<InputWithColor
menu_level={this.props.level}
className="medium bottom-margin full_width"
focusOnDidMount
placeholder={Languages.t('components.tagpicker.tag_name', [], 'Tag name')}
value={[this.state.edited_tag_color, this.state.edited_tag_name]}
onEnter={() => {
this.saveTag(tag);
}}
onChange={value => {
this.setState({ edited_tag_color: value[0], edited_tag_name: value[1] });
}}
/>
<div style={{ textAlign: 'right' }}>
<Button
className="small"
onClick={() => {
this.saveTag(tag);
}}
value={Languages.t('general.save')}
/>
</div>
</div>
);
}
}
export default class TagSelector extends React.Component {
constructor(props) {
super();
this.props = props;
this.state = {
edited_tag_name: '',
edited_tag_color: '',
};
this.colors_random_list = ColorPicker.colors.map(a => a).sort(() => Math.random() - 0.5);
this.next_new_color = 0;
}
onUpdate(item) {}
onCreate(text, cb) {
var color = this.colors_random_list[this.next_new_color];
this.next_new_color = (this.next_new_color + 1) % this.colors_random_list.length;
var new_tag = Collections.get('tags').edit();
new_tag.name = text;
new_tag.color = color;
new_tag.workspace_id = Workspaces.currentWorkspaceId;
Collections.get('tags').save(new_tag, Workspaces.currentWorkspaceId, tag => {
cb(new_tag);
});
return new_tag;
}
onRemove(item, ev) {}
renderItemChoosen(item) {
return '';
}
editTag(evt, id) {
var tag = Collections.get('tags').find(id);
if (!tag) {
return;
}
var menu = [
{ type: 'title', text: Languages.t('general.edit') },
{
type: 'react-element',
reactElement: level => {
return <TagEditor tag={tag} parent={this} level={level} />;
},
},
{ type: 'separator' },
{
text: Languages.t('general.remove'),
className: 'error',
icon: 'trash',
onClick: ev => {
var tag = Collections.get('tags').find(id);
if (tag) {
AlertManager.confirm(() => {
MenusManager.closeSubMenu(this.props.level - 1);
Collections.get('tags').remove(tag, Workspaces.currentWorkspaceId);
});
}
return false;
},
},
];
MenusManager.openSubMenu(menu, { x: evt.clientX, y: evt.clientY }, this.props.level, 'right');
}
renderItem(item) {
var add_option = false;
if (typeof item == 'string') {
add_option = true;
item = { name: item, color: this.colors_random_list[this.next_new_color] };
}
var tag = (
<div
className={
'tag ' + (this.props.disabledTags.indexOf(item.id || item.name) >= 0 ? 'disabled' : '')
}
style={{ backgroundColor: item.color, margin: '5px 0' }}
>
{item.name}
</div>
);
if (add_option) {
return tag;
}
return (
<div className="tag_selectable">
<div className="tag_part">{tag}</div>
{!add_option && !this.props.data && (
<div
className="edit"
onClick={ev => {
ev.stopPropagation();
ev.preventDefault();
this.editTag(ev, item.id);
}}
>
<Icon type="ellipsis-h" />
</div>
)}
</div>
);
}
search(text, cb) {
var res = Collections.get('tags').findBy({ workspace_id: Workspaces.currentWorkspaceId });
if (this.props.data) {
res = this.props.data;
}
res = res
.filter(function (item) {
if (
Strings.removeAccents(item.name.toLowerCase().replace(/ +/, '')).indexOf(
Strings.removeAccents(text.toLowerCase().replace(/ +/, '')),
) !== -1
) {
return true;
}
return false;
})
.sort((a, b) => {
return this.props.disabledTags.indexOf(a.id) >= 0 ? -1 : 1;
});
cb(res);
}
render() {
return (
<Picker
className={'tagPicker ' + (this.props.disabled ? 'disabled ' : '')}
ref={picker => {
this.picker = picker;
}}
title={false}
search={(text, cb) => {
this.search(text, cb);
}}
renderItem={item => {
return this.renderItem(item);
}}
renderItemChoosen={item => {
return this.renderItemChoosen(item);
}}
renderItemSimply={item => {
return item.name;
}}
canCreate={this.props.canCreate !== false}
onCreate={(text, cb) => this.onCreate(text, cb)}
onSelect={item => this.onSelect(item)}
onChange={this.props.onChange}
value={this.props.value}
readOnly={this.props.readOnly}
inline={this.props.inline}
/>
);
}
}
@@ -1,255 +0,0 @@
/* eslint-disable react/jsx-key */
import React, { Component } from 'react';
import Workspaces from '@deprecated/workspaces/workspaces.jsx';
import Button from 'components/buttons/button.jsx';
import Languages from '@features/global/services/languages-service';
import Collections from '@deprecated/CollectionsV1/Collections/Collections.js';
import Emojione from 'components/emojione/emojione';
import Loader from 'components/loader/loader.jsx';
import TasksService from '@deprecated/Apps/Tasks/Tasks.js';
import LeftIcon from '@material-ui/icons/KeyboardArrowLeftOutlined';
import './task-picker.scss';
export default class TaskPicker extends Component {
/*
props : {
mode : "select_board" / "select_task"
}
*/
constructor(props) {
super(props);
this.tasks_collection_key = 'tasks_picker_' + Workspaces.currentWorkspaceId;
this.collection_key = [];
this.state = {
taskRepository: Collections.get('task'),
currentBoard: null,
currentList: null,
taskSelected: null,
boardRepository: Collections.get('boards'),
};
Languages.addListener(this);
Collections.get('boards').addListener(this);
Collections.get('lists').addListener(this);
Collections.get('tasks').addListener(this);
Collections.get('boards').addSource(
{
http_base_url: 'tasks/board',
http_options: {
workspace_id: Workspaces.currentWorkspaceId,
},
websockets: [
{ uri: 'boards/' + Workspaces.currentWorkspaceId, options: { type: 'board' } },
],
},
this.tasks_collection_key,
);
}
componentWillUnmount() {
Languages.removeListener(this);
Collections.get('boards').removeSource(this.tasks_collection_key);
Collections.get('lists').removeSource(this.tasks_collection_key);
Collections.get('tasks').removeSource(this.tasks_collection_key);
Collections.get('boards').removeListener();
Collections.get('lists').removeListener();
Collections.get('tasks').removeListener();
}
selectBoard(board) {
if (this.collection_key.indexOf(this.tasks_collection_key + '_' + board.id) < 0) {
this.collection_key.push(this.tasks_collection_key + '_' + board.id);
Collections.get('lists').addSource(
{
http_base_url: 'tasks/list',
http_options: {
board_id: board.id,
},
websockets: [{ uri: 'board_lists/' + board.id, options: { type: 'list' } }],
},
this.tasks_collection_key + '_' + board.id,
);
Collections.get('tasks').addSource(
{
http_base_url: 'tasks/task',
http_options: {
board_id: board.id,
},
websockets: [{ uri: 'board_tasks/' + board.id, options: { type: 'task' } }],
},
this.tasks_collection_key + '_' + board.id + '_tasks',
);
}
this.setState({ currentBoard: board });
}
selectList(list) {
this.setState({ currentList: list });
}
selectTask(task) {
this.setState({ taskSelected: task });
}
submit() {
if (this.props.mode === 'select_task' && this.state.taskSelected) {
if (this.props.onChoose) {
this.props.onChoose(this.state.taskSelected);
}
}
}
renderBoardPicker() {
var boards = Collections.get('boards').findBy({ workspace_id: Workspaces.currentWorkspaceId });
var loading =
!Collections.get('boards').did_load_first_time[this.tasks_collection_key] &&
boards.length === 0;
return (
<div className="boardPicker">
{loading && (
<div className="loading">
<Loader color="#CCC" className="app_loader" />
</div>
)}
{!loading &&
boards.map(board => {
return (
<div className="picker_item" onClick={() => this.selectBoard(board)}>
<div className="board_name_picker">
{board.emoji && (
<Emojione type={board.emoji} s16 className="board_emoji_picker" />
)}
<div className="text">{board.title}</div>
</div>
</div>
);
})}
</div>
);
}
renderListPicker() {
var loading =
!Collections.get('lists').did_load_first_time[
this.tasks_collection_key + '_' + this.state.currentBoard.id
];
var lists = Collections.get('lists').findBy({ board_id: this.state.currentBoard.id });
return (
<div className="">
{loading && (
<div className="loading">
<Loader color="#CCC" className="app_loader" />
</div>
)}
{!loading &&
lists
.sort(
(a, b) =>
TasksService.getElementIndex(a, 'lists_' + a.board_id) -
TasksService.getElementIndex(b, 'lists_' + b.board_id),
)
.map((item, index) => {
return (
<div
className="picker_item"
onClick={() => {
this.selectList(item);
}}
>
<div className="colorBloc" style={{ backgroundColor: item.color }} />
<div className="text">{item.title}</div>
</div>
);
})}
</div>
);
}
renderTaskPicker() {
var loading =
!Collections.get('tasks').did_load_first_time[
this.tasks_collection_key + '_' + this.state.currentBoard.id + '_tasks'
];
var tasks = Collections.get('tasks').findBy({
board_id: this.state.currentBoard.id,
list_id: this.state.currentList.id,
archived: false,
});
return (
<div className="item">
{loading && (
<div className="loading">
<Loader color="#CCC" className="app_loader" />
</div>
)}
{!loading &&
(tasks || [])
.sort(
(a, b) =>
TasksService.getElementIndex(a, 'tasks_' + a.list_id) -
TasksService.getElementIndex(b, 'tasks_' + b.list_id),
)
.map((item, index) => (
<div
className={
'picker_item ' +
(this.state.taskSelected &&
this.props.mode === 'select_task' &&
this.state.taskSelected.id === item.id
? 'is_selected'
: '')
}
onClick={() => {
this.selectTask(item);
}}
>
<div className="text">{item.title}</div>
</div>
))}
</div>
);
}
render() {
return (
<div className="taskPicker">
{!this.state.currentBoard && (
<div className="title">{Languages.t('scenes.apps.tasks.task')}</div>
)}
{this.state.currentBoard && !this.state.currentList && (
<div className="title">
<LeftIcon
className="m-icon-small getback"
onClick={() => {
Collections.get('lists').removeListener();
this.setState({ currentBoard: null });
}}
/>
{this.state.currentBoard.title}
</div>
)}
{this.state.currentBoard && this.state.currentList && (
<div className="title">
<LeftIcon
className="m-icon-small getback"
onClick={() => {
Collections.get('tasks').removeListener();
this.setState({ currentList: null });
}}
/>
{this.state.currentBoard.title} - {this.state.currentList.title}
</div>
)}
<div className="list">
{!this.state.currentBoard && this.renderBoardPicker()}
{this.state.currentBoard && !this.state.currentList && this.renderListPicker()}
{this.state.currentBoard && this.state.currentList && this.renderTaskPicker()}
</div>
<div className="menu-buttons">
{this.props.mode === 'select_task' &&
this.state.taskSelected &&
this.state.taskSelected.id && (
<Button
className="small"
value={Languages.t('scenes.app.taskpicker.select', [], 'Sélectionner')}
onClick={() => this.submit()}
/>
)}
</div>
</div>
);
}
}
@@ -1,76 +0,0 @@
.taskPicker {
position: relative;
.title {
background: #ffffff;
margin-bottom: 0px;
font-size: 16px;
color: #000;
line-height: 20px;
padding-bottom: 8px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.getback {
background: var(--grey-background);
border-radius: var(--border-radius-base);
width: 20px;
height: 20px;
vertical-align: top;
margin-right: 5px;
margin-left: 0px;
cursor: pointer;
}
}
.list {
height: 150px;
overflow: auto;
width: 100%;
.loading {
position: absolute;
margin: auto;
width: 50px;
height: 50px;
right: 0px;
bottom: 0px;
top: 0px;
left: 0px;
}
.picker_item {
display: flex;
flex-direction: row;
padding-left: 8px;
padding-right: 8px;
bottom: 0px;
color: var(--black);
height: 40px;
font-weight: 500;
width: 100%;
box-sizing: border-box;
border-top: 1px solid var(--grey-background);
font-size: 14px;
cursor: pointer;
.text {
display: inline-block;
line-height: 40px;
margin-left: 6px;
}
.board_emoji_picker {
vertical-align: top;
line-height: 40px;
}
.colorBloc {
height: 30px;
width: 4px;
margin-top: 5px;
}
}
.picker_item:hover {
background: var(--primary-background);
border-color: #fff;
}
.is_selected {
background: var(--primary);
color: #fff;
}
}
}
@@ -1,113 +0,0 @@
import React, { Component } from 'react';
import './tooltip.scss';
export default class Tooltip extends Component {
/*
props = {
tooltip : text or react element to show
visible :
overable : default : true
position :
}
*/
constructor(props) {
super(props);
this.state = {
top: 'unset',
left: 'unset',
right: 'unset',
bottom: '0px',
};
}
componentWillUnmount() {}
componentDidUpdate(prevProps, prevState) {
if (this.props.visible === true && prevProps.visible === false) {
this.open();
} else if (this.props.visible === false && prevProps.visible === true) {
this.close();
}
}
openWithTimeOut(timeout = 5) {
var that = this;
that.open();
setTimeout(() => {
that.close();
}, timeout * 1000);
}
open() {
if (this.props.position === 'left') {
this.setState({
visible: true,
left: (this.tooltip.clientWidth + 8) * -1 + 'px',
right: 'unset',
});
} else if (this.props.position === 'right') {
this.setState({
visible: true,
right: (this.tooltip.clientWidth + 8) * -1 + 'px',
left: 'unset',
});
} else if (this.props.position === 'bottom') {
this.setState({
visible: true,
bottom: (this.tooltip.clientHeight + 5) * -1 + 'px',
top: 'unset',
});
} else {
this.setState({
visible: true,
top: (this.tooltip.clientHeight + 5) * -1 + 'px',
bottom: 'unset',
});
}
}
close() {
this.setState({ visible: false, top: 'unset', left: '0px', right: 'unset', bottom: '0px' });
}
componentDidMount() {
var that = this;
if (this.parent && this.children) {
window.tooltip = this.tooltip;
this.parent.addEventListener('mouseenter', () => {
if (this.props.overable !== false) {
that.open();
}
});
this.parent.addEventListener('mouseleave', () => {
if (this.props.overable !== false) {
that.close();
}
});
}
}
render() {
return (
<div
className={'tooltip ' + (this.props.className || '')}
ref={parent => (this.parent = parent)}
>
<div
style={{
top: this.state.top,
left: this.state.left,
bottom: this.state.bottom,
right: this.state.right,
}}
className={
'tooltipAbsolute ' +
(this.state.visible ? 'visible' : '') +
' ' +
(this.props.position ? this.props.position : 'top')
}
ref={obj => (this.tooltip = obj)}
>
{this.props.tooltip}
</div>
<div className="contentTooltip" ref={children => (this.children = children)}>
{this.props.children}
</div>
</div>
);
}
}
@@ -1,68 +0,0 @@
.tooltip {
position: relative;
display: inline-block;
width: 100%;
font-size: 14px;
font-weight: 500;
.tooltipAbsolute {
position: absolute;
opacity: 0;
z-index: -1;
background: black;
color: white;
border-bottom: 1px dotted black;
padding: 5px 10px;
border-radius: var(--border-radius-base);
text-align: center;
font-size: 12px;
line-height: 16px;
}
.tooltipAbsolute::after {
content: '';
position: absolute;
border-width: 5px;
border-style: solid;
}
.right,
.left {
top: 50% !important;
transform: translateY(-50%);
}
.top,
.bottom {
left: 50% !important;
transform: translateX(-50%);
}
.top::after {
left: calc(50% - 5px);
top: 100%;
border-color: black transparent transparent transparent;
}
.right::after {
left: -10px;
border-color: transparent black transparent transparent;
top: calc(50% - 5px);
margin-top: -5px;
}
.bottom::after {
bottom: 100%;
border-color: transparent transparent black transparent;
left: calc(50% - 5px);
margin-left: -5px;
}
.left::after {
right: -10px;
border-color: transparent transparent transparent black;
top: calc(50% - 5px);
margin-top: -5px;
}
.visible {
z-index: 50;
opacity: 1;
transition: opacity 0.2s;
}
.contentTooltip {
width: 100%;
display: inline-block;
}
}
@@ -1,14 +0,0 @@
import React, { Component } from 'react';
import './elements.scss';
export default class UserConnectionDot extends React.Component {
render() {
if (this.props.connected && !this.props.notificationsDisabled) {
return <div className="connexion_dot green" />;
} else if (!this.props.connected && !this.props.notificationsDisabled) {
return <div className="connexion_dot grey" />;
} else {
return <div className="connexion_dot red" />;
}
}
}
@@ -1,58 +0,0 @@
import Observable from '@deprecated/CollectionsV1/observable.js';
class UploadManager extends Observable {
constructor() {
super();
this.setObservableName('upload_manager');
this.reinit();
window.uploadManager = this;
}
reinit() {
if (this.reinitTimeout) clearTimeout(this.reinitTimeout);
if (this.reinitTimeoutBefore) clearTimeout(this.reinitTimeoutBefore);
this.currentUploadTotalSize = 0;
this.currentUploadTotalNumber = 0;
this.currentUploadedTotalSize = 0;
this.currentUploadedFilesNumber = 0;
this.currentUploadingFilesNumber = 0;
this.currentCancelledFilesNumber = 0;
this.currentWaitingFilesNumber = 0;
this.currentErrorFilesNumber = 0;
this.currentUploadFiles = [];
this.currentUploadStartTime = new Date();
this.will_close = false;
this.notify();
}
abort(elements) {
var that = this;
if (elements.length === undefined) {
elements = [elements];
}
elements.forEach(element => {
if (element.resumable) element.resumable.cancel();
element.xhr_cancelled = true;
element.cancelled = true;
that.currentCancelledFilesNumber++;
that.currentUploadingFilesNumber--;
if (
that.currentUploadedFilesNumber +
that.currentCancelledFilesNumber +
that.currentErrorFilesNumber >=
that.currentUploadTotalNumber
) {
that.reinitAfterDelay();
}
});
that.notify();
}
}
const service = new UploadManager();
export default service;
@@ -1,162 +0,0 @@
import React from 'react';
import UploadManager from './upload-manager.js';
import CloseIcon from '@material-ui/icons/CloseOutlined';
import './uploads.scss';
import moment from 'moment';
import Languages from '@features/global/services/languages-service';
export default class UploadViewer extends React.Component {
constructor(props) {
super();
this.state = {
upload_manager: UploadManager,
};
UploadManager.addListener(this);
}
componentWillUnmount() {
UploadManager.removeListener(this);
}
render() {
if (this.state.upload_manager.currentUploadTotalNumber <= 0) {
// eslint-disable-next-line react/no-direct-mutation-state
this.state.large = true;
return '';
}
var documents = this.state.upload_manager.currentUploadFiles.filter(d => !d.path.substr(1));
var folders = {};
var folders_content = {};
this.state.upload_manager.currentUploadFiles
.filter(d => d.path.substr(1))
.forEach(d => {
var folder = d.path.substr(1).split('/')[0];
if (!folders[folder]) {
folders[folder] = {
total: 0,
total_progress: 0,
total_uploaded: 0,
error: true,
cancelled: true,
};
folders_content[folder] = [];
}
if (!d.cancelled) {
folders[folder].cancelled = false;
}
if (!d.error) {
folders[folder].error = false;
}
if (!d.error && !d.cancelled) {
folders[folder].total++;
folders[folder].total_progress += d.progress;
if (d.progress === 1) {
folders[folder].total_uploaded += 1;
}
folders_content[folder].push(d);
}
});
Object.keys(folders).forEach(name => {
documents.push({
progress: folders[name].total_progress / folders[name].total,
name: name,
cancelled: folders[name].cancelled,
error: folders[name].error,
folder_total: folders[name].total,
folder_total_uploaded: folders[name].total_uploaded,
all_files: folders_content[name],
});
});
var total_finished =
this.state.upload_manager.currentUploadedFilesNumber +
this.state.upload_manager.currentCancelledFilesNumber +
this.state.upload_manager.currentErrorFilesNumber;
var todo = this.state.upload_manager.currentUploadTotalNumber;
var total_finished_size = this.state.upload_manager.currentUploadFiles
.map(a => {
if (a.error || a.cancelled) {
return (a.file || {}).size || 0;
}
if (a.progress > 0) {
return ((a.file || {}).size || 0) * a.progress;
}
return 0;
})
.reduce((a, b) => {
return a + b;
});
var todo_size = this.state.upload_manager.currentUploadFiles
.map(a => {
return (a.file || {}).size || 0;
})
.reduce((a, b) => {
return a + b;
});
var remaining_time = 0;
if (total_finished_size > 0) {
remaining_time =
((todo_size - total_finished_size) / 1000000) *
((new Date().getTime() - this.state.upload_manager.currentUploadStartTime) /
(total_finished_size / 1000000));
}
return (
<div
className={
'upload_viewer ' +
(this.state.upload_manager.will_close ? 'fade_out ' : 'skew_in_left_nobounce ')
}
>
<div className="title" onClick={() => this.setState({ large: !this.state.large })}>
{Languages.t('general.uploading')} {total_finished}/{todo}
</div>
{remaining_time > 0 && (
<div className="subtitle">
Will end {moment(new Date().getTime() + remaining_time).fromNow()}
</div>
)}
<div className="uploads" style={{ display: this.state.large ? 'block' : 'none' }}>
{documents
.sort((a, b) => (a.progress === 1) - (b.progress === 1))
.map(item => {
return (
<div
key={item.unid}
className={
'uploadingFile ' +
(item.cancelled || item.error ? 'stopped ' : '') +
(item.progress === 1 && !item.error ? 'done ' : '') +
(item.progress < 1 && !item.error && !item.cancelled ? 'progress ' : '')
}
>
<div
className="progress_bar"
style={{ width: parseInt(item.progress * 100) + '%' }}
/>
<div className="name">
{item.name} {item.folder_total !== undefined && '(Folder)'}
</div>
{item.path && item.path.substr(1) && (
<div className="path">{item.path.substr(1)}</div>
)}
{item.folder_total !== undefined && (
<div className="path">
{item.folder_total_uploaded}/{item.folder_total}
</div>
)}
<div className="progress">{parseInt((item.progress || 0) * 100)}%</div>
</div>
);
})}
</div>
</div>
);
}
}
@@ -2,7 +2,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React from 'react';
import UploadManager from './upload-manager';
import Languages from '@features/global/services/languages-service';
import { Upload } from 'react-feather';
import classNames from 'classnames';
@@ -18,8 +17,6 @@ type StateType = { [key: string]: any };
type FileInputType = any;
type FileObjectType = { [key: string]: any };
let sharedFileInput: any = null;
let sharedFolderInput: any = null;
@@ -30,14 +27,7 @@ export default class UploadZone extends React.Component<PropsType, StateType> {
constructor(props: PropsType) {
super(props);
this.state = {
upload_manager: UploadManager,
};
UploadManager.addListener(this);
}
componentWillUnmount() {
UploadManager.removeListener(this);
this.state = {};
}
componentDidMount() {
@@ -1,87 +0,0 @@
import React, { Component } from 'react';
import Picker from 'components/picker/picker.jsx';
import Icon from '@components/icon/icon.jsx';
import './user-picker.scss';
import User from 'components/ui/user.jsx';
import UsersService from '@features/users/services/current-user-service';
import Languages from '@features/global/services/languages-service';
export default class UserPicker extends React.Component {
/*
hello
*/
constructor(props) {
super();
this.props = props;
this.state = {
currentSelected: [],
inputValue: '',
currentList: [],
selected: [],
};
}
componentWillUnmount() {}
onUpdate(item) {}
onRemove(item, ev) {}
renderItem(item, withEditor) {
return (
<div
className={
'itemContoured ' + (withEditor ? 'itemSelected ' : '') + (this.props.mini ? 'mini ' : '')
}
>
<User data={item} mini={this.props.mini} />
{!this.props.readOnly && withEditor && (
<div
className="close"
onClick={ev => {
this.picker.onRemove(item);
ev.stopPropagation();
ev.preventDefault();
}}
>
<Icon type="close" />
</div>
)}
</div>
);
}
search(text, cb) {
UsersService.search(text, { scope: this.props.scope || 'all' }, res => {
cb(res);
});
}
render() {
return (
<Picker
className="userPicker"
ref={picker => {
this.picker = picker;
}}
title={
this.props.title || Languages.t('components.drive.modify_uslist', [], 'Modify user list')
}
search={(text, cb) => {
this.search(text, cb);
}}
renderItem={item => {
return this.renderItem(item, false);
}}
renderItemChoosen={item => {
return this.renderItem(item, true);
}}
renderItemSimply={item => {
item = item || {};
return item.username;
}}
max={10}
onSelect={item => this.onSelect(item)}
onChange={this.props.onChange}
value={this.props.value}
readOnly={this.props.readOnly}
inline={this.props.inline}
/>
);
}
}
@@ -1,50 +0,0 @@
.userPicker {
.itemSelected {
display: inline-flex;
max-width: 100%;
.user_bloc {
flex: 1;
}
.close {
font-size: 8px;
margin-left: 5px;
opacity: 0.4;
display: inline-block;
margin-right: -1px;
bottom: 0px;
position: relative;
vertical-align: top;
margin-top: 5px;
}
.close:hover {
opacity: 0.8;
cursor: pointer;
}
}
.itemContoured {
display: inline-flex;
box-sizing: border-box;
color: #000;
margin: 5px;
height: 18px;
line-height: 16px;
font-size: 12px;
text-transform: capitalize;
border-radius: 9px;
padding-right: 6px;
&.mini {
padding-right: 0px;
margin-right: 0px;
}
}
.pickerInput:not(.readOnly) {
.itemContoured:hover {
background: #eee;
}
}
.menu .itemContoured {
margin: 3px;
}
}
@@ -2,8 +2,6 @@
import React, { Component } from 'react';
import UserService from '@features/users/services/current-user-service';
import NotificationParameters from '@deprecated/user/notification_parameters.js';
import ListenUsers from '@features/users/services/listen-users-service';
import UserOnlineStatus from '../online-user-status/online-user-status';
import './user.scss';
@@ -12,18 +10,9 @@ export default class User extends Component {
constructor(props) {
super();
}
UNSAFE_componentWillMount() {
ListenUsers.listenUser(this.props.user.id);
}
componentWillUnmount() {
ListenUsers.cancelListenUser(this.props.user.id);
}
render() {
var user = this.props.user;
var notifications_disabled = false;
if (user && NotificationParameters.hasNotificationsDisabled(user.notifications_preferences)) {
notifications_disabled = true;
}
return (
<div
className={
@@ -1,7 +1,6 @@
import Api from '@features/global/framework/api-service';
import Observable from '@deprecated/CollectionsV1/observable.js';
import Number from '@features/global/utils/Numbers';
import MultipleSecuredConnections from './MultipleSecuredConnections.js';
import LocalStorage from '@features/global/framework/local-storage-service';
/** Collection
* Act like a doctrine repository and try to be allways in sync with server in realtime
@@ -65,24 +64,6 @@ export default class Collection extends Observable {
this.sources_to_be_removed_timeout = {};
this.sources_on_event = {};
this._last_modified = {};
this.connections = new MultipleSecuredConnections((event, data) => {
Object.keys(this.sources_on_event).forEach(source_key => {
if (this.sources_on_event[source_key].routes.indexOf(data._route) >= 0) {
this.sources_on_event[source_key].callback(event, data);
}
});
if (event === 'event') {
if (data.multiple && data.multiple.forEach) {
data.multiple.forEach(item => {
this.ws_message(item);
});
} else {
this.ws_message(data);
}
}
});
}
updatedOptions() {
@@ -345,7 +326,6 @@ export default class Collection extends Observable {
var ws_identifier = collection_id || this.collection_id;
// eslint-disable-next-line no-redeclare
var options = options || this.options || {};
this.connections.addConnection(ws_identifier, options, http_options, key);
return true;
}
@@ -363,7 +343,6 @@ export default class Collection extends Observable {
if (this.total_subscribe_by_route[collection_id] <= 0) {
var ws_identifier = collection_id || this.collection_id;
this.connections.removeConnection(ws_identifier);
}
return;
@@ -865,14 +844,7 @@ export default class Collection extends Observable {
}
//To use only on very specific cases !!!
shareRemove(deleted_front_id) {
this.connections.publish({
client_id: this.client_id,
action: 'remove',
object_type: this.object_type,
front_id: deleted_front_id,
});
}
shareRemove(deleted_front_id) {}
//To use only on very specific cases !!!
share(object) {
@@ -884,13 +856,6 @@ export default class Collection extends Observable {
delete object[key];
}
});
this.connections.publish({
client_id: this.client_id,
action: 'save',
object_type: this.object_type,
object: object,
});
}
ws_message(data) {
@@ -927,7 +892,6 @@ export default class Collection extends Observable {
data: data,
client_id: this.client_id,
};
this.connections.publish(_d);
}
addWebsocketListener(callback) {
@@ -1,98 +0,0 @@
import SecuredConnection from './SecuredConnection.js';
/** MultipleSecuredConnections
* Manage multiple secured connections as one (multiple publish and centralized event manager)
*/
export default class MultipleSecuredConnections {
constructor(callback, options) {
if (!options) {
options = {};
}
//Options :
this.disableDuplicates = options.disableDuplicates !== false;
this.secured_connections = {};
this.callback = callback;
this.removing_secured_connections_timeouts = {};
}
publish(data) {
Object.keys(this.secured_connections).forEach(secured_connection_key => {
var secured_connection = this.secured_connections[secured_connection_key];
secured_connection.publish(data, () => {});
});
}
event(route, event, data) {
//Disable duplicates
var string = JSON.stringify(data);
if (
string === this.last_event &&
this.disableDuplicates &&
!(event === 'close' || event === 'open' || event === 'init')
) {
return;
}
this.last_event = string;
if (!data) {
data = {};
}
data._route = route;
if (this.callback) this.callback(event, data);
}
addConnection(route, option, http_options, key) {
if (this.secured_connections[route]) {
this.secured_connections[route].removing = false;
if (this.removing_secured_connections_timeouts[route]) {
clearTimeout(this.removing_secured_connections_timeouts[route]);
}
return;
}
if (this.secured_connections[route]) {
return;
}
this.secured_connections[route] = new SecuredConnection(
route,
option,
(event, data) => {
this.event(route, event, data);
},
http_options,
key,
);
}
removeConnection(route) {
if (!this.secured_connections[route] || this.secured_connections[route].removing) {
return;
}
if (this.removing_secured_connections_timeouts[route]) {
clearTimeout(this.removing_secured_connections_timeouts[route]);
}
this.secured_connections[route].removing = true;
this.removing_secured_connections_timeouts[route] = setTimeout(() => {
if (this.secured_connections[route]) {
this.secured_connections[route].close();
delete this.secured_connections[route];
}
}, 10000);
}
getRoutes() {
return Object.keys(this.secured_connections);
}
closeAll() {
Object.keys(this.secured_connections).foEach(route => {
this.removeConnection(route);
});
}
}
@@ -1,265 +0,0 @@
import Api from '@features/global/framework/api-service';
import ws from '@deprecated/websocket/websocket.js';
import CryptoJS from 'crypto-js';
import sha256 from 'crypto-js/sha256';
/** SecuredConnection
* Create websockets encrypted connection
*/
import Globals from '@features/global/services/globals-tdrive-app-service';
export default class SecuredConnection {
constructor(route, options, callback, http_options, collectionId) {
this.ready = false;
this.collectionId = collectionId;
this.route = route;
this.options = options;
this.callback = callback;
//Room idea
this.websocket_id = '';
//Store keys
this.keys = [];
this.keys_by_version = {};
this.publish_buffer = [];
this.http_options = http_options;
this.init(http_options);
Globals.window.CryptoAES = CryptoJS.AES;
}
init(http_options) {
this.ready = false;
this.options.get_options = http_options;
var data = {
collection_id: this.route,
options: this.options,
_grouped: true,
};
Api.GroupedQueryApiPost('/ajax/core/collections/init', data, res => {
var did_get = false;
if (res.data) {
var data = res.data;
this.ready = true;
this.websocket_id = data.room_id;
this.receiveNewKey(data.key, data.key_version, false);
this.open();
if (data.get) {
this.callback('get', { collectionId: this.collectionId, data: data.get });
did_get = true;
}
}
if (!did_get) {
this.callback('init', {});
}
});
}
open() {
var websocket_id = this.websocket_id;
ws.subscribe(
'collections/' + websocket_id,
(uri, obj) => {
this.receiveEvent(obj);
},
websocket_id,
);
ws.onReconnect(websocket_id, () => {
this.close();
this.init();
});
this.callback('open', {});
this.publish();
}
close() {
var websocket_id = this.websocket_id;
ws.offReconnect(websocket_id);
ws.unsubscribe(
'collections/' + websocket_id,
(uri, obj) => {
this.receiveEvent(obj);
},
websocket_id,
);
this.callback('close', {});
}
publish(data, callback) {
if (!data) {
//Read buffer
var publish_buffer = JSON.parse(JSON.stringify(this.publish_buffer));
this.publish_buffer = [];
publish_buffer.forEach(item => {
this.publish(item, callback);
});
return;
}
if (!this.ready) {
this.publish_buffer.push(data);
return;
}
var websocket_id = this.websocket_id;
var encrypted = this.encrypt(data);
ws.publish('collections/' + websocket_id, encrypted);
if (callback) {
callback();
}
}
receiveEvent(event) {
if (event.encrypted) {
event = this.decrypt(event);
}
if (event.new_key) {
this.receiveNewKey(event.new_key, event.key_version, true);
return;
}
if (!event) {
return;
}
this.callback('event', event);
}
//Receive new key as addon for previous key or as replacing key (sent by php server)
receiveNewKey(key, version, asAddon) {
if (asAddon) {
var lastKey = this.keys[this.keys.length - 1];
key = sha256(lastKey.key + key).toString(); //Mix keys
}
this.keys.push({
key: key,
version: version,
});
this.keys_by_version[version] = key;
this.prepareEncryptKey();
}
prepareEncryptKey() {
var lastKey = this.keys[this.keys.length - 1];
try {
if (this.prepared_key_original !== lastKey) {
this.prepared_key_salt = CryptoJS.lib.WordArray.random(256);
this.prepared_key_original = lastKey;
this.prepared_key = CryptoJS.PBKDF2(lastKey.key, this.prepared_key_salt, {
hasher: CryptoJS.algo.SHA512,
keySize: 64 / 8,
iterations: 9,
});
}
} catch (e) {
console.log(e);
}
return this.prepared_key;
}
encrypt(data) {
var lastKey = this.keys[this.keys.length - 1];
var prepared_key = this.prepareEncryptKey();
if (!prepared_key) {
prepared_key = '';
}
var iv = CryptoJS.lib.WordArray.random(16);
var encrypted = CryptoJS.AES.encrypt(JSON.stringify(data), prepared_key, { iv: iv });
// eslint-disable-next-line no-redeclare
var data = {
encrypted: CryptoJS.enc.Base64.stringify(encrypted.ciphertext),
iv: CryptoJS.enc.Hex.stringify(iv),
salt: CryptoJS.enc.Hex.stringify(this.prepared_key_salt),
key_version: lastKey.version,
};
return data;
}
decrypt(data) {
if (!data.key_version) {
return false;
}
let res = '';
let str = '';
try {
if (data.public) {
return typeof data.public === 'string' ? JSON.parse(data.public) : data.public;
}
var encrypted_data = data.encrypted;
var key = this.keys_by_version[data.key_version];
if (!key) {
if (
this.getKeyTimestamp(key) > this.getKeyTimestamp(this.keys[this.keys.length - 1].version)
) {
//We have a old key, we have to update and request the message again
this.init(this.http_options);
//TODO reask lost message after init
} else {
//We have only newer keys, ask everybody to update
//TODO ws.publish('collections/' + websocket_id, encrypted);
}
return false;
}
var salt = CryptoJS.enc.Hex.parse(data.salt);
var prepared_key = CryptoJS.PBKDF2(key, salt, {
hasher: CryptoJS.algo.SHA512,
keySize: 64 / 8,
iterations: 9,
});
var iv = CryptoJS.enc.Hex.parse(data.iv);
var bytes = CryptoJS.AES.decrypt(encrypted_data, prepared_key, { iv: iv });
str = bytes.toString(CryptoJS.enc.Utf8);
if (str) {
res = JSON.parse(str);
} else {
res = '';
}
} catch (err) {
console.error('Unable to read encrypted websocket event', str, err.message);
}
return res;
}
getKeyTimestamp(key) {
try {
if (typeof key !== 'string') {
if (key !== undefined) {
console.log('wrong websocket key format: ', key);
}
return 0;
}
return parseInt((key || '').split('-')[1]);
} catch (e) {
console.log(key, e);
}
}
}
@@ -1,50 +0,0 @@
import Observable from '@deprecated/CollectionsV1/observable.js';
import Globals from '@features/global/services/globals-tdrive-app-service';
class ConfiguratorsManager extends Observable {
constructor() {
super();
this.setObservableName('configurators_manager');
Globals.window.configuratorsManager = this;
this.currentConfigurators = {};
this.configurator_order = [];
}
openConfigurator(app, form, hidden_data, id) {
this.currentConfigurators[app.id] = {
app: app,
form: form,
hidden_data: hidden_data,
id: id,
};
var _new_order = [];
this.configurator_order.forEach(id => {
if (app.id !== id) {
_new_order.push(id);
}
});
_new_order.push(app.id);
this.configurator_order = _new_order;
this.notify();
}
closeConfigurator(app) {
delete this.currentConfigurators[app.id];
var _new_order = [];
this.configurator_order.forEach(id => {
if (app.id !== id) {
_new_order.push(id);
}
});
this.configurator_order = _new_order;
this.notify();
}
}
const conf_serv = new ConfiguratorsManager();
export default conf_serv;
@@ -1,91 +0,0 @@
import Observable from '@deprecated/CollectionsV1/observable.js';
import Globals from '@features/global/services/globals-tdrive-app-service';
class SelectionsManager extends Observable {
constructor() {
super();
this.setObservableName('selections_manager');
this.type = '';
this.selected_per_type = {};
this.last_change = {};
Globals.window.selected_drive = this;
}
toggle(id) {
if (this.selected_per_type[this.type][id]) {
this.unselect(id);
} else {
this.select(id);
}
}
select(id) {
this.selected_per_type[this.type][id] = true;
this.last_change[id] = new Date();
this.notify();
}
unselect(id) {
delete this.selected_per_type[this.type][id];
this.last_change[id] = new Date();
this.notify();
}
unselectAll() {
if (Object.keys(this.selected_per_type[this.type] || {}).length === 0) {
return;
}
Object.keys(this.selected_per_type[this.type] || {}).forEach(id => {
this.last_change[id] = new Date();
});
this.selected_per_type[this.type] = {};
this.notify();
}
setType(type) {
if (type !== this.type) {
this.type = type;
if (!this.selected_per_type[this.type]) {
this.selected_per_type[this.type] = {};
}
}
}
//Observable improvement, a listener can listen only for modifications on some objects to prevent massive notify and state updates
listenOnly(listener, object_front_ids) {
if (!listener._observable) {
listener._observable = {};
}
if (!listener._observable[this.observableName]) {
listener._observable[this.observableName] = {};
}
listener._observable[this.observableName].listen_only = object_front_ids;
}
shouldNotify(node) {
if (!this._last_modified) {
this._last_modified = {};
}
var update = true;
if (
node._observable &&
node._observable[this.observableName] &&
node._observable[this.observableName].listen_only
) {
update = false;
// eslint-disable-next-line array-callback-return
node._observable[this.observableName].listen_only.map(item => {
if (!this._last_modified[item] || this.last_change[item] > this._last_modified[item]) {
this._last_modified[item] = this.last_change[item];
update = true;
}
});
}
return update;
}
}
const service = new SelectionsManager();
export default service;
@@ -1,226 +0,0 @@
import Api from '@features/global/framework/api-service';
import Languages from '@features/global/services/languages-service';
import DepreciatedCollections from '@deprecated/CollectionsV1/Collections/Collections.js';
import Login from '../../features/auth/login-service';
import { getUser } from '../../features/users/hooks/use-user-list';
/**
* This service is depreciated as Tdrive will exclusively use Console in the future
*/
class Account {
notify() {
Login.notify();
}
/**
* Recover password
*/
recover(mail, funct, th) {
var data = {
email: mail,
};
var that = this;
this.login_loading = true;
that.error_recover_nosuchmail = false;
this.notify();
Api.post('/ajax/users/recover/mail', data, function (res) {
if (res.data.token) {
that.recover_token = res.data.token;
that.login_loading = false;
that.notify();
funct(th);
} else {
that.error_recover_nosuchmail = true;
that.login_loading = false;
that.notify();
//appel de funtion error
}
});
}
recoverCode(code, funct, th) {
var data = {
code: code,
token: this.recover_token,
};
var that = this;
that.error_recover_badcode = false;
this.login_loading = true;
this.notify();
Api.post('/ajax/users/recover/verify', data, function (res) {
if (res.data.status === 'success') {
that.recover_code = code;
that.login_loading = false;
that.notify();
funct(th);
} else {
that.error_recover_badcode = true;
that.login_loading = false;
that.notify();
}
});
}
recoverNewPassword(password, password2, funct, th) {
this.login_loading = true;
this.notify();
if (password !== password2 || password.length < 8) {
this.error_recover_badpasswords = true;
this.login_loading = false;
this.notify();
return;
}
var data = {
code: this.recover_code,
token: this.recover_token,
password: password,
};
var that = this;
that.error_recover_badpasswords = false;
that.error_recover_unknown = false;
this.notify();
Api.post('/ajax/users/recover/password', data, function (res) {
if (res.data.status === 'success') {
funct(th);
that.login_loading = false;
that.notify();
} else {
that.error_recover_unknown = true;
that.login_loading = false;
that.notify();
}
});
}
subscribeMail(username, password, name, first_name, phone, mail, newsletter, cb, th) {
if (this.doing_subscribe) {
return;
}
var data = {
email: mail,
username: username,
password: password,
name: name,
first_name: first_name,
phone: phone,
language: Languages.language,
newsletter: newsletter,
};
var that = this;
that.error_subscribe_mailalreadyused = false;
that.login_loading = true;
that.doing_subscribe = true;
this.notify();
Api.post('/ajax/users/subscribe/mail', data, function (res) {
that.login_loading = false;
that.doing_subscribe = false;
that.notify();
if (res.data.token) {
that.subscribe_token = res.data.token;
cb(th, 0);
that.waitForVerification(username, password, th);
} else {
cb(th, 1);
that.error_subscribe_mailalreadyused = true;
}
});
}
waitForVerification(username, password, th) {
if (this.waitForVerificationTimeout) {
clearTimeout(this.waitForVerificationTimeout);
}
this.waitForVerificationTimeout = setTimeout(() => {
this.waitForVerification(username, password, th);
Login.login({ username, password }, true);
}, 2000);
}
doVerifyMail(mail, code, token, success, fail) {
Api.post(
'/ajax/users/subscribe/doverifymail',
{
code: code,
token: token,
mail: mail,
device: {},
},
function (res) {
if (res.data.status === 'success') {
success();
} else {
fail();
}
},
);
}
checkMailandUsername(mail, username, callback, th) {
var that = this;
that.error_subscribe_username = false;
that.error_subscribe_mailalreadyused = false;
var data = {
mail: mail,
username: username,
};
that.login_loading = true;
that.notify();
Api.post('/ajax/users/subscribe/availability', data, function (res) {
that.login_loading = false;
if (res.data.status === 'success') {
callback(th, 0);
} else {
if (res.errors.length === 1 && res.errors[0] === 'mailalreadytaken') {
callback(th, 1);
that.error_subscribe_mailalreadyused = true;
} else if (res.errors.length === 1 && res.errors[0] === 'usernamealreadytaken') {
callback(th, 2);
that.error_subscribe_username = true;
} else {
callback(th, 3);
that.error_subscribe_mailalreadyused = true;
that.error_subscribe_username = true;
}
}
that.notify();
});
}
addNewMail(mail, cb, thot) {
var that = this;
that.loading = true;
that.error_secondary_mail_already = false;
that.error_code = false;
that.notify();
Api.post('/ajax/users/account/addmail', { mail: mail }, function (res) {
that.loading = false;
if (res.errors.indexOf('badmail') > -1) {
that.error_secondary_mail_already = true;
that.notify();
} else {
that.addmail_token = res.data.token;
that.notify();
cb(thot);
}
});
}
verifySecondMail(mail, code, cb, thot) {
//unimplemented
}
}
const æccount = new Account();
export default æccount;
@@ -1,211 +0,0 @@
import Observable from '@deprecated/CollectionsV1/observable.js';
import Globals from '@features/global/services/globals-tdrive-app-service';
class UploadManager extends Observable {
constructor() {
super();
this.observableName = 'uploadService';
this.globalStatus = 0;
this.uploads = [];
this.sessionUploaded = 0;
this.uploaded_entities = [];
this.uploading = 0;
Globals.window.uploads = this;
}
updateData() {
var totalSize = 0;
var totalUp = 0;
for (var i = 0; i < this.uploads.length; i++) {
totalSize += this.uploads[i].size;
totalUp += this.uploads[i].uploaded;
this.uploads[i].status = (100 * this.uploads[i].uploaded) / (this.uploads[i].size + 1);
}
if (this.sessionUploaded + this.uploads.length === 0) {
this.globalStatus = 0;
} else if (totalSize === 0) {
this.globalStatus =
(100 * this.sessionUploaded) / (this.sessionUploaded + this.uploads.length);
} else {
this.globalStatus =
(100 * (totalUp / totalSize + this.sessionUploaded)) /
(this.sessionUploaded + this.uploads.length);
}
if (this.uploads.length === 0) {
this.globalStatus = 0;
this.sessionUploaded = 0;
if (this.callback) {
var uploaded = [];
// eslint-disable-next-line no-redeclare
for (var i = 0; i < this.uploaded_entities.length; i++) {
uploaded.push(this.uploaded_entities[i]);
}
this.callback(uploaded);
this.uploaded_entities = [];
}
}
this.notify();
}
getFilesTree(event, fcb) {
function newDirectoryApi(input, cb) {
var fd = [],
files = [];
var iterate = function (entries, path, resolve) {
var promises = [];
entries.forEach(function (entry) {
promises.push(
new Promise(function (resolve) {
if ('getFilesAndDirectories' in entry) {
entry.getFilesAndDirectories().then(function (entries) {
iterate(entries, entry.path + '/', resolve);
});
} else {
if (entry.name) {
var p = (path + entry.name).replace(/^[/\\]/, '');
fd.push(entry);
files.push(p);
}
resolve();
}
}),
);
});
Promise.all(promises).then(resolve);
};
input.getFilesAndDirectories().then(function (entries) {
new Promise(function (resolve) {
iterate(entries, '/', resolve);
}).then(cb.bind(null, fd, files));
});
}
// old prefixed API implemented in Chrome 11+ as well as array fallback
function arrayApi(input, cb) {
var fd = [],
files = [];
[].slice.call(input.files).forEach(function (file) {
fd.push(file);
files.push(file.webkitRelativePath || file.name);
});
cb(fd, files);
}
// old drag and drop API implemented in Chrome 11+
function entriesApi(items, cb) {
var fd = [],
files = [],
rootPromises = [];
function readEntries(entry, reader, oldEntries, cb) {
var dirReader = reader || entry.createReader();
dirReader.readEntries(function (entries) {
var newEntries = oldEntries ? oldEntries.concat(entries) : entries;
if (entries.length) {
setTimeout(readEntries.bind(null, entry, dirReader, newEntries, cb), 0);
} else {
cb(newEntries);
}
});
}
function readDirectory(entry, path, resolve) {
if (!path) path = entry.name;
readEntries(entry, 0, 0, function (entries) {
var promises = [];
entries.forEach(function (entry) {
promises.push(
new Promise(function (resolve) {
if (entry.isFile) {
entry.file(function (file) {
var p = path + '/' + file.name;
fd.push(file);
files.push(p);
resolve();
}, resolve.bind());
} else readDirectory(entry, path + '/' + entry.name, resolve);
}),
);
});
Promise.all(promises).then(resolve.bind());
});
}
[].slice.call(items).forEach(function (entry) {
entry = entry.webkitGetAsEntry();
if (entry) {
rootPromises.push(
new Promise(function (resolve) {
if (entry.isFile) {
entry.file(function (file) {
fd.push(file);
files.push(file.name);
resolve();
}, resolve.bind());
} else if (entry.isDirectory) {
readDirectory(entry, null, resolve);
}
}),
);
}
});
Promise.all(rootPromises).then(cb.bind(null, fd, files));
}
var cb = function (event, files, paths) {
var tree = {};
paths.forEach(function (path, file_index) {
var dirs = tree;
var real_file = files[file_index];
path.split('/').forEach(function (dir, dir_index) {
if (dir.indexOf('.') === 0) {
return;
}
if (dir_index === path.split('/').length - 1) {
dirs[dir] = real_file;
} else {
if (!dirs[dir]) {
dirs[dir] = {};
}
dirs = dirs[dir];
}
});
});
fcb(tree);
};
if (event.dataTransfer) {
var dt = event.dataTransfer;
if (dt.items && dt.items.length && 'webkitGetAsEntry' in dt.items[0]) {
entriesApi(dt.items, cb.bind(null, event));
} else if ('getFilesAndDirectories' in dt) {
newDirectoryApi(dt, cb.bind(null, event));
} else if (dt.files) {
arrayApi(dt, cb.bind(null, event));
} else cb();
} else if (event.target) {
var t = event.target;
if (t.files && t.files.length) {
arrayApi(t, cb.bind(null, event));
} else if ('getFilesAndDirectories' in t) {
newDirectoryApi(t, cb.bind(null, event));
} else {
cb(event);
}
} else {
fcb(event);
}
}
}
const upload = new UploadManager();
export default upload;
@@ -4,7 +4,6 @@ import $ from 'jquery';
import Login from '@features/auth/login-service';
import Collections from '@deprecated/CollectionsV1/Collections/Collections';
import Api from '@features/global/framework/api-service';
import ws from '@deprecated/websocket/websocket';
import Observable from '@deprecated/CollectionsV1/observable';
import Number from '@features/global/utils/Numbers';
import AlertManager from '@features/global/services/alert-manager-service';
@@ -87,11 +86,7 @@ class CurrentUser extends Observable {
const data = {
status: user.tutorial_status,
};
Api.post('/ajax/users/account/set_tutorial_status', data, () => {
ws.publish('users/' + Login.currentUserId, {
user: { tutorial_status: user.tutorial_status },
});
});
Api.post('/ajax/users/account/set_tutorial_status', data, () => {});
}
updateUserName(username: string) {
@@ -106,7 +101,6 @@ class CurrentUser extends Observable {
Api.post('/ajax/users/account/username', { username }, (res: any) => {
that.loading = false;
if (res.errors.length === 0) {
ws.publish('users/' + Login.currentUserId, { user: update });
that.errorUsernameExist = false;
} else {
that.errorUsernameExist = true;
@@ -166,7 +160,6 @@ class CurrentUser extends Observable {
update.thumbnail = resp.data.thumbnail || '';
}
Collections.get('users').updateObject(update);
ws.publish('users/' + Login.currentUserId, { user: update });
that.notify();
}
}
@@ -1,213 +0,0 @@
import Languages from '@features/global/services/languages-service';
import Observable from '@deprecated/CollectionsV1/observable.js';
import ws from '@deprecated/websocket/websocket.js';
import Api from '@features/global/framework/api-service';
import Login from '@features/auth/login-service';
import Collections from '@deprecated/CollectionsV1/Collections/Collections.js';
import AlertManager from '@features/global/services/alert-manager-service';
import Globals from '@features/global/services/globals-tdrive-app-service';
/**
* FIXME: This service seems still used by some components but we now have ./NotificationPreferences one which seems to overlap...
*/
class NotificationParameters extends Observable {
constructor() {
super();
this.setObservableName('notifications_parameters');
Globals.window.notificationsParametersServices = this;
this.preferences = {};
this.loading = true;
}
initFirstTime() {
//Save default 'do not disturb' with correct timezone
this.init();
}
init(callback) {
this.loading = true;
this.notify();
var that = this;
Api.post('/ajax/users/account/get_notifications', {}, function (res) {
that.preferences = res.data;
that.original_preferences = JSON.parse(JSON.stringify(that.preferences));
if (that.preferences['dont_disturb_between'] == null) {
var l = that.transform_period(22, 8.5, new Date().getTimezoneOffset() / 60);
that.preferences['dont_disturb_between'] = l[0];
that.preferences['dont_disturb_and'] = l[1];
that.save(['dont_disturb_and', 'dont_disturb_between']);
}
that.loading = false;
that.notify();
if (callback) callback();
});
}
save(keys, no_notif) {
if (!keys) {
keys = [];
}
if (!this.preferences) {
return;
}
if (!keys || keys.length === 0) {
this.original_preferences = JSON.parse(JSON.stringify(this.preferences));
}
this.notify();
var data = {
preferences: JSON.parse(JSON.stringify(this.original_preferences)),
};
keys.forEach(key => {
data.preferences[key] = this.preferences[key];
});
this.original_preferences = data.preferences;
this.saveElements(data.preferences, no_notif);
}
saveElements(pref, no_notif) {
var data = {
preferences: JSON.parse(JSON.stringify(pref)),
};
var user = Collections.get('users').find(Login.currentUserId);
if (!user.notifications_preferences) {
user.notifications_preferences = {};
}
Object.keys(pref).forEach(key => {
user.notifications_preferences[key] = pref[key];
});
Collections.get('users').updateObject(user);
this.loading = true;
var that = this;
Api.post('/ajax/users/account/set_notifications', data, function (res) {
that.loading = false;
ws.publish('users/' + Login.currentUserId, {
user: {
notifications_preferences: Collections.get('users').find(Login.currentUserId)
.notifications_preferences,
},
});
that.notify();
if (!no_notif) {
AlertManager.alert(() => {}, {
text: Languages.t(
'services.user.notification_parameters_update_alert',
[],
'Les paramètres de notification ont été mis à jour.',
),
});
}
});
}
is_in_period(a, b) {
var currentDate = new Date().getHours() + Math.floor(new Date().getMinutes() / 30) / 2;
if (a != null && b != null) {
if (a < b && currentDate >= a && currentDate < b) {
return true;
}
if (a > b && (currentDate >= a || currentDate < b)) {
return true;
}
}
return false;
}
hasNotificationsDisabled(preferences) {
if (!preferences) {
return false;
}
var l = this.transform_period(
preferences.dont_disturb_between,
preferences.dont_disturb_and,
-new Date().getTimezoneOffset() / 60,
);
if (this.is_in_period(l[0], l[1])) {
return true;
}
if (preferences.disable_until > new Date().getTime() / 1000) {
return true;
}
return false;
}
getNotificationsStatus(user) {
this.dont_disturb = this.transform_period(
(user.notifications_preferences || {}).dont_disturb_between,
(user.notifications_preferences || {}).dont_disturb_and,
-new Date().getTimezoneOffset() / 60,
);
var notifications_state = 'on';
if (
(user.notifications_preferences || {}).disable_until < new Date().getTime() / 1000 &&
!this.is_in_period(this.dont_disturb[0], this.dont_disturb[1])
) {
notifications_state = 'on';
} else if (
(user.notifications_preferences || {}).disable_until <
new Date().getTime() / 1000 + 60 * 60 * 24 ||
this.is_in_period(this.dont_disturb[0], this.dont_disturb[1])
) {
notifications_state = 'paused';
} else {
notifications_state = 'off';
}
return notifications_state;
}
transform_period(a, b, offset) {
a = parseFloat(a);
b = parseFloat(b);
if (offset > 0) {
offset += -24;
}
a += offset;
b += offset;
if (a < 0 || b < 0) {
a += 24;
b += 24;
if (b >= 24) {
if (a < b) {
b += -24;
} else {
b += -24;
var c = b;
b = a;
a = c;
}
}
if (a >= 24) {
if (b < a) {
a += -24;
} else {
a += -24;
// eslint-disable-next-line no-redeclare
var c = a;
a = b;
b = c;
}
}
}
return [a, b];
}
}
export default new NotificationParameters();
@@ -1,184 +0,0 @@
import Number from '@features/global/utils/Numbers';
import Observable from '@deprecated/CollectionsV1/observable.js';
import LoginService from '@features/auth/login-service';
import Logger from '@features/global/framework/logger-service';
import Globals from '@features/global/services/globals-tdrive-app-service';
import WebSocket from '../../features/global/types/websocket-types';
/**
* @deprecated Keeps old PHP websocket working doing the bridge with the new implementation
*/
class DeprecatedWebsocket extends Observable {
constructor() {
super();
this.logger = Logger.getLogger('Websocket');
this.setObservableName('websockets');
this.ws = null;
this.connection = null;
this.afterError = false;
this.lastData = 1;
this.testNetwork = false;
this.connected = true;
this.subscribed = {};
this.subscribedKey = {};
this.disconnectListeners = {};
this.alive_connected = true;
this.firstTime = true;
this.last_reconnect_call = new Date();
this.last_reconnect_call_if_needed = new Date();
window.websocketsManager = this;
this.window_focus = true;
this.window_last_blur = new Date();
this.deconnectionBlurTimeout = setTimeout(() => {}, 0);
Globals.window.addEventListener('focus', () => {
this.reconnectIfNeeded(60);
clearTimeout(this.deconnectionBlurTimeout);
this.didFocusedLastMinute = true;
this.window_focus = true;
this.window_last_blur = new Date();
if (this.ws == null || new Date().getTime() - this.window_last_blur.getTime() > 30000) {
this.alive_connected = false;
this.alive();
}
});
Globals.window.addEventListener('blur', () => {
this.window_focus = false;
this.window_last_blur = new Date();
});
this.alive.bind(this);
this.message.bind(this);
this.updateConnected.bind(this);
}
//Send I'm alive !
alive() {
var nw = new Date().getTime();
if (!this.lastAlive || nw - this.lastAlive > 60000) {
// Wait at least 60 seconds
this.lastAlive = nw;
this.alive_timeout = setTimeout(() => {
this.alive_connected = false;
}, 5100);
this.reconnectIfNeeded();
clearTimeout(this.alive_timeout);
this.alive_connected = true;
this.didFocusedLastMinute = Globals.isReactNative || Globals.window.document.hasFocus();
}
}
//Receive server message
message(unid, route, data) {
route = (route || '').split('previous::').pop();
if (unid !== this.subscribedKey[route]) {
return;
}
if (this.subscribed[route]) {
this.subscribed[route].forEach(c => {
try {
c.callback(route, data);
} catch (err) {
console.log(err);
}
});
} else {
this.unsubscribe(route);
}
}
//Subscribe to channel
subscribe(route, callback, key) {
this.logger.debug(`Subscribe to ${route}`);
route = (route || '').split('previous::').pop();
if (!key) {
key = callback.name;
}
if (!this.subscribed[route]) {
this.subscribed[route] = [];
}
this.subscribed[route].push({ name: key, callback: callback });
if (this.subscribed[route].length === 1) {
const unid = Number.unid();
this.subscribedKey[route] = unid;
WebSocket.get().join('previous::' + route, '', '', (type, data) => {
if (type === 'realtime:event') {
this.message(unid, data.name, data.data);
}
if (type === 'connected') {
this.updateConnected(true);
}
if (type === 'disconnected') {
this.updateConnected(false);
}
});
}
}
//Unsubscribe from channel
unsubscribe(route, callback, key) {
this.logger.debug(`Unsubscribe from ${route}`);
route = (route || '').split('previous::').pop();
if (!key && callback) {
key = callback.name;
}
if (!key) {
key = 'default';
}
if (!this.subscribed[route]) {
this.subscribed[route] = [];
}
var remaining = [];
this.subscribed[route].forEach(c => {
if (c.name !== key) {
remaining.push(c);
}
});
this.subscribed[route] = remaining;
if (this.subscribed[route].length === 0) {
WebSocket.get().leave('previous::' + route, '');
}
}
publish(route, value) {
this.logger.debug(`Publish to ${route}`);
WebSocket.get().send('previous::' + route, value);
}
reconnectIfNeeded(seconds = 30) {
if (new Date().getTime() - this.last_reconnect_call_if_needed.getTime() > seconds * 1000) {
//30 seconds
if (LoginService.currentUserId) {
LoginService.updateUser();
}
this.last_reconnect_call_if_needed = new Date();
}
}
onReconnect(id, callback) {
this.disconnectListeners[id] = callback;
}
offReconnect(id) {
delete this.disconnectListeners[id];
}
updateConnected(state) {
if (state !== this.connected) {
this.connected = state;
this.notify();
}
}
isConnected() {
return this.connected;
}
}
export default new DeprecatedWebsocket();
@@ -1,125 +0,0 @@
import Observable from '@deprecated/CollectionsV1/observable.js';
import Api from '@features/global/framework/api-service';
import ws from '@deprecated/websocket/websocket.js';
import Collections from '@deprecated/CollectionsV1/Collections/Collections.js';
import Workspaces from '@deprecated/workspaces/workspaces.jsx';
import ListenGroups from './listen_groups.js';
import $ from 'jquery';
import JWTStorage from '@features/auth/jwt-storage-service';
import CompanyAPIClient from '../../features/companies/api/company-api-client';
import UserService from '@features/users/services/current-user-service';
import Globals from '@features/global/services/globals-tdrive-app-service';
class Groups extends Observable {
constructor() {
super();
this.setObservableName('groups');
this.currentGroupId = null;
this.user_groups = {};
Globals.window.gs = this;
}
select(group) {
if (this.currentGroupId === group.id) {
return;
}
if (!group.id) {
return;
}
if (this.currentGroupId) {
ListenGroups.cancelListenGroup(this.currentGroupId);
}
ListenGroups.listenGroup(group.id);
this.currentGroupId = group.id;
Workspaces.changeGroup(group);
this.notify();
}
addToUser(group) {
var current_group = Collections.get('groups').find(group.id);
if (current_group && current_group._user_hasnotifications) {
group._user_hasnotifications =
group._user_hasnotifications || current_group._user_hasnotifications;
}
var id = group.id;
Collections.get('groups').updateObject(group);
this.user_groups[id] = Collections.get('groups').known_objects_by_id[id];
}
async getOrderedGroups() {
const userCompanies = await CompanyAPIClient.listCompanies(UserService.getCurrentUserId());
return userCompanies;
//.sort((a, b) => String(a.name).localeCompare(String(b.name)));
}
updateName(name) {
var that = this;
this.loading = true;
this.notify();
Api.post('/ajax/workspace/group/data/name', { groupId: this.currentGroupId, name }, res => {
if (res.errors.length === 0) {
var group = { id: that.currentGroupId, name: name };
Collections.get('groups').updateObject(group);
ws.publish('group/' + group.id, { data: { group: group } });
}
that.loading = false;
that.notify();
});
}
updateLogo(logo) {
this.loading = true;
this.notify();
var route = `${Globals.api_root_url}/ajax/workspace/group/data/logo`;
var data = new FormData();
if (logo !== false) {
data.append('logo', logo);
}
data.append('groupId', this.currentGroupId);
var that = this;
$.ajax({
url: route,
type: 'POST',
data: data,
cache: false,
contentType: false,
processData: false,
headers: {
Authorization: JWTStorage.getAutorizationHeader(),
},
xhrFields: {
withCredentials: true,
},
xhr: function () {
var myXhr = $.ajaxSettings.xhr();
myXhr.onreadystatechange = function () {
if (myXhr.readyState === XMLHttpRequest.DONE) {
that.loading = false;
var resp = JSON.parse(myXhr.responseText);
if (resp.errors.indexOf('badimage') > -1) {
that.error_identity_badimage = true;
} else {
var group = resp.data;
Collections.get('groups').updateObject(group);
ws.publish('group/' + group.id, { data: { group: group } });
}
that.notify();
}
};
return myXhr;
},
});
}
}
const service = new Groups();
export default service;
+3
View File
@@ -0,0 +1,3 @@
export const Groups: { currentGroupId: null | string } = {
currentGroupId: null,
};
@@ -1,47 +0,0 @@
import ws from '@deprecated/websocket/websocket.js';
import Collections from '@deprecated/CollectionsV1/Collections/Collections.js';
class ListenGroups {
constructor() {
this.groups_repository = Collections.get('groups');
this.listenerCount = {};
}
listenGroup(idGroup) {
if (!idGroup) {
return;
}
if (!this.listenerCount[idGroup]) {
this.listenerCount[idGroup] = 0;
}
this.listenerCount[idGroup] += 1;
var that = this;
if (this.listenerCount[idGroup] === 1) {
ws.subscribe('group/' + idGroup, function (uri, data) {
data = data.data;
if (data.group) {
data.group.id = idGroup;
that.groups_repository.updateObject(data.group);
}
});
}
}
cancelListenGroup(idGroup) {
if (!idGroup) {
return;
}
if (this.listenerCount[idGroup]) {
this.listenerCount[idGroup] += -1;
}
if (!this.listenerCount[idGroup] || this.listenerCount[idGroup] === 0) {
ws.unsubscribe('group/' + idGroup);
}
}
}
const service = new ListenGroups();
export default service;
@@ -1,46 +0,0 @@
import ws from '@deprecated/websocket/websocket.js';
import Collections from '@deprecated/CollectionsV1/Collections/Collections.js';
class ListenWorkspace {
constructor() {
this.workspaces_repository = Collections.get('workspaces');
this.listenerCount = {};
}
listenWorkspace(idWorkspace) {
if (!idWorkspace) {
return;
}
if (!this.listenerCount[idWorkspace]) {
this.listenerCount[idWorkspace] = 0;
}
this.listenerCount[idWorkspace] += 1;
var that = this;
if (this.listenerCount[idWorkspace] === 1) {
ws.subscribe('workspace/' + idWorkspace, function (uri, data) {
if (data.workspace) {
data.workspace.id = idWorkspace;
that.workspaces_repository.updateObject(data.workspace);
}
});
}
}
cancelListenWorkspace(idWorkspace) {
if (!idWorkspace) {
return;
}
if (this.listenerCount[idWorkspace]) {
this.listenerCount[idWorkspace] += -1;
}
if (!this.listenerCount[idWorkspace] || this.listenerCount[idWorkspace] === 0) {
ws.unsubscribe('workspace/' + idWorkspace);
}
}
}
const service = new ListenWorkspace();
export default service;
@@ -1,9 +1,6 @@
import DepreciatedCollections from '@deprecated/CollectionsV1/Collections/Collections.js';
import Observable from '@deprecated/CollectionsV1/observable.js';
import { default as popupManager, default as PopupManager } from '@deprecated/popupManager/popupManager.js';
import ws from '@deprecated/websocket/websocket.js';
import Groups from '@deprecated/workspaces/groups.js';
import workspacesApps from '@deprecated/workspaces/workspaces_apps.jsx';
import JWTStorage from '@features/auth/jwt-storage-service';
import loginService from '@features/auth/login-service';
import ConsoleService from '@features/console/services/console-service';
@@ -41,42 +38,11 @@ class Workspaces extends Observable {
}
updateCurrentWorkspaceId(workspaceId, notify = false) {
if (this.currentWorkspaceId !== workspaceId && workspaceId) {
const workspace = DepreciatedCollections.get('workspaces').find(workspaceId);
if (!workspace) {
return;
}
this.currentWorkspaceId = workspaceId;
this.currentWorkspaceIdByGroup[workspace.company_id] = workspaceId;
if (!this.getting_details[workspaceId]) {
this.getting_details[workspaceId] = true;
workspacesApps.unload(this.currentWorkspaceId);
WorkspaceAPIClient.get(workspace.company_id, workspaceId)
.then(workspace => {
if (!workspace) {
this.removeFromUser(workspaceId);
}
DepreciatedCollections.get('workspaces').updateObject(workspace);
notify && this.notify();
// FIXME: What is this???
setTimeout(() => {
this.getting_details[workspaceId] = false;
}, 10000);
})
.catch(() => {
this.removeFromUser(workspaceId);
});
}
}
}
updateCurrentCompanyId(companyId, notify = false) {
if (this.currentGroupId !== companyId && companyId) {
Groups.currentGroupId = companyId;
this.currentGroupId = companyId;
notify && this.notify();
}
@@ -228,10 +194,7 @@ class Workspaces extends Observable {
name,
});
this.logger.debug('Workspace updated', result);
DepreciatedCollections.get('workspaces').updateObject({
id: this.currentWorkspaceId,
name,
});
} catch (err) {
this.logger.error('Can not update the workspace', err);
}
@@ -275,9 +238,6 @@ class Workspaces extends Observable {
that.error_identity_badimage = true;
that.notify();
} else {
var update = resp.data;
DepreciatedCollections.get('workspaces').updateObject(update);
ws.publish('workspace/' + update.id, { workspace: update });
that.notify();
}
}
@@ -300,10 +260,6 @@ class Workspaces extends Observable {
}
window.location.reload();
}
getCurrentWorkspace() {
return DepreciatedCollections.get('workspaces').find(this.currentWorkspaceId) || {};
}
}
export default new Workspaces();
@@ -1,262 +0,0 @@
import React from 'react';
import Observable from '@deprecated/CollectionsV1/observable.js';
import CurrentUser from '@deprecated/user/CurrentUser';
import Api from '@features/global/framework/api-service';
import ws from '@deprecated/websocket/websocket.js';
import Collections from '@deprecated/CollectionsV1/Collections/Collections.js';
import Groups from './groups.js';
import Workspaces from './workspaces.jsx';
import Globals from '@features/global/services/globals-tdrive-app-service';
import Icon from '@components/icon/icon';
import { getUser } from '@features/users/hooks/use-user-list';
import Login from '@features/auth/login-service';
import { Folder, Calendar, CheckSquare, Hexagon } from 'react-feather';
import { getCompanyApplication as getApplication } from '@features/applications/state/company-applications';
class WorkspacesApps extends Observable {
constructor() {
super();
this.setObservableName('workspaces_apps');
Collections.get('applications');
var options = {
base_url: 'applications',
use_cache: true,
};
Collections.updateOptions('applications', options);
this.apps_by_workspace = {};
this.apps_by_group = {};
this.findingApp = {};
this.did_first_load = {};
Globals.window.workspacesApps = this;
this.loading_by_workspace = {};
}
notifyApp(app_id, type, event, data, workspace_id = undefined, group_id = undefined) {
workspace_id = workspace_id || Workspaces.currentWorkspaceId;
group_id = group_id || Workspaces.currentGroupId;
const connection_id = CurrentUser.unique_connection_id;
// eslint-disable-next-line no-redeclare
data = {
workspace_id: workspace_id,
company_id: group_id,
app_id: app_id,
type: type,
name: event,
data: { user: Collections.get('users').find(Login.currentUserId), ...data },
content: {},
connection_id: connection_id,
};
Api.post(
`/internal/services/applications/v1/applications/${app_id}/event`,
data,
res => {},
).then();
}
unload(workspace_id) {
ws.unsubscribe('workspace_apps/' + workspace_id);
}
load(workspace_id, reset_offset, options) {
if (!workspace_id) {
return;
}
if (!options) {
options = {};
}
if (this.loading_by_workspace[workspace_id]) {
return false;
}
this.loading_by_workspace[workspace_id] = true;
this.notify();
var data = {
workspace_id: workspace_id,
};
var loadApps = data => {
if (data.length > 0) {
this.apps_by_workspace[data[0].workspace_id] = {};
}
data.forEach(item => {
this.apps_by_workspace[item.workspace_id][item.app.id] = item.app;
Collections.get('applications').updateObject(item.app);
});
this.did_first_load[workspace_id] = true;
this.notify();
};
if (options.apps) {
this.loading_by_workspace[workspace_id] = false;
loadApps(options.apps);
} else {
Api.post('/ajax/workspace/apps/get', data, res => {
if (res.data) {
loadApps(res.data);
}
this.loading_by_workspace[workspace_id] = false;
});
}
}
loadGroupApps() {
Api.post('/ajax/workspace/group/apps/get', { group_id: Groups.currentGroupId }, res => {
if (res.data) {
res.data.forEach(item => {
var app_link = {
workspace_count: item.workspace_count,
workspace_default: item.workspace_default,
app: item.app,
};
if (!this.apps_by_group) this.apps_by_group = {};
if (!this.apps_by_group[item.group_id]) this.apps_by_group[item.group_id] = {};
this.apps_by_group[item.group_id][item.app.id] = app_link;
});
this.notify();
}
});
}
recieveWS(res) {
if (res.type === 'add') {
var app_link = {
workspace_count: res.workspace_app.workspace_count,
workspace_default: res.workspace_app.workspace_default,
app: res.workspace_app.app,
};
this.apps_by_workspace[res.workspace_app.workspace_id][res.workspace_app.app.id] =
res.workspace_app.app;
this.apps_by_group[res.workspace_app.group_id][res.workspace_app.app.id] = app_link;
Collections.get('applications').completeObject(
res.workspace_app.app,
res.workspace_app.app.front_id,
);
} else if (res.type === 'remove') {
delete this.apps_by_workspace[res.workspace_app.workspace_id][res.workspace_app.app.id];
}
this.notify();
}
forceInEntreprise(id) {
var data = {
group_id: Groups.currentGroupId,
app_id: id,
};
if (
this.apps_by_group[Groups.currentGroupId] &&
this.apps_by_group[Groups.currentGroupId][id]
) {
this.apps_by_workspace[Workspaces.currentWorkspaceId][id] =
this.apps_by_group[Groups.currentGroupId][id].app;
}
Api.post('/ajax/workspace/group/application/force', data, function (res) {});
this.notify();
}
forceRemoveFromEntreprise(id) {
var data = {
group_id: Groups.currentGroupId,
app_id: id,
};
if (
this.apps_by_group[Groups.currentGroupId] &&
this.apps_by_group[Groups.currentGroupId][id]
) {
delete this.apps_by_group[Groups.currentGroupId][id];
}
if (
this.apps_by_workspace[Workspaces.currentWorkspaceId] &&
this.apps_by_workspace[Workspaces.currentWorkspaceId][id]
) {
delete this.apps_by_workspace[Workspaces.currentWorkspaceId][id];
}
Api.post('/ajax/workspace/group/application/remove', data, function (res) {});
this.notify();
}
defaultForWorkspacesInEntreprise(id, state) {
var data = {
group_id: Groups.currentGroupId,
app_id: id,
state: state,
};
if (
this.apps_by_group[Groups.currentGroupId] &&
this.apps_by_group[Groups.currentGroupId][id]
) {
this.apps_by_group[Groups.currentGroupId][id].workspace_default = state;
}
Api.post('/ajax/workspace/group/workspacedefault/set', data, function (res) {});
this.notify();
}
openAppPopup(app_id) {}
getAppIcon(app, feather = false) {
if (app && app?.identity?.code) {
switch (app?.identity?.code.toLocaleLowerCase()) {
case 'tdrive_calendar':
return feather ? Calendar : 'calendar-alt';
case 'tdrive_drive':
return feather ? Folder : 'folder';
case 'tdrive_tasks':
return feather ? CheckSquare : 'check-square';
default:
return app.identity?.icon || (feather ? Hexagon : 'puzzle-piece');
}
}
return feather ? Hexagon : 'puzzle-piece';
}
getAppIconComponent(item, options = {}) {
const application = getApplication(item.application_id ? item.application_id : item.id);
const IconType = this.getAppIcon(application, true);
if (item.code === 'jitsi') {
return (
<div
className="menu-app-icon"
style={item.icon_url ? { backgroundImage: 'url(' + item.icon_url + ')' } : {}}
/>
);
} else {
if (typeof IconType === 'string') {
return (
<Icon
type={IconType}
style={{ width: options.size || 18, height: options.size || 18 }}
className="small-right-margin"
/>
);
} else {
return <IconType size={options.size || 18} className="small-right-margin" />;
}
}
}
}
const workspaces = new WorkspacesApps();
export default workspaces;
@@ -3,7 +3,6 @@ import Logger from 'app/features/global/framework/logger-service';
import AccessRightsService from 'app/features/workspace-members/services/workspace-members-access-rights-service';
import CurrentUser from 'app/deprecated/user/CurrentUser';
import JWT from 'app/features/auth/jwt-storage-service';
import WorkspacesListener from '../../workspaces/services/workspaces-listener-service';
import LocalStorage from '../../global/framework/local-storage-service';
import WebSocket from '../../global/types/websocket-types';
@@ -29,14 +28,11 @@ class Application {
const ws = WebSocket.get();
ws.connect();
WorkspacesListener.startListen();
AccessRightsService.resetLevels();
CurrentUser.start();
}
stop(): void {
WorkspacesListener.cancelListen();
LocalStorage.clear();
JWT.clear();
}
@@ -19,7 +19,6 @@ export const onChangeCompanyApplications = (companyId: string, _applications: Ap
(applications || []).forEach(a => {
if (!_.isEqual(a, companyApplicationMap.get(a.id))) {
companyApplicationMap.set(a.id, a);
Collections.get('applications').updateObject(a);
}
});
};
@@ -21,15 +21,7 @@ import LocalStorage from '@features/global/framework/local-storage-service';
import Globals from '@features/global/services/globals-tdrive-app-service';
type AccountType = 'remote' | 'internal';
export type LoginState =
| ''
| 'app'
| 'error'
| 'signin'
| 'verify_mail'
| 'forgot_password'
| 'logged_out'
| 'logout';
export type LoginState = '' | 'app' | 'error' | 'signin' | 'logged_out' | 'logout';
type InitState = '' | 'initializing' | 'initialized';
@TdriveService('AuthService')
@@ -10,7 +10,6 @@ import _ from 'lodash';
import RouterService from '@features/router/services/router-service';
import WorkspacesService from '@deprecated/workspaces/workspaces.jsx';
import AccessRightsService from '@features/workspace-members/services/workspace-members-access-rights-service';
import Groups from '@deprecated/workspaces/groups.js';
import LoginService from '@features/auth/login-service';
import UserAPIClient from '@features/users/api/user-api-client';
import { useRealtimeRoom } from '@features/global/hooks/use-realtime';
@@ -72,7 +71,6 @@ export const useCurrentCompany = () => {
//Always set the current company in localstorage to open it automatically later
if (routerCompanyId && company) {
//Depreciated retrocompatibility
Groups.addToUser(company);
AccessRightsService.updateCompanyLevel(
company.id,
company.role === 'admin' || company.role === 'owner'
@@ -21,7 +21,6 @@ export const CompaniesState = atomFamily<CompanyType | null, string>({
onSet(company => {
if (company?.id) {
companies[company.id] = company;
Collections.get('groups').updateObject(_.cloneDeep(company));
}
});
},
@@ -1,9 +1,8 @@
import FileUploadService from '@features/files/services/file-upload-service';
import RouterServices from '@features/router/services/router-service';
import { useRecoilState, useRecoilValue } from 'recoil';
import { PendingFilesListState } from '../state/atoms/pending-files-list';
import { CurrentTaskSelector } from '../state/selectors/current-task';
import RouterServices from '@features/router/services/router-service';
import { AttachedFileType } from '@features/files/types/file';
export const useUpload = () => {
const { companyId } = RouterServices.getStateFromRoute();
@@ -2,15 +2,14 @@
/* eslint-disable @typescript-eslint/ban-types */
import { v1 as uuid } from 'uuid';
import { FileType, PendingFileType } from '@features/files/types/file';
import JWTStorage from '@features/auth/jwt-storage-service';
import RouterServices from '@features/router/services/router-service';
import { FileType, PendingFileType } from '@features/files/types/file';
import Resumable from '@features/files/utils/resumable';
import Logger from '@features/global/framework/logger-service';
import RouterServices from '@features/router/services/router-service';
import _ from 'lodash';
import FileUploadAPIClient from '../api/file-upload-api-client';
import { isPendingFileStatusPending } from '../utils/pending-files';
import Logger from '@features/global/framework/logger-service';
import _ from 'lodash';
import { AttachedFileType } from '@features/files/types/file';
export enum Events {
ON_CHANGE = 'notify',
@@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/ban-types */
import Observable from '@deprecated/CollectionsV1/observable';
import DateTime from '@features/global/utils/datetime';
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-http-backend';
@@ -66,7 +65,6 @@ class LanguagesService extends Observable {
}
await i18n.changeLanguage(language);
DateTime.setCurrentLanguage(language);
this.notify();
}
@@ -1,22 +0,0 @@
import Observable from '@deprecated/CollectionsV1/observable.js';
class Autocomplete extends Observable {
constructor() {
super();
this.observableName = 'autocompleteService';
this.isOpen = false;
}
open() {
this.isOpen = true;
this.notify();
}
close() {
this.isOpen = false;
this.notify();
}
}
const autocompleteService = new Autocomplete();
export default autocompleteService;
@@ -1,39 +0,0 @@
import $ from 'jquery';
class DroppableManager {
constructor() {
this.drop = {};
this.draggingData = {};
this.dragging = false;
}
over(key, callback, event) {
this.drop[key] = {
callback: callback,
element: event.target,
};
}
out(key) {
this.drop[key] = undefined;
delete this.drop[key];
}
up() {
var that = this;
if (!that.draggingData.type || !that.draggingData.data || that.draggingData.data.length == 0) {
return;
}
if (!this.dragging) {
return;
}
this.drop.forEach(el => {
if (el && el.callback) {
el.callback(that.draggingData);
}
});
}
}
const instanceDroppableManager = new DroppableManager();
export default instanceDroppableManager;
@@ -1,191 +0,0 @@
import moment from 'moment';
import 'moment/locale/ru';
import 'moment/locale/fr';
import 'moment/locale/de';
import 'moment/locale/ja';
import 'moment/locale/es';
import Observable from '@deprecated/CollectionsV1/observable.js';
import UserService from '@features/users/services/current-user-service';
import Globals from '@features/global/services/globals-tdrive-app-service';
class DateTime extends Observable {
constructor() {
super();
if (!Globals.window.navigator) {
Globals.window.navigator = {};
}
this.observableName = 'dateTimeService';
this.locale = this.cleanLocal(
Globals.window.navigator.userLanguage || Globals.window.navigator.language || 'en',
);
}
getCurrentLanguage() {
return this.locale;
}
setCurrentLanguage(lang) {
this.locale = this.cleanLocal(lang);
moment.locale(this.locale);
this.notify();
}
cleanLocal(string) {
if (string.split('-').length > 1) {
return string.split('-')[0];
}
return string;
}
getDefaultTimeFormat() {
var h24list = [
'af',
'ar-dz',
'ar-kw',
'ar-ly',
'ar-ma',
'ar-sa',
'ar-tn',
'ar',
'az',
'be',
'bg',
'bn',
'bo',
'br',
'bs',
'ca',
'cs',
'cv',
'cy',
'da',
'de-at',
'de-ch',
'de',
'dv',
'el',
'en-au',
'en-ca',
'en-gb',
'en-ie',
'en-nz',
'eo',
'es-do',
'es',
'et',
'eu',
'fa',
'fi',
'fo',
'fr-ca',
'fr-ch',
'fr',
'fy',
'gd',
'gl',
'gom-latn',
'he',
'hi',
'hr',
'hu',
'hy-am',
'id',
'is',
'it',
'ja',
'jv',
'ka',
'kk',
'km',
'kn',
'ko',
'ky',
'lb',
'lo',
'lt',
'lv',
'me',
'mi',
'mk',
'ml',
'mr',
'ms-my',
'ms',
'my',
'nb',
'ne',
'nl-be',
'nl',
'nn',
'pa-in',
'pl',
'pt-br',
'pt',
'ro',
'ru',
'sd',
'se',
'si',
'sk',
'sl',
'sq',
'sr-cyrl',
'sr',
'ss',
'sv',
'sw',
'ta',
'te',
'tet',
'th',
'tl-ph',
'tlh',
'tr',
'tzl',
'tzm-latn',
'tzm',
'uk',
'ur',
'uz-latn',
'uz',
'vi',
'x-pseudo',
'yo',
'zh-cn',
'zh-hk',
'zh-tw',
];
if (
h24list.indexOf(
(UserService.getCurrentUser() || {}).language ||
Globals.window.navigator.language ||
Globals.window.navigator.userLanguage ||
'en',
) >= 0
) {
return 'H:mm';
}
return 'LT';
}
isDateFirstInFormat() {
var numbers = moment().format('L').split('/');
if (numbers[0] === new Date().getDate()) {
return true;
} else {
return false;
}
}
getDefaultDateFormat() {
/*var numbers = moment().format("L").split("/");
if(numbers[0] == (new Date()).getDate()){
return "DD/MM/YYYY";
}else{
return "MM/DD/YYYY";
}*/
return 'LL'; //Default format for country better but US is "May 11, 2019" instead of "11 may 2019"
}
}
var x = new DateTime();
Globals.window.dateTimeService = x;
export default x;
@@ -1,31 +0,0 @@
import Observable from '@deprecated/CollectionsV1/observable.js';
import Api from '@features/global/framework/api-service';
class WorkspacePicker extends Observable {
constructor() {
super();
this.observableName = 'workspacePicker';
this.searchedWorkspace = null;
this.refresh = false;
this.searchInput = '';
this.searchLabel = '';
this.isSearching = false;
}
getWorkspaceByName(name) {
var data = {
name: name,
};
this.isSearching = true;
this.notify();
var that = this;
Api.post('/ajax/workspace/getByName', data, function (res) {
that.searchedWorkspace = res.data.workspace;
that.isSearching = false;
that.notify();
});
}
}
const wsPicker = new WorkspacePicker();
export default wsPicker;
@@ -1,13 +1,13 @@
import { delayRequest } from '@features/global/utils/managedSearchRequest';
import { useRecoilState, useRecoilValue } from 'recoil';
import { SearchInputState } from '../state/search-input';
import { DriveApiClient } from '@features/drive/api-client/api-client';
import { LoadingState } from '@features/global/state/atoms/Loading';
import { SearchDriveItemsResultsState } from '../state/search-drive-items-result';
import { RecentDriveItemsState } from '../state/recent-drive-items';
import _ from 'lodash';
import { useGlobalEffect } from '@features/global/hooks/use-global-effect';
import { LoadingState } from '@features/global/state/atoms/Loading';
import { delayRequest } from '@features/global/utils/managedSearchRequest';
import useRouterCompany from '@features/router/hooks/use-router-company';
import _ from 'lodash';
import { useRecoilState, useRecoilValue } from 'recoil';
import { RecentDriveItemsState } from '../state/recent-drive-items';
import { SearchDriveItemsResultsState } from '../state/search-drive-items-result';
import { SearchInputState } from '../state/search-input';
import { useSearchModal } from './use-search';
export const useSearchDriveItemsLoading = () => {
@@ -6,7 +6,6 @@ import { useRecoilState } from 'recoil';
import { CurrentUserState } from '../state/atoms/current-user';
import { useRealtimeRoom } from '@features/global/hooks/use-realtime';
import Languages from '@features/global/services/languages-service';
import ConfiguratorsManager from '@deprecated/Configurators/ConfiguratorsManager.js';
import { RealtimeApplicationEvent } from '@features/global/types/realtime-types';
import { useSetUserList } from './use-user-list';
@@ -41,25 +40,6 @@ export const useCurrentUser = () => {
return { user, refresh, updateStatus };
};
const applicationEventHandler = (event: RealtimeApplicationEvent) => {
switch (event.action) {
case 'configure':
if (event.form) {
ConfiguratorsManager.openConfigurator(
event.application,
event.form,
event.hidden_data,
event.configurator_id,
);
} else {
ConfiguratorsManager.closeConfigurator(event.application);
}
break;
default:
console.error(`Unknown application action: ${event.action}`);
}
};
export const useCurrentUserRealtime = () => {
const { user, refresh } = useCurrentUser();
const room = UserAPIClient.websocket(user?.id || '');
@@ -74,9 +54,6 @@ export const useCurrentUserRealtime = () => {
refresh();
}, 1000) as any;
break;
case 'application':
applicationEventHandler(resource);
break;
default:
console.error('Unknown resource type');
}
@@ -1,26 +0,0 @@
import { useState, useEffect } from 'react';
import { isArray } from 'lodash';
import UserListenerService from '@features/users/services/listen-users-service';
import Collections from '@deprecated/CollectionsV1/Collections/Collections';
import UsersService from '@features/users/services/current-user-service';
import userAsyncGet from '@features/users/utils/async-get';
export const useUsersListener = (usersIds: string[] = []) => {
const users = (isArray(usersIds) ? usersIds : []).filter(
e => (usersIds.length || 0) === 1 || e !== UsersService.getCurrentUserId(),
);
Collections.get('users').useListener(useState, users);
useEffect(() => {
users.forEach(userId => {
UserListenerService.listenUser(userId);
userAsyncGet(userId);
});
return () => {
users.forEach(userId => UserListenerService.cancelListenUser(userId));
};
}, [users]);
return users;
};
@@ -1,156 +0,0 @@
/* eslint-disable @typescript-eslint/no-this-alias */
/* eslint-disable @typescript-eslint/no-explicit-any */
import ws from '@deprecated/websocket/websocket';
import Collections from '@deprecated/CollectionsV1/Collections/Collections';
import UserService from './current-user-service';
import Globals from '@features/global/services/globals-tdrive-app-service';
import userAsyncGet from '@features/users/utils/async-get';
type Timeout = ReturnType<typeof setTimeout>;
class ListenUsers {
users_repository: any;
connectedPing: { [key: string]: Timeout };
listenerCount: { [key: string]: number };
pingTimeouts: { [key: string]: Timeout };
was_connected_last_check: { [key: string]: boolean };
lastPong: number;
constructor() {
this.users_repository = Collections.get('users');
this.listenerCount = {};
this.connectedPing = {};
this.pingTimeouts = {};
this.was_connected_last_check = {};
this.lastPong = 0;
(Globals.window as any).listenUsers = this;
}
/**
* Check if the given user is active: new Date().getTime() - ws.lastAlive < 1000*60*5
*
* @param idUser
*/
ping(idUser: string): void {
ws.publish(`users/${idUser}`, {
ping: true,
user: { connected: true, id: UserService.getCurrentUserId() },
});
if (this.pingTimeouts[idUser]) {
clearTimeout(this.pingTimeouts[idUser]);
}
this.pingTimeouts[idUser] = setTimeout(() => {
//Only say this to me !
const user = Collections.get('users').find(idUser);
if (user) {
if (user.connected) {
user.connected = false;
this.users_repository.updateObject(user);
}
}
this.was_connected_last_check[idUser] = false;
}, 5000);
}
pong(): void {
this.lastPong = new Date().getTime();
ws.publish(`users/${UserService.getCurrentUserId()}`, {
user: { connected: true, id: UserService.getCurrentUserId() },
});
}
listenUser(idUser: string): void {
if (!idUser) {
return;
}
if (!this.listenerCount[idUser]) {
this.listenerCount[idUser] = 0;
}
this.listenerCount[idUser] += 1;
const that = this;
if (this.listenerCount[idUser] === 1) {
ws.subscribe(
`users/${idUser}`,
(_uri: string, data: any) => {
/*if (idUser == UserServiceImpl.getCurrentUserId()) {
if (data.ping) {
that.pong();
}
}*/
if (data.user && data.user.id) {
/*that.setUserPingTimeout(data.user.id);
if (data.user.connected && that.pingTimeouts[idUser]) {
clearTimeout(that.pingTimeouts[idUser]);
}*/
if (that.users_repository.find(data.user.id)) {
that.was_connected_last_check[idUser] = data.user.connected;
if (
data.user.username ||
data.user.notifications_preferences ||
data.user.connected !== that.users_repository.find(data.user.id).connected
) {
that.users_repository.updateObject(data.user);
}
} else if (data.user.id) {
userAsyncGet(data.user.id);
}
}
},
null,
);
if (idUser !== UserService.getCurrentUserId()) {
/*setTimeout(() => {
if (idUser != UserServiceImpl.getCurrentUserId()) {
this.setUserPingTimeout(idUser);
this.ping(idUser);
}
}, 1000);
if (new Date().getTime() - this.lastPong > 60000) {
this.pong();
}*/
}
}
}
setUserPingTimeout(idUser: string): void {
if (this.connectedPing[idUser]) {
clearTimeout(this.connectedPing[idUser]);
}
this.connectedPing[idUser] = setTimeout(() => {
this.ping(idUser);
this.setUserPingTimeout(idUser);
}, 600000);
}
cancelListenUser(idUser: string) {
if (!idUser) {
return;
}
if (this.listenerCount[idUser]) {
this.listenerCount[idUser] += -1;
}
if (!this.listenerCount[idUser] || this.listenerCount[idUser] === 0) {
ws.unsubscribe(`users/${idUser}`, null, null);
if (this.connectedPing[idUser]) {
clearInterval(this.connectedPing[idUser]);
}
}
}
}
const service = new ListenUsers();
(Globals.services as any) = service;
export default service;
@@ -2,18 +2,14 @@
/* eslint-disable @typescript-eslint/no-this-alias */
/* eslint-disable @typescript-eslint/no-empty-function */
/* eslint-disable @typescript-eslint/no-explicit-any */
import Languages from '@features/global/services/languages-service';
import Observable from '@deprecated/CollectionsV1/observable.js';
import User from '@features/users/services/current-user-service';
import Api from '@features/global/framework/api-service';
import ws from '@deprecated/websocket/websocket.js';
import Collections from '@deprecated/CollectionsV1/Collections/Collections.js';
import workspaceService from '@deprecated/workspaces/workspaces.jsx';
import Numbers from '@features/global/utils/Numbers';
import WorkspaceUserRights from '@features/workspaces/services/workspace-user-rights-service';
import CurrentUser from '@deprecated/user/CurrentUser';
import Api from '@features/global/framework/api-service';
import AlertManager from '@features/global/services/alert-manager-service';
import Globals from '@features/global/services/globals-tdrive-app-service';
import Languages from '@features/global/services/languages-service';
import User from '@features/users/services/current-user-service';
import WorkspaceUserRights from '@features/workspaces/services/workspace-user-rights-service';
const prefixRoute = '/internal/services/workspaces/v1';
@@ -54,35 +50,13 @@ class WorkspacesUsers extends Observable {
(Globals.window as any).workspaceUserService = this;
}
getAdminLevel(idWorkspace = workspaceService.currentWorkspaceId) {
const levels = Collections.get('workspaces').find(idWorkspace).levels;
if (levels) {
for (let i = 0; i < levels.length; i++) {
if (levels[i].admin) {
return levels[i];
}
}
}
return false;
}
getDefaultLevel(idWorkspace = workspaceService.currentWorkspaceId) {
const levels = Collections.get('workspaces').find(idWorkspace).levels;
if (levels) {
for (let i = 0; i < levels.length; i++) {
if (levels[i].default) {
return levels[i];
}
}
}
return false;
}
isGroupManager() {}
getLevel(idLevel: string) {
const levels = Collections.get('workspaces').find(workspaceService.currentWorkspaceId).levels;
for (let i = 0; i < levels.length; i++) {
if (idLevel === levels[i].id) {
return levels[i];
}
}
return false;
}
@@ -90,113 +64,6 @@ class WorkspacesUsers extends Observable {
return (this.users_by_workspace || {})[workspace_id] || {};
}
unload(workspace_id: string) {
ws.unsubscribe('workspace_users/' + workspace_id, null, null);
}
load(workspace_id: string, reset_offset: string, options: any) {
if (!options) {
options = {};
}
const that = this;
const workspace = Collections.get('workspaces').find(workspace_id);
if (!workspace) {
return;
}
const group_id = workspace?.group?.id || workspace.company_id;
if (!this.users_by_group[group_id]) {
this.users_by_group[group_id] = {};
}
if (!this.users_by_workspace[workspace_id]) {
this.users_by_workspace[workspace_id] = {};
}
if (!this.offset_by_workspace_id[workspace_id] || reset_offset) {
this.offset_by_workspace_id[workspace_id] = [0, false];
}
if (!this.offset_by_group_id[group_id] || reset_offset) {
this.offset_by_group_id[group_id] = [0, false];
}
const loadMembers = (data: any) => {
if (!data) {
return;
}
if (typeof data.members === 'object' && data.members.members) {
data = data.members;
}
if (data.members) {
(data.members || []).forEach((item: any) => {
if (
!that.offset_by_workspace_id[workspace_id][1] ||
Numbers.compareTimeuuid(item.user.id, that.offset_by_workspace_id[workspace_id][1]) > 0
) {
that.offset_by_workspace_id[workspace_id][1] = item.user.id;
}
that.offset_by_workspace_id[workspace_id][0]++;
Collections.get('users').completeObject(item.user, item.user.front_id);
that.users_by_group[group_id][item.user.id] = item;
that.users_by_workspace[workspace_id][item.user.id] = item;
});
that.notify();
}
if (data.mails) {
that.membersPending = data.mails || [];
that.notify();
}
if (data.stats.total_members > 1 && WorkspaceUserRights.hasWorkspacePrivilege()) {
CurrentUser.updateTutorialStatus('did_invite_collaborators');
}
};
const data = {
workspaceId: workspace_id,
max: this.offset_by_workspace_id[workspace_id][0] === 0 ? 100 : 40,
};
if (options.members) {
loadMembers(options.members || []);
} else {
Api.post('/ajax/workspace/members/list', data, (res: any) => {
if (res.data) {
loadMembers({ members: res.data });
}
});
Api.post('/ajax/workspace/members/pending', data, (res: any) => {
if (res.data) {
loadMembers({ mails: res.data });
}
});
loadMembers(options.members || []);
}
const loadGroupUsers = (data: any) => {
data.users.forEach((item: any) => {
if (
!that.offset_by_group_id[group_id][1] ||
Numbers.compareTimeuuid(item.user.id, that.offset_by_group_id[group_id][1]) > 0
) {
that.offset_by_group_id[group_id][1] = item.user.id;
}
that.offset_by_group_id[group_id][0]++;
Collections.get('users').completeObject(item.user, item.user.front_id);
that.users_by_group[group_id][item.user.id] = item;
});
that.notify();
};
if (options.group_users) {
loadMembers(options.group_users);
}
}
canShowUserInWorkspaceList(member: any) {
// if user is interne or wexterne => no restriction
if (!WorkspaceUserRights.isInvite()) {
@@ -206,28 +73,6 @@ class WorkspacesUsers extends Observable {
// if other user is interne or wexterne
return true;
}
// check in all channel if 2 chavite are in the same channel
const channelsInWorkspace = Collections.get('channels').findBy({
direct: false,
application: false,
original_workspace: workspaceService.currentWorkspaceId,
});
for (let i = 0; i < channelsInWorkspace.length; i++) {
// check in all channel if 2 chavite are in the same channel
if (channelsInWorkspace[i].ext_members) {
let bothAreInChannel = 0; // if 1 : one of 2 users searched is in channel as chavite, 2 : both are in channel as chavite
const extMembers = channelsInWorkspace[i].ext_members;
for (let j = 0; j < extMembers.length; j++) {
if (extMembers[j] === member || extMembers[j] === CurrentUser.get().id) {
bothAreInChannel++;
if (bothAreInChannel >= 2) {
return true;
}
}
}
}
}
}
return false;
}
@@ -44,13 +44,6 @@ export const useWorkspacesCommons = (companyId = '') => {
);
}
//Retro compatibility
workspaces?.forEach(w => {
Collections.get('workspaces').updateObject(_.cloneDeep(w));
AccessRightsService.updateLevel(w.id, w.role as RightsOrNone);
});
//End
return { workspaces, loading, refresh };
};
@@ -1,53 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ws from '@deprecated/websocket/websocket.js';
import Workspaces from '@deprecated/workspaces/workspaces.jsx';
import User from '@features/users/services/current-user-service';
import LoginService from '@features/auth/login-service';
import { RightsOrNone } from '../../workspace-members/services/workspace-members-access-rights-service';
import Logger from '@features/global/framework/logger-service';
type WebsocketWorkspace = {
type: 'remove' | 'add' | 'update_group_privileges' | 'update_workspace_level';
group_id: string;
workspace_id: string;
level: RightsOrNone;
workspace: any; // TODO
privileges: any; // TODO
};
class WorkspacesListener {
private logger: Logger.Logger;
constructor() {
this.logger = Logger.getLogger('WorkspacesListener');
}
startListen() {
this.logger.debug('Start listener');
ws.subscribe(
`workspaces_of_user/${User.getCurrentUserId()}`,
(_uri: any, data: WebsocketWorkspace) => {
this.logger.debug('Got a message', data);
LoginService.updateUser();
if (data.workspace) {
if (data.type === 'remove') {
Workspaces.removeFromUser(data.workspace);
Workspaces.notify();
} else if (data.type === 'add') {
Workspaces.notify();
}
}
},
null,
);
}
cancelListen() {
this.logger.debug('Cancel listener');
ws.unsubscribe(`workspaces_of_user/${User.getCurrentUserId()}`, null, null);
}
}
export default new WorkspacesListener();
@@ -22,9 +22,6 @@ export const WorkspaceListStateFamily = atomFamily<WorkspaceType[], string>({
({ onSet }) => {
onSet(workspaces => {
workspacesCompanyMap[companyId] = workspaces;
workspaces.map(w => {
Collections.get('workspaces').updateObject(_.cloneDeep(w));
});
});
},
],
@@ -2,7 +2,7 @@
import classNames from 'classnames';
import React, { Suspense, useState } from 'react';
import ChatUploadsViewer from '@components/file-uploads/uploads-viewer';
import UploadsViewer from '@components/file-uploads/uploads-viewer';
import { useFeatureToggles } from '@components/locked-features-components/feature-toggles-hooks';
import MenusBodyLayer from '@components/menus/menus-body-layer.jsx';
import ModalComponent from '@components/modal/modal-component';
@@ -16,7 +16,6 @@ import ConnectionIndicator from 'components/connection-indicator/connection-indi
import NewVersionComponent from 'components/new-version/new-version-component';
import PopupComponent from 'components/popup-component/popup-component.jsx';
import SearchPopup from 'components/search-popup/search-popup';
import DriveUploadViewer from 'components/uploads/upload-viewer.jsx';
import MainView from './body';
import DownloadAppBanner from '@components/download-app-banner/download-app-banner';
@@ -94,12 +93,11 @@ export default React.memo((): JSX.Element => {
{PopupService.isOpen() && <PopupComponent key="PopupComponent" />}
{page}
<MenusBodyLayer />
<DriveUploadViewer />
<Viewer />
<ModalComponent />
<SearchPopup />
<ConnectionIndicator />
<ChatUploadsViewer />
<UploadsViewer />
</>
);
});
@@ -1,76 +0,0 @@
import Languages from '@features/global/services/languages-service';
import * as Text from '@atoms/text';
import { ToasterService as Toaster } from '@features/global/services/toaster-service';
import UserAPIClient from '@features/users/api/user-api-client';
import { useCurrentUser } from '@features/users/hooks/use-current-user';
import Radio from 'components/inputs/radio.jsx';
export type preferencesType = Record<string, unknown>;
export type AssistantPreferencesType = {
user_id: string;
company_id: string;
workspace_id: string;
preferences: preferencesType;
};
let locked = false;
export default () => {
const { user, refresh } = useCurrentUser();
const setPreferences = async (value: 'all' | 'nothing' | 'metadata') => {
if (locked) return;
locked = true;
try {
await UserAPIClient.setUserPreferences({
knowledge_graph: value,
});
await refresh();
Toaster.success(Languages.t('scenes.apps.account.assistant.success'));
} catch (err) {
Toaster.error('' + err);
}
locked = false;
};
return (
<>
<div className="title">{Languages.t('scenes.apps.account.assistant.title')}</div>
<Text.Info>{Languages.t('scenes.apps.account.assistant.description')}</Text.Info>
<div className="parameters_form" style={{ maxWidth: 'none', paddingTop: 10 }}>
<Radio
small
label={Languages.t('scenes.apps.account.assistant.share.nothing')}
value={user?.preferences?.knowledge_graph === 'nothing'}
onChange={() => {
setPreferences('nothing');
}}
/>
<br />
<Radio
small
label={Languages.t('scenes.apps.account.assistant.share.metadata')}
value={
!user?.preferences?.knowledge_graph || user?.preferences?.knowledge_graph === 'metadata'
}
onChange={() => {
setPreferences('metadata');
}}
/>
<br />
<Radio
small
label={Languages.t('scenes.apps.account.assistant.share.all')}
value={user?.preferences?.knowledge_graph === 'all'}
onChange={() => {
setPreferences('all');
}}
/>
</div>
</>
);
};
@@ -1,26 +1,23 @@
/* eslint-disable react/prop-types */
/* eslint-disable react/jsx-key */
import React, { Component } from 'react';
import { Component } from 'react';
import Languages from '@features/global/services/languages-service';
import Collections from '@deprecated/CollectionsV1/Collections/Collections.js';
import LoginService from '@features/auth/login-service';
import popupManager from '@deprecated/popupManager/popupManager.js';
import userService from '@features/users/services/current-user-service';
import currentUserService from '@deprecated/user/CurrentUser';
import uploadService from '@deprecated/uploadManager/upload-manager.js';
import ButtonWithTimeout from 'components/buttons/button-with-timeout.jsx';
import Attribute from 'components/parameters/attribute.jsx';
import Assistant from './Pages/Assistant';
import MenuList from 'components/menus/menu-component.jsx';
import './UserParameter.scss';
import Input from 'components/inputs/input.jsx';
import { Button } from '../../../../atoms/button/button';
import { ExternalLinkIcon } from '@heroicons/react/outline';
import InitService from '@features/global/services/init-service';
import * as Text from '../../../../atoms/text';
import workspaceService from '@deprecated/workspaces/workspaces.jsx';
import { getFilesTree } from '@components/uploads/file-tree-utils';
import Collections from '@deprecated/CollectionsV1/Collections/Collections.js';
import popupManager from '@deprecated/popupManager/popupManager.js';
import currentUserService from '@deprecated/user/CurrentUser';
import LoginService from '@features/auth/login-service';
import InitService from '@features/global/services/init-service';
import Languages from '@features/global/services/languages-service';
import userService from '@features/users/services/current-user-service';
import { ExternalLinkIcon } from '@heroicons/react/outline';
import ButtonWithTimeout from 'components/buttons/button-with-timeout.jsx';
import Input from 'components/inputs/input.jsx';
import MenuList from 'components/menus/menu-component.jsx';
import Attribute from 'components/parameters/attribute.tsx';
import { Button } from '../../../../atoms/button/button';
import * as Text from '../../../../atoms/text';
import './UserParameter.scss';
export default class UserParameter extends Component {
constructor(props) {
@@ -605,9 +602,6 @@ export default class UserParameter extends Component {
</div>
);
}
if (this.state.page === 4) {
return <Assistant />;
}
}
setPage(page) {
@@ -630,18 +624,6 @@ export default class UserParameter extends Component {
this.setPage(1);
},
},
{
type: 'menu',
text: this.state.i18n.t('scenes.apps.account.assistant.title'),
emoji: ':robot:',
hide:
document.location.origin === 'https://web.tdrive.app' &&
workspaceService.currentGroupId !== '56393af2-e5fe-11e9-b894-0242ac120004',
selected: this.state.page === 4 ? 'selected' : '',
onClick: () => {
this.setPage(4);
},
},
]}
/>
</div>
@@ -1,332 +0,0 @@
import React, { Component } from 'react';
import Languages from '@features/global/services/languages-service';
import LoginService from '@features/auth/login-service';
import AccountService from '@deprecated/login/account.js';
import Emojione from 'components/emojione/emojione';
import StepCounter from 'components/step-counter/step-counter.jsx';
import ButtonWithTimeout from 'components/buttons/button-with-timeout.jsx';
import Input from 'components/inputs/input.jsx';
import { Typography } from 'antd';
export default class ForgotPassword extends Component {
constructor() {
super();
this.state = {
login: LoginService,
i18n: Languages,
email: '',
password1: '',
password2: '',
code: '',
page: 1,
invalidForm: false,
patternRegMail: new RegExp(
"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?",
),
mailAvailable: true,
usernameAvailable: true,
errorPassword: false,
errorCode: false,
};
LoginService.addListener(this);
Languages.addListener(this);
}
componentDidMount() {
if (this.input) {
this.input.focus();
}
}
componentWillUnmount() {
LoginService.removeListener(this);
Languages.removeListener(this);
}
componentDidUpdate(_prevProps, prevState) {
if (prevState.page === this.state.page - 1 || prevState.page === this.state.page + 1) {
if (this.input) {
this.input.focus();
}
if (this.inputcode) {
this.inputcode.focus();
}
if (this.inputpassword) {
this.inputpassword.focus();
}
}
}
displayStep() {
if (this.state.page === 1) {
return (
<div className="">
<div className="subtitle">{this.state.i18n.t('scenes.login.forgot_password.text')}</div>
<Input
refInput={ref => {
this.input = ref;
}}
type="text"
className={
'bottom-margin full_width big ' +
(this.state.login.error_recover_nosuchmail ? 'error' : '')
}
onKeyDown={e => {
if (e.keyCode === 13 && this.checkForm() && !this.state.login.login_loading) {
this.next();
}
}}
value={this.state.email}
id="email_to_recover"
placeholder={this.state.i18n.t('scenes.login.forgot_password.email_to_recover')}
onChange={evt => this.setState({ email: evt.target.value })}
/>
{this.state.login.error_recover_nosuchmail && (
<span className="text error">
{this.state.i18n.t('scenes.login.forgot_password.mail_doesnt_exist')}
</span>
)}
<div className="bottom">
<Typography.Link onClick={() => this.previous()}>
{this.state.i18n.t('general.back')}
</Typography.Link>
<ButtonWithTimeout
id="continue3_btn"
className="medium"
disabled={!this.checkForm() || this.state.login.login_loading}
onClick={() => this.next()}
value={this.state.i18n.t('general.continue')}
loading={this.state.login.login_loading}
loadingTimeout={2000}
/>
</div>
</div>
);
}
if (this.state.page === 2) {
return (
<div className="">
<div className="subtitle">
{this.state.i18n.t('scenes.login.forgot_password.text2')} <Emojione type=":mailbox:" />
</div>
<Input
refInput={ref => {
this.inputcode = ref;
}}
id="code"
type="text"
onKeyDown={e => {
if (
e.keyCode === 13 &&
this.state.code.length > 0 &&
this.checkForm() &&
!this.state.login.login_loading
) {
this.next();
}
}}
placeholder={'123-456-789'}
value={this.state.code}
onChange={evt => this.setState({ code: evt.target.value })}
className={
'bottom-margin full_width big ' +
(this.state.login.error_recover_badcode ? 'error' : '')
}
style={{ textAlign: 'center' }}
/>
<br />
{this.state.login.error_recover_badcode && (
<span id="invalid_code_information" className="text error">
{this.state.i18n.t('scenes.login.forgot_password.invalid_code')}
</span>
)}
<div className="bottom">
<Typography.Link onClick={() => this.previous()}>
{this.state.i18n.t('general.back')}
</Typography.Link>
<ButtonWithTimeout
id="continue4_btn"
className="medium"
disabled={!this.checkForm() || this.state.login.login_loading}
onClick={() => this.next()}
value={this.state.i18n.t('general.continue')}
loading={this.state.login.login_loading}
loadingTimeout={2000}
/>
</div>
</div>
);
}
if (this.state.page === 3) {
this.checkForm();
return (
<div className="">
<div className="subtitle">{this.state.i18n.t('scenes.login.forgot_password.text3')}</div>
<Input
refInput={ref => {
this.inputpassword = ref;
}}
type="password"
onKeyDown={e => {
if (e.keyCode === 13 && this.checkForm() && !this.state.login.login_loading) {
this.next();
}
}}
placeholder={this.state.i18n.t('scenes.login.forgot_password.password')}
value={this.state.password1}
onChange={evt => this.setState({ password1: evt.target.value })}
className={
'bottom-margin full_width medium ' +
((this.state.errorPasswordDoesNotMatch || this.state.errorPassword) &&
this.state.password1.length
? 'error'
: '')
}
/>
<Input
type="password"
onKeyDown={e => {
if (e.keyCode === 13 && this.checkForm() && !this.state.login.login_loading) {
this.next();
}
}}
placeholder={this.state.i18n.t('scenes.login.forgot_password.password2')}
value={this.state.password2}
onChange={evt => this.setState({ password2: evt.target.value })}
className={
'bottom-margin full_width medium ' +
((this.state.errorPasswordDoesNotMatch || this.state.errorPassword) &&
this.state.password1.length
? 'error'
: '')
}
/>
{(this.state.login.error_recover_badpasswords ||
this.state.login.error_recover_unknown ||
this.state.errorPasswordDoesNotMatch ||
this.state.errorPassword) &&
this.state.password1.length > 0 && (
<span className="text error">
{this.state.i18n.t('scenes.login.forgot_password.password_dont_match')}
</span>
)}
<div className="bottom">
<Typography.Link onClick={() => this.previous()}>
{this.state.i18n.t('general.back')}
</Typography.Link>
<ButtonWithTimeout
className="medium"
disabled={
this.state.errorPasswordDoesNotMatch ||
this.state.errorPassword ||
this.state.password1.length === 0 ||
this.state.login.login_loading
}
onClick={() => this.next()}
value={this.state.i18n.t('general.continue')}
loading={this.state.login.login_loading}
loadingTimeout={2000}
/>
</div>
</div>
);
}
if (this.state.page === 4) {
return (
<div className="">
<div className="subtitle">
{this.state.i18n.t('scenes.login.forgot_password.finished')}{' '}
<Emojione type=":raised_hands:" />
</div>
<div className="bottom">
<ButtonWithTimeout
className="medium"
disabled={this.state.exiting}
onClick={() => {
this.setState({ exiting: true });
this.next();
}}
value={this.state.i18n.t('general.continue')}
loading={this.state.login.login_loading}
loadingTimeout={2000}
/>
</div>
</div>
);
}
}
checkForm() {
if (this.state.password1.length < 8) {
this.setState({ errorPassword: true });
} else {
this.setState({ errorPassword: false });
}
if (this.state.password1 !== this.state.password2) {
this.setState({ errorPasswordDoesNotMatch: true });
} else {
this.setState({ errorPasswordDoesNotMatch: false });
}
return (
this.state.patternRegMail.test(this.state.email.toLocaleLowerCase()) &&
this.state.email.length > 0
);
}
previous() {
if (this.state.page <= 1) {
this.state.login.changeState('logged_out');
} else {
this.setState({ page: this.state.page - 1 });
}
}
next() {
if (this.state.page === 1) {
if (this.checkForm()) {
AccountService.recover(this.state.email, () => {
this.setState({ page: this.state.page + 1 });
});
}
} else if (this.state.page === 2) {
if (this.checkForm()) {
AccountService.recoverCode(this.state.code, () => {
this.setState({ page: this.state.page + 1 });
});
}
} else if (this.state.page === 3) {
if (this.checkForm()) {
AccountService.recoverNewPassword(this.state.password1, this.state.password2, () => {
this.setState({ page: this.state.page + 1 });
});
}
} else if (this.state.page === 4) {
this.state.login.init();
} else {
this.setState({ page: this.state.page + 1 });
}
}
render() {
return (
<div className="forgotPassword">
<div className="center_box_container login_view fade_in">
<div className="center_box white_box_with_shadow" style={{ width: '400px' }}>
<StepCounter total={4} current={this.state.page} />
<div className="title">
{this.state.i18n.t('scenes.login.forgot_password.title')} {this.state.page}/4
</div>
{this.displayStep()}
</div>
</div>
</div>
);
}
}
@@ -9,8 +9,6 @@ import Icon from '@components/icon/icon.jsx';
import LoginView from './login-view/login-view';
import Signin from './signin/signin.jsx';
import VerifyMail from './verify-mail/verify-mail.jsx';
import ForgotPassword from './forgot-password/index.jsx';
import Error from './error';
import './login.scss';
@@ -45,8 +43,6 @@ export default () => {
{LoginService.state === 'error' && <Error />}
{LoginService.state === 'logged_out' && <LoginView />}
{LoginService.state === 'signin' && <Signin />}
{LoginService.state === 'verify_mail' && <VerifyMail />}
{LoginService.state === 'forgot_password' && <ForgotPassword />}
<div className={'app_version_footer '}>
<div className="version_name fade_in">Tdrive {Globals.version.version_name}</div>
@@ -134,15 +134,6 @@ export default class LoginView extends Component {
{this.state.i18n.t('scenes.login.home.create_account')}
</Typography.Link>
)}
{/*
<Typography.Link
onClick={() => this.state.login.changeState('forgot_password')}
id="forgot_password_btn"
className="blue_link"
>
{this.state.i18n.t('scenes.login.home.lost_password')}
</Typography.Link>
*/}
</div>
)}
</div>
@@ -1,101 +0,0 @@
import React, { Component } from 'react';
import Languages from '@features/global/services/languages-service';
import LoginService from '@features/auth/login-service';
import AccountService from '@deprecated/login/account';
import Emojione from 'components/emojione/emojione';
import WindowState from '@features/global/utils/window';
export default class VerifyMail extends Component {
constructor() {
super();
this.state = {
login: LoginService,
i18n: Languages,
status: 'pending',
};
LoginService.addListener(this);
Languages.addListener(this);
}
componentDidMount() {
AccountService.doVerifyMail(
WindowState.findGetParameter('m'),
WindowState.findGetParameter('c'),
WindowState.findGetParameter('token'),
() => {
this.setState({ status: 'success' });
document.location.replace('/');
LoginService.init();
},
() => {
this.setState({ status: 'error' });
},
);
}
componentWillUnmount() {
LoginService.removeListener(this);
Languages.removeListener(this);
}
render() {
return (
<div className="verify_mail">
<div className="center_box_container login_view fade_in">
<div className="center_box white_box_with_shadow" style={{ width: '400px' }}>
<div className="title">
{Languages.t('scenes.login.verifymail.alert', [], 'Nous vérifions votre e-mail !')}
</div>
{this.state.status === 'pending' && (
<div className="subtitle">
<Emojione type=":hourglass:" />{' '}
{Languages.t(
'scenes.login.verifymail.verification_waiting',
[],
'En attente de vérification...',
)}
</div>
)}
{this.state.status === 'success' && [
<div className="subtitle" key='scenes.login.verifymail.success'>
<Emojione type=":white_check_mark:" />{' '}
{Languages.t(
'scenes.login.verifymail.success',
[],
'Votre e-mail a été vérifié avec succès!',
)}
</div>,
// eslint-disable-next-line jsx-a11y/anchor-is-valid
<a href="#" onClick={() => (document.location = '/')} className="blue_link" key="scenes.login.verifymail.signin_button">
{Languages.t('scenes.login.verifymail.signin_button', [], 'Se connecter')}
</a>,
]}
{this.state.status === 'error' && [
<div className="subtitle" key="scenes.login.verifymail.error_message">
<Emojione type=":confused:" />{' '}
{Languages.t(
'scenes.login.verifymail.error_message',
[],
"Une erreur s'est produite",
)}
</div>,
// eslint-disable-next-line jsx-a11y/anchor-is-valid
<a
key="scenes.login.home.create_account"
onClick={() => {
LoginService.changeState('signin');
document.location.replace('/');
}}
className="blue_link"
>
{this.state.i18n.t('scenes.login.home.create_account')}
</a>,
]}
</div>
</div>
</div>
);
}
}