One Calendar is a privacy-first calendar web app built with Next.js. It has modern security features, including e2ee, password-protected sharing, and self-destructing share links ๐Ÿ“… calendar.xyehr.cn
5
fork

Configure Feed

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

feat: add oauth icons and share page footer metadata

+92 -2
+25
components/app/profile/shared-event.tsx
··· 4 4 import { useEffect, useState } from "react"; 5 5 import { useRouter } from "next/navigation"; 6 6 import Image from "next/image"; 7 + import Link from "next/link"; 7 8 import { format } from "date-fns"; 8 9 import { 9 10 MapPin, ··· 58 59 handle?: string; 59 60 } 60 61 62 + const APP_VERSION = process.env.NEXT_PUBLIC_APP_VERSION ?? "unknown"; 63 + 64 + function SharePageFooter({ isZh }: { isZh: boolean }) { 65 + return ( 66 + <footer className="fixed bottom-0 inset-x-0 z-20 px-4 py-3"> 67 + <div className="mx-auto flex w-full max-w-5xl items-center justify-between text-xs text-muted-foreground"> 68 + <span className="font-mono">v{APP_VERSION}</span> 69 + <div className="flex items-center gap-4"> 70 + <Link href="/privacy" className="transition-colors hover:text-foreground"> 71 + {isZh ? "้š็งๆ”ฟ็ญ–" : "Privacy Policy"} 72 + </Link> 73 + <Link href="/terms" className="transition-colors hover:text-foreground"> 74 + {isZh ? "ๆœๅŠกๆกๆฌพ" : "Terms of Service"} 75 + </Link> 76 + </div> 77 + </div> 78 + </footer> 79 + ); 80 + } 81 + 61 82 function getDarkerColorClass(color: string) { 62 83 const colorMapping: Record<string, string> = { 63 84 "bg-[#E6F6FD]": "#3B82F6", ··· 330 351 <p className="mt-6 text-lg font-medium text-gray-600 dark:text-gray-300"> 331 352 {isZh ? "ๅŠ ่ฝฝไธญ..." : "Loading..."} 332 353 </p> 354 + <SharePageFooter isZh={isZh} /> 333 355 </div> 334 356 ); 335 357 } ··· 461 483 </Card> 462 484 </motion.div> 463 485 </div> 486 + <SharePageFooter isZh={isZh} /> 464 487 </div> 465 488 ); 466 489 } ··· 528 551 </Card> 529 552 </motion.div> 530 553 </div> 554 + <SharePageFooter isZh={isZh} /> 531 555 </div> 532 556 ); 533 557 } ··· 728 752 </Card> 729 753 </motion.div> 730 754 </div> 755 + <SharePageFooter isZh={isZh} /> 731 756 </div> 732 757 ); 733 758 }
+5 -1
components/auth/login-form.tsx
··· 20 20 OAUTH_PROVIDER_CONFIG, 21 21 type OAuthStrategy, 22 22 } from "@/lib/clerk-oauth"; 23 + import { OAuthProviderIcon } from "@/components/auth/oauth-provider-icon"; 23 24 24 25 export function LoginForm({ 25 26 className, ··· 161 162 type="button" 162 163 onClick={() => handleOAuthLogin(provider.strategy)} 163 164 > 164 - <span>Login with {provider.label}</span> 165 + <span className="flex items-center justify-center gap-2"> 166 + <OAuthProviderIcon providerKey={providerKey} /> 167 + <span>Login with {provider.label}</span> 168 + </span> 165 169 </Button> 166 170 ); 167 171 })}
+57
components/auth/oauth-provider-icon.tsx
··· 1 + "use client"; 2 + 3 + import type { OAuthProviderKey } from "@/lib/clerk-oauth"; 4 + 5 + interface OAuthProviderIconProps { 6 + providerKey: OAuthProviderKey; 7 + className?: string; 8 + } 9 + 10 + export function OAuthProviderIcon({ 11 + providerKey, 12 + className = "h-5 w-5", 13 + }: OAuthProviderIconProps) { 14 + if (providerKey === "microsoft") { 15 + return ( 16 + <svg viewBox="0 0 23 23" className={className} aria-hidden="true"> 17 + <path fill="#f25022" d="M1 1h10v10H1z" /> 18 + <path fill="#00a4ef" d="M12 1h10v10H12z" /> 19 + <path fill="#7fba00" d="M1 12h10v10H1z" /> 20 + <path fill="#ffb900" d="M12 12h10v10H12z" /> 21 + </svg> 22 + ); 23 + } 24 + 25 + if (providerKey === "google") { 26 + return ( 27 + <svg viewBox="0 0 24 24" className={className} aria-hidden="true"> 28 + <path 29 + d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" 30 + fill="#4285F4" 31 + /> 32 + <path 33 + d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" 34 + fill="#34A853" 35 + /> 36 + <path 37 + d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" 38 + fill="#FBBC05" 39 + /> 40 + <path 41 + d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" 42 + fill="#EA4335" 43 + /> 44 + <path d="M1 1h22v22H1z" fill="none" /> 45 + </svg> 46 + ); 47 + } 48 + 49 + return ( 50 + <svg viewBox="0 0 24 24" className={className} aria-hidden="true"> 51 + <path 52 + d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" 53 + fill="currentColor" 54 + /> 55 + </svg> 56 + ); 57 + }
+5 -1
components/auth/sign-up-form.tsx
··· 14 14 OAUTH_PROVIDER_CONFIG, 15 15 type OAuthStrategy, 16 16 } from "@/lib/clerk-oauth"; 17 + import { OAuthProviderIcon } from "@/components/auth/oauth-provider-icon"; 17 18 18 19 export function SignUpForm({ 19 20 className, ··· 318 319 type="button" 319 320 onClick={() => handleOAuthSignUp(provider.strategy)} 320 321 > 321 - <span>Continue with {provider.label}</span> 322 + <span className="flex items-center justify-center gap-2"> 323 + <OAuthProviderIcon providerKey={providerKey} /> 324 + <span>Continue with {provider.label}</span> 325 + </span> 322 326 </Button> 323 327 ); 324 328 })}