feat: JTR branding + Pink Pulse community partner integration
This commit is contained in:
29
deploy/check-deploy-fail.py
Normal file
29
deploy/check-deploy-fail.py
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python3
|
||||
import paramiko
|
||||
|
||||
c = paramiko.SSHClient()
|
||||
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
c.connect("10.10.0.10", username="localadministrator", password="Bbt9115xty9176!", timeout=20, allow_agent=False, look_for_keys=False)
|
||||
|
||||
def run(cmd, t=120):
|
||||
print("$", cmd[:200])
|
||||
_, o, e = c.exec_command(cmd, timeout=t)
|
||||
code = o.channel.recv_exit_status()
|
||||
out = (o.read() + e.read()).decode(errors="replace")
|
||||
print(out[-12000:])
|
||||
print("exit", code, "\n")
|
||||
return out
|
||||
|
||||
run("cd /tmp/exposedgays && git fetch origin && git reset --hard origin/main && git log -1 --oneline")
|
||||
run(
|
||||
"docker exec coolify-db psql -U coolify -d coolify -t -c "
|
||||
"\"SELECT id,status FROM application_deployment_queues ORDER BY id DESC LIMIT 5;\""
|
||||
)
|
||||
run(
|
||||
"docker exec coolify-db psql -U coolify -d coolify -t -c "
|
||||
"\"SELECT deployment_logs FROM application_deployment_queues ORDER BY id DESC LIMIT 1;\" | tail -c 8000"
|
||||
)
|
||||
run("ls -la /data/coolify/applications/ira4bw4nhbvm87o7s3fgqu6v/logs/ 2>/dev/null | tail -5")
|
||||
run("tail -80 /data/coolify/applications/ira4bw4nhbvm87o7s3fgqu6v/logs/*.log 2>/dev/null | tail -80")
|
||||
|
||||
c.close()
|
||||
23
deploy/check-deploy-fail2.py
Normal file
23
deploy/check-deploy-fail2.py
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
import paramiko
|
||||
|
||||
c = paramiko.SSHClient()
|
||||
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
c.connect("10.10.0.10", username="localadministrator", password="Bbt9115xty9176!", timeout=20, allow_agent=False, look_for_keys=False)
|
||||
|
||||
def run(cmd, t=300):
|
||||
print("$", cmd[:220])
|
||||
_, o, e = c.exec_command(cmd, timeout=t)
|
||||
code = o.channel.recv_exit_status()
|
||||
out = (o.read() + e.read()).decode(errors="replace")
|
||||
print(out[-15000:])
|
||||
print("exit", code, "\n")
|
||||
return out
|
||||
|
||||
run("docker exec coolify-db psql -U coolify -d coolify -c \"\\d application_deployment_queues\"")
|
||||
run("docker exec coolify-db psql -U coolify -d coolify -t -c \"SELECT logs FROM application_deployment_queues WHERE id=1116;\" | tail -c 12000")
|
||||
run("find /data/coolify -name '*l9hjct9e8yxydqjl8okovpr5*' 2>/dev/null | head -5")
|
||||
run("find /data/coolify -path '*ira4bw4nhbvm87o7s3fgqu6v*' -name '*.log' 2>/dev/null | head -10")
|
||||
run("cd /data/coolify/applications/ira4bw4nhbvm87o7s3fgqu6v && docker compose build 2>&1 | tail -60")
|
||||
|
||||
c.close()
|
||||
41
deploy/redeploy-v2.py
Normal file
41
deploy/redeploy-v2.py
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env python3
|
||||
import paramiko
|
||||
|
||||
HOST = "10.10.0.10"
|
||||
USER = "localadministrator"
|
||||
PASS = "Bbt9115xty9176!"
|
||||
APP = "ira4bw4nhbvm87o7s3fgqu6v"
|
||||
|
||||
c = paramiko.SSHClient()
|
||||
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
c.connect(HOST, username=USER, password=PASS, timeout=20, allow_agent=False, look_for_keys=False)
|
||||
|
||||
def run(cmd, t=300):
|
||||
print("$", cmd[:180])
|
||||
_, o, e = c.exec_command(cmd, timeout=t)
|
||||
code = o.channel.recv_exit_status()
|
||||
out = (o.read() + e.read()).decode(errors="replace")
|
||||
print(out[-6000:])
|
||||
print("exit", code, "\n")
|
||||
return out
|
||||
|
||||
run("cd /tmp/exposedgays && git fetch origin && git reset --hard origin/main && git log -1 --oneline")
|
||||
run(
|
||||
"TOKEN=$(cat /home/localadministrator/.coolify-token | tail -1); "
|
||||
f"curl -sS -X POST 'http://127.0.0.1:8000/api/v1/deploy?uuid={APP}&force_rebuild=true' "
|
||||
'-H "Authorization: Bearer $TOKEN"'
|
||||
)
|
||||
|
||||
for i in range(150):
|
||||
_, out = run("docker exec coolify php artisan queue:work --queue=high,default --once --timeout=900 2>&1", t=920)
|
||||
if "ApplicationDeploymentJob" in out:
|
||||
if "DONE" in out or "finished" in out.lower():
|
||||
print("=== DEPLOY SUCCESS ===")
|
||||
break
|
||||
if "FAIL" in out:
|
||||
print("=== DEPLOY FAILED ===")
|
||||
break
|
||||
|
||||
run("docker exec coolify-db psql -U coolify -d coolify -t -c \"SELECT id,status FROM application_deployment_queues ORDER BY id DESC LIMIT 1;\"")
|
||||
run("curl -sS https://exposedgays.com/ 2>&1 | head -c 500")
|
||||
c.close()
|
||||
11
public/partners/jtr-wordmark.svg
Normal file
11
public/partners/jtr-wordmark.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Just Two Roommates — Wordmark Only -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1999 220" role="img" aria-label="Just Two Roommates">
|
||||
<title>Just Two Roommates — Wordmark</title>
|
||||
<g font-family="Montserrat, 'Helvetica Neue', Arial, sans-serif" font-weight="900" font-size="148">
|
||||
<text x="30" y="160" fill="#FAFAFA">JUST</text>
|
||||
<text x="458" y="160" fill="#DD3EDD">TWO</text>
|
||||
<text x="886" y="160" fill="#9A7BC0">ROOMMATES</text>
|
||||
</g>
|
||||
<text x="1949" y="50" font-family="Montserrat, Arial, sans-serif" font-weight="700" font-size="24" fill="#9A7BC0">™</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 646 B |
56
public/partners/pinkpulse-icon.svg
Normal file
56
public/partners/pinkpulse-icon.svg
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- The Pink Pulse — Icon Mark (Pulse Wave) -->
|
||||
<!-- Use for: favicons, app icons, social avatars, small-space applications -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" role="img" aria-label="The Pink Pulse icon">
|
||||
<title>The Pink Pulse — Icon</title>
|
||||
<defs>
|
||||
<filter id="ic-glow" x="-40%" y="-40%" width="180%" height="180%">
|
||||
<feGaussianBlur in="SourceGraphic" stdDeviation="10" result="b1"/>
|
||||
<feGaussianBlur in="SourceGraphic" stdDeviation="3" result="b2"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="b1"/>
|
||||
<feMergeNode in="b2"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<linearGradient id="ic-bg" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#1A003A"/>
|
||||
<stop offset="55%" stop-color="#470070"/>
|
||||
<stop offset="100%" stop-color="#08001C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="ic-stroke" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" stop-color="#F92B9B"/>
|
||||
<stop offset="100%" stop-color="#8B22CC"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="ic-bloom" cx="50%" cy="55%" r="50%">
|
||||
<stop offset="0%" stop-color="#F92B9B" stop-opacity="0.55"/>
|
||||
<stop offset="70%" stop-color="#8B22CC" stop-opacity="0.15"/>
|
||||
<stop offset="100%" stop-color="#08001C" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Rounded square container -->
|
||||
<rect x="16" y="16" width="480" height="480" rx="108" ry="108" fill="url(#ic-bg)"/>
|
||||
<rect x="16" y="16" width="480" height="480" rx="108" ry="108" fill="url(#ic-bloom)"/>
|
||||
<rect x="22" y="22" width="468" height="468" rx="102" ry="102" fill="none" stroke="url(#ic-stroke)" stroke-width="4" opacity="0.9"/>
|
||||
|
||||
<!-- Pulse / heartbeat line -->
|
||||
<g filter="url(#ic-glow)">
|
||||
<path d="M64 256 L152 256 L184 180 L232 336 L280 140 L328 320 L360 256 L448 256"
|
||||
fill="none"
|
||||
stroke="#F92B9B"
|
||||
stroke-width="16"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"/>
|
||||
</g>
|
||||
<path d="M64 256 L152 256 L184 180 L232 336 L280 140 L328 320 L360 256 L448 256"
|
||||
fill="none"
|
||||
stroke="#FAFAFA"
|
||||
stroke-width="8"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
opacity="0.95"/>
|
||||
|
||||
<!-- Anchor dot on right -->
|
||||
<circle cx="448" cy="256" r="10" fill="#8B22CC"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -3,9 +3,11 @@ export default function PrivacyPage() {
|
||||
<div className="mx-auto max-w-2xl px-4 py-8 prose prose-invert">
|
||||
<h1>Privacy Policy</h1>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
We collect minimal data. Location is used for map features and city chat auto-join.
|
||||
Anonymous browsing is supported. Cart data stays in your browser (localStorage).
|
||||
Placeholder policy — consult legal counsel before launch.
|
||||
ExposedGays is operated by Just Two Roommates LLC. We collect minimal data.
|
||||
Location is used for map features and city chat auto-join. Anonymous browsing
|
||||
is supported. Cart data stays in your browser (localStorage). When you follow
|
||||
links to our community partner The Pink Pulse, their privacy policy applies on
|
||||
their site. Placeholder policy — consult legal counsel before launch.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -15,6 +15,7 @@ import { AuthModal } from "@/components/AuthModal";
|
||||
import { PhotoGalleryManager } from "@/components/PhotoGalleryManager";
|
||||
import { CannedMessagesPanel } from "@/components/CannedMessagesPanel";
|
||||
import { IdVerificationModal } from "@/components/IdVerificationModal";
|
||||
import { PinkPulsePartner } from "@/components/PinkPulsePartner";
|
||||
import {
|
||||
US_STATES,
|
||||
getUserState,
|
||||
@@ -307,6 +308,8 @@ export default function ProfilePage() {
|
||||
|
||||
<CannedMessagesPanel />
|
||||
|
||||
<PinkPulsePartner variant="card" />
|
||||
|
||||
{message && (
|
||||
<p className={`text-sm text-center ${message.startsWith("Error") ? "text-destructive" : "text-green-400"}`}>
|
||||
{message}
|
||||
|
||||
@@ -3,9 +3,12 @@ export default function TermsPage() {
|
||||
<div className="mx-auto max-w-2xl px-4 py-8 prose prose-invert">
|
||||
<h1>Terms of Service</h1>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
ExposedGays is a free adult platform. You must be 18+ to use this site.
|
||||
All content is user-generated. We reserve the right to remove illegal content.
|
||||
No paid subscriptions — ever. Placeholder terms — consult legal counsel before launch.
|
||||
ExposedGays is a free adult platform operated by Just Two Roommates LLC. You must
|
||||
be 18+ to use this site. All content is user-generated. We reserve the right to
|
||||
remove illegal content. No paid subscriptions — ever. Links to our community
|
||||
partner The Pink Pulse (thepinkpulse.com) are provided for LGBTQ+ news, events,
|
||||
and local resources — separate editorial property, same parent company.
|
||||
Placeholder terms — consult legal counsel before launch.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
|
||||
import { Send, Image as ImageIcon, Users, Lock, Zap, ChevronDown, ChevronUp } from "lucide-react";
|
||||
import { timeAgo } from "@/lib/utils";
|
||||
import { CannedMessagesPanel } from "@/components/CannedMessagesPanel";
|
||||
import { PinkPulsePartner } from "@/components/PinkPulsePartner";
|
||||
|
||||
interface Message {
|
||||
id: string;
|
||||
@@ -148,9 +149,10 @@ export function ChatPanel({ dmUserId }: ChatPanelProps) {
|
||||
<GroupRoom name="Leather Night FTL" members={8} />
|
||||
<GroupRoom name="Beach Cruisers" members={15} />
|
||||
<GroupRoom name="Gym Sauna Squad" members={4} />
|
||||
<p className="text-xs text-muted-foreground text-center pt-4">
|
||||
<p className="text-xs text-muted-foreground text-center pt-2">
|
||||
Create a group room — free, no limits.
|
||||
</p>
|
||||
<PinkPulsePartner variant="card" className="mt-4" />
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import type { Profile, CruisingSpot, MapFilters, Position } from "@/types";
|
||||
import { KINK_OPTIONS, POSITION_COLORS } from "@/types";
|
||||
import { MOCK_PROFILES, MOCK_SPOTS } from "@/lib/mock-data";
|
||||
import { useOpenIdVerification } from "@/components/Providers";
|
||||
import { PinkPulsePartner } from "@/components/PinkPulsePartner";
|
||||
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import "leaflet.markercluster/dist/MarkerCluster.css";
|
||||
@@ -279,9 +280,15 @@ export function CruisingMap({ vanillaMode, onActiveCountChange }: MapProps) {
|
||||
vanillaMode={vanillaMode}
|
||||
/>
|
||||
))}
|
||||
<PinkPulsePartner variant="card" className="mt-2" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Mobile — community partner strip above bottom nav */}
|
||||
<div className="md:hidden absolute bottom-0 left-0 right-0 z-[999] pb-16">
|
||||
<PinkPulsePartner variant="strip" />
|
||||
</div>
|
||||
|
||||
{/* Map */}
|
||||
{leafletReady && iconFactory && (
|
||||
<MapContainer
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { Map, MessageCircle, MapPin, ShoppingBag, User, Eye, EyeOff, LogIn, LogOut, ShieldCheck } from "lucide-react";
|
||||
import { Map, MessageCircle, MapPin, ShoppingBag, User, Eye, EyeOff, LogIn, LogOut, ShieldCheck, Sparkles } from "lucide-react";
|
||||
import { PINK_PULSE } from "@/lib/partners";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Logo } from "@/components/Logo";
|
||||
@@ -76,6 +77,16 @@ export function Nav({
|
||||
</nav>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Link
|
||||
href={PINK_PULSE.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hidden lg:flex items-center gap-1.5 text-xs text-muted-foreground hover:text-[#f92b9b] transition-colors px-2 py-1 rounded-md border border-transparent hover:border-[#f92b9b]/20"
|
||||
title={`Community partner: ${PINK_PULSE.name}`}
|
||||
>
|
||||
<Sparkles className="h-3.5 w-3.5" />
|
||||
<span className="font-medium">{PINK_PULSE.shortName}</span>
|
||||
</Link>
|
||||
{idBlur && onIdVerifyClick && (
|
||||
<Button
|
||||
variant="outline"
|
||||
|
||||
145
src/components/PinkPulsePartner.tsx
Normal file
145
src/components/PinkPulsePartner.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { ExternalLink, Sparkles } from "lucide-react";
|
||||
import { PINK_PULSE } from "@/lib/partners";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface PinkPulsePartnerProps {
|
||||
variant?: "card" | "strip" | "inline";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function PinkPulsePartner({ variant = "card", className }: PinkPulsePartnerProps) {
|
||||
if (variant === "strip") {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-3 overflow-x-auto px-3 py-2 border-t border-border/60 bg-horny-surface/90 backdrop-blur-sm",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Link
|
||||
href={PINK_PULSE.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-2 shrink-0 pr-2 border-r border-border/50"
|
||||
>
|
||||
<Image
|
||||
src={PINK_PULSE.logo}
|
||||
alt={PINK_PULSE.name}
|
||||
width={28}
|
||||
height={28}
|
||||
className="rounded-lg"
|
||||
/>
|
||||
<div className="leading-tight">
|
||||
<p className="text-[10px] text-muted-foreground uppercase tracking-wide">
|
||||
Community partner
|
||||
</p>
|
||||
<p className="text-xs font-semibold text-[#f92b9b]">{PINK_PULSE.shortName}</p>
|
||||
</div>
|
||||
</Link>
|
||||
{PINK_PULSE.links.slice(0, 5).map((link) => (
|
||||
<Link
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="shrink-0 text-xs px-3 py-1.5 rounded-full border border-[#f92b9b]/25 text-foreground/90 hover:bg-[#f92b9b]/10 hover:border-[#f92b9b]/40 transition-colors touch-manipulation"
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (variant === "inline") {
|
||||
return (
|
||||
<Link
|
||||
href={PINK_PULSE.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={cn(
|
||||
"inline-flex items-center gap-2 text-xs text-muted-foreground hover:text-[#f92b9b] transition-colors",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Image src={PINK_PULSE.logo} alt="" width={18} height={18} className="rounded" />
|
||||
<span>
|
||||
Community partner: <span className="font-medium text-[#f92b9b]">{PINK_PULSE.name}</span>
|
||||
</span>
|
||||
<ExternalLink className="h-3 w-3 opacity-60" />
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-xl border border-[#f92b9b]/20 bg-gradient-to-br from-[#1a003a]/40 to-horny-surface p-3 space-y-3",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<Link
|
||||
href={PINK_PULSE.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="shrink-0"
|
||||
>
|
||||
<Image
|
||||
src={PINK_PULSE.logo}
|
||||
alt={PINK_PULSE.name}
|
||||
width={40}
|
||||
height={40}
|
||||
className="rounded-xl"
|
||||
/>
|
||||
</Link>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Sparkles className="h-3 w-3 text-[#f92b9b]" />
|
||||
<p className="text-[10px] uppercase tracking-wider text-muted-foreground">
|
||||
Community partner
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href={PINK_PULSE.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm font-semibold text-[#f92b9b] hover:underline"
|
||||
>
|
||||
{PINK_PULSE.name}
|
||||
</Link>
|
||||
<p className="text-xs text-muted-foreground mt-0.5">{PINK_PULSE.tagline}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-1.5">
|
||||
{PINK_PULSE.links.map((link) => (
|
||||
<Link
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="rounded-lg border border-border/60 px-2.5 py-2 hover:border-[#f92b9b]/30 hover:bg-[#f92b9b]/5 transition-colors group"
|
||||
>
|
||||
<p className="text-xs font-medium group-hover:text-[#f92b9b]">{link.label}</p>
|
||||
<p className="text-[10px] text-muted-foreground truncate">{link.description}</p>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href={PINK_PULSE.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-1.5 text-xs text-[#f92b9b] font-medium hover:underline"
|
||||
>
|
||||
Visit thepinkpulse.com
|
||||
<ExternalLink className="h-3 w-3" />
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { AgeGate } from "@/components/AgeGate";
|
||||
import { Nav } from "@/components/Nav";
|
||||
import { MobileHeader } from "@/components/MobileHeader";
|
||||
import { IdVerificationModal } from "@/components/IdVerificationModal";
|
||||
import { SiteFooter } from "@/components/SiteFooter";
|
||||
import { AuthProvider, useAuth } from "@/lib/auth/context";
|
||||
import {
|
||||
mustBlurExplicit,
|
||||
@@ -90,9 +91,10 @@ function InnerProviders({ children }: ProvidersProps) {
|
||||
onVanillaToggle={handleVanillaToggle}
|
||||
activeCount={activeCount}
|
||||
/>
|
||||
<main className="md:pt-14 pt-12 pb-16 md:pb-0 min-h-screen min-h-[100dvh]">
|
||||
<main className="md:pt-14 pt-12 pb-16 md:pb-0 min-h-screen min-h-[100dvh] flex flex-col">
|
||||
<ActiveCountContext.Provider value={setActiveCount}>
|
||||
{children}
|
||||
<div className="flex-1 flex flex-col">{children}</div>
|
||||
<SiteFooter />
|
||||
</ActiveCountContext.Provider>
|
||||
</main>
|
||||
<IdVerificationModal
|
||||
|
||||
96
src/components/SiteFooter.tsx
Normal file
96
src/components/SiteFooter.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { JTR, PINK_PULSE } from "@/lib/partners";
|
||||
import { PinkPulsePartner } from "@/components/PinkPulsePartner";
|
||||
|
||||
export function SiteFooter() {
|
||||
const pathname = usePathname();
|
||||
const isMapHome = pathname === "/";
|
||||
|
||||
if (isMapHome) return null;
|
||||
|
||||
return (
|
||||
<footer className="border-t border-border bg-horny-dark/80 mt-auto">
|
||||
<div className="mx-auto max-w-5xl px-4 py-6 space-y-5">
|
||||
<div className="hidden sm:block max-w-sm">
|
||||
<PinkPulsePartner variant="card" />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:flex-row sm:items-end sm:justify-between gap-4">
|
||||
<div className="space-y-2">
|
||||
<p className="text-[10px] uppercase tracking-widest text-muted-foreground">
|
||||
Designed & operated by
|
||||
</p>
|
||||
<Link
|
||||
href={JTR.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-block opacity-90 hover:opacity-100 transition-opacity"
|
||||
aria-label={JTR.name}
|
||||
>
|
||||
<Image
|
||||
src={JTR.logo}
|
||||
alt={JTR.name}
|
||||
width={220}
|
||||
height={28}
|
||||
className="h-6 w-auto max-w-[200px] sm:max-w-[240px]"
|
||||
/>
|
||||
</Link>
|
||||
<p className="text-xs text-muted-foreground max-w-sm">
|
||||
ExposedGays is a free adult platform from the Just Two Roommates media
|
||||
family — alongside{" "}
|
||||
<Link
|
||||
href={PINK_PULSE.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-[#f92b9b] hover:underline"
|
||||
>
|
||||
{PINK_PULSE.name}
|
||||
</Link>
|
||||
, your queer city guide.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
|
||||
<Link href="/terms" className="hover:text-foreground">
|
||||
Terms
|
||||
</Link>
|
||||
<Link href="/privacy" className="hover:text-foreground">
|
||||
Privacy
|
||||
</Link>
|
||||
<Link
|
||||
href={PINK_PULSE.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-[#f92b9b] inline-flex items-center gap-1"
|
||||
>
|
||||
<Image
|
||||
src={PINK_PULSE.logo}
|
||||
alt=""
|
||||
width={14}
|
||||
height={14}
|
||||
className="rounded-sm"
|
||||
/>
|
||||
{PINK_PULSE.shortName}
|
||||
</Link>
|
||||
<Link
|
||||
href={JTR.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-foreground"
|
||||
>
|
||||
{JTR.name}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-[10px] text-muted-foreground/70 text-center sm:text-left">
|
||||
© {new Date().getFullYear()} Just Two Roommates LLC · ExposedGays.com · Free forever
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
47
src/lib/partners.ts
Normal file
47
src/lib/partners.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
/** Just Two Roommates network — partner properties (JTR only, not RMP). */
|
||||
|
||||
export const JTR = {
|
||||
name: "Just Two Roommates",
|
||||
url: "https://justtworoommates.com",
|
||||
logo: "/partners/jtr-wordmark.svg",
|
||||
} as const;
|
||||
|
||||
export const PINK_PULSE = {
|
||||
name: "The Pink Pulse",
|
||||
shortName: "Pink Pulse",
|
||||
tagline: "LGBTQ+ news, events & 165+ cities",
|
||||
url: "https://thepinkpulse.com",
|
||||
logo: "/partners/pinkpulse-icon.svg",
|
||||
links: [
|
||||
{
|
||||
label: "The Latest",
|
||||
href: "https://thepinkpulse.com/the-latest",
|
||||
description: "Queer news & stories",
|
||||
},
|
||||
{
|
||||
label: "Events",
|
||||
href: "https://thepinkpulse.com/events-calendar",
|
||||
description: "This weekend near you",
|
||||
},
|
||||
{
|
||||
label: "Directory",
|
||||
href: "https://thepinkpulse.com/directory",
|
||||
description: "Local LGBTQ+ businesses",
|
||||
},
|
||||
{
|
||||
label: "Health",
|
||||
href: "https://thepinkpulse.com/health-wellness",
|
||||
description: "Testing, PrEP & wellness",
|
||||
},
|
||||
{
|
||||
label: "Nightlife",
|
||||
href: "https://thepinkpulse.com/nightlife",
|
||||
description: "Bars, drag & going out",
|
||||
},
|
||||
{
|
||||
label: "Groups",
|
||||
href: "https://thepinkpulse.com/groups",
|
||||
description: "Community meetups",
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
Reference in New Issue
Block a user