mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-26 04:57:08 -05:00
Footer desktop initial
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -11,4 +11,5 @@ dump
|
||||
/cypress/videos
|
||||
/cypress/screenshots
|
||||
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
.excalidraw
|
||||
@@ -5,7 +5,11 @@ import type * as React from "react";
|
||||
import { ArrowUpIcon } from "./icons/ArrowUp";
|
||||
|
||||
export function SubNav({ children }: { children: React.ReactNode }) {
|
||||
return <nav className="sub-nav__container">{children}</nav>;
|
||||
return (
|
||||
<div>
|
||||
<nav className="sub-nav__container">{children}</nav>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function SubNavLink({
|
||||
|
||||
92
app/components/layout/Footer.tsx
Normal file
92
app/components/layout/Footer.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
import { Link, useLoaderData } from "@remix-run/react";
|
||||
import type { RootLoaderData } from "~/root";
|
||||
import { discordFullName } from "~/utils/strings";
|
||||
import {
|
||||
FAQ_PAGE,
|
||||
SENDOU_INK_DISCORD_URL,
|
||||
SENDOU_INK_GITHUB_URL,
|
||||
SENDOU_INK_PATREON_URL,
|
||||
SENDOU_TWITTER_URL,
|
||||
userPage,
|
||||
} from "~/utils/urls";
|
||||
import { DiscordIcon } from "../icons/Discord";
|
||||
import { GitHubIcon } from "../icons/GitHub";
|
||||
import { PatreonIcon } from "../icons/Patreon";
|
||||
import { TwitterIcon } from "../icons/Twitter";
|
||||
|
||||
// xxx: add some sendouLove
|
||||
export function Footer() {
|
||||
const data = useLoaderData<RootLoaderData>();
|
||||
|
||||
return (
|
||||
<footer className="layout__footer">
|
||||
<div className="layout__footer__link-list">
|
||||
<a href={SENDOU_TWITTER_URL} target="_blank" rel="noreferrer">
|
||||
sendou.ink by Sendou
|
||||
</a>
|
||||
<Link to={"/"}>Contributors</Link>
|
||||
<Link to={FAQ_PAGE}>FAQ</Link>
|
||||
</div>
|
||||
<div className="layout__footer__socials">
|
||||
<a
|
||||
className="layout__footer__social-link"
|
||||
href={SENDOU_INK_GITHUB_URL}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<div className="layout__footer__social-header">
|
||||
GitHub<p>Source code</p>
|
||||
</div>
|
||||
<GitHubIcon className="layout__footer__social-icon github" />
|
||||
</a>
|
||||
<a
|
||||
className="layout__footer__social-link"
|
||||
href={SENDOU_TWITTER_URL}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<div className="layout__footer__social-header">
|
||||
Twitter<p>Updates</p>
|
||||
</div>
|
||||
<TwitterIcon className="layout__footer__social-icon twitter" />
|
||||
</a>
|
||||
<a
|
||||
className="layout__footer__social-link"
|
||||
href={SENDOU_INK_DISCORD_URL}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<div className="layout__footer__social-header">
|
||||
Discord<p>Help & feedback</p>
|
||||
</div>{" "}
|
||||
<DiscordIcon className="layout__footer__social-icon discord" />
|
||||
</a>
|
||||
<a
|
||||
className="layout__footer__social-link"
|
||||
href={SENDOU_INK_PATREON_URL}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<div className="layout__footer__social-header">
|
||||
Patreon<p>Support</p>
|
||||
</div>{" "}
|
||||
<PatreonIcon className="layout__footer__social-icon patreon" />
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="layout__footer__patron-title">
|
||||
Thanks to the patrons for the support
|
||||
</h4>
|
||||
<ul className="layout__footer__patron-list">
|
||||
{data.patrons.map((patron) => (
|
||||
<li key={patron.id}>
|
||||
<Link to={userPage(patron.discordId)}>
|
||||
{discordFullName(patron)}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { UserItem } from "./UserItem";
|
||||
import navItems from "./nav-items.json";
|
||||
import { useLocation } from "@remix-run/react";
|
||||
import { Image } from "../Image";
|
||||
import { Footer } from "./Footer";
|
||||
|
||||
export const Layout = React.memo(function Layout({
|
||||
children,
|
||||
@@ -19,7 +20,7 @@ export const Layout = React.memo(function Layout({
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="layout__container">
|
||||
<header className="layout__header">
|
||||
{currentPagesNavItem ? (
|
||||
<h1 className="layout__page-heading">
|
||||
@@ -45,6 +46,7 @@ export const Layout = React.memo(function Layout({
|
||||
</header>
|
||||
<Menu expanded={menuOpen} closeMenu={() => setMenuOpen(false)} />
|
||||
{children}
|
||||
</>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -145,3 +145,25 @@ export function findAllPlusMembers() {
|
||||
plusTier: NonNullable<UserWithPlusTier["plusTier"]>;
|
||||
}>;
|
||||
}
|
||||
|
||||
const findAllPatronsStm = sql.prepare(`
|
||||
select
|
||||
"id",
|
||||
"discordId",
|
||||
"discordName",
|
||||
"discordDiscriminator",
|
||||
"patronTier"
|
||||
from "User"
|
||||
where "patronTier" is not null
|
||||
order by "patronTier" desc, "patronSince" asc
|
||||
`);
|
||||
|
||||
export type FindAllPatrons = Array<
|
||||
Pick<
|
||||
User,
|
||||
"id" | "discordId" | "discordName" | "discordDiscriminator" | "patronTier"
|
||||
>
|
||||
>;
|
||||
export function findAllPatrons() {
|
||||
return findAllPatronsStm.all() as FindAllPatrons;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { sql } from "~/db/sql";
|
||||
import type { UpsertManyPlusVotesArgs } from "./models/plusVotes.server";
|
||||
import { ADMIN_DISCORD_ID } from "~/constants";
|
||||
import shuffle from "just-shuffle";
|
||||
import { dateToDatabaseTimestamp } from "~/utils/dates";
|
||||
|
||||
const ADMIN_TEST_AVATAR = "fcfd65a3bea598905abb9ca25296816b";
|
||||
|
||||
@@ -26,6 +27,7 @@ const basicSeeds = [
|
||||
badgesToAdmin,
|
||||
badgesToUsers,
|
||||
badgeManagers,
|
||||
patrons,
|
||||
];
|
||||
|
||||
export function seed() {
|
||||
@@ -42,7 +44,7 @@ function wipeDB() {
|
||||
"PlusVote",
|
||||
"PlusSuggestion",
|
||||
"PlusVote",
|
||||
"BadgeOwner",
|
||||
"TournamentBadgeOwner",
|
||||
"BadgeManager",
|
||||
];
|
||||
|
||||
@@ -226,7 +228,7 @@ function badgesToAdmin() {
|
||||
for (const id of badgesWithDuplicates) {
|
||||
sql
|
||||
.prepare(
|
||||
`insert into "BadgeOwner" ("badgeId", "userId") values ($id, $userId)`
|
||||
`insert into "TournamentBadgeOwner" ("badgeId", "userId") values ($id, $userId)`
|
||||
)
|
||||
.run({ id, userId: 1 });
|
||||
}
|
||||
@@ -259,7 +261,7 @@ function badgesToUsers() {
|
||||
const userToGetABadge = userIds.shift()!;
|
||||
sql
|
||||
.prepare(
|
||||
`insert into "BadgeOwner" ("badgeId", "userId") values ($id, $userId)`
|
||||
`insert into "TournamentBadgeOwner" ("badgeId", "userId") values ($id, $userId)`
|
||||
)
|
||||
.run({ id, userId: userToGetABadge });
|
||||
|
||||
@@ -278,3 +280,22 @@ function badgeManagers() {
|
||||
.run({ id, userId: 2 });
|
||||
}
|
||||
}
|
||||
|
||||
function patrons() {
|
||||
const userIds = sql
|
||||
.prepare(`select "id" from "User" order by random() limit 50`)
|
||||
.all()
|
||||
.map((u) => u.id);
|
||||
|
||||
for (const id of userIds) {
|
||||
sql
|
||||
.prepare(
|
||||
`update user set "patronTier" = $patronTier, "patronSince" = $patronSince where id = $id`
|
||||
)
|
||||
.run({
|
||||
id,
|
||||
patronSince: dateToDatabaseTimestamp(faker.date.past()),
|
||||
patronTier: faker.helpers.arrayElement([1, 1, 2, 2, 2, 3, 3, 4]),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import layoutStyles from "~/styles/layout.css";
|
||||
import resetStyles from "~/styles/reset.css";
|
||||
import { Catcher } from "./components/Catcher";
|
||||
import { Layout } from "./components/layout";
|
||||
import { db } from "./db";
|
||||
import type { FindAllPatrons } from "./db/models/users.server";
|
||||
import type { UserWithPlusTier } from "./db/types";
|
||||
import { getUser } from "./modules/auth";
|
||||
|
||||
@@ -41,6 +43,7 @@ export const meta: MetaFunction = () => ({
|
||||
});
|
||||
|
||||
export interface RootLoaderData {
|
||||
patrons: FindAllPatrons;
|
||||
user?: Pick<
|
||||
UserWithPlusTier,
|
||||
"id" | "discordId" | "discordAvatar" | "plusTier"
|
||||
@@ -51,6 +54,7 @@ export const loader: LoaderFunction = async ({ request }) => {
|
||||
const user = await getUser(request);
|
||||
|
||||
return json<RootLoaderData>({
|
||||
patrons: db.users.findAllPatrons(),
|
||||
user: user
|
||||
? {
|
||||
discordAvatar: user.discordAvatar,
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
--theme-vibrant: hsl(255deg 78% 65%);
|
||||
--theme-transparent: hsl(255deg 66.7% 75% / 40%);
|
||||
--theme-transparent-vibrant: hsl(255deg 78% 65% / 54%);
|
||||
--theme-semi-transparent-vibrant: hsl(255deg 78% 65% / 75%);
|
||||
--theme-secondary: hsl(85deg 66.7% 55.3%);
|
||||
|
||||
/* background-pattern.svg not using this currently */
|
||||
@@ -312,6 +313,11 @@ article {
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
select {
|
||||
all: unset;
|
||||
width: 90%;
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
.layout__container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.layout__header {
|
||||
--item-size: 2.75rem;
|
||||
|
||||
@@ -35,6 +41,7 @@
|
||||
margin: 0 auto;
|
||||
padding-block-end: var(--s-32);
|
||||
padding-inline: var(--s-3);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.layout__burger {
|
||||
@@ -182,3 +189,97 @@
|
||||
flex-direction: column;
|
||||
gap: var(--s-2);
|
||||
}
|
||||
|
||||
.layout__footer {
|
||||
background-color: var(--bg-darker);
|
||||
margin-block-start: auto;
|
||||
padding: var(--s-2-5);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--s-6);
|
||||
}
|
||||
|
||||
.layout__footer__link-list {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: var(--fonts-xxs);
|
||||
}
|
||||
|
||||
.layout__footer__link-list > a {
|
||||
flex: 1 1 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.layout__footer__socials {
|
||||
display: flex;
|
||||
gap: var(--s-2);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.layout__footer__social-link {
|
||||
background-color: var(--theme-transparent-vibrant);
|
||||
border-radius: var(--rounded);
|
||||
height: 16rem;
|
||||
flex: 1 1 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: var(--s-4);
|
||||
font-size: var(--fonts-lg);
|
||||
cursor: pointer;
|
||||
max-width: 10rem;
|
||||
}
|
||||
|
||||
.layout__footer__social-link:hover {
|
||||
background-color: var(--theme-semi-transparent-vibrant);
|
||||
}
|
||||
|
||||
.layout__footer__social-link:hover > .layout__footer__social-icon {
|
||||
transform: translateY(-0.3rem);
|
||||
}
|
||||
|
||||
/* .layout__footer__social-link:hover > .layout__footer__social-icon.github {
|
||||
fill: #333;
|
||||
}
|
||||
|
||||
.layout__footer__social-link:hover > .layout__footer__social-icon.twitter {
|
||||
fill: #1da1f2;
|
||||
}
|
||||
|
||||
.layout__footer__social-link:hover > .layout__footer__social-icon.discord {
|
||||
fill: #5865f2;
|
||||
}
|
||||
|
||||
.layout__footer__social-link:hover > .layout__footer__social-icon.patreon {
|
||||
fill: #f96854;
|
||||
} */
|
||||
|
||||
.layout__footer__social-icon {
|
||||
height: 2.25rem;
|
||||
transition: transform 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
.layout__footer__social-header {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.layout__footer__social-header > p {
|
||||
font-size: var(--fonts-xxs);
|
||||
}
|
||||
|
||||
.layout__footer__patron-title {
|
||||
text-align: center;
|
||||
font-weight: var(--semi-bold);
|
||||
font-size: var(--fonts-sm);
|
||||
}
|
||||
|
||||
.layout__footer__patron-list {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
flex-wrap: wrap;
|
||||
font-size: var(--fonts-xs);
|
||||
gap: var(--s-1);
|
||||
justify-content: center;
|
||||
margin-block-start: var(--s-2);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Badge } from "~/db/types";
|
||||
|
||||
export const SENDOU_INK_DISCORD_URL = "https://discord.gg/sendou";
|
||||
export const SENDOU_TWITTER_URL = "https://twitter.com/sendouc";
|
||||
export const SENDOU_INK_TWITTER_URL = "https://twitter.com/sendouink";
|
||||
export const SENDOU_INK_PATREON_URL = "https://patreon.com/sendou";
|
||||
export const SENDOU_INK_GITHUB_URL = "https://github.com/Sendouc/sendou.ink";
|
||||
|
||||
Reference in New Issue
Block a user