2022-01-12 20:31:54 +01:00
|
|
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
|
|
import { useSelector } from 'react-redux';
|
|
|
|
import { getEnvironmentType } from '../../app/scripts/lib/util';
|
|
|
|
import { selectMatchingFragment } from '../selectors';
|
|
|
|
import {
|
|
|
|
finalizeEventFragment,
|
|
|
|
createEventFragment,
|
|
|
|
updateEventFragment,
|
|
|
|
} from '../store/actions';
|
2022-03-25 15:26:09 +01:00
|
|
|
import { useSegmentContext } from './useSegmentContext';
|
2022-01-12 20:31:54 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves a fragment from memory or initializes new fragment if one does not
|
|
|
|
* exist. Returns three methods that are tied to the fragment, as well as the
|
|
|
|
* fragment id.
|
|
|
|
*
|
|
|
|
* @param {string} existingId
|
2022-08-24 20:57:47 +02:00
|
|
|
* @param {object} fragmentOptions
|
2022-01-12 20:31:54 +01:00
|
|
|
* @returns
|
|
|
|
*/
|
2022-02-01 18:53:03 +01:00
|
|
|
export function useEventFragment(existingId, fragmentOptions = {}) {
|
2022-01-12 20:31:54 +01:00
|
|
|
// To prevent overcalling the createEventFragment background method a ref
|
|
|
|
// is used to store a boolean value of whether we have already called the
|
|
|
|
// method.
|
|
|
|
const createEventFragmentCalled = useRef(false);
|
|
|
|
|
|
|
|
// In order to immediately return a created fragment, instead of waiting for
|
|
|
|
// background state to update and find the newly created fragment, we have a
|
|
|
|
// state element that is updated with the fragmentId returned from the
|
|
|
|
// call into the background process.
|
|
|
|
const [createdFragmentId, setCreatedFragmentId] = useState(undefined);
|
|
|
|
|
|
|
|
// Select a matching fragment from state if one exists that matches the
|
|
|
|
// criteria. If an existingId is passed in it is preferred, if not and the
|
|
|
|
// fragmentOptions has the persist key set to true, a fragment with matching
|
|
|
|
// successEvent will be pulled from memory if it exists.
|
|
|
|
const fragment = useSelector((state) =>
|
|
|
|
selectMatchingFragment(state, {
|
|
|
|
fragmentOptions,
|
|
|
|
existingId: existingId ?? createdFragmentId,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
// If no valid existing fragment can be found, a new one must be created that
|
|
|
|
// will then be found by the selector above. To do this, invoke the
|
|
|
|
// createEventFragment method with the fragmentOptions and current sessionId.
|
|
|
|
// As soon as we call the background method we also update the
|
|
|
|
// createEventFragmentCalled ref's current value to true so that future calls
|
|
|
|
// are suppressed.
|
|
|
|
useEffect(() => {
|
|
|
|
if (fragment === undefined && createEventFragmentCalled.current === false) {
|
|
|
|
createEventFragmentCalled.current = true;
|
|
|
|
createEventFragment({
|
|
|
|
...fragmentOptions,
|
|
|
|
environmentType: getEnvironmentType(),
|
|
|
|
}).then((createdFragment) => {
|
|
|
|
setCreatedFragmentId(createdFragment.id);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}, [fragment, fragmentOptions]);
|
|
|
|
|
2022-03-25 15:26:09 +01:00
|
|
|
const context = useSegmentContext();
|
2022-01-12 20:31:54 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* trackSuccess is used to close a fragment with the affirmative action. This
|
|
|
|
* method is just a thin wrapper around the background method that sets the
|
|
|
|
* necessary values.
|
|
|
|
*/
|
|
|
|
const trackSuccess = useCallback(() => {
|
|
|
|
finalizeEventFragment(fragment.id, { context });
|
|
|
|
}, [fragment, context]);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* trackFailure is used to close a fragment as abandoned. This method is just a
|
|
|
|
* thin wrapper around the background method that sets the necessary values.
|
|
|
|
*/
|
|
|
|
const trackFailure = useCallback(() => {
|
|
|
|
finalizeEventFragment(fragment.id, { abandoned: true, context });
|
|
|
|
}, [fragment, context]);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* updateEventFragmentProperties is a thin wrapper around updateEventFragment
|
|
|
|
* that supplies the fragment id as the first parameter. This function will
|
|
|
|
* be passed back from the hook as 'updateEventFragment', but is named
|
|
|
|
* updateEventFragmentProperties to avoid naming conflicts.
|
|
|
|
*/
|
|
|
|
const updateEventFragmentProperties = useCallback(
|
|
|
|
(payload) => {
|
|
|
|
updateEventFragment(fragment.id, payload);
|
|
|
|
},
|
|
|
|
[fragment],
|
|
|
|
);
|
|
|
|
|
|
|
|
return {
|
|
|
|
trackSuccess,
|
|
|
|
trackFailure,
|
|
|
|
updateEventFragment: updateEventFragmentProperties,
|
|
|
|
fragment,
|
|
|
|
};
|
|
|
|
}
|