Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
madhead committed Oct 25, 2023
1 parent b8bcb58 commit c79b194
Show file tree
Hide file tree
Showing 14 changed files with 288 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package me.madhead.tyzenhaus.launcher.fly.modules

import io.ktor.http.HttpStatusCode
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.Application
import io.ktor.server.application.install
Expand All @@ -10,6 +11,8 @@ import io.ktor.server.plugins.callloging.CallLogging
import io.ktor.server.plugins.compression.Compression
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.plugins.defaultheaders.DefaultHeaders
import io.ktor.server.response.header
import io.ktor.server.response.respond
import io.ktor.server.routing.routing
import io.micrometer.prometheus.PrometheusMeterRegistry
import java.time.Instant
Expand Down Expand Up @@ -68,8 +71,19 @@ fun Application.tyzenhaus() {
return@authenticate null
}
val tokenRepository by inject<APITokenRepository>()
val apiToken = tokenRepository.get(token)

tokenRepository.get(token)?.takeIf { it.validUntil > Instant.now() }?.let { APITokenPrincipal(it.groupId, it.scope) }
return@authenticate when {
apiToken == null -> null

apiToken.validUntil < Instant.now() -> {
this.response.header("X-Token-Expired", apiToken.validUntil)
this.respond(HttpStatusCode.Forbidden)
null
}

else -> APITokenPrincipal(apiToken.groupId, apiToken.scope)
}
}
}
}
Expand Down
39 changes: 39 additions & 0 deletions mini-app/.pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Binary file not shown.
2 changes: 2 additions & 0 deletions mini-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
"i18next": "^23.6.0",
"i18next-browser-languagedetector": "^7.1.0",
"i18next-http-backend": "^2.2.2",
"moment": "^2.29.4",
"normalize.css": "^8.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-fast-marquee": "^1.6.2",
"react-i18next": "^13.3.1"
},
"devDependencies": {
Expand Down
10 changes: 9 additions & 1 deletion mini-app/src/common/AppWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type AuthWrapperProps = AppWrapperProps;

function AuthWrapper({ children }: AuthWrapperProps) {
const [ok, setOk] = useState<boolean | null>(null);
const [expired, setExpired] = useState<boolean>(false);

useEffect(() => {
async function authenticate() {
Expand All @@ -34,6 +35,9 @@ function AuthWrapper({ children }: AuthWrapperProps) {
});

setOk(response.ok);
if (!response.ok) {
setExpired(response.headers.has("X-Token-Expired"));
}
}

authenticate();
Expand All @@ -49,7 +53,11 @@ function AuthWrapper({ children }: AuthWrapperProps) {
if (ok === null) {
return null;
} else if (ok === false) {
return <Error error={t("errors.unauthorized")} />;
if (expired) {
return <Error error={t("errors.expired")} />;
} else {
return <Error error={t("errors.unauthorized")} />;
}
} else {
return children;
}
Expand Down
93 changes: 93 additions & 0 deletions mini-app/src/common/transaction/Transaction.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
.transaction {
display: flex;
flex-direction: column;
margin: 8px 0;

& > .info {
display: flex;
flex-direction: row;

& > .timestamp {
display: flex;
flex-direction: column;

color: var(--tg-theme-hint-color);
text-align: center;
min-width: 40px;
margin-inline-end: 8px;

@media (pointer: coarse), (hover: none) {
&::after {
content: "";
transition: width 2s;
}
&:hover::after {
content: attr(title);
// transition: width 2s;
position: absolute;
// transform: translate(40px, 0);
z-index: 2;
background-color: var(--tg-theme-bg-color);
// background-color: var(--tg-theme-secondary-bg-color);
box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2),
0px 6px 10px 0px rgba(0, 0, 0, 0.14),
0px 1px 18px 0px rgba(0, 0, 0, 0.12);
padding: 0 8px;
height: 36pt;
line-height: 36pt;
font-size: 16pt;
// border: 1px solid var(--tg-theme-secondary-bg-color);
// clip-path: polygon(8pt 0%, 100% 0%, 100% 100%, 8pt 100%, 0% 50%);
transition: 0.1s;
}
}

& > .month {
font-size: 12pt;
line-height: 12pt;
text-transform: uppercase;
}

& > .date {
font-size: 24pt;
line-height: 24pt;
}
}

& > .title {
font-size: 16pt;
line-height: 36pt;
color: var(--tg-theme-text-color);
flex-grow: 1;
padding-inline: 8px 8px;
max-height: 36pt;
overflow: hidden;

.spacer {
display: block;
width: 25vw;
}
}

& > .amount {
display: flex;
flex-direction: column;

color: var(--tg-theme-link-color);
text-align: end;

margin-inline-start: 8px;

& > .currency {
font-size: 12pt;
line-height: 12pt;
text-transform: uppercase;
}

& > .amount {
font-size: 24pt;
line-height: 24pt;
}
}
}
}
94 changes: 94 additions & 0 deletions mini-app/src/common/transaction/Transaction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import moment from "moment";
import { useEffect, useRef, useState } from "react";
import Marquee from "react-fast-marquee";
import "./Transaction.less";

export type Transaction = {
id: number;
payer: number;
recipients: Set<number>;
amount: string;
currency: string;
title: string;
timestamp: number;
};

export default function TransactionCard(transaction: Transaction) {
return (
<div className="transaction">
<div className="info">
<Timestamp timestamp={transaction.timestamp} />
<Title title={transaction.title} />
<Amount amount={transaction.amount} currency={transaction.currency} />
</div>
<div className="participants"></div>
</div>
);
}

function Timestamp({ timestamp }: { timestamp: number }) {
const m = moment(timestamp);

return (
<div className="timestamp" title={m.format("llll")}>
<div className="month">{m.format("MMM")}</div>
<div className="date">{m.format("DD")}</div>
</div>
);
}

function Title({ title }: { title: string }) {
const titleContainerRef = useRef<HTMLDivElement>(null);
const [marquee, setMarquee] = useState(false);

useEffect(() => {
if (!titleContainerRef.current) {
return;
}

const element = titleContainerRef.current;

if (
element.offsetWidth < element.scrollWidth ||
element.offsetHeight < element.scrollHeight
) {
setMarquee(true);
// element.classList.add("marquee");
}
}, [titleContainerRef]);

return (
<div className="title" ref={titleContainerRef}>
{marquee && (
<Marquee delay={3}>
{title}
<span className="spacer" />
</Marquee>
)}
{!marquee && <span>{title}</span>}
</div>
);
}

function Amount({ amount, currency }: { amount: string; currency: string }) {
function formatAmount(amount: string) {
let result = parseFloat(amount).toFixed(2);

while (result[result.length - 1] === "0") {
result = result.slice(0, result.length - 1);
}

if (result[result.length - 1] === ".") {
result = result.slice(0, result.length - 1);
}

return result;
}

return (
<div className="amount">
<div className="currency">{currency}</div>
<div className="amount">{formatAmount(amount)}</div>
</div>
);
}
Loading

0 comments on commit c79b194

Please sign in to comment.