Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
6af75088af
|
|||
|
59f8809788
|
|||
|
f36be9a0f8
|
|||
|
d7803c07ea
|
|||
|
94a35b1ea6
|
+8
-5
@@ -4,7 +4,8 @@ ENV NEXT_PUBLIC_APP_VERSION=$APP_VERSION
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN corepack enable && corepack prepare yarn@4.9.1 --activate
|
||||
RUN corepack enable
|
||||
RUN corepack prepare yarn@4.12.0 --activate
|
||||
|
||||
COPY package.json yarn.lock .yarnrc.yml ./
|
||||
RUN yarn install --immutable
|
||||
@@ -22,9 +23,10 @@ LABEL org.opencontainers.image.version=$APP_VERSION
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN corepack enable && corepack prepare yarn@4.9.1 --activate \
|
||||
&& addgroup -S nodejs -g 1001 \
|
||||
&& adduser -S nextjs -u 1001
|
||||
RUN corepack enable
|
||||
RUN corepack prepare yarn@4.12.0 --activate
|
||||
RUN addgroup -S nodejs -g 1001
|
||||
RUN adduser -S nextjs -u 1001
|
||||
|
||||
COPY --from=builder /app/yarn.lock ./yarn.lock
|
||||
COPY --from=builder /app/.yarnrc.yml ./.yarnrc.yml
|
||||
@@ -33,7 +35,8 @@ COPY --from=builder /app/public ./public
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
COPY --from=builder /app/package.json ./package.json
|
||||
|
||||
RUN mkdir -p /app/.yarn && chown -R nextjs:nodejs /app/.yarn
|
||||
RUN mkdir -p /app/.yarn /app/.next/cache/images
|
||||
RUN chown -R nextjs:nodejs /app
|
||||
|
||||
USER nextjs
|
||||
EXPOSE 3000
|
||||
|
||||
+22
-22
@@ -9,36 +9,36 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@icons-pack/react-simple-icons": "10.0.0",
|
||||
"@icons-pack/react-simple-icons": "^13.8.0",
|
||||
"@mdi/js": "^7.4.47",
|
||||
"@mdi/react": "^1.6.1",
|
||||
"@react-spring/three": "^9.7.5",
|
||||
"@react-spring/web": "^9.7.5",
|
||||
"@react-three/drei": "^10.0.7",
|
||||
"@react-three/fiber": "^9.1.2",
|
||||
"@react-spring/three": "^10.0.3",
|
||||
"@react-spring/web": "^10.0.3",
|
||||
"@react-three/drei": "^10.7.7",
|
||||
"@react-three/fiber": "^9.4.2",
|
||||
"ajv": "^8.17.1",
|
||||
"ajv-formats": "^3.0.1",
|
||||
"awesome-ajv-errors": "^5.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"next": "15.3.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-pdf": "^9.2.1",
|
||||
"three": "^0.175.0"
|
||||
"next": "^16.0.8",
|
||||
"react": "^19.2.1",
|
||||
"react-dom": "^19.2.1",
|
||||
"react-pdf": "^10.2.0",
|
||||
"three": "^0.182.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/lodash": "^4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@types/three": "^0",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "15.3.0",
|
||||
"tailwindcss": "^4",
|
||||
"@eslint/eslintrc": "^3.3.3",
|
||||
"@tailwindcss/postcss": "^4.1.17",
|
||||
"@types/lodash": "^4.17.21",
|
||||
"@types/node": "^24.10.2",
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/three": "^0.181.0",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-config-next": "^16.0.8",
|
||||
"tailwindcss": "^4.1.17",
|
||||
"tailwindcss-text-fill-stroke": "^2.0.0-beta.3",
|
||||
"typescript": "^5"
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"packageManager": "yarn@4.9.1+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538"
|
||||
"packageManager": "yarn@4.12.0"
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="0 0 24 24"><title>LinkedIn</title><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" fill="#0a66c2"/></svg>
|
||||
|
After Width: | Height: | Size: 625 B |
@@ -0,0 +1,34 @@
|
||||
// https://github.com/f2etw/detect-inapp/blob/master/src/inapp.js
|
||||
|
||||
import { findKey } from "lodash";
|
||||
|
||||
const BROWSER = {
|
||||
messenger: /\bFB[\w_]+\/(Messenger|MESSENGER)/,
|
||||
facebook: /\bFB[\w_]+\//,
|
||||
twitter: /\bTwitter/i,
|
||||
line: /\bLine\//i,
|
||||
wechat: /\bMicroMessenger\//i,
|
||||
puffin: /\bPuffin/i,
|
||||
miui: /\bMiuiBrowser\//i,
|
||||
instagram: /\bInstagram/i,
|
||||
chrome: /\bCrMo\b|CriOS|Android.*Chrome\/[.0-9]* (Mobile)?/,
|
||||
safari: /Version.*Mobile.*Safari|Safari.*Mobile|MobileSafari/,
|
||||
ie: /IEMobile|MSIEMobile/,
|
||||
firefox:
|
||||
/fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile|FxiOS/,
|
||||
};
|
||||
|
||||
export const useInAppBrowser = (): boolean => {
|
||||
const userAgent =
|
||||
navigator.userAgent ||
|
||||
navigator.vendor ||
|
||||
(window as unknown as { opera: string }).opera;
|
||||
|
||||
const rules = ["WebView", "(iPhone|iPod|iPad)(?!.*Safari/)", "Android.*(wv)"];
|
||||
const regex = new RegExp(`(${rules.join("|")})`, "ig");
|
||||
|
||||
return (
|
||||
Boolean(userAgent.match(regex)) &&
|
||||
findKey(BROWSER, (regex) => regex.test(userAgent)) !== undefined
|
||||
);
|
||||
};
|
||||
@@ -32,7 +32,7 @@ import round from "lodash/round";
|
||||
import Stack from "@/util/stack";
|
||||
import isMobile from "@/util/isMobile";
|
||||
import { Canvas } from "@react-three/fiber";
|
||||
import { mdiArrowLeftBold, mdiOpenInNew } from "@mdi/js";
|
||||
import { mdiArrowLeftBold, mdiClose, mdiOpenInNew } from "@mdi/js";
|
||||
import { Icon } from "@mdi/react";
|
||||
import { PDF } from "../util/PDF";
|
||||
import { CellphoneUI } from "../ui/CellphoneUI";
|
||||
@@ -48,6 +48,7 @@ import { useWindowSize } from "../hooks/useWindowSize";
|
||||
import { Info } from "../util/Info";
|
||||
import { Loader } from "../ui/Loader";
|
||||
import { MobileMenu } from "../ui/MobileMenu";
|
||||
import { useInAppBrowser } from "../hooks/useInAppBrowser";
|
||||
|
||||
const AnimatedText = threeAnimated(Text);
|
||||
const AnimatedCam = threeAnimated(PerspectiveCamera);
|
||||
@@ -60,10 +61,12 @@ export function Scene(props: JSX.IntrinsicElements["group"]) {
|
||||
const { width, height } = useWindowSize();
|
||||
const config = useConfig();
|
||||
const mobile = isMobile();
|
||||
const isInAppBrowser = useInAppBrowser();
|
||||
|
||||
// States
|
||||
const [fps, setFps] = useState(0);
|
||||
const [dpr, setDpr] = useState(2);
|
||||
const [warningDismissed, setWarningDismissed] = useState(false);
|
||||
const [pendingView, setPendingView] = useState<View | null>(null);
|
||||
const [backHovered, setBackHovered] = useState(false);
|
||||
const [backClicked, setBackClicked] = useState(false);
|
||||
@@ -174,6 +177,20 @@ export function Scene(props: JSX.IntrinsicElements["group"]) {
|
||||
/>
|
||||
</AnimatedButton>
|
||||
)}
|
||||
{!warningDismissed && isInAppBrowser ? (
|
||||
<div className="absolute top-0 right-0 bg-amber-500 m-3 p-2 rounded-xl flex gap-2 items-center justify-between">
|
||||
<p className="text-black text-md">
|
||||
For the best experience, please open this page in a regular
|
||||
browser
|
||||
</p>
|
||||
<button
|
||||
className="transition-all duration-300 pointer-events-auto hover:scale-110 hover:bg-amber-600 rounded-lg"
|
||||
onClick={() => setWarningDismissed(true)}
|
||||
>
|
||||
<Icon path={mdiClose} color="black" className="size-6" />
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="min-w-12 text-white bottom-0 right-0 absolute m-3 p-1 text-xs text-right text-stroke-2 text-stroke-black paint-sfm">
|
||||
<Info />
|
||||
{fps > 0 && (
|
||||
|
||||
@@ -4,8 +4,6 @@ import {
|
||||
SiAppstore,
|
||||
SiAppstoreHex,
|
||||
SiMatrix,
|
||||
SiLinkedin,
|
||||
SiLinkedinHex,
|
||||
SiGitea,
|
||||
SiGiteaHex,
|
||||
SiGithub,
|
||||
@@ -33,6 +31,7 @@ import Icon from "@mdi/react";
|
||||
import { useRef, useState } from "react";
|
||||
import { Time } from "../util/Time";
|
||||
import { useConfig } from "../hooks/useConfig";
|
||||
import Image from "next/image";
|
||||
|
||||
export const CellphoneUI = () => {
|
||||
const config = useConfig();
|
||||
@@ -119,7 +118,13 @@ export const CellphoneUI = () => {
|
||||
onMouseOver={handleEnter}
|
||||
onMouseOut={handleLeave}
|
||||
>
|
||||
<SiLinkedin color={SiLinkedinHex} className="size-full p-2" />
|
||||
<Image
|
||||
src="/assets/linkedin.svg"
|
||||
alt="LinkedIn"
|
||||
className="size-full p-2"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
</a>
|
||||
)}
|
||||
{config.socials?.gitea && (
|
||||
|
||||
@@ -40,7 +40,6 @@ export const Loader = ({ minDuration = 750, fadeMs = 600 }) => {
|
||||
(styles, show) =>
|
||||
show && (
|
||||
<div>
|
||||
{/* @ts-expect-error children not typed bug */}
|
||||
<animated.div
|
||||
style={{
|
||||
...styles,
|
||||
@@ -70,11 +69,9 @@ export const Loader = ({ minDuration = 750, fadeMs = 600 }) => {
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
{/* @ts-expect-error children not typed bug */}
|
||||
<animated.div
|
||||
style={{ ...barStyles, height: "100%", background: "#fff" }}
|
||||
>
|
||||
{/* @ts-expect-error children not typed bug */}
|
||||
<animated.div className="text-[#1b1b1b] text-xs flex h-full items-center justify-center">
|
||||
{barStyles.width.to((w) => {
|
||||
const p = parseInt(w) || 0;
|
||||
|
||||
@@ -82,7 +82,6 @@ export const MobileMenu = ({
|
||||
className="stroke-[0.75] stroke-black"
|
||||
/>
|
||||
</AnimatedButton>
|
||||
{/* @ts-expect-error children not typed bug */}
|
||||
<animated.div
|
||||
style={{
|
||||
transform: slideProps.x.to((x) => `translateX(${x}%)`),
|
||||
|
||||
+19
-5
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
@@ -11,7 +15,7 @@
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
@@ -19,9 +23,19 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
".next/dev/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user