Source

externalCode/documentsScreen.js

/**
 * @typedef {Object} DocumentItemComponentProps
 * @property {Number} currentUserId
 * @property {Number} profileUserId
 * @property {Function} formatDateFunc Function to help format dates
 * @property {TranslationFunction} t
 * @property {String} token access token for authentication
 * @property {Object} global App global style
 * @property {Object} colors App colors
 * @property {DocumentViewModel} viewModel
 * @property {NavigationService} navigation
 * @property {Object} item Data fetched from API
 *
 */

/**
 * @typedef {Object} DocumentViewModel
 * @property {Number} id Document id
 * @property {Number} activityId
 * @property {String} size Document size
 * @property {String} extension_description
 * @property {Number} parent
 * @property {Number} count
 * @property {String} title
 * @property {String} filename
 * @property {String} extension
 * @property {String} description
 * @property {String} type
 * @property {String} dateModified Date when document was last modified
 * @property {String} displayName Name of document uploader
 * @property {String} privacy Privacy of document
 * @property {Boolean} can_report Returns `true` if item can be reported
 * @property {Boolean} reported Returns `true` if item has been already reported
 * @property {String} visibility Members who can view the document
 * @property {Number} groupId Group where document is uploaded
 * @property {Number} folderId Folder where document is uploaded
 * @property {String} groupName Group name where document is uploaded
 * @property {String} groupStatus Status of group
 * @property {Number} userId User id of uploader
 * @property {String} svgIcon
 * @property {String} dateCreatedFormatted
 * @property {String} downloadUrl Download url of document
 *
 */

/**
 * @typedef {Function} TransformDocumentsParams
 * @param {FetchDocumentsParams}
 * @return {Object}
 */

/**
 * @typedef {Object} FetchDocumentsParams {@link https://www.buddyboss.com/resources/api/#api-Document-GetBBDocuments}
 * @property {Number} group_id A unique numeric ID for the Group.
 * @property {Number} folder_id A unique numeric ID for the Document Folder.
 * @property {Number} user_id Limit result set to items created by a specific user (ID).
 * @property {String} order Order sort attribute ascending or descending. Allowed values: `asc` or `desc`
 * @property {String} orderby Order by a specific parameter. Default value: `title`. Allowed values: `title`, `date_created`, `date_modified`, `group_id`, `privacy`
 * @property {String} search Limit results to those matching a string.
 *
 */

/**
 * @class
 * Documents Hooks.
 * Instance name: documentsScreenApi
  
   You can use this hook to customize different document features such as creating your own document component or overriding parameters that fetch documents in the Document screen.
 * @example
 * externalCodeSetup.documentsScreenApi.METHOD_NAME
 */
export class DocumentsScreenApi {
	DocumentItemComponent = null;
	/**
	 * You can use this hook to add a custom document component such as an image component in the documents list screen.
	 * @method
	 * @param {React.ComponentType<DocumentItemComponentProps>} DocumentItemComponent
	 * @example <caption> Add more details to existing document item component </caption>
	 *
	 * //In custom_code/components/DocumentItem.js
	 *
	 * import React, {useState, useRef, useCallback} from "react";
	 * import { Linking, Text, View } from "react-native";
	 * import { FontWeights, DEVICE_HEIGHT, DEVICE_WIDTH } from "@src/styles/global";
	 * import {renderImageJSX, navigateHandler, truncate, getIcon} from "@src/components/Documents/utils";
	 * import Icon from "@src/components/Icon";
	 * import PortaledScrollableModal from "@src/components/Modals/PortaledScrollableModal";
	 * import {TouchableOpacity, TouchableWithoutFeedback} from "react-native-gesture-handler";
	 * import {BubbleIcon} from "@src/components/BubbleIcon";
	 * import DocumentSingle from "@src/components/Documents/DocumentSingle";
	 * import DocumentMove from "@src/components/Documents/DocumentMove";
	 *
	 * const len = () => {
	 *	if (DEVICE_WIDTH < 600) { return 30; }
	 *	if (DEVICE_WIDTH < 415) { return 25; }
	 *	if (DEVICE_WIDTH < 376) { return 20; }
	 *	return 35;
	 * };
	 *
	 * const DocumentItem = ({
	 *	viewModel,
	 *	global,
	 *	colors,
	 *	navigation,
	 *	formatDateFunc,
	 *	token,
	 *	t,
	 *	profileUserId,
	 *	currentUserId,
	 *	isGroup,
	 *   item
	 * }) => {
	 *	const [moveModalVisible, setMoveModalVisible] = useState(false);
	 *
	 *	const openMoveModal = useCallback(() => {
	 *		setMoveModalVisible(true);
	 *	}, []);
	 *
	 *	const closeMoveModal = useCallback(() => {
	 *		setMoveModalVisible(false);
	 *	}, []);
	 *
	 *	const modalRef = useRef();
	 *	const handleClose = () => {
	 *		modalRef.current.close();
	 *	};
	 *	const handleOpen = () => {
	 *		modalRef.current.open();
	 *	};
	 *
	 *	const filename =
	 *		viewModel.type === "folder"
	 *			? [viewModel.title, ""]
	 *			: truncate(viewModel.filename, len(), "...");
	 *
	 *	return (
	 *		<>
	 *			<View
	 *				style={[
	 *					{
	 *						paddingHorizontal: 16,
	 *						paddingTop: 18,
	 *						flexDirection: "row",
	 *						alignItems: "flex-start",
	 *						flex: 1
	 *					}
	 *				]}
	 *			>
	 *				{viewModel.svgIcon ? (
	 *					renderImageJSX(viewModel.svgIcon, 34, colors.textIconColor)()
	 *				) : (
	 *					<Icon
	 *						icon={getIcon(viewModel.extension)}
	 *						styles={{width: 34, maxHeight: 45, tintColor: colors.textIconColor}}
	 *					/>
	 *				)}
	 *
	 *				<View
	 *					style={{
	 *						flex: 1,
	 *						marginLeft: 18,
	 *						paddingBottom: 15,
	 *						...global.bottomBorder
	 *					}}
	 *				>
	 *					<TouchableWithoutFeedback
	 *						onPress={() =>
	 *							navigateHandler(
	 *								viewModel,
	 *								navigation,
	 *								handleOpen,
	 *								profileUserId,
	 *								currentUserId,
	 *								isGroup
	 *							)
	 *						}
	 *					>
	 *						<View style={{flex: 1, flexDirection: "row", alignItems: "center"}}>
	 *							<View style={{flex: 1}}>
	 *								<Text
	 *									style={{
	 *										...global.text,
	 *										fontWeight: FontWeights.semiBold,
	 *										marginTop: 2,
	 *										marginBottom: 5
	 *									}}
	 *								>
	 *									{filename}
	 *								</Text>
	 *								{!!viewModel.groupId &&
	 *									!isGroup && (
	 *										<Text
	 *											style={{
	 *												...global.documentMeta,
	 *												color: colors.textColor,
	 *												fontWeight: FontWeights["medium"],
	 *												marginBottom: 9
	 *											}}
	 *										>
	 *											{viewModel.groupName}
	 *										</Text>
	 *									)}
	 *								<View style={{...global.row, flex: 1, flexWrap: "wrap"}}>
	 *									<Text style={global.documentMeta}>
	 *										{viewModel.displayName}
	 *									</Text>
	 *									<View style={global.dotSep} />
	 *									<Text style={global.documentMeta}>
	 *										{formatDateFunc(viewModel.dateModified)}
	 *									</Text>
	 *								</View>
	 *
	 *                                //Add more details
	 *								<View style={{...global.row, flex: 1, flexWrap: "wrap"}}>
	 *                                   <Text style={[global.documentMeta, {marginRight: 10}]}>
	 *                                        Size: {viewModel.size}
	 *									</Text>
	 *									<Text style={global.documentMeta}>
	 *                                       Privacy: {viewModel.visibility}
	 *									</Text>
	 *								</View>
	 *
	 *                               //Add download link
	 *								<View style={{...global.row, flex: 1, flexWrap: "wrap", marginTop: 10}}>
	 *	                                    <Text>Download</Text>
	 *                                    <TouchableOpacity style={[global.documentMeta, {marginRight: 10}]} onPress={() => Linking.openURL(item.download_url)}>
	 *                                        <Text style={{color: "blue"}}>{item.download_url}</Text>
	 *									</TouchableOpacity>
	 *								</View>
	 *
	 *							</View>
	 *							{viewModel.type === "folder" && (
	 *								<View style={global.row}>
	 *									<BubbleIcon
	 *										{...{
	 *											global,
	 *											text: viewModel.count
	 *										}}
	 *									/>
	 *									<Icon
	 *										icon={{fontIconName: "angle-right", weight: 400}}
	 *										webIcon={"IconArrowBack"}
	 *										tintColor={colors.textIconColor}
	 *										styles={{
	 *											height: 20,
	 *											marginLeft: 10
	 *										}}
	 *									/>
	 *								</View>
	 *							)}
	 *						</View>
	 *					</TouchableWithoutFeedback>
	 *				</View>
	 *			</View>
	 *			<PortaledScrollableModal
	 *				ref={modalRef}
	 *				modalHeight={DEVICE_HEIGHT}
	 *				modalTopOffset={0}
	 *				modalStyle={{
	 *					zIndex: 2
	 *				}}
	 *				adjustToContentHeight={false}
	 *				scrollViewProps={{
	 *					keyboardShouldPersistTaps: "handled",
	 *					contentContainerStyle: {
	 *						flexGrow: 1
	 *					}
	 *				}}
	 *				useNativeDriver={true}
	 *			>
	 *				{!!viewModel ? (
	 *					<DocumentSingle
	 *						openMoveModal={openMoveModal}
	 *						navigation={navigation}
	 *						handleClose={handleClose}
	 *						cacheId={viewModel.type.charAt(0) + viewModel.id}
	 *					/>
	 *				) : null}
	 *			</PortaledScrollableModal>
	 *
	 *			<DocumentMove
	 *				isGroup={isGroup}
	 *				moveModalVisible={moveModalVisible}
	 *				profileUserId={profileUserId}
	 *				navigation={navigation}
	 *				cacheId={viewModel.type.charAt(0) + viewModel.id}
	 *				closeMoveModal={closeMoveModal}
	 *			/>
	 *		</>
	 *	);
	 * };
	 *
	 * export default DocumentItem;
	 *
	 * //In custom_code/index.js
	 *
	 * ...
	 *
	 * import DocumentItem from "./components/DocumentItem";
	 * export const applyCustomCode = externalCodeSetup => {
	 *  externalCodeSetup.documentsScreenApi.setDocumentItemComponent((props) => <DocumentItem {...props} />)
	 * }
	 *
	 */
	setDocumentItemComponent = DocumentItemComponent =>
		(this.DocumentItemComponent = DocumentItemComponent);

	fetchParamsFilter = params => params;

	/**
	 * It overrides the parameters that are used to fetch documents in the Documents screen so that you can make it as customizable as possible when calling its API.
	 * @method
	 * @param {TransformDocumentsParams} fetchParamsFilter
	 *
	 * @example <caption> Create a custom filter in documents screen </caption>
	 *
	 * //In components/DocumentBeforeList.js...
	 *
	 * import React, { useState } from "react";
	 * import { TextInput, View, Button } from 'react-native'
	 * import { useDispatch } from "react-redux";
	 * import { documentsRequested } from "@src/actions/documents";
	 * import { getExternalCodeSetup } from "@src/externalCode/externalRepo";
	 * import withGlobalStyles from "@src/components/hocs/withGlobalStyles";
	 *
	 * const hook = getExternalCodeSetup().documentsScreenApi;
	 *
	 * const screenName = "book";
	 *
	 * const filter = "all"; //"all", "personal", "group"
	 * const subfilters = {
	 *    orderBy: "title", //"title", "date_modified", "group_id", "privacy"
	 *    order: "desc" //"asc", "desc"
	 * };
	 * const refresh = true; //Set to true to refresh list
	 *
	 * const DocumentsBeforeList = (props) => {
	 *
	 *    const { navigation, route, colors } = props;
	 *
	 *    const dispatch = useDispatch();
	 *
	 *    //If showing the matched screen, show custom filter before displaying list component
	 *    if (route?.params?.item?.object === screenName) {
	 *
	 *        const [minAge, setMinAge] = useState('')
	 *        const [maxAge, setMaxAge] = useState('');
	 *
	 *        const handleSubmit = () => {
	 *
	 *            //Set custom parameters before fetching documents
	 *            hook.setFetchParamsFilter((props) => {
	 *
	 *                //You can add more parameters such as "subject", "keyword" etc...
	 *                return {
	 *                    ...props,
	 *                    minAge: minAge,
	 *                    maxAge: maxAge
	 *                }
	 *            })
	 *
	 *            //Dispatch redux action to call api using customized filters
	 *            dispatch(documentsRequested(filter, subfilters, refresh));
	 *
	 *        }
	 *
	 *        return <View style={{ backgroundColor: colors.whiteColor}}>
	 *
	 *            <TextInput
	 *                style={{paddingHorizontal: 20, marginTop: 10, fontSize: 20}}
	 *                autoFocus
	 *                value={minAge}
	 *                onChangeText={minAge => setMinAge(minAge)}
	 *                placeholder="Min Age"
	 *            />
	 *
	 *            <TextInput
	 *                style={{paddingHorizontal: 20, marginTop: 10, fontSize: 20}}
	 *                autoFocus
	 *                value={maxAge}
	 *                onChangeText={maxAge => setMaxAge(maxAge)}
	 *                placeholder="Max Age"
	 *            />
	 *
	 *            <Button
	 *                onPress={() => handleSubmit()}
	 *                title="Filter"
	 *            />
	 *        </View>
	 *    }
	 *
	 *    return null;
	 *
	 * }
	 *
	 * export default withGlobalStyles(DocumentsBeforeList);
	 *
	 * //In components/MyCustomScreen.js...
	 * import React from 'react';
	 * import DocumentsScreen from "@src/containers/Custom/DocumentsScreen";
	 *
	 * const MyCustomScreen = props => (<DocumentsScreen {...props} showSearch={false} hideFilters={true} screenTitle="My Docs" headerHeight={250} />)
	 *
	 *
	 * export default MyCustomScreen;
	 *
	 * //In custom_code/index.js...
	 *
	 * ...
	 *
	 * import DocumentsBeforeList from "./components/DocumentBeforeList";
	 * export const applyCustomCode = externalCodeSetup => {
	 *
	 *    externalCodeSetup.filterScreenApiHooks.setAfterFilterComponent(DocumentsBeforeList);
	 *
	 *    externalCodeSetup.navigationApi.addNavigationRoute(
	 *       "book",
	 *       "BookScreen",
	 *       MyCustomScreen,
	 *       "All"
	 *    );
	 *    externalCodeSetup.navigationApi.addNavigationRoute(
	 *       "book",
	 *       "BookScreen",
	 *       MyCustomScreen,
	 *       "Main"
	 *    );
	 * }
	 *
	 * @example <caption> Add params when fetching documents api </caption>
	 *	externalCodeSetup.documentsScreenApi.setFetchParamsFilter((props) => {
	 *		return {
	 *			...props,
	 *			search: "foo.jpg",
	 *			subject: "bar",
	 *			keyword: "baz"
	 *		}
	 *	})
	 */
	setFetchParamsFilter = fetchParamsFilter => {
		this.fetchParamsFilter = fetchParamsFilter;
	};
}