Home Reference Source

src/views/assessment/freetext/FreeTextView.js

import React from 'react';
import {
    Keyboard,
    KeyboardAvoidingView,
    Platform,
    ScrollView,
    StyleSheet,
    TextInput,
    TouchableWithoutFeedback,
} from 'react-native';
import Animated, {ZoomInRight, ZoomOutRight} from 'react-native-reanimated';

import ClearAllButton from '../components/buttons/ClearAllButton';
import ColorSelectorHorizontal from '../components/ColorSelectorHorizontal';
import ConfirmationMenu from '../components/ConfirmationMenu';
import ConfirmationBoxOverlay from '../../../components/dialogBoxes/ConfirmationBoxOverlay';
import FontColorButton from '../components/buttons/FontColorButton';
import Helper from '../../../components/helper/Helper';
import LeftMenu from '../components/LeftMenu';
import PaperColorButton from '../components/buttons/PaperColorButton';
import SaveAlertBoxOverlay from '../../../components/dialogBoxes/SaveAlertBoxOverlay';

import {
    getAssessment,
    getComment,
    setComment,
} from '../../../objects/Assessment';

import {ButtonPosition} from '../../../utils/DimensionsConstants';
import {ColorPalette} from '../../../utils/ColorConstants';
import {globalStyles} from '../../../styles/styles';
import {Module} from '../../../utils/ModuleConstants';

/**
 * The view where the user can write free text.
 * @param {function} setShowFreeText - If set to false, the user is redirected back to menu
 * @returns {Object} The view for freetext
 */
export default function FreeTextView({setShowFreeText}) {
    //Get colors for this assessment or null if there are no colors.
    const previousTextColor = getAssessment().comment.textColor;
    const previousPaperColor = getAssessment().comment.paperColor;

    //State for text (Initialized to previous text for this assessment)
    const [text, setText] = React.useState(getComment());

    //If true => the user change color of paper. If false the user change color of text
    const [colorPaper, setColorPaper] = React.useState(false);

    //State to determine wheter ColorSelector is active
    const [showColors, setShowColors] = React.useState(false);

    //State for text color (Initialized to previosTextColor or black if there are no previous)
    const [textColor, setTextColor] = React.useState(
        previousTextColor == null ? 'black' : previousTextColor,
    );

    //State for paper color (Initialized to previosPaperColor or ALMOST_WHITE if there are no previous)
    const [paperColor, setPaperColor] = React.useState(
        previousPaperColor == null
            ? ColorPalette.ALMOST_WHITE
            : previousPaperColor,
    );

    //Function to toggle ColorSelector
    const toggleColorMenu = () => setShowColors(!showColors);

    //Function to reset component
    const reset = () => {
        setText('');
        setTextColor('black');
        setPaperColor(ColorPalette.ALMOST_WHITE);
    };

    //Reference to scrollview
    const scrollViewRef = React.useRef();

    //Logic to ensure the keyboard does not cover where the user is writing
    const keyboardShown = Keyboard.addListener('keyboardDidShow', () => {
        if (scrollViewRef.current != null)
            scrollViewRef.current.scrollToEnd({
                animated: true,
            });
    });

    //Setting the visibility of the alert window
    const [alertBoxVisible, setAlertBoxVisible] = React.useState(false);

    //Toggling the alert window
    const toggleAlertBoxVisibility = () => {
        setAlertBoxVisible(!alertBoxVisible);
    };
    //Function to display alert window
    const displayAlertBox = () => setAlertBoxVisible(true);

    //Setting the visibility of the confirmation window
    const [confirmationBoxVisible, setConfirmationBoxVisible] =
        React.useState(false);

    //Toggling the confirmation window
    const toggleConfirmationBoxVisibility = () => {
        setConfirmationBoxVisible(!confirmationBoxVisible);
    };

    //Function to remove the confirmation and go back to menu
    const hideConfirmationBox = () => {
        setConfirmationBoxVisible(false);
        setShowFreeText(false);
    };

    //The actual component
    return (
        <Animated.View //Entire component is animated
            style={[globalStyles.background, styles.container]}
            entering={ZoomInRight} //Zooms in and out from right
            exiting={ZoomOutRight}>
            <LeftMenu module={Module.FREE_TEXT} />

            <KeyboardAvoidingView //To ensure keyboard is not covering text
                style={[styles.textContainer, {backgroundColor: paperColor}]}
                behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
                <TouchableWithoutFeedback //If user presses outside of keyboard, the keyboard is closed
                    onPress={() => Keyboard.dismiss()}
                    style={styles.dummyTouchableWOFeedback}>
                    <ScrollView //Text is scrollview, so user can write long text and still se what they are writing
                        style={styles.scrollView}
                        ref={scrollViewRef} //Binding to the scrollViewRef
                        onContentSizeChange={() =>
                            //When text gets larger (needs new line), automatically scrolls to last line.
                            scrollViewRef.current.scrollToEnd({
                                animated: true,
                            })
                        }>
                        <TextInput //Where the user writes
                            value={text} //The text state is value
                            style={[
                                styles.textInput,
                                {
                                    color: textColor, //Color of text is textColor state
                                    fontFamily: 'Noteworthy-Bold',
                                },
                            ]}
                            placeholder="Trykk her og skriv!" //If no text
                            onChangeText={text => {
                                setText(text); //Always have updated text inside text variable
                                setShowColors(false);
                            }}
                            multiline={true} //Text can be multiple lines
                        />
                    </ScrollView>
                </TouchableWithoutFeedback>
            </KeyboardAvoidingView>

            {/* All the different buttons */}
            <Animated.View style={styles.writingTools}>
                <FontColorButton //Button for changing color of text
                    position={ButtonPosition.RIGHT}
                    fillColor={textColor} //Make icon change colors to current textcolor
                    onPress={() => {
                        //When pressed, make colorselector update text
                        Keyboard.dismiss(); //Remove keyboard
                        if (!colorPaper || !showColors) toggleColorMenu();
                        //Should update color of text
                        setColorPaper(false);
                    }}
                />
                <PaperColorButton //Button for changing color of paper
                    position={ButtonPosition.RIGHT}
                    fillColor={paperColor} //Make icon change colors to current papercolor
                    onPress={() => {
                        Keyboard.dismiss(); //Remove keyboard
                        //When pressed, make colorselector update paper
                        if (colorPaper || !showColors) toggleColorMenu();
                        setColorPaper(true); //Should update color of paper
                    }}
                />
                <ClearAllButton //Button for clearing the paper (reset)
                    position={ButtonPosition.RIGHT}
                    onPress={() => {
                        reset();
                    }}
                />
            </Animated.View>
            {showColors && (
                <ColorSelectorHorizontal //The colorselector.
                    //Needs to know what to color
                    showsPaperColorMenu={colorPaper}
                    showsBrushColorMenu={!colorPaper}
                    //And function to update the colors
                    updateBrushColor={setTextColor}
                    updatePaperColor={setPaperColor}
                />
            )}

            <ConfirmationMenu //Button to save text
                onPressClose={displayAlertBox} //Includes logic for going back to menu
                onPressOk={() => {
                    setComment(text, textColor, paperColor); //Save
                    keyboardShown.remove(); //Cleanup
                    setConfirmationBoxVisible(true); //Notify user that text is saved
                }}
            />

            <SaveAlertBoxOverlay //If user only exits, they still can save
                modalVisible={alertBoxVisible}
                onModalRequestCloseAction={toggleAlertBoxVisibility}
                onPressCancelButtonAction={toggleAlertBoxVisibility}
                onPressSaveButtonAction={() => {
                    //User wants to save
                    setComment(text, textColor, paperColor); //Save
                    keyboardShown.remove(); //Cleanup
                    setShowFreeText(false); //Goes back to menu
                    setConfirmationBoxVisible(true); //Notify user
                }}
                onPressDeleteButtonAction={() => {
                    //User dont want to save
                    reset(); //Reset
                    setShowFreeText(false); //Goes back to main menu
                }}
                message={'Vil du lagre teksten din ?'} //The displaytext
            />

            <ConfirmationBoxOverlay //Notify the user that text is saved
                modalVisible={confirmationBoxVisible}
                onModalRequestCloseAction={toggleConfirmationBoxVisibility}
                onPressOkButtonAction={hideConfirmationBox}
                message={'Din tekst er lagret !'} //Display text
            />
            {/* The helper for freetext (id === 3) */}
            <Helper id={3} />
        </Animated.View>
    );
}

/**
 * Local styles
 * @type {Object}
 */
const styles = StyleSheet.create({
    container: {
        flexDirection: 'row',
    },

    textContainer: {
        flex: 4.5,
        marginTop: '5%',
        height: '80%',
    },
    dummyTouchableWOFeedback: {
        backgroundColor: 'yellow',
        width: '100%',
        height: '100%',
        flex: 1,
    },
    scrollView: {
        flex: 1,
        padding: 20,
    },
    textInput: {
        height: '100%',
        marginHorizontal: '5%',
        marginVertical: '5%',
        fontSize: 25,
        textAlign: 'left',
    },
    writingTools: {
        flex: 0.8,
        flexDirection: 'column',
        height: '75%',
        right: 0,
        alignItems: 'center',
        marginTop: '10%',
    },
    buttonContainer: {
        position: 'absolute',
        alignItems: 'flex-end',
        bottom: 0,
        right: 0,
    },
    button: {
        margin: 15,
    },
});