diff --git a/app.json b/app.json
index aef4374..b957482 100644
--- a/app.json
+++ b/app.json
@@ -1,6 +1,6 @@
{
"expo": {
- "name": "fake-check-bd",
+ "name": "FactChecker",
"slug": "fake-check-bd",
"version": "1.0.0",
"orientation": "portrait",
diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx
index 7e7a53d..2a36f55 100644
--- a/app/(tabs)/index.tsx
+++ b/app/(tabs)/index.tsx
@@ -1,219 +1,228 @@
import { WebView } from "react-native-webview";
-import { useRef } from "react";
+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) => {
+ 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 }),
+ });
+
+ 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);
+
+ // Send the result back to WebView to show toast
+ webviewRef.current?.injectJavaScript(`
+ (function() {
+ showToast('${result.evidence.replace(/'/g, "\\'")}');
+ 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);
+
+ 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() {
+ enableAllButtons();
+ const button = document.querySelector('button[data-post-id="${data.postId}"]');
+ if (button) {
+ button.disabled = false;
+ button.style.opacity = '1';
+ button.textContent = 'Check';
+ }
+ 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() {
- try {
- // Inject CSS for toast notifications
- 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;
- }
- \`;
- document.head.appendChild(style);
-
- // Fact-checking logic
- let lastClickTime = 0;
- const COOLDOWN_DURATION = 60000;
- let buttonClicked = false;
- let fullCaption = "";
- let imageUrls = [];
- let isApiCallInProgress = false;
-
- // Toast notification function
- function showToast(message, duration = 5000) {
- const container = document.getElementById('toast-container') ||
- (() => {
- const cont = document.createElement('div');
- cont.id = 'toast-container';
- document.body.appendChild(cont);
- return cont;
- })();
-
- const toast = document.createElement('div');
- toast.className = 'toast';
- toast.textContent = message;
- container.appendChild(toast);
-
- setTimeout(() => {
- toast.classList.add('fade-out');
- toast.addEventListener('transitionend', () => {
- toast.remove();
- });
- }, duration);
+ // 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;
}
-
- // Function to disable all buttons
- function disableAllButtons() {
- document.querySelectorAll("button").forEach((btn) => {
- btn.disabled = true;
- btn.style.opacity = "0.5";
- btn.style.cursor = "not-allowed";
- });
+ .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 enable all buttons
- function enableAllButtons() {
- document.querySelectorAll("button").forEach((btn) => {
+ // 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";
- });
- }
-
- // Fact-checking API call
- async function checkFacts(caption, button) {
- try {
- isApiCallInProgress = true;
- disableAllButtons();
- const response = await fetch('http://localhost:8000/check-facts', {
- method: 'POST',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({ query: caption }),
- });
-
- if (!response.ok) {
- throw new Error('Network response was not ok');
- }
-
- const result = await response.json();
- showToast(result.evidence);
- button.textContent = 'Processed';
- } catch (error) {
- console.error('Error:', error);
- showToast('An error occurred while checking facts');
- } finally {
- isApiCallInProgress = false;
- enableAllButtons();
- button.disabled = false;
- button.style.opacity = "1";
}
- }
+ });
+ };
- function collectPostData(post) {
- if (post.dataset.processed === "true") return;
+ // Toast notification function
+ window.showToast = function(message, duration = 5000, isError = false) {
+ const container = document.getElementById('toast-container') ||
+ (() => {
+ const cont = document.createElement('div');
+ cont.id = 'toast-container';
+ document.body.appendChild(cont);
+ return cont;
+ })();
- const captionElement = post.querySelector(
- "div.m div.m div[data-type='text'] div.native-text"
- );
- let caption = captionElement ? captionElement.textContent.trim() : null;
+ const toast = document.createElement('div');
+ toast.className = 'toast' + (isError ? ' error-toast' : '');
+ toast.textContent = message;
+ container.appendChild(toast);
- const imageElements = post.querySelectorAll("div.m.bg-s13 img");
- let imageURLs = [];
- imageElements.forEach((imageElement) => {
- if (imageElement.src) imageURLs.push(imageElement.src);
+ setTimeout(() => {
+ toast.classList.add('fade-out');
+ toast.addEventListener('transitionend', () => {
+ toast.remove();
});
+ }, duration);
+ };
- if (buttonClicked) {
- fullCaption = caption;
- imageUrls.push(...imageURLs);
- }
- post.dataset.processed = "true";
-
- const button = createButton(post, caption, imageURLs);
- if (caption?.length > 30 || imageURLs.length > 0) {
- post.style.position = "relative";
- post.appendChild(button);
- }
- }
-
- function createButton(post, initialCaption, initialImageURLs) {
- const button = document.createElement("button");
- button.textContent = "Check";
-
- Object.assign(button.style, {
- position: "absolute",
- fontSize: "20px",
- right: "10px",
- bottom: "10px",
- background: "linear-gradient(135deg, #6a11cb, #2575fc)",
- color: "#fff",
- border: "none",
- padding: "12px 20px",
- borderRadius: "8px",
- cursor: "pointer",
- zIndex: 1000000,
- display: "flex",
- justifyContent: "center",
- alignItems: "center",
- gap: "8px",
- pointerEvents: "auto",
- transition: "all 0.3s",
- });
-
- button.addEventListener("click", (event) => {
- event.stopPropagation();
- const currentTime = new Date().getTime();
-
- lastClickTime = currentTime;
-
- button.disabled = true;
- button.textContent = "Processing...";
- button.style.opacity = "0.5";
-
- const seeMoreElement = post.querySelector("span[style*='color:#65676b']");
-
- if (seeMoreElement) {
- handleSeeMoreClick(
- seeMoreElement,
- post,
- button,
- initialCaption,
- initialImageURLs
- );
- } else {
- collectDataImmediately(post, button, initialCaption, initialImageURLs);
- }
- });
-
- return button;
- }
-
- async function handleSeeMoreClick(seeMoreElement, post, button, initialCaption, initialImageURLs) {
+ // Function to handle "See More" click
+ async function handleSeeMoreClick(seeMoreElement, post, button, initialCaption, initialImageURLs, postId) {
return new Promise((resolve) => {
setTimeout(() => {
seeMoreElement.click();
- buttonClicked = true;
+ setTimeout(() => {
+ const captionElement = post.querySelector(
+ "div.m div.m div[data-type='text'] div.native-text"
+ );
+ fullCaption = captionElement ? captionElement.textContent.trim() : initialCaption;
- setTimeout(async () => {
- await checkFacts(fullCaption, button);
-
- button.textContent = "Processed";
- button.style.opacity = "0.7";
- button.disabled = true;
+ // Send data to React Native
+ window.ReactNativeWebView.postMessage(JSON.stringify({
+ type: 'postData',
+ caption: fullCaption,
+ imageUrls: initialImageURLs,
+ postId
+ }));
resolve();
}, 2000);
@@ -221,78 +230,157 @@ export default function HomeScreen() {
});
}
- async function collectDataImmediately(
- post,
- button,
- initialCaption,
- initialImageURLs
- ) {
- buttonClicked = true;
-
+ // 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"
);
- let caption = captionElement
- ? captionElement.textContent.trim()
- : initialCaption;
+ const caption = captionElement ? captionElement.textContent.trim() : initialCaption;
- const imageElements = post.querySelectorAll("div.m img");
- let imageURLs = [];
- imageElements.forEach((imageElement) => {
- if (imageElement.src) imageURLs.push(imageElement.src);
- });
+ // Send data to React Native
+ window.ReactNativeWebView.postMessage(JSON.stringify({
+ type: 'postData',
+ caption,
+ imageUrls: initialImageURLs,
+ postId
+ }));
+ }
- if (imageURLs.length === 0) {
- imageURLs = initialImageURLs;
+ 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 > 40) || 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: "-30px",
+ border: "none",
+ borderRadius: "8px",
+ cursor: "pointer",
+ zIndex: 1000000,
+ 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";
+
+ const seeMoreElement = post.querySelector("span[style*='color:#65676b']");
+
+ if (seeMoreElement) {
+ await handleSeeMoreClick(seeMoreElement, post, button, caption, imageURLs, postId);
+ } else {
+ collectDataImmediately(post, button, caption, imageURLs, postId);
}
-
- await checkFacts(caption, button);
- button.textContent = "Processed";
- button.style.opacity = "0.7";
- button.disabled = true;
- }
-
- // 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.bg-s3").forEach((post) => {
- if (!post.dataset.processed) {
- collectPostData(post);
- }
- });
- });
-
- true; // Successful injection
- } catch (error) {
- console.error('Injection error:', error);
- false;
+ 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.bg-s3").forEach((post) => {
+ if (!post.dataset.processed) {
+ collectPostData(post);
+ }
+ });
+ });
+
+ return true;
})();
`;
return (
-
+
+
+
);
-}
\ No newline at end of file
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ marginTop: 30,
+ },
+});
diff --git a/package.json b/package.json
index ad88c20..5289b39 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "name": "fake-check-bd",
+ "name": "fact-checker",
"main": "expo-router/entry",
"version": "1.0.0",
"scripts": {