/**
* @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;
};
}
Source