Skip to content

Commit

Permalink
(chore) Change offline setup to event based part-1
Browse files Browse the repository at this point in the history
  • Loading branch information
icrc-jofrancisco committed Dec 7, 2023
1 parent cda01e1 commit cd6c8ae
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import {
ModalFooter,
ModalHeader,
Button,
ProgressBar,
InlineLoading,
} from "@carbon/react";
import { useTranslation } from "react-i18next";
import { showToast } from "@openmrs/esm-framework";
import { getCurrentOfflineMode, showToast } from "@openmrs/esm-framework";

export interface OfflineActionsProgressModalProps {
items?: Array<any>;
Expand All @@ -19,40 +19,29 @@ const OfflineReadyModal: React.FC<OfflineActionsProgressModalProps> = ({
items,
}) => {
const { t } = useTranslation();
const [progress, setProgress] = useState(0);
const [isRunning, setIsRunning] = useState(true);
const [abortController, setAbortController] = useState(
() => new AbortController()
);

async function runAsyncFunctionsInParallel(asyncFunctions) {
const totalFunctions = asyncFunctions.length;
async function dispatchOfflineEvent() {
//TODO CHANGE MODE
let mode = getCurrentOfflineMode().active;
window.dispatchEvent(
new CustomEvent(`openmrs:offline-${mode ? "enabled" : "disabled"}`, {
detail: getCurrentOfflineMode(),
})
);

if (totalFunctions === 0) {
console.warn("No tasks to run.");
setProgress(100);
return;
}

let completedFunctions = 0;

const promises = asyncFunctions.map(async (asyncFunction) => {
await asyncFunction(abortController);
completedFunctions++;
const progress = (completedFunctions / totalFunctions) * 100;
setProgress(progress);
});

await Promise.all(promises);

console.warn("All tasks completed!");
setIsRunning(false);
}

useEffect(() => {
runAsyncFunctionsInParallel(items || []);
dispatchOfflineEvent();
}, [abortController, items]);

const handleClose = useCallback(() => {
if (progress < 100) {
if (isRunning) {
abortController.abort();

showToast({
Expand All @@ -74,7 +63,7 @@ const OfflineReadyModal: React.FC<OfflineActionsProgressModalProps> = ({
});
closeModal(true);
}
}, [abortController, closeModal, progress, t]);
}, [abortController, closeModal, isRunning, t]);

return (
<>
Expand All @@ -83,25 +72,18 @@ const OfflineReadyModal: React.FC<OfflineActionsProgressModalProps> = ({
closeModal={handleClose}
/>
<ModalBody>
<ProgressBar
id="progress-bar"
value={progress}
status={progress === 100 ? "finished" : "active"}
max={100}
label={t("progressBarLabel", "{progress}% Complete", {
progress: Math.round(progress),
})}
/>
{isRunning && (
<InlineLoading
// className={styles.loader}
description={t("loading", "Loading") + "..."}
/>
)}
</ModalBody>
<ModalFooter>
<Button kind="danger" onClick={handleClose} disabled={progress === 100}>
<Button kind="danger" onClick={handleClose} disabled={!isRunning}>
{t("cancel", "Cancel")}
</Button>
<Button
kind="primary"
onClick={handleClose}
disabled={progress !== 100}
>
<Button kind="primary" onClick={handleClose} disabled={isRunning}>
{t("confirm", "Confirm")}
</Button>
</ModalFooter>
Expand Down
2 changes: 2 additions & 0 deletions packages/apps/esm-offline-tools-app/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ export function startupApp() {
setupOffline();
setupSynchronizingOfflineActionsNotifications();

// registerOfflineHandler(setupOffline);

registerBreadcrumbs([
{
path: `${window.spaBase}/${routes.offlineTools}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,41 +23,51 @@ const OfflineActionsModeButton: React.FC = () => {
const [active, setActive] = useState(() => getCurrentOfflineMode().active);

const toggle = useCallback(() => {
if (window.installedModules && window.installedModules.length > 0) {
const dispose = showModal("offline-tools-offline-ready-modal", {
items: window.installedModules
.filter((app) => app.length >= 3)
.map((app) => app[2]),
closeModal: (result) => {
setActive(result);
setCurrentOfflineMode(result ? "on" : "off");
dispose();
},
});
} else {
console.warn("No installed modules found.");
}
const dispose = showModal("offline-tools-offline-ready-modal", {
closeModal: (result) => {
setActive(result);
setCurrentOfflineMode(result ? "on" : "off");
dispose();
},
});
}, [setActive]);

const handleRefresh = useCallback(() => {
toggle();
}, [toggle]);

return (
<div className={styles.offlineModeButtonContainer}>
<div>
<Network_3 size={20} />
<span onClick={doNotCloseMenu} role="none">
{t("offlineReady", "Offline Ready")}
</span>
isOnline && (
<div className={styles.offlineModeButtonContainer}>
<div>
<Network_3 size={20} />
<span onClick={doNotCloseMenu} role="none">
{t("offlineReady", "Offline Ready")}
</span>
</div>
<DefinitionTooltip
openOnHover
align="top"
definition={`${t("lastRun", "Last Run")}: ${
active ? lastRun : t("never", "Never")
}`}
>
{active && (
<Button kind="ghost" onClick={handleRefresh}>
{t("refresh", "Refresh")}
</Button>
)}
</DefinitionTooltip>
{!active && (
<Toggle
className={styles.toggle}
id="offlineModeSwitch"
toggled={active}
onToggle={toggle}
/>
)}
</div>
<Toggle
className={styles.toggle}
id="offlineModeSwitch"
toggled={active}
onToggle={toggle}
/>
</div>
)
);
};

Expand Down
4 changes: 1 addition & 3 deletions packages/framework/esm-globals/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,14 @@ declare global {
/**
* Gets the installed modules, which are tuples consisting of the module's name and exports.
*/
installedModules: Array<[string, OpenmrsAppRoutes, OfflineReadyFunction]>;
installedModules: Array<[string, OpenmrsAppRoutes]>;
/**
* The i18next instance for the app.
*/
i18next: i18n;
}
}

export type OfflineReadyFunction = (abortController: AbortController) => void;

export type SpaEnvironment = "production" | "development" | "test";

export interface ImportMap {
Expand Down
9 changes: 9 additions & 0 deletions packages/framework/esm-offline/src/mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ async function isPrivateBrowsing() {
}

export type OfflineMode = "on" | "off" | "unavailable";
export type OfflineHandler = () => void | Promise<void>;

export interface OfflineModeResult {
current: OfflineMode;
Expand Down Expand Up @@ -68,6 +69,14 @@ export function setCurrentOfflineMode(mode: OfflineMode) {
}
}

export function registerOfflineHandler(setupOffline: OfflineHandler) {
window.addEventListener("openmrs:offline-enabled", setupOffline);
const offlineMode = getCurrentOfflineMode();
if (offlineMode.active) {
setupOffline();
}
}

export async function activateOfflineCapability() {
const isPrivate = await isPrivateBrowsing();

Expand Down
1 change: 1 addition & 0 deletions packages/framework/esm-offline/src/public.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export {
type OfflineMode,
type OfflineModeResult,
getCurrentOfflineMode,
registerOfflineHandler,
} from "./mode";
export * from "./offline-patient-data";
export * from "./service-worker-messaging";
Expand Down
30 changes: 0 additions & 30 deletions packages/shell/esm-app-shell/src/apps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,36 +147,6 @@ function getLoader(
};
}

//TODO: MOCK asyncTask
const OFFLINE_READY_FUNCTION = "offlineReady";

const asyncTask = async (abortController) => {
return new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => {
console.warn("Task completed");
resolve("Task completed");
}, Math.floor(Math.random() * (7000 - 1000 + 1)) + 1000);
abortController.signal.addEventListener("abort", () => {
clearTimeout(timeoutId);
console.warn("Operation aborted");
reject("Operation aborted");
});
});
};

/**
* This function creates a loader function suitable for use in either a single-spa
* application or parcel.
*
* This function returns a function responsible for executing the actions required
* to prepare the module for offline mode.
*/

export function getOfflineReadyLoader(appName: string) {
//TODO dynamic import
return asyncTask;
}

/**
* This is the main entry-point for registering an app with the app shell.
* Each app has a name and should have a `routes.json` file that defines it's
Expand Down
4 changes: 1 addition & 3 deletions packages/shell/esm-app-shell/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import {
finishRegisteringAllApps,
registerApp,
tryRegisterExtension,
getOfflineReadyLoader,
} from "./apps";
import { setupI18n } from "./locale";
import { appName, getCoreExtensions } from "./ui";
Expand Down Expand Up @@ -92,8 +91,7 @@ async function setupApps() {
const modules: typeof window.installedModules = [];
const registrationPromises = Object.entries(routes).map(
async ([module, routes]) => {
const offlineLoader = getOfflineReadyLoader(module);
modules.push([module, routes, offlineLoader]);
modules.push([module, routes]);
registerApp(module, routes);
}
);
Expand Down

0 comments on commit cd6c8ae

Please sign in to comment.