Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 29 additions & 35 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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) => {
Expand All @@ -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);
}

Expand All @@ -55,7 +61,7 @@ function App(props) {
}
return task;
});
HybridWebView.SendInvokeMessageToDotNet("EditTask", [id, newName]);
HybridWebViewSendInvokeMessageToDotNet("EditTask", [id, newName]);
setTasks(editedTaskList);
}

Expand Down Expand Up @@ -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]);
}

Expand All @@ -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(() => {
Expand All @@ -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 (
<div className="todoapp stack-large">
<h2>Loading items...</h2>
</div>
);
}
else {
return (
<div className="todoapp stack-large">
<Form addTask={addTask} />
<div className="filters btn-group stack-exception">{filterList}</div>
<h2 id="list-heading" tabIndex="-1" ref={listHeadingRef}>
{headingText}
</h2>
<ul
className="todo-list stack-large stack-exception"
aria-labelledby="list-heading">
{taskList}
</ul>
</div>
);
}
return (
<div className="todoapp stack-large">
<Form addTask={addTask} />
<div className="filters btn-group stack-exception">{filterList}</div>
<h2 id="list-heading" tabIndex="-1" ref={listHeadingRef}>
{headingText}
</h2>
<ul
className="todo-list stack-large stack-exception"
aria-labelledby="list-heading">
{taskList}
</ul>
</div>
);
}

export default App;
100 changes: 71 additions & 29 deletions src/hybridwebview/HybridWebView.js
Original file line number Diff line number Diff line change
@@ -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();