bear-junior / react-native-draganddrop-board

👆DragAndDropBoard is a simple React Native library, enabling to create a scrollable board component with carousel, sortable columns and draggable cards for your iOS and Android apps
192 stars 47 forks source link

Excessive number of pending callbacks. #22

Open IsraelDCastro opened 4 years ago

IsraelDCastro commented 4 years ago

Screen Shot 2020-11-30 at 2 43 28 PM Screen Shot 2020-11-30 at 2 43 36 PM

Hi, this happened when I try to move the cards to another column, then the app crashed and stopped working.

AiTranXuan commented 3 years ago

same issues with u @@

ariesbrylle-bms commented 3 years ago

same issue here. Have you fix it?

TacticCoder commented 3 years ago

Hi, try to run the ONLY Board component no menu or other screens, if it does fix the problem its probably caused by wrong styles of Board parent container (try to add height/ flex it )

TacticCoder commented 3 years ago

same issue here. Have you fix it?

Solved?

iwalker7 commented 3 years ago

Hi, try to run the ONLY Board component no menu or other screens, if it does fix the problem its probably caused by wrong styles of Board parent container (try to add height/ flex it )

same issue here. i think its not solved.

AiTranXuan commented 3 years ago

Hi, try to run the ONLY Board component no menu or other screens, if it does fix the problem its probably caused by wrong styles of Board parent container (try to add height/ flex it )

same issue here. i think its not solved.

I agree with u.

TacticCoder commented 3 years ago

In my end its works great, If one of you guys can upload his project or at least his tech stack with the board code it will be great.

AiTranXuan commented 3 years ago

@TacticCoder `// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information.

import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {intlShape} from 'react-intl'; import {Alert, Animated, Keyboard, StyleSheet, TouchableOpacity, View, Text, Dimensions} from 'react-native'; import {General} from '@mm-redux/constants'; import EventEmitter from '@mm-redux/utils/event_emitter'; import {goToThreadScreen, showModal, showModalOverCurrentContext} from '@actions/navigation'; import LocalConfig from '@assets/config'; import {UPDATE_NATIVE_SCROLLVIEW, TYPING_VISIBLE} from '@constants/post_draft'; import CompassIcon from '@components/compass_icon'; import EphemeralStore from '@store/ephemeral_store'; import {unsupportedServer} from '@utils/supported_server'; import {preventDoubleTap} from '@utils/tap'; import {setNavigatorStyles} from '@utils/theme'; import tracker from '@utils/time_tracker';

import PushNotifications from '@init/push_notifications'; import telemetry from 'app/telemetry'; import {map, get, size, findIndex, find, noop} from 'lodash'; import {PluginType} from 'app/constants/plugin'; import {COOKIE_RESPONSE} from 'app/keycloak/index'; import AsyncStorage from '@react-native-community/async-storage'; import FastImage from 'react-native-fast-image'; import {BoardRepository, Board} from 'react-native-draganddrop-board'; import {KanbanTypes, KanbanUIStatus} from '@constants/'; import {genAssignUsers, getStateOfKanbanChannel, tagToColor} from 'app/utils/kanban'; import {getCookies} from 'app/utils/url';

export let ClientUpgradeListener; const {height} = Dimensions.get('window');

export default class ChannelBase extends PureComponent { static propTypes = { actions: PropTypes.shape({ getChannelStats: PropTypes.func.isRequired, loadChannelsForTeam: PropTypes.func.isRequired, selectDefaultTeam: PropTypes.func.isRequired, selectInitialChannel: PropTypes.func.isRequired, recordLoadTime: PropTypes.func.isRequired, getListUnreadMessage: PropTypes.func.isRequired, handleHideKanban: PropTypes.func.isRequired, handleOffKanban: PropTypes.func.isRequired, handleOnKanban: PropTypes.func.isRequired, handleShowKanban: PropTypes.func.isRequired, getPostThread: PropTypes.func.isRequired, selectPost: PropTypes.func.isRequired, }).isRequired, componentId: PropTypes.string.isRequired, currentChannelId: PropTypes.string, currentTeamId: PropTypes.string, members: PropTypes.array, currentUserId: PropTypes.string, disableTermsModal: PropTypes.bool, isSupportedServer: PropTypes.bool, isSystemAdmin: PropTypes.bool, teamName: PropTypes.string, theme: PropTypes.object.isRequired, showTermsOfService: PropTypes.bool, skipMetrics: PropTypes.bool, serverURL: PropTypes.string, kanbanStatus: PropTypes.string, };

static contextTypes = {
    intl: intlShape.isRequired,
};

static defaultProps = {
    disableTermsModal: false,
};

constructor(props) {
    super(props);

    this.postDraft = React.createRef();

    this.state = {
        channelsRequestFailed: false,
        dataKanban: [],
        modalKanbanVisible: false,
        modalChooseLaneVisible: false,
        isAddCard: false,
        card: null,
        lane_id: null,
        cookie: null,
    };

    if (LocalConfig.EnableMobileClientUpgrade && !ClientUpgradeListener) {
        ClientUpgradeListener = require('app/components/client_upgrade_listener').default;
    }

    this.typingAnimations = [];
}

handleOnKanban = async () => {
    const {currentChannelId} = this.props;
    await getStateOfKanbanChannel(currentChannelId, (kanbanStatus) => {
        if (kanbanStatus) {
            switch (kanbanStatus) {
            case KanbanUIStatus.ON:
                // Nếu Kanban đang ở trạng thái On [default =Hide]=> hide
                this.props.actions.handleHideKanban();
                break;
            case KanbanUIStatus.SHOW:
                // Nếu Kanban đang ở trạng thái show => SHOW
                this.props.actions.handleShowKanban();
                break;
            case KanbanUIStatus.HIDE:
                // Nếu Kanban đang ở trạng thái Hide [default =Hide]=> Hide
                this.props.actions.handleHideKanban();
                break;
            default:
                this.props.actions.handleHideKanban();
                break;
            }
        }
    });
}

handleOffKanban = () => {
    this.props.actions.handleOffKanban();
}

convertDataAndArrange = async (data) => {
    const result = [];

    map(data?.lanes, async (lane) => {
        // indexLane is the position of the Lane on the Board
        const indexLane = findIndex(data?.ordered_lanes, (o) => {
            return o === lane?.id;
        });
        const laneResult = {
            id: indexLane + 1,
            lane_id: lane?.id,
            name: lane?.title,
            creator: lane?.creator,
            rows: [],
        };
        const rowsNow = [];

        map(lane?.cards, async (card) => {
            // eslint-disable-next-line max-nested-callbacks
            const indexCard = findIndex(lane?.ordered_cards, (obj) => {
                return obj === card?.id;
            });

            const assignUsersFull = await genAssignUsers(card?.data?.assignUsers, this.props.members);

            // const assignUsersFull = [];
            // if (size(card?.data?.assignUsers) > 0) {
            //     forEach(card?.data?.assignUsers, async (user) => {
            //         if (size(this.props.members) > 0) {
            //             const resultMember = find(this.props.members, (member) => {
            //                 return member?.username === user;
            //             });
            //             assignUsersFull.push({username: user, avatar: resultMember?.avatar || null});
            //         }
            //     });
            // }
            rowsNow[indexCard] = {
                id: (indexCard + 1),
                card_id: card?.id,
                lane_id: card?.lane_id,
                name: card?.title,
                description: card?.description,
                tags: card?.data?.tags,
                assignUsers: card?.data?.assignUsers || [],
                assignUsersFull,
                thread_id: card?.thread_id,
                creator: card?.creator,
            };
        });
        laneResult.rows = rowsNow;
        result[indexLane] = laneResult;
    });

    return result;
}

updateDataKanban = (dataKanban) => {
    this.setState({dataKanban});
    if (this.state.card) {
        map(dataKanban, async (lane) => {
            const cardResult = find(lane?.rows, (obj) => {
                return obj?.card_id === this.state.card?.card_id;
            });
            if (cardResult) {
                this.setState({card: cardResult});
            }
        });

        // update data card
    }
}

getBoardContent = (callback = null) => {
    const {serverURL, currentChannelId} = this.props;
    AsyncStorage.getItem(COOKIE_RESPONSE).then(async (cookies) => {
        if (cookies) {
            const cookie = getCookies(JSON.parse(cookies));
            this.setState({cookie});
            const url_get_board = encodeURI(serverURL + `/plugins/${PluginType.CHATOPS_KANBAN_PLUGIN}/c-${currentChannelId}`);
            // eslint-disable-next-line no-console
            console.log('url_get_board ', url_get_board, ' \n-cookie: ', cookie);
            fetch(url_get_board, {
                method: 'GET',
                credentials: 'include',
                headers: {
                    Accept: 'application/json',
                    'x-requested-with': 'XMLHttpRequest',
                    cookie,
                },
            }).
                then((res) => {
                    return res.json();
                }).
                then(async (json) => {
                    // eslint-disable-next-line no-console
                    // console.log('Dữ liệu kanban: ', JSON.stringify(json));
                    if (size(json) > 0) {
                        // eslint-disable-next-line no-console
                        console.log('getBoardContent data: ', JSON.stringify(json), ' member: ', this.props.members);
                        if (size(get(json, 'lanes', [])) > 0) {
                            const dataKanban = await this.convertDataAndArrange(json);
                            this.updateDataKanban(dataKanban);
                        } else {
                            this.setState({
                                dataKanban: [],
                                card: null,
                                lane_id: null,
                            });
                        }
                        // eslint-disable-next-line no-unused-expressions
                        callback && callback(true);
                    } else {
                        // Không có dữ liệu kanban = disable
                        this.handleOffKanban();
                        // eslint-disable-next-line no-unused-expressions
                        callback && callback(false);
                    }
                }).
                catch((e) => {
                    // Chạy vào đây tức là plugin kanban bị disable hoặc ko được cài plugin kanban
                    this.handleOffKanban();
                    // eslint-disable-next-line no-unused-expressions
                    callback && callback(false);
                    // eslint-disable-next-line no-console
                    console.log(
                        '[Keycloak] Call API get_board_channel FAIL',
                        e,
                    );
                });
        } else {
            // eslint-disable-next-line no-console
            console.log('[COOKIE_RESPONSE] not found => Ko gọi đc api get plugin');
            this.handleOffKanban();
        }
    });
}

// nếu đang ở board
getBoardKanban = async () => {
    this.getBoardContent((statusBoard) => {
        if (statusBoard) {
            this.handleOnKanban();
        }
    });
}

// Get board khi nhận đc socket
getBoardCurrent = async (channelIdSocket) => {
    const {currentChannelId} = this.props;
    if (channelIdSocket === currentChannelId) {
        this.getBoardContent((statusCurrent) => {
            if (statusCurrent) {
                if (this.props.kanbanStatus !== KanbanUIStatus.SHOW) {
                    this.handleOnKanban();
                }
            }
        });

        // this.props.actions.getPostThread(rootId);
        // this.props.actions.selectPost(rootId);
    } else {
        // eslint-disable-next-line no-console
        console.log('Không phải channel đang mở nên ko cần update data kanban');
    }
}

deleteLane = (lane) => {
    const {serverURL, currentChannelId} = this.props;
    AsyncStorage.getItem(COOKIE_RESPONSE).then(async (cookies) => {
        if (cookies) {
            const cookie = getCookies(JSON.parse(cookies));
            this.setState({cookie});
            const url_delete_card = encodeURI(serverURL + `/plugins/${PluginType.CHATOPS_KANBAN_PLUGIN}/channel/${currentChannelId}/lane/${lane?.lane_id}`);
            // eslint-disable-next-line no-console
            fetch(url_delete_card, {
                method: 'DELETE',
                headers: {
                    Accept: 'application/json',
                    'Content-Type': 'application/json',
                    'x-requested-with': 'XMLHttpRequest',
                    cookie,
                },
                credentials: 'include',
                body: null,
            }).
                // eslint-disable-next-line @typescript-eslint/no-unused-vars
                then((res) => {
                    // return res.json();
                }).

                // then(async (json) => {
                //     console.log('[Done] Du lieu delete card; ', json);
                //     this.hideCardDetail();
                // }).
                catch((e) => {
                    // eslint-disable-next-line no-console
                    console.log(
                        '[Keycloak] Call API delete lane FAIL',
                        e,
                    );
                });
        } else {
            // eslint-disable-next-line no-console
            console.log('[COOKIE_RESPONSE] not found => Ko gọi đc api delete lane');

            // this.handleOffKanban();
        }
    });
}

deleteCard = (item) => {
    const {serverURL, currentChannelId} = this.props;
    AsyncStorage.getItem(COOKIE_RESPONSE).then(async (cookies) => {
        if (cookies) {
            const cookie = getCookies(JSON.parse(cookies));
            this.setState({cookie});
            const url_delete_card = encodeURI(serverURL + `/plugins/${PluginType.CHATOPS_KANBAN_PLUGIN}/channel/${currentChannelId}/card/${item?.lane_id}/${item?.card_id}`);
            // eslint-disable-next-line no-console
            fetch(url_delete_card, {
                method: 'DELETE',
                headers: {
                    Accept: 'application/json',
                    'Content-Type': 'application/json',
                    'x-requested-with': 'XMLHttpRequest',
                    cookie,
                },
                credentials: 'include',
                body: null,
            }).
                // eslint-disable-next-line @typescript-eslint/no-unused-vars
                then((res) => {
                    // return res.json();
                }).

                // then(async (json) => {
                //     console.log('[Done] Du lieu delete card; ', json);
                //     this.hideCardDetail();
                // }).
                catch((e) => {
                    // eslint-disable-next-line no-console
                    console.log(
                        '[Keycloak] Call API delete card FAIL',
                        e,
                    );
                });
        } else {
            // eslint-disable-next-line no-console
            console.log('[COOKIE_RESPONSE] not found => Ko gọi đc api create card');

            // this.handleOffKanban();
        }
    });
}

clickDeleteLane = (item) => () => {
    const {intl} = this.context;

    Alert.alert(
        intl.formatMessage({
            id: 'kanban.deleteLane',
            defaultMessage: 'Delete Lane',
        }),
        intl.formatMessage({
            id: 'kanban.confirmDeleteLane',
            defaultMessage: 'Are you sure you want to delete this lane?',
        }),
        [{
            text: intl.formatMessage({
                id: 'mobile.channel_list.alertNo',
                defaultMessage: 'No',
            }),
            onPress: noop,
        }, {
            text: intl.formatMessage({
                id: 'mobile.channel_list.alertYes',
                defaultMessage: 'Yes',
            }),
            onPress: () => {
                this.deleteLane(item);
            },
        }],
    );
}

clickDeleteCard = (item) => () => {
    const {intl} = this.context;

    Alert.alert(
        intl.formatMessage({
            id: 'kanban.deleteCard',
            defaultMessage: 'Delete Card',
        }),
        intl.formatMessage({
            id: 'kanban.confirmDeleteCard',
            defaultMessage: 'Are you sure you want to delete this card?',
        }),
        [{
            text: intl.formatMessage({
                id: 'mobile.channel_list.alertNo',
                defaultMessage: 'No',
            }),
            onPress: noop,
        }, {
            text: intl.formatMessage({
                id: 'mobile.channel_list.alertYes',
                defaultMessage: 'Yes',
            }),
            onPress: () => {
                this.deleteCard(item);
            },
        }],
    );
}

clickMoveCard = (card) => () => {
    this.setState({
        lane_id: card?.lane_id,
        card,
    });
    setTimeout(() => {
        this.showChooseLane();
    }, 350);
}

componentDidMount = async () => {
    const {
        actions,
        currentChannelId,
        currentTeamId,
        disableTermsModal,
        isSupportedServer,
        isSystemAdmin,
        showTermsOfService,
        skipMetrics,
    } = this.props;

    AsyncStorage.getItem(COOKIE_RESPONSE).then(async (cookies) => {
        if (cookies) {
            const cookie = getCookies(JSON.parse(cookies));
            this.setState({cookie});
        }
    });
    EventEmitter.on('leave_team', this.handleLeaveTeam);
    EventEmitter.on(TYPING_VISIBLE, this.runTypingAnimations);
    EventEmitter.on(General.REMOVED_FROM_CHANNEL, this.handleRemovedFromChannel);
    EventEmitter.on(KanbanTypes.GET_DATA_CHANNEL_KANBAN, this.getBoardCurrent);

    if (currentTeamId) {
        this.loadChannels(currentTeamId);
    } else {
        actions.selectDefaultTeam();
    }

    if (currentChannelId) {
        PushNotifications.clearChannelNotifications(currentChannelId);
        requestAnimationFrame(() => {
            actions.getChannelStats(currentChannelId);
        });
    }

    if (tracker.initialLoad && !skipMetrics) {
        actions.recordLoadTime('Start time', 'initialLoad');
    }

    if (showTermsOfService && !disableTermsModal) {
        this.showTermsOfServiceModal();
    } else if (!isSupportedServer) {
        // Only display the Alert if the TOS does not need to show first
        unsupportedServer(isSystemAdmin, this.context.intl.formatMessage);
    }

    if (!skipMetrics) {
        telemetry.end(['start:channel_screen']);
    }

    this.getBoardKanban();
}

componentDidUpdate(prevProps) {
    if (tracker.teamSwitch) {
        this.props.actions.recordLoadTime('Switch Team', 'teamSwitch');
    }

    if (prevProps.isSupportedServer && !this.props.isSupportedServer) {
        unsupportedServer(this.props.isSystemAdmin, this.context.intl.formatMessage);
    }

    if (this.props.theme !== prevProps.theme) {
        setNavigatorStyles(this.props.componentId, this.props.theme);
        EphemeralStore.allNavigationComponentIds.forEach((componentId) => {
            if (this.props.componentId !== componentId) {
                setNavigatorStyles(componentId, this.props.theme);
            }
        });
    }

    if (this.props.currentTeamId &&
        (!this.props.currentChannelId || this.props.currentTeamId !== prevProps.currentTeamId)) {
        this.loadChannels(this.props.currentTeamId);
    }

    if (this.props.currentChannelId && this.props.currentChannelId !== prevProps.currentChannelId) {
        // Thay đổi channel thì get lại dữ liệu
        this.getBoardKanban();
        PushNotifications.clearChannelNotifications(this.props.currentChannelId);

        requestAnimationFrame(() => {
            this.props.actions.getChannelStats(this.props.currentChannelId);
            this.updateNativeScrollView();
        });
    }

    if (LocalConfig.EnableMobileClientUpgrade && !ClientUpgradeListener) {
        ClientUpgradeListener = require('app/components/client_upgrade_listener').default;
    }
}

componentWillUnmount() {
    EventEmitter.off('leave_team', this.handleLeaveTeam);
    EventEmitter.off(TYPING_VISIBLE, this.runTypingAnimations);
    EventEmitter.off(General.REMOVED_FROM_CHANNEL, this.handleRemovedFromChannel);
    EventEmitter.off(KanbanTypes.GET_DATA_CHANNEL_KANBAN, this.getBoardCurrent);
}

registerTypingAnimation = (animation) => {
    const length = this.typingAnimations.push(animation);
    const removeAnimation = () => {
        const animationIndex = length - 1;
        this.typingAnimations = this.typingAnimations.filter((a, index) => index !== animationIndex);
    };

    return removeAnimation;
}

runTypingAnimations = (typingVisible) => {
    Animated.parallel(
        this.typingAnimations.map((animation) => animation(typingVisible)),
    ).start();
}

goToChannelInfo = preventDoubleTap(() => {
    const {intl} = this.context;
    const {theme} = this.props;
    const screen = 'ChannelInfo';
    const title = intl.formatMessage({id: 'mobile.routes.channelInfo', defaultMessage: 'Info'});
    CompassIcon.getImageSource('close', 24, theme.sidebarHeaderTextColor).then((source) => {
        const options = {
            topBar: {
                leftButtons: [{
                    id: 'close-info',
                    icon: source,
                    testID: 'close.channel_info.button',
                }],
            },
        };

        Keyboard.dismiss();

        showModal(screen, title, null, options);
    });
}, 1000);

handleLeaveTeam = () => {
    this.props.actions.selectDefaultTeam();
};

handleRemovedFromChannel = (channelName) => {
    const {formatMessage} = this.context.intl;

    Alert.alert(
        formatMessage({
            id: 'mobile.user_removed.title',
            defaultMessage: 'Removed from {channelName}',
        }, {channelName}),
        formatMessage({
            id: 'mobile.user_removed.message',
            defaultMessage: 'You were removed from the channel.',
        }),
    );
};

loadChannels = (teamId) => {
    const {loadChannelsForTeam, selectInitialChannel} = this.props.actions;
    if (EphemeralStore.getStartFromNotification()) {
        // eslint-disable-next-line no-console
        console.log('Switch to channel from a push notification');
        EphemeralStore.setStartFromNotification(false);
    } else {
        loadChannelsForTeam(teamId).then((result) => {
            if (result?.error) {
                this.setState({channelsRequestFailed: true});
                return;
            }

            this.setState({channelsRequestFailed: false});
            selectInitialChannel(teamId);
        });
    }
};

retryLoad = () => {
    const {currentTeamId, actions} = this.props;
    if (currentTeamId) {
        this.loadChannels(currentTeamId);
    } else {
        actions.selectDefaultTeam();
    }
}

retryLoadChannels = () => {
    this.loadChannels(this.props.currentTeamId);
};

renderLoadingOrFailedChannel() {
    const {formatMessage} = this.context.intl;
    const {
        currentChannelId,
        teamName,
        theme,
    } = this.props;

    const {channelsRequestFailed} = this.state;
    if (!currentChannelId) {
        if (channelsRequestFailed) {
            const FailedNetworkAction = require('app/components/failed_network_action').default;
            const title = formatMessage({id: 'mobile.failed_network_action.teams_title', defaultMessage: 'Something went wrong'});
            const message = formatMessage({
                id: 'mobile.failed_network_action.teams_channel_description',
                defaultMessage: 'Channels could not be loaded for {teamName}.',
            }, {teamName});

            return (
                <FailedNetworkAction
                    errorMessage={message}
                    errorTitle={title}
                    onRetry={this.retryLoadChannels}
                    theme={theme}
                />
            );
        }

        const Loading = require('app/components/channel_loader').default;
        return (
            <Loading
                channelIsLoading={true}
                color={theme.centerChannelColor}
                retryLoad={this.retryLoad}
            />
        );
    }

    return null;
}

showTermsOfServiceModal = async () => {
    const {intl} = this.context;
    const {isSupportedServer, isSystemAdmin, theme} = this.props;
    const screen = 'TermsOfService';
    const title = intl.formatMessage({id: 'mobile.tos_link', defaultMessage: 'Terms of Service'});
    CompassIcon.getImageSource('close', 24, theme.sidebarHeaderTextColor).then((closeButton) => {
        const passProps = {
            closeButton,
            isSupportedServer,
            isSystemAdmin,
        };
        const options = {
            layout: {
                componentBackgroundColor: theme.centerChannelBg,
            },
            topBar: {
                visible: true,
                height: null,
                title: {
                    color: theme.sidebarHeaderTextColor,
                    text: title,
                },
            },
        };

        showModalOverCurrentContext(screen, passProps, options);
    });
};

updateNativeScrollView = () => {
    EventEmitter.emit(UPDATE_NATIVE_SCROLLVIEW, this.props.currentChannelId);
};

goToThread = (card) => {
    // console.log('post138; ', JSON.stringify(post));
    telemetry.start(['post_list:thread']);
    const {actions, currentChannelId} = this.props;

    const channelId = currentChannelId;
    const rootId = (card?.thread_id || card?.root_id);
    const title = '';
    const passProps = {
        card,
        channelId,
        rootId,
    };

    // eslint-disable-next-line no-console
    console.log('passProps:goToThread ', passProps);
    Keyboard.dismiss();
    actions.getPostThread(rootId);
    actions.selectPost(rootId);

    requestAnimationFrame(() => {
        goToThreadScreen(title, passProps);
    });
};

clickItem = (card) => () => {
    this.setState({
        card,
        lane_id: card?.lane_id,

        // modalKanbanVisible: true,
    });
    this.goToThread(card);

    // this.showCardDetail();
}

showChooseLane = () => {
    this.setState({modalChooseLaneVisible: true});
}

hideChooseLane = () => {
    this.setState({modalChooseLaneVisible: false});
}

showCardDetail = () => {
    this.setState({modalKanbanVisible: true});
}

hideCardDetail = () => {
    this.setState({modalKanbanVisible: false, isAddCard: false});
}

showModalAddCard = (lane) => () => {
    // eslint-disable-next-line no-console
    console.log('lane add card: ', JSON.stringify(lane));
    this.setState({
        lane_id: lane?.lane_id, isAddCard: true,
    });
    setTimeout(() => {
        this.setState({modalKanbanVisible: true});
    }, 350);
}

hideModalAddCard = () => {
    this.setState({modalKanbanVisible: false, isAddCard: false});
}

renderEmptyComponent = (lane) => {
    const {intl} = this.context;
    return (<View style={style.ev1}>
        <FastImage
            tintColor={'#1b2c3e'}
            style={style.icon}
            source={require('@assets/images/emptyBox.png')}
        />
        <TouchableOpacity
            onPress={this.showModalAddCard(lane)}
            style={style.ev2}
        >
            <Text style={style.ev3}>{intl.formatMessage({id: 'kanban.emptyText', defaultMessage: 'Does not exist any card!'})}</Text>
            <Text style={style.ev4}>{intl.formatMessage({id: 'kanban.addCardNow', defaultMessage: 'Add card now'})}</Text>
        </TouchableOpacity>
    </View>);
}

renderAvatarUser = (member, idx) => {
    return (member?.avatar ? <FastImage
        key={idx}
        source={{
            uri: member?.avatar,
            headers: {cookie: this.state.cookie},
        }}
        resizeMode='contain'
        style={style.v15}
    /> :
        (<View
            key={idx}
            style={style.v14}
        >
            <Text style={style.v13}>{member.username[0]}</Text>
        </View>)
    );
}

renderAssignUsers = (item) => {
    // eslint-disable-next-line no-console
    // console.log('item?.assignUsersFull: ', item?.assignUsersFull, ' member: ', this.props.members);
    return (
        size(item?.assignUsersFull) > 0 ?
            <View style={style.v12}>
                {
                    // eslint-disable-next-line react/jsx-closing-bracket-location
                    map(item?.assignUsersFull, ((member, idx) => {
                        if (idx <= 4) {
                            return (this.renderAvatarUser(member, idx));
                        }
                        if (idx === 5) {
                            return (<View
                                key={idx}
                                style={style.v14}
                            // eslint-disable-next-line react/jsx-closing-bracket-location
                            >
                                <Text style={style.v13}>{`+${size(item?.assignUsersFull) - 5}`}</Text>
                            </View>);
                        }
                        return null;
                    }))
                }
            </View> : null
    );
};

renderCardContent = (item) => {
    return (
        <TouchableOpacity
            key={item?.card_id}
            activeOpacity={0.5}
            onPress={this.clickItem(item)}
            style={style.v1}
        >
            <View style={style.v2}>
                <View style={style.v3}>
                    <Text
                        numberOfLines={1}
                        style={style.v4}
                    >{item?.name}</Text>
                </View>
                <View style={{flexDirection: 'row'}}>
                    <TouchableOpacity
                        onPress={this.clickMoveCard(item)}
                        style={style.v5}
                    >
                        <FastImage
                            tintColor={'#1b2c3e'}
                            style={style.iconMoveCard}
                            source={require('@assets/images/ic_move_card.png')}
                        />
                    </TouchableOpacity>
                    <TouchableOpacity
                        onPress={this.clickDeleteCard(item)}
                        style={style.v5}
                    >
                        <CompassIcon
                            name='trash-can-outline'
                            size={16}
                            color={'#1b2c3e'}
                            style={style.v6}
                        />
                    </TouchableOpacity>
                </View>
            </View>
            {this.renderAssignUsers(item)}
            <View style={style.v7}>
                <Text
                    numberOfLines={3}
                    style={style.v8}
                >{item?.description}</Text>
            </View>
            {
                item?.tags ? <View style={style.v9}>
                    {
                        // eslint-disable-next-line react/jsx-closing-bracket-location
                        map(item?.tags, ((obj, index) => {
                            return (
                                <View
                                    key={index}
                                    style={[style.v10, {backgroundColor: tagToColor(obj)}]}
                                >
                                    <Text
                                        numberOfLines={1}
                                        style={style.v11}
                                    >{obj}</Text>
                                </View>
                            );
                        }))
                    }
                </View> : null
            }

        </TouchableOpacity>
    );
}

renderAddCardUI = (lane) => {
    return (
        <TouchableOpacity
            onPress={this.showModalAddCard(lane)}
            style={style.addCardBtn}
        >
            <FastImage
                tintColor={'#1b2c3e'}
                style={style.iconTini}
                source={require('@assets/images/add_card.png')}
            />
        </TouchableOpacity>);
}

renderRightHeaderEmptyColumn = (lane) => {
    return (
        <TouchableOpacity
            onPress={this.clickDeleteLane(lane)}
            style={style.addCardBtn}
        >
            <CompassIcon
                name='trash-can-outline'
                size={16}
                color={'#1b2c3e'}
                style={style.v6}
            />
        </TouchableOpacity>
    );
}

renderKanban = () => {
    // console.log('!!!!!-------dataKanban ', JSON.stringify(this.state.dataKanban));
    return (size(this.state.dataKanban) > 0 ? <View style={style.main}>
        <Board
            boardRepository={new BoardRepository(this.state.dataKanban)}
            emptyIconColor={'#1b2c3e'}
            boardBackground={'#fff'}

            // emptyText={intl.formatMessage({ id: 'kanban.emptyText', defaultMessage: 'Does not exist any card!' })}
            columnBackgroundColor={'#dddddd'}
            columnNameTextColor={'#1b2c3e'}
            columnNameFontSize={16}
            columnHeight={height - 80}
            badgeHeight={20}
            badgeWidth={20}
            badgeBorderRadius={8}
            badgeTextFontSize={10}
            badgeBackgroundColor={'#F01E24'}
            // eslint-disable-next-line @typescript-eslint/no-empty-function
            onDragEnd={() => { }}
            // eslint-disable-next-line @typescript-eslint/no-empty-function
            open={() => { }}
            cardContent={this.renderCardContent}
            viewRightHeaderColumn={this.renderAddCardUI}
            viewRightHeaderEmptyColumn={this.renderRightHeaderEmptyColumn}
            emptyComponent={this.renderEmptyComponent}
        />
    </View> : <></>);
}

render() {
    // Overriden in channel.android.js and channel.ios.js
    // but defined here for channel_base.test.js
    return; // eslint-disable-line no-useless-return
}

}

export const style = StyleSheet.create({ addCardBtn: {padding: 4, borderRadius: 4}, flex: {flex: 1}, icon: {width: 50, height: 50}, iconTini: {width: 20, height: 20}, iconMoveCard: {width: 18, height: 18}, main: {width: '100%', flex: 1}, ev1: {flex: 1, width: '100%', alignItems: 'center', justifyContent: 'center'}, ev2: {paddingVertical: 4}, ev3: {fontSize: 13, color: '#1b2c3e', textAlign: 'center'}, av4: {fontSize: 12, color: '#1b2c3e', textAlign: 'center', paddingVertical: 4}, ev4: {textDecorationStyle: 'solid', textDecorationLine: 'underline', fontSize: 14, color: '#1b2c3e', textAlign: 'center', paddingHorizontal: 16, paddingVertical: 4}, v1: {backgroundColor: '#fff', marginBottom: 8, padding: 8, borderRadius: 4}, v2: {width: '100%', borderBottomWidth: 0.5, borderBottomColor: '#00000050', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between'}, v3: {flex: 1}, v4: {paddingBottom: 4, fontSize: 13, fontWeight: 'bold', color: '#000'}, v5: {alignItems: 'center', paddingBottom: 4}, v6: {paddingLeft: 16}, v7: {width: '100%'}, v8: {paddingVertical: 4, fontSize: 13, color: '#000'}, v9: {width: '100%', flexDirection: 'row', flexWrap: 'wrap'}, v12: {width: '100%', flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'flex-end', alignItems: 'center', paddingTop: 8}, v10: { alignItems: 'center', justifyContent: 'center', marginRight: 4, marginTop: 4, borderRadius: 4, overflow: 'hidden', }, v11: {paddingHorizontal: 4, paddingVertical: 3, color: '#fff', fontSize: 12}, v13: {paddingHorizontal: 4, paddingVertical: 3, color: '#162A4C', fontSize: 10, textTransform: 'uppercase'}, v14: {backgroundColor: '#CCCCCC80', width: 24, height: 24, borderRadius: 12, alignItems: 'center', justifyContent: 'center', marginRight: 2}, v15: {width: 24, height: 24, borderRadius: 12, backgroundColor: '#ccc', marginRight: 2}, }); `