import { WebView } from "react-native-webview";
import { useRef, useState } from "react";
import { Alert, StyleSheet, View } from "react-native";
export default function HomeScreen() {
const webviewRef = useRef(null);
const [postData, setPostData] = useState([]);
const [isApiCallInProgress, setIsApiCallInProgress] = useState(false);
// Function to call the fact-check API
const checkFacts = async (caption, imageUrl) => {
try {
const response = await fetch('https://factcheck.planpostai.com/check-facts', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ query: caption, url:imageUrl }),
});
const responseText = await response.text();
console.log('Raw API Response:', responseText);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status} - ${responseText}`);
}
const result = JSON.parse(responseText);
let color;
if (result?.verdict === 'True') {
color = "#2E7D32";
} else if (result?.verdict === 'False') {
color = "#D32F2F";
} else {
color = "#F9A825";
}
// Send the result back to WebView to show toast
webviewRef.current?.injectJavaScript(`
(function() {
showToast('${result.evidence.replace(/'/g, "\\'")}', 10000, '${color}');
return true;
})();
`);
return result;
} catch (error) {
console.error('API Error Details:', {
message: error.message,
stack: error.stack,
name: error.name
});
// Show error in WebView
webviewRef.current?.injectJavaScript(`
(function() {
showToast('Error: ${error.message.replace(/'/g, "\\'")}', 5000, true);
return true;
})();
`);
throw error;
}
};
const handleMessage = async (event) => {
try {
const data = JSON.parse(event.nativeEvent.data);
switch (data.type) {
case 'postData':
setPostData(prevData => [...prevData, {
caption: data.caption,
imageUrls: data.imageUrls,
postId: data.postId
}]);
if (isApiCallInProgress) {
return;
}
setIsApiCallInProgress(true);
try {
const result = await checkFacts(data.caption, data.imageUrls[0]);
setIsApiCallInProgress(false);
webviewRef.current?.injectJavaScript(`
(function() {
enableAllButtons();
const button = document.querySelector('button[data-post-id="${data.postId}"]');
if (button) {
button.textContent = 'Processed';
button.style.background="green";
button.style.color="white";
button.style.padding="5px 5px";
button.style.opacity = '0.7';
button.disabled = true;
}
return true;
})();
`);
} catch (error) {
setIsApiCallInProgress(false);
webviewRef.current?.injectJavaScript(`
(function() {
const svgCode=\`
\`;
enableAllButtons();
const button = document.querySelector('button[data-post-id="${data.postId}"]');
if (button) {
button.disabled = false;
button.style.opacity = '1';
button.innerHTML = svgCode;
}
return true;
})();
`);
} finally {
setIsApiCallInProgress(false);
}
break;
case 'error':
console.error(`WebView Error: ${data.message}`);
break;
}
} catch (error) {
console.error('Error handling message:', error);
}
};
const injectedJavaScript = `
(function() {
// Add toast CSS
const style = document.createElement('style');
style.innerHTML = \`
#toast-container {
position: fixed;
top: 50px;
right: 20px;
left: 20px;
z-index: 999999;
display: flex;
flex-direction: column;
gap: 10px;
pointer-events: auto;
}
.toast {
background-color: #1db146;
color: #fff;
font-weight: 800;
padding: 12px 16px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
font-size: 16px;
opacity: 1;
position: relative;
width: 90%;
z-index: 99999999999;
transition: opacity 0.5s ease;
}
.toast.fade-out {
opacity: 0;
}
.error-toast {
background-color: #ff4444;
}
\`;
document.head.appendChild(style);
// Function to disable all buttons
window.disableAllButtons = function() {
document.querySelectorAll("button").forEach((btn) => {
btn.disabled = true;
btn.style.opacity = "0.5";
btn.style.cursor = "not-allowed";
});
};
// Function to enable all buttons
window.enableAllButtons = function() {
document.querySelectorAll("button").forEach((btn) => {
if (btn.textContent !== "Processed") {
btn.disabled = false;
btn.style.opacity = "1";
btn.style.cursor = "pointer";
}
});
};
// Toast notification function
window.showToast = function showToast(message, duration, color = '#333') {
// Create the toast element
const toast = document.createElement('div');
toast.textContent = message;
// Apply basic styles
toast.style.position = 'fixed';
toast.style.top = '5px';
toast.style.left = '50%';
toast.style.fontSize="16px";
toast.style.fontWeight="800";
toast.style.transform = 'translateX(-50%)';
toast.style.padding = '10px 20px';
toast.style.color = '#fff';
toast.style.backgroundColor = color; // Dynamic background color
toast.style.borderRadius = '8px';
toast.style.boxShadow = '0 2px 10px rgba(0, 0, 0, 0.3)';
toast.style.zIndex = '9999';
toast.style.opacity = '1';
toast.style.transition = 'opacity 0.5s ease-in-out';
toast.style.width = '85%';
// Add toast to the DOM
document.body.appendChild(toast);
// Auto-remove the toast after the specified duration
setTimeout(() => {
toast.style.opacity = '0'; // Fade out
setTimeout(() => {
document.body.removeChild(toast); // Remove from DOM
}, 500); // Match fade-out duration
}, duration);
};
// Function to handle "See More" click
async function handleSeeMoreClick(seeMoreElement, post, button, initialCaption, initialImageURLs, postId) {
return new Promise((resolve) => {
setTimeout(() => {
seeMoreElement.click();
setTimeout(() => {
const captionElement = post.querySelector(
"div.m div.m div[data-type='text'] div.native-text"
);
fullCaption = captionElement ? captionElement.textContent.trim() : initialCaption;
// Send data to React Native
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'postData',
caption: fullCaption,
imageUrls: initialImageURLs,
postId
}));
resolve();
}, 2000);
}, 100);
});
}
// Function to collect post data immediately
function collectDataImmediately(post, button, initialCaption, initialImageURLs, postId) {
const captionElement = post.querySelector(
"div.m div.m div[data-type='text'] div.native-text"
);
const caption = captionElement ? captionElement.textContent.trim() : initialCaption;
// Send data to React Native
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'postData',
caption,
imageUrls: initialImageURLs,
postId
}));
}
const svgCode=\`
\`
// Function to collect post data and send to React Native
function collectPostData(post) {
if (post.dataset.processed === "true") return;
// Get the caption
const captionElement = post.querySelector(
"div.m div.m div[data-type='text'] div.native-text"
);
const caption = captionElement ? captionElement.textContent.trim() : null;
// Get the first image (if exists)
const singleImageElement = post.querySelector("div.m.bg-s13 img");
const singleImageURL = singleImageElement && singleImageElement.src ? singleImageElement.src : null;
// Collect all image elements
const imageElements = post.querySelectorAll("div.m.bg-s13 img");
const imageURLs = Array.from(imageElements)
.filter(img => img.src)
.map(img => img.src);
post.dataset.processed = "true";
// Prioritize using a single image if only one exists, otherwise use all images
let imagesToUse = [];
if (singleImageURL) {
imagesToUse = [singleImageURL];
} else if (imageURLs.length > 0) {
imagesToUse = imageURLs;
}
// Create button if there's a caption or at least one image
if ((caption && caption.length > 50) || imagesToUse.length > 0) {
const postId = 'post_' + Math.random().toString(36).substr(2, 9);
createButton(post, caption, imagesToUse, postId);
}
}
// Create check button
function createButton(post, caption, imageURLs, postId) {
post.style.position = "relative";
const button = document.createElement("button");
button.textContent = "Check";
button.setAttribute('data-post-id', postId);
button.innerHTML=svgCode
Object.assign(button.style, {
position: "absolute",
right: "5px",
top: "-34px",
border: "none",
borderRadius: "8px",
cursor: "pointer",
zIndex: 9999,
pointerEvents: "auto",
transition: "all 0.3s",
});
button.addEventListener("click", async () => {
disableAllButtons();
button.textContent = "Processing";
button.style.opacity = "0.5";
button.style.color="white";
button.style.padding="5px 5px";
button.style.background="blue";
collectDataImmediately(post, button, caption, imageURLs, postId);
});
post.appendChild(button);
}
// Mutation observer to handle new posts
function handleMutations(mutationsList) {
mutationsList.forEach((mutation) => {
if (mutation.type === "childList" || mutation.type === "subtree") {
document.querySelectorAll("div.m.bg-s3").forEach((post) => {
if (!post.querySelector("button")) {
collectPostData(post);
}
});
}
});
}
const observer = new MutationObserver(handleMutations);
observer.observe(document.querySelector(".m"), {
childList: true,
subtree: true,
});
document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll("div.m.displayed.bg-s3").forEach((post) => {
if (!post.dataset.processed) {
collectPostData(post);
}
});
});
return true;
})();
`;
return (
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 5,
},
});