diff --git a/src/App.js b/src/App.js index 3b59325ff..1ac0055f8 100644 --- a/src/App.js +++ b/src/App.js @@ -3,7 +3,7 @@ import Form from "./components/Form"; import FilterButton from "./components/FilterButton"; import Todo from "./components/Todo"; import { nanoid } from "nanoid"; -import HybridWebView from './hybridwebview/HybridWebView.js'; +import './hybridwebview/HybridWebView.js'; function usePrevious(value) { const ref = useRef(); @@ -22,9 +22,15 @@ const FILTER_MAP = { const FILTER_NAMES = Object.keys(FILTER_MAP); function App(props) { - const [tasks, setTasks] = useState([]); + const [tasks, setTasks] = useState(props.tasks); const [filter, setFilter] = useState("All"); const [isLoading, setIsLoading] = useState(true); + const HybridWebView = window.HybridWebView; + + function HybridWebViewSendInvokeMessageToDotNet(methodName, args) { + const jsonArgs = JSON.stringify(args); + HybridWebView.SendRawMessage("Invoke:" + methodName + ":" + jsonArgs); + } function toggleTaskCompleted(id) { const updatedTasks = tasks.map((task) => { @@ -36,13 +42,13 @@ function App(props) { } return task; }); - HybridWebView.SendInvokeMessageToDotNet("ToggleCompletedTask", [id]); + HybridWebViewSendInvokeMessageToDotNet("ToggleCompletedTask", [id]); setTasks(updatedTasks); } function deleteTask(id) { const remainingTasks = tasks.filter((task) => id !== task.id); - HybridWebView.SendInvokeMessageToDotNet("DeleteTask", [id]); + HybridWebViewSendInvokeMessageToDotNet("DeleteTask", [id]); setTasks(remainingTasks); } @@ -55,7 +61,7 @@ function App(props) { } return task; }); - HybridWebView.SendInvokeMessageToDotNet("EditTask", [id, newName]); + HybridWebViewSendInvokeMessageToDotNet("EditTask", [id, newName]); setTasks(editedTaskList); } @@ -84,7 +90,7 @@ function App(props) { function addTask(name) { const newTask = { id: "todo-" + nanoid(), name: name, completed: false }; - HybridWebView.SendInvokeMessageToDotNet("AddTask", [newTask]); + HybridWebViewSendInvokeMessageToDotNet("AddTask", [newTask]); setTasks([...tasks, newTask]); } @@ -94,11 +100,11 @@ function App(props) { const listHeadingRef = useRef(null); const prevTaskLength = usePrevious(tasks.length); - window.globalSetData = function(newData) - { + window.globalSetData = function (newData) { setIsLoading(false); setTasks(newData); console.log("New data arrived with " + newData.length + " item(s)"); + return "null"; }; useEffect(() => { @@ -108,35 +114,23 @@ function App(props) { // Start initial data load console.log("Start loading..."); - HybridWebView.SendInvokeMessageToDotNet("StartTaskLoading"); + HybridWebViewSendInvokeMessageToDotNet("StartTaskLoading"); }, [tasks.length, prevTaskLength]); - - //window.setTimeout(function() { setIsLoading(false);}, 2000); - - if (isLoading) { - return ( -
-

Loading items...

-
- ); - } - else { - return ( -
-
-
{filterList}
-

- {headingText} -

- -
- ); - } + return ( +
+ +
{filterList}
+

+ {headingText} +

+ +
+ ); } export default App; diff --git a/src/hybridwebview/HybridWebView.js b/src/hybridwebview/HybridWebView.js index 7e5ab312c..0ac7af11a 100644 --- a/src/hybridwebview/HybridWebView.js +++ b/src/hybridwebview/HybridWebView.js @@ -1,38 +1,80 @@ // Standard methods for HybridWebView -export default class HybridWebView { +window.HybridWebView = { + "Init": function () { + function DispatchHybridWebViewMessage(message) { + const event = new CustomEvent("HybridWebViewMessageReceived", { detail: { message: message } }); + window.dispatchEvent(event); + } - static SendRawMessageToDotNet = function (message) { - this.SendMessageToDotNet(0, message); - } + if (window.chrome && window.chrome.webview) { + // Windows WebView2 + window.chrome.webview.addEventListener('message', arg => { + DispatchHybridWebViewMessage(arg.data); + }); + } + else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) { + // iOS and MacCatalyst WKWebView + window.external = { + "receiveMessage": message => { + DispatchHybridWebViewMessage(message); + } + }; + } + else { + // Android WebView + window.addEventListener('message', arg => { + DispatchHybridWebViewMessage(arg.data); + }); + } + }, + + "SendRawMessage": function (message) { + window.HybridWebView.__SendMessageInternal('RawMessage', message); + }, + + "__SendMessageInternal": function (type, message) { - static SendInvokeMessageToDotNet = function (methodName, paramValues) { - if (typeof paramValues !== 'undefined') { - if (!Array.isArray(paramValues)) { - paramValues = [paramValues]; + const messageToSend = type + '|' + message; + + if (window.chrome && window.chrome.webview) { + // Windows WebView2 + window.chrome.webview.postMessage(messageToSend); + } + else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) { + // iOS and MacCatalyst WKWebView + window.webkit.messageHandlers.webwindowinterop.postMessage(messageToSend); } - for (var i = 0; i < paramValues.length; i++) { - paramValues[i] = JSON.stringify(paramValues[i]); + else { + // Android WebView + window.hybridWebViewHost.sendMessage(messageToSend); } - } + }, - this.SendMessageToDotNet(1, JSON.stringify({ "MethodName": methodName, "ParamValues": paramValues })); - } + "InvokeMethod": function (taskId, methodName, args) { + if (methodName[Symbol.toStringTag] === 'AsyncFunction') { + // For async methods, we need to call the method and then trigger the callback when it's done + const asyncPromise = methodName(...args); + asyncPromise + .then(asyncResult => { + window.HybridWebView.__TriggerAsyncCallback(taskId, asyncResult); + }) + .catch(error => console.error(error)); + } else { + // For sync methods, we can call the method and trigger the callback immediately + const syncResult = methodName(...args); + window.HybridWebView.__TriggerAsyncCallback(taskId, syncResult); + } + }, - static SendMessageToDotNet = function (messageType, messageContent) { - var message = JSON.stringify({ "MessageType": messageType, "MessageContent": messageContent }); - - if (window.chrome && window.chrome.webview) { - // Windows WebView2 - window.chrome.webview.postMessage(message); - } - else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) { - // iOS and MacCatalyst WKWebView - window.webkit.messageHandlers.webwindowinterop.postMessage(message); - } - else { - // Android WebView - window.hybridWebViewHost.sendMessage(message); - } + "__TriggerAsyncCallback": function (taskId, result) { + // Make sure the result is a string + if (result && typeof (result) !== 'string') { + result = JSON.stringify(result); + } + + window.HybridWebView.__SendMessageInternal('InvokeMethodCompleted', taskId + '|' + result); } -}; +} + +window.HybridWebView.Init();