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