This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

add new backend connection

+54 -335
+46 -19
src/accountData.js
··· 95 95 } 96 96 97 97 /*********************************************************************** 98 + * NEW: Helper Function to Send Account Data to Backend API for Scoring 99 + ***********************************************************************/ 100 + async function fetchScores(accountData) { 101 + try { 102 + const response = await fetch('https://api.cred.blue/api/score', { // Update URL when ready publicly 103 + method: 'POST', 104 + headers: { 105 + 'Content-Type': 'application/json' 106 + }, 107 + body: JSON.stringify(accountData) 108 + }); 109 + if (!response.ok) { 110 + throw new Error(`Error: ${response.statusText}`); 111 + } 112 + return await response.json(); 113 + } catch (error) { 114 + console.error("Error fetching scores:", error); 115 + throw error; 116 + } 117 + } 118 + 119 + /*********************************************************************** 98 120 * Utility Function to Find the First "createdAt" in a Record 99 121 ***********************************************************************/ 100 122 // This function recursively searches for the first occurrence of "createdAt" in an object. ··· 654 676 const totalBskyRecordsPerDay = days ? totalBskyRecords / days : 0; 655 677 const totalNonBskyRecordsPerDay = days ? totalNonBskyRecords / days : 0; 656 678 657 - // Fetch posts and reposts for the period and merge them 658 - const postsRecordsPosts = await fetchRecordsForCollection( 659 - "app.bsky.feed.post", 660 - () => { updateProgress(); }, 661 - 20, 662 - cutoffTime 663 - ); 664 - const postsRecordsReposts = await fetchRecordsForCollection( 665 - "app.bsky.feed.repost", 666 - () => { updateProgress(); }, 667 - 20, 668 - cutoffTime 669 - ); 670 - const postsRecords = postsRecordsPosts.concat(postsRecordsReposts); 679 + // Fetch posts and reposts for the period and merge them 680 + const postsRecordsPosts = await fetchRecordsForCollection( 681 + "app.bsky.feed.post", 682 + () => { updateProgress(); }, 683 + 20, 684 + cutoffTime 685 + ); 686 + const postsRecordsReposts = await fetchRecordsForCollection( 687 + "app.bsky.feed.repost", 688 + () => { updateProgress(); }, 689 + 20, 690 + cutoffTime 691 + ); 692 + const postsRecords = postsRecordsPosts.concat(postsRecordsReposts); 671 693 672 - const postsCount = postsRecords.length; 673 - const postStats = computePostStats(postsRecords, days); 674 - 694 + const postsCount = postsRecords.length; 695 + const postStats = computePostStats(postsRecords, days); 675 696 676 697 // Compute engagements for the period 677 698 const engagements = await calculateEngagements(cutoffTime); ··· 737 758 }, 738 759 }); 739 760 740 - // Assign data to the respective period 741 - accountDataPerPeriod[`accountData${label}`] = { 761 + // Build the account data object for this period. 762 + let periodData = { 742 763 profile: { 743 764 ...profile, 744 765 did: profile.did || did, ··· 748 769 did: profile.did || did, 749 770 profileEditedDate: profile.indexedAt, 750 771 profileCompletion: calculateProfileCompletion(profile), 772 + // Temporary score placeholders (will be replaced by backend calculations) 751 773 combinedScore: 250, 752 774 blueskyScore: 150, 753 775 atprotoScore: 100, ··· 805 827 }, 806 828 }, 807 829 }; 830 + 831 + // Send the account data object to the backend scoring API 832 + // and update periodData with the scored result. 833 + periodData = await fetchScores(periodData); 834 + accountDataPerPeriod[`accountData${label}`] = periodData; 808 835 } 809 836 810 837 // Compute accountData for all-time data (optional, not required per user request)
-50
src/components/ScoreForm.css
··· 1 - /* src/components/ScoreForm.css */ 2 - 3 - .form-label { 4 - display: block; 5 - font-weight: bold; 6 - text-align: center; 7 - color: #333333; 8 - margin-bottom: 5px; 9 - } 10 - 11 - .form-input { 12 - width: 100%; 13 - padding: 10px 12px; 14 - border: 1px solid #cccccc; 15 - border-radius: 4px; 16 - font-size: 1rem; 17 - box-sizing: border-box; 18 - transition: border-color 0.3s ease; 19 - } 20 - 21 - .form-input:focus { 22 - border-color: #007bff; /* Blue border on focus */ 23 - outline: none; 24 - } 25 - 26 - .form-group { 27 - margin: 10px; 28 - } 29 - 30 - .submit-button { 31 - padding: 10px 20px; 32 - background-color: #007bff; /* Blue background */ 33 - color: #ffffff; /* White text */ 34 - border: none; 35 - border-radius: 4px; 36 - font-size: 1rem; 37 - cursor: pointer; 38 - transition: background-color 0.3s ease; 39 - margin-bottom: 20px; 40 - } 41 - 42 - .submit-button:hover { 43 - background-color: #0056b3; 44 - } 45 - 46 - .form-input:disabled { 47 - background-color: #e9ecef; 48 - cursor: not-allowed; 49 - } 50 -
+3
src/components/UserProfile/UserProfile.js
··· 113 113 <h1>{displayName}</h1> 114 114 <h2>@{resolvedHandle}</h2> 115 115 <p><strong>Combined Score: {selectedAccountData.combinedScore}</strong></p> 116 + <p><strong>Overall Status: {selectedAccountData.activityAll.activityStatus}</strong></p> 116 117 <p>Bluesky Score: {selectedAccountData.blueskyScore}</p> 118 + <p>Bluesky Status: {selectedAccountData.activityAll.bskyActivityStatus}</p> 117 119 <p>Atproto Score: {selectedAccountData.atprotoScore}</p> 120 + <p>Atproto Status: {selectedAccountData.activityAll.atprotoActivityStatus}</p> 118 121 119 122 {/* Toggle Switch */} 120 123 <div className="toggle-switch">
-78
src/components/UserProfile/UserProfileLegacy.css
··· 1 - /* src/components/UserProfile/UserProfile.css */ 2 - 3 - .user-profile { 4 - max-width: 800px; 5 - margin: 0 auto; 6 - padding: 20px; 7 - } 8 - 9 - .user-profile h2 { 10 - font-size: 2em; 11 - margin-bottom: 10px; 12 - } 13 - 14 - .user-profile p { 15 - font-size: 1.1em; 16 - margin: 5px 0; 17 - } 18 - 19 - .score-section { 20 - margin-top: 20px; 21 - } 22 - 23 - .score-section h3 { 24 - font-size: 1.5em; 25 - margin-bottom: 10px; 26 - } 27 - 28 - .score-section ul { 29 - list-style-type: none; 30 - padding: 0; 31 - } 32 - 33 - .score-section li { 34 - background: #f0f0f0; 35 - margin: 5px 0; 36 - padding: 10px; 37 - border-radius: 5px; 38 - } 39 - 40 - .error { 41 - color: red; 42 - font-weight: bold; 43 - } 44 - 45 - /* Container for the loading state */ 46 - .user-profile.loading-container { 47 - display: flex; 48 - flex-direction: column; 49 - align-items: center; 50 - justify-content: center; 51 - height: 80vh; 52 - } 53 - 54 - /* Progress bar container (e.g., 80% wide and centered) */ 55 - .progress-bar-container { 56 - width: 80%; 57 - height: 10px; 58 - background-color: #e0e0e0; 59 - border-radius: 5px; 60 - overflow: hidden; 61 - margin-bottom: 1rem; 62 - } 63 - 64 - /* The progress bar element */ 65 - .progress-bar { 66 - height: 100%; 67 - background-color: #3b82f6; /* blue color, change as needed */ 68 - transition: width 0.3s ease-in-out; 69 - } 70 - 71 - /* Loading text styling */ 72 - .loading-text { 73 - font-size: 1.1rem; 74 - color: #3b82f6; 75 - text-align: center; 76 - } 77 - 78 -
-182
src/components/UserProfile/UserProfileLegacy.js
··· 1 - // src/components/UserProfile/UserProfile.jsx 2 - 3 - import React, { useEffect, useState } from "react"; 4 - import { useParams } from "react-router-dom"; 5 - import { loadAccountData } from "../../accountData"; // Ensure the path is correct 6 - import "./UserProfile.css"; // Ensure this CSS file is styled appropriately 7 - 8 - const UserProfile = () => { 9 - const { username } = useParams(); // Extract the handle from the URL (e.g., "dame.bsky.social") 10 - const [accountData, setAccountData] = useState(null); 11 - const [progress, setProgress] = useState(0); // Track progress percentage 12 - const [loading, setLoading] = useState(true); 13 - const [error, setError] = useState(null); 14 - 15 - useEffect(() => { 16 - const fetchAccountData = async () => { 17 - try { 18 - // Pass both the input handle and the onProgress callback. 19 - const data = await loadAccountData(username, (prog) => { 20 - setProgress(prog); 21 - }); 22 - if (data.error) { 23 - throw new Error(data.error); 24 - } 25 - setAccountData(data.accountData); 26 - } catch (err) { 27 - console.error("Error fetching account data:", err); 28 - setError(err.message); 29 - } finally { 30 - setLoading(false); 31 - } 32 - }; 33 - 34 - fetchAccountData(); 35 - }, [username]); 36 - 37 - if (loading) { 38 - return ( 39 - <div className="user-profile loading-container"> 40 - <div className="progress-bar-container"> 41 - <div className="progress-bar" style={{ width: `${progress}%` }} /> 42 - </div> 43 - <p className="loading-text">Loading account data... {Math.floor(progress)}%</p> 44 - </div> 45 - ); 46 - } 47 - 48 - if (error) { 49 - return <div className="user-profile error">Error: {error}</div>; 50 - } 51 - 52 - if (!accountData) { 53 - return <div className="user-profile">No profile information available.</div>; 54 - } 55 - 56 - // Destructure the key sections from the accountData object. 57 - const { 58 - profile, 59 - displayName, 60 - handle: resolvedHandle, 61 - did, 62 - createdAt, 63 - ageInDays, 64 - agePercentage, 65 - blobsCount, 66 - followersCount, 67 - followsCount, 68 - postsCount, 69 - rotationKeys, 70 - era, 71 - postingStyle, 72 - socialStatus, 73 - activityAll, 74 - activityLast30Days, 75 - alsoKnownAs, 76 - analysis, 77 - scoreGeneratedAt, 78 - serviceEndpoint, 79 - pdsType, 80 - } = accountData; 81 - 82 - return ( 83 - <div className="user-profile"> 84 - <h1>{displayName}</h1> 85 - 86 - {/* Profile Overview */} 87 - <section className="profile-overview"> 88 - <h2>Profile Overview</h2> 89 - <p><strong>Username:</strong> {resolvedHandle}</p> 90 - <p><strong>DID:</strong> {did}</p> 91 - <p> 92 - <strong>Account Created:</strong> {new Date(createdAt).toLocaleDateString()} {" "} 93 - (<em>{Math.floor(ageInDays)} days old | {Math.floor(agePercentage * 100)}%</em>) 94 - </p> 95 - <p><strong>Profile Completion:</strong> {profile.profileCompletion}</p> 96 - <p><strong>Service Endpoint:</strong> {serviceEndpoint}</p> 97 - <p><strong>PDS Type:</strong> {pdsType}</p> 98 - </section> 99 - 100 - {/* Blobs & Posts Data */} 101 - <section className="blobs-posts"> 102 - <h2>Blobs & Posts Data</h2> 103 - <p><strong>Blobs Count:</strong> {blobsCount}</p> 104 - <p> 105 - <strong>Blobs Per Day:</strong>{" "} 106 - {ageInDays ? (blobsCount / ageInDays).toFixed(2) : "0"} 107 - </p> 108 - <p> 109 - <strong>Blobs Per Post:</strong>{" "} 110 - {postsCount ? (blobsCount / postsCount).toFixed(2) : "0"} 111 - </p> 112 - <p><strong>Posts Count:</strong> {postsCount}</p> 113 - </section> 114 - 115 - {/* Overall Activity Overview */} 116 - <section className="activity-overview"> 117 - <h2>Overall Activity</h2> 118 - <p><strong>Total Records:</strong> {activityAll.totalRecords}</p> 119 - <p><strong>Records Per Day:</strong> {activityAll.totalRecordsPerDay}</p> 120 - <p> 121 - <strong>Total Bluesky Records:</strong> {activityAll.totalBskyRecords} ( 122 - {Math.floor(activityAll.totalBskyRecordsPercentage * 100)}%) 123 - </p> 124 - <p><strong>Rotation Keys:</strong> {rotationKeys}</p> 125 - <p> 126 - <strong>Followers:</strong> {followersCount} | <strong>Following:</strong> {followsCount} {" "} 127 - (<em>{followersCount ? (followsCount / followersCount).toFixed(2) : "0"}</em>) 128 - </p> 129 - <p><strong>Posting Style:</strong> {postingStyle}</p> 130 - <p><strong>Social Status:</strong> {socialStatus}</p> 131 - <p><strong>Era:</strong> {era}</p> 132 - </section> 133 - 134 - {/* Last 30 Days Activity */} 135 - <section className="activity-recent"> 136 - <h2>Last 30 Days Activity</h2> 137 - <p><strong>Total Records:</strong> {activityLast30Days.totalRecords}</p> 138 - <p><strong>Records Per Day:</strong> {activityLast30Days.totalRecordsPerDay}</p> 139 - <p><strong>Total Bluesky Records:</strong> {activityLast30Days.totalBskyRecords}</p> 140 - <p><strong>Total Non-Bluesky Records:</strong> {activityLast30Days.totalNonBskyRecords}</p> 141 - {activityLast30Days.collections && ( 142 - <div className="collection-stats"> 143 - <h3>Per-Collection Stats (Last 30 Days):</h3> 144 - <p>{JSON.stringify(activityLast30Days.collections, null, 2)}</p> 145 - </div> 146 - )} 147 - </section> 148 - 149 - {/* Alias Information */} 150 - <section className="aliases"> 151 - <h2>Alias Information</h2> 152 - <p><strong>Total AKAs:</strong> {alsoKnownAs.totalAkas}</p> 153 - <p><strong>Active AKAs:</strong> {alsoKnownAs.activeAkas}</p> 154 - <p><strong>Bsky AKAs:</strong> {alsoKnownAs.totalBskyAkas}</p> 155 - <p><strong>Custom AKAs:</strong> {alsoKnownAs.totalCustomAkas}</p> 156 - <p><strong>Domain Rarity:</strong> {alsoKnownAs.domainRarity}</p> 157 - <p><strong>Handle Type:</strong> {alsoKnownAs.handleType}</p> 158 - </section> 159 - 160 - {/* Analysis Narrative */} 161 - <section className="narrative"> 162 - <h2>Analysis Narrative</h2> 163 - <p>{analysis.narrative}</p> 164 - </section> 165 - 166 - {/* Additional Data Dump */} 167 - <section className="data-dump"> 168 - <h2>Full Account Data JSON</h2> 169 - <p>{JSON.stringify(accountData, null, 2)}</p> 170 - </section> 171 - 172 - {/* Generated At Footer */} 173 - <section className="generated-info"> 174 - <p> 175 - <small>Score generated at: {new Date(scoreGeneratedAt).toLocaleString()}</small> 176 - </p> 177 - </section> 178 - </div> 179 - ); 180 - }; 181 - 182 - export default UserProfile;
+5 -5
src/components/UserProfile/components/ProfileCard.js
··· 21 21 <strong>Incept Date:</strong> {new Date(accountData.createdAt).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })} 22 22 </p> 23 23 <p> 24 + <strong>Account Era:</strong> {accountData.era} 25 + </p> 26 + <p> 24 27 <strong>Account Age:</strong> {Math.floor(accountData.ageInDays)} days old 25 28 </p> 26 29 <p> 27 30 <strong>Contextual Age:</strong> {new Intl.NumberFormat('en-US', { style: 'percent', minimumFractionDigits: 0 }).format(accountData.agePercentage)} of Bluesky's history 28 - </p> 29 - <p> 30 - <strong>Era:</strong> {accountData.era} 31 31 </p> 32 32 <p> 33 33 <strong>Posting Style:</strong> {accountData.postingStyle} ··· 42 42 <strong>PDS Host:</strong> {accountData.serviceEndpoint} 43 43 </p> 44 44 <p> 45 - <strong>Profile Edited:</strong> {accountData.profileEditedDate} 45 + <strong>Profile State:</strong> {accountData.profileCompletion} 46 46 </p> 47 47 <p> 48 - <strong>Profile State:</strong> {accountData.profileCompletion} 48 + <strong>Profile Last Edited:</strong> {accountData.profileEditedDate} 49 49 </p> 50 50 </> 51 51 );
-1
src/react-app-env.d.ts
··· 1 - /// <reference types="react-scripts" />