🧹 Merge pull request #31 from linagora/removing-js-files
🧹 Removing js files
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
-139
@@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
-49
@@ -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();
|
||||
}
|
||||
};
|
||||
-100
@@ -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" />;
|
||||
}
|
||||
}
|
||||
-42
@@ -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;
|
||||
}
|
||||
}
|
||||
-21
@@ -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;
|
||||
}
|
||||
}
|
||||
-46
@@ -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;
|
||||
-72
@@ -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) {
|
||||
|
||||
-98
@@ -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;
|
||||
@@ -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;
|
||||
+4
-159
@@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user