This repository has no description
0

Configure Feed

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

tweak the resource styling

+283 -113
+2
src/components/About/About.css
··· 1 + /* src/components/About/About.css */ 2 + 1 3 .about-page { 2 4 margin: 20px auto 20px; 3 5 max-width: 450px;
+5 -1
src/components/About/About.js
··· 3 3 import React from 'react'; 4 4 import './About.css'; 5 5 import { Link } from 'react-router-dom'; 6 + import RelatedPagesNav from '../common/RelatedPagesNav'; 6 7 7 8 const About = () => { 8 9 return ( ··· 43 44 44 45 <h3>Why is cred.blue in a "beta" state?</h3> 45 46 <p>To put it simply, cred.blue is very experimental and at this early stage things will likely change a lot. The first version of the scoring algorithm lays a foundation for future plans, but it will take some time (and real-world usage) to calibrate the model's weights and variables.</p> 47 + 48 + {/* Add the related pages navigation */} 49 + <RelatedPagesNav currentPage="about" /> 46 50 </div> 47 51 </main> 48 52 </> 49 53 ); 50 54 }; 51 55 52 - export default About; 56 + export default About;
+2
src/components/Definitions/Definitions.css
··· 1 + /* src/components/Definitions/Definitions.css */ 2 + 1 3 .definitions-page { 2 4 margin: 20px auto 20px; 3 5 max-width: 450px;
+4
src/components/Definitions/Definitions.js
··· 1 1 import React, { useState } from 'react'; 2 2 import './Definitions.css'; 3 3 import { Link } from 'react-router-dom'; 4 + import RelatedPagesNav from '../common/RelatedPagesNav'; 4 5 5 6 const Definitions = () => { 6 7 // State to track which definition is expanded ··· 225 226 ))} 226 227 </div> 227 228 </section> 229 + 230 + {/* Add the related pages navigation */} 231 + <RelatedPagesNav currentPage="definitions" /> 228 232 </div> 229 233 </main> 230 234 </>
-1
src/components/Navbar/Navbar.css
··· 238 238 .dropdown-trigger { 239 239 position: relative; 240 240 display: inline-block; 241 - padding-right: 20px; /* Space for the arrow indicator */ 242 241 white-space: nowrap; 243 242 } 244 243
+157 -60
src/components/Resources/Resources.css
··· 15 15 transition: background-color 0.3s ease, border-color 0.3s ease; 16 16 } 17 17 18 + /* Improved header structure */ 18 19 .resources-header { 19 - margin-bottom: 1.5rem; 20 + margin-bottom: 2rem; 20 21 } 21 22 22 23 .resources-header h1 { 23 - font-size: 1.5rem; 24 + font-size: 2rem; 24 25 font-weight: bold; 25 - margin-bottom: 0.5rem; 26 + margin-bottom: 1rem; 26 27 color: var(--button-bg); 28 + text-align: center; 29 + } 30 + 31 + .resources-intro { 32 + max-width: 800px; 33 + margin: 0 auto 1.5rem auto; 27 34 } 28 35 29 36 .resources-page ul { 30 37 list-style: none; 31 38 text-align: center; 32 - margin: 0px auto; 33 - padding: 0px; 39 + margin: 0 auto 1rem auto; 40 + padding: 0; 34 41 width: 100%; 35 - opacity: 0.5; 42 + opacity: 0.8; 36 43 } 37 44 38 45 .resources-description { ··· 40 47 line-height: 1.5; 41 48 opacity: 0.8; 42 49 transition: color 0.3s ease; 43 - margin-top: 1rem; 50 + text-align: center; 51 + font-size: 1.1rem; 52 + margin: 0 auto; 44 53 } 45 54 55 + /* Improved disclaimer styling */ 46 56 .resources-disclaimer { 47 57 background-color: var(--card-border); 48 58 border-left: 4px solid #ffd700; 49 59 padding: 12px 16px; 50 - margin-bottom: 24px; 60 + margin: 1.5rem auto; 51 61 border-radius: 4px; 62 + max-width: 900px; 52 63 } 53 64 54 65 .resources-disclaimer p { ··· 58 69 } 59 70 60 71 .share-button-container { 61 - margin-top: 1rem; 72 + text-align: center; 73 + margin: 1.5rem auto; 62 74 } 63 75 64 76 .share-button { 65 77 background-color: var(--button-bg); 66 78 color: var(--button-text); 67 - padding: 10px 16px; 79 + padding: 10px 20px; 68 80 border: none; 69 81 border-radius: 20px; 70 - font-size: 0.9rem; 82 + font-size: 0.95rem; 71 83 font-weight: 600; 72 84 cursor: pointer; 73 85 display: inline-flex; ··· 79 91 opacity: 0.8; 80 92 } 81 93 94 + /* Improved search and filters */ 82 95 .resources-filters { 83 96 margin-bottom: 30px; 97 + max-width: 900px; 98 + margin-left: auto; 99 + margin-right: auto; 84 100 } 85 101 86 102 .search-container { ··· 89 105 90 106 .search-input { 91 107 width: 100%; 92 - padding: 12px 16px; 108 + padding: 14px 18px; 93 109 font-size: 1rem; 94 - border: 1px solid var(--card-border); 110 + border: 2px solid var(--card-border); 95 111 border-radius: 8px; 96 112 box-sizing: border-box; 97 113 background-color: var(--navbar-bg); ··· 102 118 .search-input:focus { 103 119 border-color: var(--button-bg); 104 120 outline: none; 121 + box-shadow: 0 0 0 2px rgba(var(--button-bg-rgb), 0.2); 105 122 } 106 123 107 124 .filter-options { ··· 112 129 justify-content: space-between; 113 130 } 114 131 115 - /* New filter dropdowns styling */ 132 + /* Filter dropdowns styling */ 116 133 .filter-dropdowns { 117 134 display: flex; 118 135 gap: 16px; 119 136 width: 100%; 137 + align-items: center; 120 138 } 121 139 122 - .category-filter-dropdown, 140 + .category-filter-dropdown { 141 + flex: 2; 142 + } 143 + 123 144 .quality-filter { 124 - flex: 1; 145 + flex: 2; 125 146 } 126 147 127 148 .filter-select { 128 149 width: 100%; 129 150 padding: 12px 16px; 130 - border: 1px solid var(--card-border); 151 + border: 2px solid var(--card-border); 131 152 border-radius: 8px; 132 153 font-size: 0.95rem; 133 154 background-color: var(--navbar-bg); ··· 145 166 outline: none; 146 167 } 147 168 169 + /* New star filter styling */ 170 + .quality-filter-stars { 171 + display: flex; 172 + align-items: center; 173 + gap: 8px; 174 + background-color: var(--navbar-bg); 175 + border: 2px solid var(--card-border); 176 + border-radius: 8px; 177 + padding: 8px 12px; 178 + height: 44px; 179 + box-sizing: border-box; 180 + } 181 + 182 + .quality-filter-label { 183 + font-size: 0.95rem; 184 + font-weight: 500; 185 + color: var(--text); 186 + margin-right: 4px; 187 + } 188 + 189 + .star-filter-container { 190 + display: flex; 191 + align-items: center; 192 + gap: 3px; 193 + } 194 + 195 + .star-filter-container .quality-star { 196 + cursor: pointer; 197 + font-size: 1.4rem; 198 + transition: transform 0.1s, color 0.2s; 199 + } 200 + 201 + .star-filter-container .quality-star:hover { 202 + transform: scale(1.1); 203 + } 204 + 205 + .star-filter-container .quality-star.filled { 206 + color: #ffd700; 207 + } 208 + 209 + .star-filter-container .quality-star.empty { 210 + color: var(--card-border); 211 + } 212 + 213 + .quality-filter-clear { 214 + font-size: 0.8rem; 215 + margin-left: 5px; 216 + cursor: pointer; 217 + color: var(--text); 218 + opacity: 0.7; 219 + font-weight: bold; 220 + } 221 + 222 + .quality-filter-clear:hover { 223 + opacity: 1; 224 + } 225 + 148 226 .featured-section, 149 227 .all-resources-section { 150 228 margin-bottom: 40px; ··· 159 237 padding-bottom: 8px; 160 238 } 161 239 162 - /* New category section styling */ 240 + /* Category section styling */ 163 241 .category-section { 164 242 margin-bottom: 30px; 165 243 } ··· 213 291 flex-direction: column; 214 292 } 215 293 294 + /* Improved resource header styling */ 295 + .resource-header { 296 + display: flex; 297 + align-items: center; 298 + gap: 8px; 299 + margin-bottom: 10px; 300 + } 301 + 216 302 .resource-name { 217 303 font-size: 1.1rem; 218 - margin: 0 0 8px 0; 304 + margin: 0; 219 305 color: var(--button-bg); 306 + line-height: 1.3; 307 + } 308 + 309 + /* Improved NEW badge styling */ 310 + .new-badge { 311 + background-color: var(--button-bg); 312 + color: var(--button-text); 313 + padding: 2px 8px; 314 + border-radius: 12px; 315 + font-size: 0.7rem; 316 + font-weight: bold; 317 + display: inline-flex; 318 + align-items: center; 319 + height: 18px; 320 + vertical-align: text-top; 321 + animation: pulse 2s infinite; 322 + } 323 + 324 + @keyframes pulse { 325 + 0% { 326 + transform: scale(1); 327 + } 328 + 50% { 329 + transform: scale(1.05); 330 + } 331 + 100% { 332 + transform: scale(1); 333 + } 220 334 } 221 335 222 336 .resource-description { ··· 256 370 } 257 371 258 372 .quality-star { 259 - font-size: 1.1rem; /* Increased from 0.9rem */ 260 - margin-left: 3px; /* Increased from 2px */ 373 + font-size: 1.1rem; 374 + margin-left: 3px; 261 375 font-weight: bold; 262 376 text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.1); 263 377 } ··· 303 417 opacity: 0.8; 304 418 } 305 419 306 - /* Add these styles to your existing Resources.css file */ 307 - 308 - /* New badge styling */ 309 - .new-badge { 310 - background-color: #007aff; 311 - color: white; 312 - padding: 2px 8px; 313 - border-radius: 12px; 314 - font-size: 0.7rem; 315 - font-weight: bold; 316 - margin-left: 8px; 317 - display: inline-block; 318 - vertical-align: middle; 319 - animation: pulse 2s infinite; 320 - } 321 - 322 - @keyframes pulse { 323 - 0% { 324 - transform: scale(1); 325 - } 326 - 50% { 327 - transform: scale(1.05); 328 - } 329 - 100% { 330 - transform: scale(1); 331 - } 332 - } 333 - 334 - /* Resource header to display name and new badge on same line */ 335 - .resource-header { 336 - display: flex; 337 - align-items: center; 338 - margin-bottom: 8px; 339 - } 340 - 341 420 /* New filter toggle styling */ 342 421 .new-filter { 343 - margin-left: 15px; 422 + flex: 1; 344 423 display: flex; 345 424 align-items: center; 425 + justify-content: flex-end; 346 426 } 347 427 348 428 .toggle-label { ··· 357 437 position: relative; 358 438 width: 40px; 359 439 height: 20px; 360 - background-color: #ddd; 440 + background-color: var(--card-border); 361 441 border-radius: 20px; 362 442 transition: background-color 0.3s; 363 443 cursor: pointer; 364 444 } 365 445 366 446 .toggle-label input[type="checkbox"]:checked { 367 - background-color: #007aff; 447 + background-color: var(--button-bg); 368 448 } 369 449 370 450 .toggle-label input[type="checkbox"]::before { ··· 393 473 .filter-options .filter-dropdowns { 394 474 flex-direction: column; 395 475 align-items: flex-start; 396 - gap: 10px; 476 + gap: 12px; 397 477 } 398 478 399 479 .category-filter-dropdown, ··· 405 485 .filter-select { 406 486 width: 100%; 407 487 } 488 + 489 + .new-filter { 490 + justify-content: flex-start; 491 + } 492 + 493 + .quality-filter-stars { 494 + width: 100%; 495 + justify-content: space-between; 496 + } 408 497 } 409 498 410 499 @keyframes spin { ··· 422 511 padding: 1rem; 423 512 } 424 513 514 + .resources-header h1 { 515 + font-size: 1.5rem; 516 + } 517 + 425 518 .resources-header { 426 519 flex-direction: column; 427 520 align-items: flex-start; ··· 444 537 @media (max-width: 480px) { 445 538 .resources-page { 446 539 max-width: 100%; 540 + } 541 + 542 + .resources-header h1 { 543 + font-size: 1.3rem; 447 544 } 448 545 }
+58 -35
src/components/Resources/Resources.js
··· 9 9 const [resources, setResources] = useState([]); 10 10 const [activeCategory, setActiveCategory] = useState('All'); 11 11 const [searchQuery, setSearchQuery] = useState(''); 12 - const [qualityFilter, setQualityFilter] = useState('All'); 12 + const [qualityFilter, setQualityFilter] = useState(0); // Changed to numeric value (0 = All) 13 13 const [showNewOnly, setShowNewOnly] = useState(false); 14 14 const [isLoading, setIsLoading] = useState(true); 15 15 ··· 36 36 try { 37 37 const preferences = JSON.parse(savedPreferences); 38 38 setActiveCategory(preferences.activeCategory || 'All'); 39 - setQualityFilter(preferences.qualityFilter || 'All'); 39 + setQualityFilter(preferences.qualityFilter || 0); 40 40 setShowNewOnly(preferences.showNewOnly || false); 41 41 } catch (error) { 42 42 console.error('Error loading preferences:', error); ··· 148 148 resource.description.toLowerCase().includes(searchQuery.toLowerCase()) || 149 149 (resource.domain && resource.domain.toLowerCase().includes(searchQuery.toLowerCase())); 150 150 151 - // Filter by quality 151 + // Filter by quality (changed to numeric) 152 152 const qualityMatch = 153 - qualityFilter === 'All' || 154 - (qualityFilter === 'High' && resource.quality >= 4) || 155 - (qualityFilter === 'Medium' && resource.quality === 3) || 156 - (qualityFilter === 'Low' && resource.quality <= 2); 153 + qualityFilter === 0 || 154 + resource.quality >= qualityFilter; 157 155 158 156 // Filter by "new" status if the toggle is active 159 157 const newMatch = !showNewOnly || isNewResource(resource.created_at); ··· 184 182 // Should show featured section only when All category is selected 185 183 const shouldShowFeatured = activeCategory === 'All'; 186 184 185 + // Handle star rating click for quality filter 186 + const handleStarClick = (rating) => { 187 + setQualityFilter(rating === qualityFilter ? 0 : rating); 188 + }; 189 + 187 190 return ( 188 - <> 189 - <main className="resources-page"> 190 - <div className="alt-card"> 191 + <main className="resources-page"> 192 + <div className="alt-card"> 191 193 <div className="resources-header"> 192 194 <h1>Bluesky Resources</h1> 193 - <ul> 194 - <li>Find tools to enhance your Bluesky experience.</li> 195 - <li>Discover analytics, feeds, clients, and more.</li> 196 - <li>Explore community-built solutions.</li> 197 - </ul> 198 - <p className="resources-description">A curated collection of third-party tools, services, and guides for the Bluesky ecosystem</p> 195 + 196 + {/* Improved header structure */} 197 + <div className="resources-intro"> 198 + <ul> 199 + <li>Find tools to enhance your Bluesky experience.</li> 200 + <li>Discover analytics, feeds, clients, and more.</li> 201 + <li>Explore community-built solutions.</li> 202 + </ul> 203 + <p className="resources-description"> 204 + A curated collection of third-party tools, services, and guides for the Bluesky ecosystem 205 + </p> 206 + </div> 207 + 208 + {/* Improved disclaimer positioning */} 209 + <div className="resources-disclaimer"> 210 + <p><strong>Disclaimer:</strong> These resources are third-party tools and services not affiliated with cred.blue or Bluesky. 211 + Use them at your own risk and exercise caution when providing access to your data.</p> 212 + </div> 199 213 200 214 <div className="share-button-container"> 201 215 <button ··· 208 222 </div> 209 223 </div> 210 224 211 - <div className="resources-disclaimer"> 212 - <p><strong>Disclaimer:</strong> These resources are third-party tools and services not affiliated with cred.blue or Bluesky. 213 - Use them at your own risk and exercise caution when providing access to your data.</p> 214 - </div> 215 - 216 225 {isLoading ? ( 217 226 <ResourceLoader /> 218 227 ) : ( 219 228 <> 229 + {/* Improved search and filters layout */} 220 230 <div className="resources-filters"> 221 231 <div className="search-container"> 222 232 <input ··· 245 255 </select> 246 256 </div> 247 257 248 - {/* Quality filter dropdown */} 258 + {/* New Quality Filter using Stars */} 249 259 <div className="quality-filter"> 250 - <select 251 - value={qualityFilter} 252 - onChange={(e) => setQualityFilter(e.target.value)} 253 - className="filter-select" 254 - > 255 - <option value="All">All Quality Levels</option> 256 - <option value="High">High Quality</option> 257 - <option value="Medium">Medium Quality</option> 258 - <option value="Low">Low Quality</option> 259 - </select> 260 + <div className="quality-filter-stars"> 261 + <span className="quality-filter-label">Quality: </span> 262 + <div className="star-filter-container"> 263 + {[1, 2, 3, 4, 5].map((rating) => ( 264 + <span 265 + key={rating} 266 + onClick={() => handleStarClick(rating)} 267 + className={`quality-star ${rating <= qualityFilter ? 'filled' : 'empty'}`} 268 + title={`${rating} stars or higher`} 269 + > 270 + 271 + </span> 272 + ))} 273 + {qualityFilter > 0 && ( 274 + <span 275 + className="quality-filter-clear" 276 + onClick={() => setQualityFilter(0)} 277 + title="Clear filter" 278 + > 279 + 280 + </span> 281 + )} 282 + </div> 283 + </div> 260 284 </div> 261 285 262 286 {/* New resources toggle */} ··· 335 359 )} 336 360 </> 337 361 )} 338 - </div> 339 - </main> 340 - </> 362 + </div> 363 + </main> 341 364 ); 342 365 }; 343 366
+4 -16
src/components/ScoringMethodology/ScoringMethodology.js
··· 2 2 import './ScoringMethodology.css'; 3 3 import ScoreGauge from '../UserProfile/ScoreGauge'; 4 4 import { Link } from 'react-router-dom'; 5 + import RelatedPagesNav from '../common/RelatedPagesNav'; 5 6 6 7 const ScoringMethodology = () => { 7 8 return ( 8 9 <main className="methodology-page"> 9 10 <div className="alt-card"> 10 11 <h1>The Scoring Methodology</h1> 11 - 12 12 <div className="methodology-page-chart"> 13 13 <ScoreGauge score={500} /> 14 14 </div> 15 - 16 15 <p>Your cred.blue score is generated based on two major categories...</p> 17 - 18 16 <h3>1. Bluesky Data</h3> 19 17 <ul className="methodology-list"> 20 18 <li>Profile content (avatar, description, etc)</li> ··· 23 21 <li>Labelers and moderation</li> 24 22 <li>etc.</li> 25 23 </ul> 26 - 27 24 <h3>2. AT Protocol Data</h3> 28 25 <ul className="methodology-list"> 29 26 <li>Personal Data Server (PDS)</li> ··· 32 29 <li>PLC logs</li> 33 30 <li>etc.</li> 34 31 </ul> 35 - 36 32 <p> 37 33 Separate scores are generated for each category and then combined to produce your final cred.blue score, allowing you to easily see which major category (Bluesky vs AT Proto) has the most impact on your score. 38 34 </p> 39 - 40 35 <h2>Score Ranges</h2> 41 36 <p> 42 37 For Version 1 of the scoring algorithm, there is a max score of 1,000 points. This may change in the future, or it could theoretically even be scaled down depending on feedback and usage. ··· 44 39 <p> 45 40 A score between 0-300 likely indicates that an account is either very new to the network or isn't very active. A score of 300-700 is within a "healthy" range. Scores that are 700+ typically indicate accounts that have been around awhile and are very active. The different score ranges are still in early development along with the algorithm, so these details are likely to change. 46 41 </p> 47 - 48 42 <h2>How do I increase my score?</h2> 49 43 <p> 50 44 The scoring methodology is fairly complex and not all of the variables can be easily changed (for instance, an account's age), but there are some specific actions you can take that can help give you a boost: ··· 62 56 <p> 63 57 This is not an exhaustive list by any means, but it should get you started. The goal of the cred.blue score isn't to attempt to max it out... rather, the point is to foster healthy behavior and activity that benefits the entire community. 64 58 </p> 65 - 66 - <div className="definitions-link-container"> 67 - <p> 68 - Looking for explanations of terms used in cred.blue or details about social statuses? 69 - </p> 70 - <Link to="/definitions" className="definitions-link"> 71 - View Definitions & Social Status Details → 72 - </Link> 73 - </div> 59 + 60 + {/* Replace the old definitions link with the new RelatedPagesNav component */} 61 + <RelatedPagesNav currentPage="methodology" /> 74 62 </div> 75 63 </main> 76 64 );
+51
src/components/common/RelatedPagesNav.jsx
··· 1 + // src/components/common/RelatedPagesNav.jsx 2 + 3 + import React from 'react'; 4 + import { Link } from 'react-router-dom'; 5 + import './RelatedPagesNav.css'; 6 + 7 + const RelatedPagesNav = ({ currentPage }) => { 8 + // Define the pages and their properties 9 + const pages = [ 10 + { 11 + id: "about", 12 + path: "/about", 13 + title: "About cred.blue", 14 + description: "Learn what cred.blue is and why it was created" 15 + }, 16 + { 17 + id: "methodology", 18 + path: "/methodology", 19 + title: "Scoring Methodology", 20 + description: "Understand how the scoring algorithm works" 21 + }, 22 + { 23 + id: "definitions", 24 + path: "/definitions", 25 + title: "Definitions & Terms", 26 + description: "Explore key terms and social status details" 27 + } 28 + ]; 29 + 30 + // Filter out the current page 31 + const otherPages = pages.filter(page => page.id !== currentPage); 32 + 33 + return ( 34 + <div className="related-pages-container"> 35 + <h3 className="related-pages-title">Related Pages</h3> 36 + <div className="related-pages-links"> 37 + {otherPages.map(page => ( 38 + <Link key={page.id} to={page.path} className="related-page-link"> 39 + <div className="related-page-content"> 40 + <h4>{page.title}</h4> 41 + <p>{page.description}</p> 42 + </div> 43 + <span className="related-page-arrow">→</span> 44 + </Link> 45 + ))} 46 + </div> 47 + </div> 48 + ); 49 + }; 50 + 51 + export default RelatedPagesNav;