A stream.place VOD client inspired by icarly.com
1
fork

Configure Feed

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

Fix DID display and iPLAY routing, clean up header layout

- Fixed getDisplayName to show full handles properly (not shortened DIDs)
- Fixed RandomPlay to use query parameter routing (/watch?id=)
- Updated VideoCard and WatchPageContent display name logic
- Cleaned up header layout with better grid organization
- Removed character image and floating NEW! label from header
- Made info/feedback buttons cleaner with proper sizing

jack 238b8f87 979ec2e7

+45 -67
+30 -33
src/app/icarly.css
··· 67 67 border-bottom: none; 68 68 padding: 15px 30px 30px; 69 69 position: relative; 70 - min-height: 220px; 70 + min-height: 240px; 71 71 box-shadow: 0 0 20px rgba(0,0,0,0.3); 72 72 display: grid; 73 - grid-template-columns: 280px 1fr 200px 160px; 73 + grid-template-columns: 280px 1fr 180px 200px; 74 74 grid-template-rows: auto auto 1fr; 75 75 grid-template-areas: 76 76 "homepage homepage homepage homepage" 77 - "logo search login ." 78 - "date search info ."; 79 - gap: 10px 20px; 80 - padding-right: 200px; /* Make room for character image */ 77 + "logo search login widgets" 78 + "date search . widgets"; 79 + gap: 10px 25px; 81 80 } 82 81 83 82 /* Homepage button */ ··· 249 248 250 249 /* Info & Feedback container */ 251 250 .header-right-widgets { 252 - grid-area: info; 253 - align-self: end; 254 - justify-self: end; 251 + grid-area: widgets; 252 + align-self: center; 253 + justify-self: center; 255 254 display: flex; 256 255 flex-direction: column; 257 - gap: 10px; 258 - align-items: flex-end; 256 + gap: 12px; 257 + align-items: center; 258 + width: 100%; 259 259 } 260 260 261 261 /* Info button */ 262 262 .info-btn { 263 263 background: #FF6B6B; 264 264 color: #fff; 265 - padding: 10px 20px; 265 + padding: 12px 18px; 266 266 font-weight: bold; 267 - font-size: 14px; 267 + font-size: 13px; 268 268 text-align: center; 269 - line-height: 1.3; 269 + line-height: 1.4; 270 270 border: 3px solid #fff; 271 271 box-shadow: 4px 4px 0 rgba(0,0,0,0.2); 272 - animation: float 5s ease-in-out infinite; 273 272 z-index: 5; 273 + width: 100%; 274 + max-width: 160px; 275 + cursor: pointer; 276 + transition: transform 0.2s; 274 277 } 275 278 276 279 .info-btn:hover { 277 280 animation: wiggle 0.3s ease-in-out; 281 + transform: translateY(-2px); 278 282 } 279 283 280 284 .info-btn span { ··· 285 289 .feedback-btn { 286 290 background: #FF4444; 287 291 color: #fff; 288 - padding: 12px 20px; 292 + padding: 14px 18px; 289 293 font-weight: bold; 290 - font-size: 16px; 294 + font-size: 15px; 291 295 text-align: center; 292 - line-height: 1.2; 296 + line-height: 1.3; 293 297 border: 3px solid #fff; 294 298 box-shadow: 5px 5px 0 rgba(0,0,0,0.2); 295 299 cursor: pointer; 296 300 z-index: 5; 297 - animation: wiggle 2.5s ease-in-out infinite; 301 + width: 100%; 302 + max-width: 160px; 303 + transition: transform 0.2s; 298 304 } 299 305 300 306 .feedback-btn:hover { 301 - animation: none; 302 - transform: scale(1.05); 307 + animation: wiggle 0.3s ease-in-out; 308 + transform: translateY(-2px); 303 309 } 304 310 305 311 .feedback-btn small { 306 - font-size: 13px; 312 + font-size: 12px; 307 313 color: var(--icarly-yellow); 308 314 } 309 315 310 - /* Character image */ 316 + /* Character image - removed from header grid flow */ 311 317 .character-image { 312 - position: absolute; 313 - right: 30px; 314 - top: 60px; 315 - width: 180px; 316 - height: 180px; 317 - border: 6px solid #fff; 318 - box-shadow: 8px 8px 0 rgba(0,0,0,0.3); 319 - overflow: hidden; 320 - z-index: 4; 321 - animation: float 6s ease-in-out infinite; 318 + display: none; 322 319 } 323 320 324 321 .character-image:hover {
+7 -2
src/app/watch/WatchPageContent.tsx
··· 30 30 if (STREAM_DISPLAY_NAMES[handle]) { 31 31 return STREAM_DISPLAY_NAMES[handle]; 32 32 } 33 + // If it's a full handle like "username.bsky.social", show just the username part 34 + if (handle.includes('.')) { 35 + return handle.split('.')[0]; 36 + } 37 + // If it's still a DID after resolution attempt, show full DID 33 38 if (handle.startsWith('did:')) { 34 - return handle.split(':').pop()?.slice(0, 8) + '...' || handle; 39 + return handle; 35 40 } 36 - return handle.split('.')[0]; 41 + return handle; 37 42 } 38 43 39 44 export default function WatchPageContent() {
-27
src/components/HomeClient.tsx
··· 186 186 feedback<br /><small>click &apos;til it hurts &gt;&gt;</small> 187 187 </div> 188 188 </div> 189 - 190 - {/* Character image - positioned outside grid */} 191 - <div className="character-image"> 192 - <Image 193 - src="/character-image.png" 194 - alt="iStream Team" 195 - width={180} 196 - height={180} 197 - style={{ objectFit: 'cover' }} 198 - /> 199 - </div> 200 - 201 - {/* Fun floating labels - positioned outside grid */} 202 - <div className="fun-label" style={{ 203 - position: 'absolute', 204 - top: '60px', 205 - right: '220px', 206 - background: '#FFD700', 207 - padding: '10px 20px', 208 - transform: 'rotate(-10deg)', 209 - fontWeight: 'bold', 210 - color: '#62166F', 211 - boxShadow: '3px 3px 0 rgba(0,0,0,0.2)', 212 - zIndex: 5 213 - }}> 214 - ✨ NEW! ✨ 215 - </div> 216 189 </header> 217 190 218 191 {/* Navigation Tabs */}
+1 -1
src/components/RandomPlay.tsx
··· 21 21 if (videos.length === 0) return; 22 22 const randomVideo = videos[Math.floor(Math.random() * videos.length)]; 23 23 const id = randomVideo.uri.split('/').pop(); 24 - router.push(`/watch/${id}`); 24 + router.push(`/watch?id=${id}`); 25 25 }; 26 26 27 27 return (
+7 -4
src/components/VideoCard.tsx
··· 25 25 if (STREAM_DISPLAY_NAMES[handle]) { 26 26 return STREAM_DISPLAY_NAMES[handle]; 27 27 } 28 - // If it's a DID, show shortened version 28 + // If it's a full handle like "username.bsky.social", show just the username part 29 + if (handle.includes('.')) { 30 + return handle.split('.')[0]; 31 + } 32 + // If it's still a DID after resolution attempt, show full DID 29 33 if (handle.startsWith('did:')) { 30 - return handle.split(':').pop()?.slice(0, 8) + '...' || handle; 34 + return handle; 31 35 } 32 - // Otherwise show the first part of the handle 33 - return handle.split('.')[0]; 36 + return handle; 34 37 } 35 38 36 39 interface VideoCardProps {