diff --git a/src/App.tsx b/src/App.tsx index 663a6a9..c48e00e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,6 +3,7 @@ import { IonReactRouter } from "@ionic/react-router"; import { Route, Redirect } from "react-router-dom"; import Home from "./pages/Home"; import MeshKitPage from "./pages/MeshKit"; +import { ROUTES } from "./constants"; /* Core CSS required for Ionic components to work properly */ import "@ionic/react/css/core.css"; @@ -29,14 +30,14 @@ const App: React.FC = () => ( - + - + - - + + diff --git a/src/components/Dashboard/Dashboard.tsx b/src/components/Dashboard/Dashboard.tsx index dcbed8d..1cb2de0 100644 --- a/src/components/Dashboard/Dashboard.tsx +++ b/src/components/Dashboard/Dashboard.tsx @@ -15,6 +15,22 @@ import { RestoreSuccessModal } from "../Modals/RestoreSuccessModal"; import * as AppGeneral from "../socialcalc/index.js"; import { useHistory } from "react-router"; import { DATA } from "../../app-data.js"; +import { + IPFS_GATEWAY_URL, + DEFAULT_FILE_NAME, + RESTORED_INVOICE_DEFAULT, + ConnectionStatus, + ActivityType, + MetricKey, + DEFAULT_METRICS, + CID_DISPLAY_LENGTH, + MAX_ACTIVITY_DISPLAY, + REPORT_FILENAME, + REPORT_HEADER, + TOAST_DURATION_LONG, + TOAST_POSITION, + ROUTES, +} from "../../constants.js"; interface DashboardProps { store: Local; @@ -26,13 +42,13 @@ const Dashboard: React.FC = ({ store, onOpenFile, currentBillTyp const [fileList, setFileList] = useState<{ [key: string]: any }>({}); const [backupHistory, setBackupHistory] = useState([]); const [activityLogs, setActivityLogs] = useState([]); - const [metrics, setMetrics] = useState({ invoicesCreated: 0, invoicesBackedUp: 0, successfulRestores: 0, filesUploaded: 0, messagesSent: 0 }); + const [metrics, setMetrics] = useState(DEFAULT_METRICS); const [isLoading, setIsLoading] = useState(false); const [toastMessage, setToastMessage] = useState(""); const [showToast, setShowToast] = useState(false); // MeshKit Status - const [connStatus, setConnStatus] = useState("Pending"); + const [connStatus, setConnStatus] = useState(ConnectionStatus.PENDING); // Modals / Alerts const [showAlertDelete, setShowAlertDelete] = useState(false); @@ -66,9 +82,9 @@ const Dashboard: React.FC = ({ store, onOpenFile, currentBillTyp const checkConnection = async () => { try { const isOnline = await testConnection(); - setConnStatus(isOnline ? "Online" : "Offline"); + setConnStatus(isOnline ? ConnectionStatus.ONLINE : ConnectionStatus.OFFLINE); } catch { - setConnStatus("Offline"); + setConnStatus(ConnectionStatus.OFFLINE); } }; @@ -118,8 +134,8 @@ const Dashboard: React.FC = ({ store, onOpenFile, currentBillTyp ); await store._saveFile(file); await store._addBackupHistory({ cid: record.cid, timestamp: new Date().toISOString(), name: key }); - await store._incrementMetric('invoicesBackedUp'); - await store._logActivity({ type: 'BACKUP', description: `Backed up ${key} to IPFS`, cid: record.cid }); + await store._incrementMetric(MetricKey.INVOICES_BACKED_UP); + await store._logActivity({ type: ActivityType.BACKUP, description: `Backed up ${key} to IPFS`, cid: record.cid }); loadData(); setBackupSuccessCid(record.cid); } catch (error) { @@ -134,12 +150,12 @@ const Dashboard: React.FC = ({ store, onOpenFile, currentBillTyp setIsLoading(true); try { const backup = await restoreInvoiceFromIPFS(cid); - let filename = overwriteKey || backup.name || "restored_invoice"; + let filename = overwriteKey || backup.name || RESTORED_INVOICE_DEFAULT; if (!overwriteKey) { let counter = 1; while (await store._checkKey(filename)) { - filename = `${backup.name || "restored_invoice"}_${counter}`; + filename = `${backup.name || RESTORED_INVOICE_DEFAULT}_${counter}`; counter++; } } @@ -156,8 +172,8 @@ const Dashboard: React.FC = ({ store, onOpenFile, currentBillTyp ); await store._saveFile(file); - await store._incrementMetric('successfulRestores'); - await store._logActivity({ type: 'RESTORE', description: `Restored ${filename} from IPFS`, cid }); + await store._incrementMetric(MetricKey.SUCCESSFUL_RESTORES); + await store._logActivity({ type: ActivityType.RESTORE, description: `Restored ${filename} from IPFS`, cid }); loadData(); handleOpen(filename); setRestoreSuccessFile(filename); @@ -170,8 +186,8 @@ const Dashboard: React.FC = ({ store, onOpenFile, currentBillTyp const handleShare = async (cid: string) => { try { - await navigator.clipboard.writeText(`CID: ${cid}\nGateway: https://gateway.pinata.cloud/ipfs/${cid}`); - await store._logActivity({ type: 'SHARE', description: `Shared IPFS CID`, cid }); + await navigator.clipboard.writeText(`CID: ${cid}\nGateway: ${IPFS_GATEWAY_URL}${cid}`); + await store._logActivity({ type: ActivityType.SHARE, description: `Shared IPFS CID`, cid }); loadData(); displayToast("CID & Gateway Link copied to clipboard!"); } catch (err) { @@ -187,7 +203,7 @@ const Dashboard: React.FC = ({ store, onOpenFile, currentBillTyp const confirmDelete = async () => { if (fileToDelete) { await store._deleteFile(fileToDelete); - await store._logActivity({ type: 'DELETE', description: `Deleted invoice ${fileToDelete}` }); + await store._logActivity({ type: ActivityType.DELETE, description: `Deleted invoice ${fileToDelete}` }); loadData(); setFileToDelete(null); displayToast("Invoice deleted"); @@ -195,7 +211,7 @@ const Dashboard: React.FC = ({ store, onOpenFile, currentBillTyp }; const handleNewFile = async (filename: string) => { - if (!filename || filename.trim() === "" || filename === "default") { + if (!filename || filename.trim() === "" || filename === DEFAULT_FILE_NAME) { displayToast("Invalid filename"); return; } @@ -206,8 +222,8 @@ const Dashboard: React.FC = ({ store, onOpenFile, currentBillTyp const content = encodeURIComponent(JSON.stringify(DATA["home"][AppGeneral.getDeviceType()]["msc"])); const file = new LocalFile(new Date().toString(), new Date().toString(), content, filename, currentBillType); await store._saveFile(file); - await store._incrementMetric('invoicesCreated'); - await store._logActivity({ type: 'CREATE', description: `Created new invoice ${filename}` }); + await store._incrementMetric(MetricKey.INVOICES_CREATED); + await store._logActivity({ type: ActivityType.CREATE, description: `Created new invoice ${filename}` }); loadData(); handleOpen(filename); }; @@ -215,7 +231,7 @@ const Dashboard: React.FC = ({ store, onOpenFile, currentBillTyp const handleExportReport = async () => { const hist = await store._getBackupHistory(); let report = "========================================\n"; - report += " MESHKIT + IPFS DECENTRALIZED REPORT \n"; + report += ` ${REPORT_HEADER} \n`; report += "========================================\n\n"; hist.forEach(h => { report += `Invoice Name : ${h.name}\n`; @@ -223,21 +239,21 @@ const Dashboard: React.FC = ({ store, onOpenFile, currentBillTyp report += `Timestamp : ${new Date(h.timestamp).toLocaleString()}\n`; report += `Provider : Pinata IPFS\n`; report += `Recovery Status : Verified & Available\n`; - report += `Gateway Link : https://gateway.pinata.cloud/ipfs/${h.cid}\n`; + report += `Gateway Link : ${IPFS_GATEWAY_URL}${h.cid}\n`; report += `----------------------------------------\n`; }); const blob = new Blob([report], { type: "text/plain" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; - a.download = "Decentralized_Storage_Report.txt"; + a.download = REPORT_FILENAME; a.click(); displayToast("Report exported successfully"); }; const handleExploreCID = () => { if (!exploreCid) return displayToast("Enter a valid CID"); - window.open(`https://gateway.pinata.cloud/ipfs/${exploreCid}`, "_blank"); + window.open(`${IPFS_GATEWAY_URL}${exploreCid}`, "_blank"); }; const uniqueCIDs = new Set(backupHistory.map(h => h.cid)).size; @@ -305,7 +321,7 @@ const Dashboard: React.FC = ({ store, onOpenFile, currentBillTyp {fileList[key].cid && ( - CID: {fileList[key].cid.substring(0, 8)}... + CID: {fileList[key].cid.substring(0, CID_DISPLAY_LENGTH)}... )} @@ -345,13 +361,13 @@ const Dashboard: React.FC = ({ store, onOpenFile, currentBillTyp

No activities recorded.

) : ( - {activityLogs.slice(0, 15).map((log, idx) => { + {activityLogs.slice(0, MAX_ACTIVITY_DISPLAY).map((log, idx) => { let icon = documentOutline; let color = "primary"; - if (log.type === 'BACKUP') { icon = cloudUploadOutline; color = "success"; } - if (log.type === 'RESTORE') { icon = cloudDownloadOutline; color = "secondary"; } - if (log.type === 'DELETE') { icon = trashOutline; color = "danger"; } - if (log.type === 'SHARE') { icon = shareSocialOutline; color = "tertiary"; } + if (log.type === ActivityType.BACKUP) { icon = cloudUploadOutline; color = "success"; } + if (log.type === ActivityType.RESTORE) { icon = cloudDownloadOutline; color = "secondary"; } + if (log.type === ActivityType.DELETE) { icon = trashOutline; color = "danger"; } + if (log.type === ActivityType.SHARE) { icon = shareSocialOutline; color = "tertiary"; } return ( @@ -389,7 +405,7 @@ const Dashboard: React.FC = ({ store, onOpenFile, currentBillTyp setShowRestorePrompt(true)}> Restore from CID - history.push("/meshkit")}> + history.push(ROUTES.MESHKIT)}> MeshKit SDK Demo @@ -424,7 +440,7 @@ const Dashboard: React.FC = ({ store, onOpenFile, currentBillTyp Connection - + {connStatus} @@ -481,8 +497,8 @@ const Dashboard: React.FC = ({ store, onOpenFile, currentBillTyp isOpen={showToast} onDidDismiss={() => setShowToast(false)} message={toastMessage} - duration={3000} - position="bottom" + duration={TOAST_DURATION_LONG} + position={TOAST_POSITION} /> { filename = filename.trim(); - if (filename === "default" || filename === "Untitled") { + if (filename === DEFAULT_FILE_NAME || filename === "Untitled") { setToastMessage("Cannot update default file!"); return false; } else if (filename === "" || !filename) { setToastMessage("Filename cannot be empty"); return false; - } else if (filename.length > 30) { + } else if (filename.length > MAX_FILENAME_LENGTH) { setToastMessage("Filename too long"); return false; } else if (/^[a-zA-Z0-9- ]*$/.test(filename) === false) { @@ -77,7 +86,7 @@ const Menu: React.FC<{ } }; const doSave = () => { - if (props.file === "default") { + if (props.file === DEFAULT_FILE_NAME) { setShowAlert1(true); return; } @@ -114,7 +123,7 @@ const Menu: React.FC<{ } }; const doBackupToIPFS = async () => { - if (props.file === "default") { + if (props.file === DEFAULT_FILE_NAME) { setShowAlertBackupMissingFile(true); return; } @@ -191,11 +200,11 @@ const Menu: React.FC<{ const content = AppGeneral.getCurrentHTMLContent(); const base64 = btoa(content); EmailComposer.open({ - to: ["jackdwell08@gmail.com"], + to: [EMAIL_RECIPIENT], cc: [], bcc: [], - body: "PFA", - attachments: [{ type: "base64", path: base64, name: "Invoice.html" }], + body: EMAIL_BODY_DEFAULT, + attachments: [{ type: "base64", path: base64, name: EMAIL_ATTACHMENT_NAME }], subject: `${APP_NAME} attached`, isHtml: true, }); @@ -357,9 +366,9 @@ const Menu: React.FC<{ setShowToast1(false); setShowAlert3(true); }} - position="bottom" + position={TOAST_POSITION} message={toastMessage} - duration={500} + duration={TOAST_DURATION_SHORT} /> ); diff --git a/src/components/Modals/BackupSuccessModal.tsx b/src/components/Modals/BackupSuccessModal.tsx index 414d491..6d02e6d 100644 --- a/src/components/Modals/BackupSuccessModal.tsx +++ b/src/components/Modals/BackupSuccessModal.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { IonModal, IonContent, IonButton, IonIcon, IonText } from '@ionic/react'; import { checkmarkCircle, copyOutline, openOutline } from 'ionicons/icons'; +import { IPFS_GATEWAY_URL } from '../../constants'; interface Props { isOpen: boolean; @@ -16,7 +17,7 @@ export const BackupSuccessModal: React.FC = ({ isOpen, onClose, cid }) => }; const openGateway = () => { - window.open(`https://gateway.pinata.cloud/ipfs/${cid}`, "_blank"); + window.open(`${IPFS_GATEWAY_URL}${cid}`, "_blank"); }; return ( diff --git a/src/components/NewFile/NewFile.tsx b/src/components/NewFile/NewFile.tsx index f1adffa..c6748d7 100644 --- a/src/components/NewFile/NewFile.tsx +++ b/src/components/NewFile/NewFile.tsx @@ -4,6 +4,7 @@ import { File, Local } from "../Storage/LocalStorage"; import { DATA } from "../../app-data.js"; import { IonAlert, IonIcon } from "@ionic/react"; import { add } from "ionicons/icons"; +import { DEFAULT_FILE_NAME } from "../../constants"; const NewFile: React.FC<{ file: string; @@ -13,7 +14,7 @@ const NewFile: React.FC<{ }> = (props) => { const [showAlertNewFileCreated, setShowAlertNewFileCreated] = useState(false); const newFile = () => { - if (props.file !== "default") { + if (props.file !== DEFAULT_FILE_NAME) { const content = encodeURIComponent(AppGeneral.getSpreadsheetContent()); const data = props.store._getFile(props.file); const file = new File( @@ -27,8 +28,8 @@ const NewFile: React.FC<{ props.updateSelectedFile(props.file); } const msc = DATA["home"][AppGeneral.getDeviceType()]["msc"]; - AppGeneral.viewFile("default", JSON.stringify(msc)); - props.updateSelectedFile("default"); + AppGeneral.viewFile(DEFAULT_FILE_NAME, JSON.stringify(msc)); + props.updateSelectedFile(DEFAULT_FILE_NAME); setShowAlertNewFileCreated(true); }; diff --git a/src/components/Storage/LocalStorage.ts b/src/components/Storage/LocalStorage.ts index 67d124e..8ca25dd 100644 --- a/src/components/Storage/LocalStorage.ts +++ b/src/components/Storage/LocalStorage.ts @@ -1,4 +1,12 @@ import { Preferences } from "@capacitor/preferences"; +import { + STORAGE_KEY_BACKUP_HISTORY, + STORAGE_KEY_ACTIVITY_LOGS, + STORAGE_KEY_METRICS, + ActivityType, + DEFAULT_METRICS, + MAX_ACTIVITY_LOGS, +} from "../../constants"; export interface BackupRecord { cid: string; @@ -8,19 +16,13 @@ export interface BackupRecord { export interface ActivityLog { id: string; - type: 'CREATE' | 'BACKUP' | 'DELETE' | 'RESTORE' | 'SHARE'; + type: ActivityType; description: string; timestamp: string; cid?: string; } -export interface AppMetrics { - invoicesCreated: number; - invoicesBackedUp: number; - successfulRestores: number; - filesUploaded: number; - messagesSent: number; -} +export type AppMetrics = typeof DEFAULT_METRICS; export class File { created: string; @@ -81,7 +83,7 @@ export class Local { const { keys } = await Preferences.keys(); for (let i = 0; i < keys.length; i++) { let fname = keys[i]; - if (fname === "_MeshKit_BackupHistory" || fname === "_MeshKit_ActivityLogs" || fname === "_MeshKit_Metrics") continue; + if (fname === STORAGE_KEY_BACKUP_HISTORY || fname === STORAGE_KEY_ACTIVITY_LOGS || fname === STORAGE_KEY_METRICS) continue; const data = await this._getFile(fname); arr[fname] = { modified: (data as any).modified, @@ -107,7 +109,7 @@ export class Local { }; _getBackupHistory = async (): Promise => { - const rawData = await Preferences.get({ key: "_MeshKit_BackupHistory" }); + const rawData = await Preferences.get({ key: STORAGE_KEY_BACKUP_HISTORY }); if (rawData.value) { return JSON.parse(rawData.value); } @@ -118,25 +120,25 @@ export class Local { const history = await this._getBackupHistory(); history.push(record); await Preferences.set({ - key: "_MeshKit_BackupHistory", + key: STORAGE_KEY_BACKUP_HISTORY, value: JSON.stringify(history) }); }; _getMetrics = async (): Promise => { - const raw = await Preferences.get({ key: "_MeshKit_Metrics" }); + const raw = await Preferences.get({ key: STORAGE_KEY_METRICS }); if (raw.value) return JSON.parse(raw.value); - return { invoicesCreated: 0, invoicesBackedUp: 0, successfulRestores: 0, filesUploaded: 0, messagesSent: 0 }; + return { ...DEFAULT_METRICS }; }; _incrementMetric = async (metric: keyof AppMetrics) => { const m = await this._getMetrics(); m[metric]++; - await Preferences.set({ key: "_MeshKit_Metrics", value: JSON.stringify(m) }); + await Preferences.set({ key: STORAGE_KEY_METRICS, value: JSON.stringify(m) }); }; _getActivityLogs = async (): Promise => { - const raw = await Preferences.get({ key: "_MeshKit_ActivityLogs" }); + const raw = await Preferences.get({ key: STORAGE_KEY_ACTIVITY_LOGS }); if (raw.value) return JSON.parse(raw.value); return []; }; @@ -148,8 +150,7 @@ export class Local { id: Math.random().toString(36).substring(2, 9), timestamp: new Date().toISOString() }); - // Keep only last 50 activities to avoid bloat - if (logs.length > 50) logs.shift(); - await Preferences.set({ key: "_MeshKit_ActivityLogs", value: JSON.stringify(logs) }); + if (logs.length > MAX_ACTIVITY_LOGS) logs.shift(); + await Preferences.set({ key: STORAGE_KEY_ACTIVITY_LOGS, value: JSON.stringify(logs) }); }; } diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..921b007 --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,89 @@ +// ============================================================ +// constants.ts — Centralized magic strings and values +// ============================================================ + +// --- Infrastructure --- +export const IPFS_GATEWAY_URL = "https://gateway.pinata.cloud/ipfs/"; +export const PROVIDER_NAME = "pinata"; + +// --- LocalStorage Keys --- +export const STORAGE_KEY_BACKUP_HISTORY = "_MeshKit_BackupHistory"; +export const STORAGE_KEY_ACTIVITY_LOGS = "_MeshKit_ActivityLogs"; +export const STORAGE_KEY_METRICS = "_MeshKit_Metrics"; + +// --- Activity Types --- +export const ActivityType = { + CREATE: "CREATE", + BACKUP: "BACKUP", + DELETE: "DELETE", + RESTORE: "RESTORE", + SHARE: "SHARE", +} as const; +export type ActivityType = typeof ActivityType[keyof typeof ActivityType]; + +// --- Metric Keys --- +export const MetricKey = { + INVOICES_CREATED: "invoicesCreated", + INVOICES_BACKED_UP: "invoicesBackedUp", + SUCCESSFUL_RESTORES: "successfulRestores", + FILES_UPLOADED: "filesUploaded", + MESSAGES_SENT: "messagesSent", +} as const; +export type MetricKey = typeof MetricKey[keyof typeof MetricKey]; + +// --- Default Metrics Object --- +export const DEFAULT_METRICS = { + [MetricKey.INVOICES_CREATED]: 0, + [MetricKey.INVOICES_BACKED_UP]: 0, + [MetricKey.SUCCESSFUL_RESTORES]: 0, + [MetricKey.FILES_UPLOADED]: 0, + [MetricKey.MESSAGES_SENT]: 0, +}; + +// --- File Name Sentinels --- +export const DEFAULT_FILE_NAME = "default"; +export const RESTORED_INVOICE_DEFAULT = "restored_invoice"; + +// --- Routes --- +export const ROUTES = { + HOME: "/home", + MESHKIT: "/meshkit", + ROOT: "/", +} as const; + +// --- Connection Status --- +export const ConnectionStatus = { + CONNECTED: "Connected", + FAILED: "Failed", + SUCCESS: "Success", + ONLINE: "Online", + OFFLINE: "Offline", + PENDING: "Pending", +} as const; + +// --- View Modes --- +export const ViewMode = { + DASHBOARD: "dashboard", + EDITOR: "editor", +} as const; +export type ViewMode = typeof ViewMode[keyof typeof ViewMode]; + +// --- Toast Config --- +export const TOAST_DURATION_LONG = 3000; +export const TOAST_DURATION_SHORT = 500; +export const TOAST_POSITION = "bottom"; + +// --- Limits / Magic Numbers --- +export const MAX_ACTIVITY_LOGS = 50; +export const MAX_FILENAME_LENGTH = 30; +export const CID_DISPLAY_LENGTH = 8; +export const MAX_ACTIVITY_DISPLAY = 15; + +// --- Email --- +export const EMAIL_RECIPIENT = "jackdwell08@gmail.com"; +export const EMAIL_BODY_DEFAULT = "PFA"; +export const EMAIL_ATTACHMENT_NAME = "Invoice.html"; + +// --- Report --- +export const REPORT_FILENAME = "Decentralized_Storage_Report.txt"; +export const REPORT_HEADER = "MESHKIT + IPFS DECENTRALIZED REPORT"; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 876a058..2c90c28 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -23,16 +23,17 @@ import Menu from "../components/Menu/Menu"; import NewFile from "../components/NewFile/NewFile"; import Dashboard from "../components/Dashboard/Dashboard"; import { useHistory } from "react-router"; +import { DEFAULT_FILE_NAME, ViewMode } from "../constants"; const Home: React.FC = () => { - const [view, setView] = useState<'dashboard' | 'editor'>('dashboard'); + const [view, setView] = useState(ViewMode.DASHBOARD); const [showMenu, setShowMenu] = useState(false); const [showPopover, setShowPopover] = useState<{ open: boolean; event: Event | undefined; }>({ open: false, event: undefined }); - const [selectedFile, updateSelectedFile] = useState("default"); + const [selectedFile, updateSelectedFile] = useState(DEFAULT_FILE_NAME); const [billType, updateBillType] = useState(1); const [device] = useState("default"); const history = useHistory(); @@ -80,15 +81,15 @@ const Home: React.FC = () => { - {view === 'editor' && ( - setView('dashboard')}> + {view === ViewMode.EDITOR && ( + setView(ViewMode.DASHBOARD)}> )} {APP_NAME} - {view === 'editor' && ( + {view === ViewMode.EDITOR && ( <> { - {view === 'dashboard' ? ( + {view === ViewMode.DASHBOARD ? ( { /> ) : null} -
+
Editing : {selectedFile} @@ -149,7 +150,7 @@ const Home: React.FC = () => {
- {view === 'editor' && ( + {view === ViewMode.EDITOR && ( setShowMenu(true)}> diff --git a/src/pages/MeshKit.tsx b/src/pages/MeshKit.tsx index 5640c4f..a3643c6 100644 --- a/src/pages/MeshKit.tsx +++ b/src/pages/MeshKit.tsx @@ -41,6 +41,15 @@ import { revokeCID, } from "../services/MeshkitService"; import { Local } from "../components/Storage/LocalStorage"; +import { + IPFS_GATEWAY_URL, + ConnectionStatus, + MetricKey, + CID_DISPLAY_LENGTH, + TOAST_DURATION_LONG, + TOAST_POSITION, + ROUTES, +} from "../constants"; const MeshKitPage: React.FC = () => { const store = new Local(); @@ -103,7 +112,7 @@ const MeshKitPage: React.FC = () => { }; const openGateway = (cid: string) => { - window.open(`https://gateway.pinata.cloud/ipfs/${cid}`, "_blank"); + window.open(`${IPFS_GATEWAY_URL}${cid}`, "_blank"); }; const handleTestConnection = async () => { @@ -112,12 +121,12 @@ const MeshKitPage: React.FC = () => { try { const isConnected = await testConnection(); if (isConnected) { - setConnStatus("Connected"); + setConnStatus(ConnectionStatus.CONNECTED); } else { - setConnStatus("Failed"); + setConnStatus(ConnectionStatus.FAILED); } } catch (error) { - setConnStatus("Failed"); + setConnStatus(ConnectionStatus.FAILED); displayToast(error instanceof Error ? error.message : "Network Error"); } finally { setConnLoading(false); @@ -162,7 +171,7 @@ const MeshKitPage: React.FC = () => { const record = await uploadFile(file); setUploadedCid(record.cid); setUploadedFileDetails({ name: file.name, size: file.size }); - await store._incrementMetric('filesUploaded'); + await store._incrementMetric(MetricKey.FILES_UPLOADED); displayToast("File uploaded successfully"); } catch (error) { displayToast(error instanceof Error ? error.message : "Upload Failed"); @@ -180,7 +189,7 @@ const MeshKitPage: React.FC = () => { const url = window.URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; - a.download = `downloaded-${downloadCid.substring(0, 8)}`; + a.download = `downloaded-${downloadCid.substring(0, CID_DISPLAY_LENGTH)}`; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); @@ -199,7 +208,7 @@ const MeshKitPage: React.FC = () => { try { const record = await sendMessage(recipientId, JSON.parse(messagePayload)); setMessageCid(record.cid); - await store._incrementMetric('messagesSent'); + await store._incrementMetric(MetricKey.MESSAGES_SENT); displayToast("Message sent successfully"); } catch (error) { displayToast(error instanceof Error ? error.message : "Failed to send message"); @@ -229,13 +238,13 @@ const MeshKitPage: React.FC = () => { try { const success = await revokeCID(revokeCidInput); if (success) { - setRevokeStatus("Success"); + setRevokeStatus(ConnectionStatus.SUCCESS); displayToast("Content revoked"); } else { - setRevokeStatus("Failed"); + setRevokeStatus(ConnectionStatus.FAILED); } } catch (error) { - setRevokeStatus("Failed"); + setRevokeStatus(ConnectionStatus.FAILED); displayToast(error instanceof Error ? error.message : "Failed to revoke"); } finally { setRevokeLoading(false); @@ -258,7 +267,7 @@ const MeshKitPage: React.FC = () => { - + MeshKit SDK Operations @@ -285,7 +294,7 @@ const MeshKitPage: React.FC = () => {
{connStatus && (
- + {connStatus}
@@ -315,8 +324,8 @@ const MeshKitPage: React.FC = () => { {revokeStatus && (
- - {revokeStatus === 'Success' ? 'Content Unpinned' : 'Revoke Failed'} + + {revokeStatus === ConnectionStatus.SUCCESS ? 'Content Unpinned' : 'Revoke Failed'}
)} @@ -529,8 +538,8 @@ const MeshKitPage: React.FC = () => { isOpen={showToast} onDidDismiss={() => setShowToast(false)} message={toastMessage} - duration={3000} - position="bottom" + duration={TOAST_DURATION_LONG} + position={TOAST_POSITION} /> ); diff --git a/src/services/MeshkitService.ts b/src/services/MeshkitService.ts index 4811439..9625e91 100644 --- a/src/services/MeshkitService.ts +++ b/src/services/MeshkitService.ts @@ -1,4 +1,5 @@ import { Meshkit, MeshkitRecord } from "@meshkit/ionic"; +import { PROVIDER_NAME } from "../constants"; let meshkitInstance: Meshkit | null = null; @@ -13,7 +14,7 @@ async function getMeshkit(): Promise { } meshkitInstance = await Meshkit.init({ - provider: "pinata", + provider: PROVIDER_NAME, providerToken: jwt, }); diff --git a/src/types/meshkit-ionic.d.ts b/src/types/meshkit-ionic.d.ts new file mode 100644 index 0000000..7f9c6d8 --- /dev/null +++ b/src/types/meshkit-ionic.d.ts @@ -0,0 +1,22 @@ +declare module "@meshkit/ionic" { + export interface MeshkitRecord { + cid: string; + data: T; + } + + export class Meshkit { + static init(options: { + provider: string; + providerToken: string; + }): Promise; + + testConnection(): Promise; + store(data: T): Promise>; + retrieve(cid: string): Promise; + upload(file: Blob): Promise>; + download(cid: string): Promise; + send(recipientId: string, payload: Record): Promise>>; + receive(cid: string): Promise>; + revoke(cid: string): Promise; + } +}