v3: fixed journal view not scrolling, fixed some user profile layout bugs, increase thumbnail size on the index
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 43s

This commit is contained in:
khannurien
2026-06-28 05:27:49 +00:00
parent bcbb8f5583
commit b567e390d9
10 changed files with 220 additions and 215 deletions

View File

@@ -129,7 +129,7 @@ router.put("/:dumpId/file", authMiddleware, async (ctx) => {
const userId = ctx.state.user?.userId;
const dump = getDump(dumpId, userId);
if (userId !== dump.userId) {
if (userId !== dump.userId && !ctx.state.user?.isAdmin) {
throw new APIException(
APIErrorCode.UNAUTHORIZED,
403,
@@ -175,7 +175,7 @@ router.put("/:dumpId", authMiddleware, async (ctx) => {
const dump = getDump(dumpId, userId);
if (userId !== dump.userId) {
if (userId !== dump.userId && !ctx.state.user?.isAdmin) {
throw new APIException(
APIErrorCode.UNAUTHORIZED,
403,
@@ -193,7 +193,7 @@ router.put("/:dumpId/thumbnail", authMiddleware, async (ctx) => {
const userId = ctx.state.user?.userId;
const dump = getDump(dumpId, userId);
if (userId !== dump.userId) {
if (userId !== dump.userId && !ctx.state.user?.isAdmin) {
throw new APIException(
APIErrorCode.UNAUTHORIZED,
403,
@@ -235,7 +235,7 @@ router.delete("/:dumpId/thumbnail", authMiddleware, async (ctx) => {
const userId = ctx.state.user?.userId;
const dump = getDump(dumpId, userId);
if (userId !== dump.userId) {
if (userId !== dump.userId && !ctx.state.user?.isAdmin) {
throw new APIException(
APIErrorCode.UNAUTHORIZED,
403,
@@ -253,7 +253,7 @@ router.post("/:dumpId/refresh-metadata", authMiddleware, async (ctx) => {
const userId = ctx.state.user?.userId;
const dump = getDump(dumpId, userId);
if (userId !== dump.userId) {
if (userId !== dump.userId && !ctx.state.user?.isAdmin) {
throw new APIException(
APIErrorCode.UNAUTHORIZED,
403,
@@ -271,7 +271,7 @@ router.delete("/:dumpId", authMiddleware, async (ctx) => {
const userId = ctx.state.user?.userId;
const dump = getDump(dumpId, userId);
if (userId !== dump.userId) {
if (userId !== dump.userId && !ctx.state.user?.isAdmin) {
throw new APIException(
APIErrorCode.UNAUTHORIZED,
403,

201
deno.lock generated
View File

@@ -20,25 +20,24 @@
"jsr:@std/path@1.0": "1.0.9",
"jsr:@std/path@^1.1.4": "1.1.4",
"jsr:@tajpouria/cors@^1.2.1": "1.2.1",
"npm:@eslint/js@^10.0.1": "10.0.1_eslint@10.5.0__jiti@2.7.0_jiti@2.7.0",
"npm:@eslint/js@^10.0.1": "10.0.1_eslint@10.6.0__jiti@2.7.0_jiti@2.7.0",
"npm:@lingui/cli@*": "6.4.0",
"npm:@lingui/cli@6.4.0": "6.4.0",
"npm:@lingui/conf@6.4.0": "6.4.0",
"npm:@lingui/core@6.4.0": "6.4.0",
"npm:@lingui/format-po@6.4.0": "6.4.0",
"npm:@lingui/react@6.4.0": "6.4.0_react@19.2.7",
"npm:@lingui/swc-plugin@6.4.0": "6.4.0_@lingui+core@6.4.0",
"npm:@lingui/vite-plugin@6.4.0": "6.4.0_@babel+core@7.29.7_vite@8.0.16__@types+node@26.0.0__jiti@2.7.0_@types+node@26.0.0_jiti@2.7.0",
"npm:@types/node@26": "26.0.0",
"npm:@lingui/swc-plugin@6.5.0": "6.5.0_@lingui+core@6.4.0",
"npm:@lingui/vite-plugin@6.4.0": "6.4.0_vite@8.1.0__@types+node@26.0.1__jiti@2.7.0_@types+node@26.0.1_jiti@2.7.0",
"npm:@types/node@^26.0.1": "26.0.1",
"npm:@types/react-dom@^19.2.3": "19.2.3_@types+react@19.2.17",
"npm:@types/react@^19.2.17": "19.2.17",
"npm:@vitejs/plugin-react-swc@^4.3.1": "4.3.1_vite@8.0.16__@types+node@26.0.0__jiti@2.7.0_@types+node@26.0.0_jiti@2.7.0",
"npm:eslint-plugin-react-hooks@^7.1.1": "7.1.1_eslint@10.5.0__jiti@2.7.0_jiti@2.7.0",
"npm:eslint-plugin-react-refresh@~0.5.3": "0.5.3_eslint@10.5.0__jiti@2.7.0_jiti@2.7.0",
"npm:eslint@*": "10.5.0_jiti@2.7.0",
"npm:eslint@^10.5.0": "10.5.0_jiti@2.7.0",
"npm:@vitejs/plugin-react-swc@^4.3.1": "4.3.1_vite@8.1.0__@types+node@26.0.1__jiti@2.7.0_@types+node@26.0.1_jiti@2.7.0",
"npm:eslint-plugin-react-hooks@^7.1.1": "7.1.1_eslint@10.6.0__jiti@2.7.0_jiti@2.7.0",
"npm:eslint-plugin-react-refresh@~0.5.3": "0.5.3_eslint@10.6.0__jiti@2.7.0_jiti@2.7.0",
"npm:eslint@^10.6.0": "10.6.0_jiti@2.7.0",
"npm:frimousse@0.3": "0.3.0_react@19.2.7_typescript@6.0.3",
"npm:globals@^17.6.0": "17.6.0",
"npm:globals@^17.7.0": "17.7.0",
"npm:jiti@^2.7.0": "2.7.0",
"npm:marked@^18.0.5": "18.0.5",
"npm:nodemailer@^9.0.1": "9.0.1",
@@ -49,11 +48,11 @@
"npm:react-router@^8.0.1": "8.0.1_react@19.2.7_react-dom@19.2.7__react@19.2.7",
"npm:react@^19.2.7": "19.2.7",
"npm:remark-gfm@^4.0.1": "4.0.1",
"npm:typescript-eslint@^8.61.1": "8.61.1_eslint@10.5.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0",
"npm:typescript-eslint@^8.62.0": "8.62.0_eslint@10.6.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0",
"npm:typescript@*": "6.0.3",
"npm:typescript@~6.0.3": "6.0.3",
"npm:vite@*": "8.0.16_@types+node@26.0.0_jiti@2.7.0",
"npm:vite@^8.0.16": "8.0.16_@types+node@26.0.0_jiti@2.7.0"
"npm:vite@*": "8.1.0_@types+node@26.0.1_jiti@2.7.0",
"npm:vite@^8.1.0": "8.1.0_@types+node@26.0.1_jiti@2.7.0"
},
"jsr": {
"@db/sqlite@0.13.0": {
@@ -269,21 +268,21 @@
"@colors/colors@1.5.0": {
"integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="
},
"@emnapi/core@1.10.0": {
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
"@emnapi/core@1.11.1": {
"integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
"dependencies": [
"@emnapi/wasi-threads",
"tslib"
]
},
"@emnapi/runtime@1.10.0": {
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
"@emnapi/runtime@1.11.1": {
"integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
"dependencies": [
"tslib"
]
},
"@emnapi/wasi-threads@1.2.1": {
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
"@emnapi/wasi-threads@1.2.2": {
"integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
"dependencies": [
"tslib"
]
@@ -418,7 +417,7 @@
"os": ["win32"],
"cpu": ["x64"]
},
"@eslint-community/eslint-utils@4.9.1_eslint@10.5.0__jiti@2.7.0_jiti@2.7.0": {
"@eslint-community/eslint-utils@4.9.1_eslint@10.6.0__jiti@2.7.0_jiti@2.7.0": {
"integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
"dependencies": [
"eslint",
@@ -448,7 +447,7 @@
"@types/json-schema"
]
},
"@eslint/js@10.0.1_eslint@10.5.0__jiti@2.7.0_jiti@2.7.0": {
"@eslint/js@10.0.1_eslint@10.6.0__jiti@2.7.0_jiti@2.7.0": {
"integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
"dependencies": [
"eslint"
@@ -617,14 +616,14 @@
"react"
]
},
"@lingui/swc-plugin@6.4.0_@lingui+core@6.4.0": {
"integrity": "sha512-Naz0MVa23DHFLSbmQjUWzCwzH3vSLkw8Uvqln5Bhgw2raRKT2X4O+zHTBpegeYwRCMsdPCQZXymNxpfu46/0vg==",
"@lingui/swc-plugin@6.5.0_@lingui+core@6.4.0": {
"integrity": "sha512-cFbcAHvr6WqOjoQJmormXULzHa6MZSCYDqjXLR80YrraOefBj8qeSSylwFm0gJ7BlOm6N7lASwRRGOGEy4hBOA==",
"dependencies": [
"@lingui/conf",
"@lingui/core"
]
},
"@lingui/vite-plugin@6.4.0_@babel+core@7.29.7_vite@8.0.16__@types+node@26.0.0__jiti@2.7.0_@types+node@26.0.0_jiti@2.7.0": {
"@lingui/vite-plugin@6.4.0_vite@8.1.0__@types+node@26.0.1__jiti@2.7.0_@types+node@26.0.1_jiti@2.7.0": {
"integrity": "sha512-15ogZWydII5TbWwTJJGfJF9OpATxiIbJGearfBv+tj13uv3TlKwLGdmpn/GV8COY8v0irKr7dfdyfxYvxLxSZQ==",
"dependencies": [
"@babel/core",
@@ -645,79 +644,79 @@
"moo"
]
},
"@napi-rs/wasm-runtime@1.1.5_@emnapi+core@1.10.0_@emnapi+runtime@1.10.0": {
"integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==",
"@napi-rs/wasm-runtime@1.1.6_@emnapi+core@1.11.1_@emnapi+runtime@1.11.1": {
"integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==",
"dependencies": [
"@emnapi/core",
"@emnapi/runtime",
"@tybys/wasm-util"
]
},
"@oxc-project/types@0.133.0": {
"integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA=="
"@oxc-project/types@0.137.0": {
"integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA=="
},
"@rolldown/binding-android-arm64@1.0.3": {
"integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==",
"@rolldown/binding-android-arm64@1.1.3": {
"integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==",
"os": ["android"],
"cpu": ["arm64"]
},
"@rolldown/binding-darwin-arm64@1.0.3": {
"integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==",
"@rolldown/binding-darwin-arm64@1.1.3": {
"integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==",
"os": ["darwin"],
"cpu": ["arm64"]
},
"@rolldown/binding-darwin-x64@1.0.3": {
"integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==",
"@rolldown/binding-darwin-x64@1.1.3": {
"integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==",
"os": ["darwin"],
"cpu": ["x64"]
},
"@rolldown/binding-freebsd-x64@1.0.3": {
"integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==",
"@rolldown/binding-freebsd-x64@1.1.3": {
"integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==",
"os": ["freebsd"],
"cpu": ["x64"]
},
"@rolldown/binding-linux-arm-gnueabihf@1.0.3": {
"integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==",
"@rolldown/binding-linux-arm-gnueabihf@1.1.3": {
"integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==",
"os": ["linux"],
"cpu": ["arm"]
},
"@rolldown/binding-linux-arm64-gnu@1.0.3": {
"integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==",
"@rolldown/binding-linux-arm64-gnu@1.1.3": {
"integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==",
"os": ["linux"],
"cpu": ["arm64"]
},
"@rolldown/binding-linux-arm64-musl@1.0.3": {
"integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==",
"@rolldown/binding-linux-arm64-musl@1.1.3": {
"integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==",
"os": ["linux"],
"cpu": ["arm64"]
},
"@rolldown/binding-linux-ppc64-gnu@1.0.3": {
"integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==",
"@rolldown/binding-linux-ppc64-gnu@1.1.3": {
"integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==",
"os": ["linux"],
"cpu": ["ppc64"]
},
"@rolldown/binding-linux-s390x-gnu@1.0.3": {
"integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==",
"@rolldown/binding-linux-s390x-gnu@1.1.3": {
"integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==",
"os": ["linux"],
"cpu": ["s390x"]
},
"@rolldown/binding-linux-x64-gnu@1.0.3": {
"integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==",
"@rolldown/binding-linux-x64-gnu@1.1.3": {
"integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==",
"os": ["linux"],
"cpu": ["x64"]
},
"@rolldown/binding-linux-x64-musl@1.0.3": {
"integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==",
"@rolldown/binding-linux-x64-musl@1.1.3": {
"integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==",
"os": ["linux"],
"cpu": ["x64"]
},
"@rolldown/binding-openharmony-arm64@1.0.3": {
"integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==",
"@rolldown/binding-openharmony-arm64@1.1.3": {
"integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==",
"os": ["openharmony"],
"cpu": ["arm64"]
},
"@rolldown/binding-wasm32-wasi@1.0.3": {
"integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==",
"@rolldown/binding-wasm32-wasi@1.1.3": {
"integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==",
"dependencies": [
"@emnapi/core",
"@emnapi/runtime",
@@ -725,13 +724,13 @@
],
"cpu": ["wasm32"]
},
"@rolldown/binding-win32-arm64-msvc@1.0.3": {
"integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==",
"@rolldown/binding-win32-arm64-msvc@1.1.3": {
"integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==",
"os": ["win32"],
"cpu": ["arm64"]
},
"@rolldown/binding-win32-x64-msvc@1.0.3": {
"integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==",
"@rolldown/binding-win32-x64-msvc@1.1.3": {
"integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==",
"os": ["win32"],
"cpu": ["x64"]
},
@@ -832,8 +831,8 @@
"@swc/counter"
]
},
"@tybys/wasm-util@0.10.2": {
"integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
"@tybys/wasm-util@0.10.3": {
"integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==",
"dependencies": [
"tslib"
]
@@ -889,8 +888,8 @@
"@types/ms@2.1.0": {
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="
},
"@types/node@26.0.0": {
"integrity": "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==",
"@types/node@26.0.1": {
"integrity": "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==",
"dependencies": [
"undici-types"
]
@@ -922,8 +921,8 @@
"@types/yargs-parser"
]
},
"@typescript-eslint/eslint-plugin@8.61.1_@typescript-eslint+parser@8.61.1__eslint@10.5.0___jiti@2.7.0__typescript@6.0.3__jiti@2.7.0_eslint@10.5.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": {
"integrity": "sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==",
"@typescript-eslint/eslint-plugin@8.62.0_@typescript-eslint+parser@8.62.0__eslint@10.6.0___jiti@2.7.0__typescript@6.0.3__jiti@2.7.0_eslint@10.6.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": {
"integrity": "sha512-o+mpz7EYiMzXoySXiKmzlabIvTVqUuK5yLrAedRPRDA0IpPFMUV1IXt6OqljIxX/kumN6EjUYp41Hqelh6p/Dw==",
"dependencies": [
"@eslint-community/regexpp",
"@typescript-eslint/parser",
@@ -938,8 +937,8 @@
"typescript"
]
},
"@typescript-eslint/parser@8.61.1_eslint@10.5.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": {
"integrity": "sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==",
"@typescript-eslint/parser@8.62.0_eslint@10.6.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": {
"integrity": "sha512-dzHeT2gySzZtLDsuqxU9AkYgIsQoHAHtRBpOqM+Ofzx1Bwrd2RcCjQJ+6iQbsHOIR6NS33bF2W1k3blN1zLDrA==",
"dependencies": [
"@typescript-eslint/scope-manager",
"@typescript-eslint/types",
@@ -950,8 +949,8 @@
"typescript"
]
},
"@typescript-eslint/project-service@8.61.1_typescript@6.0.3": {
"integrity": "sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==",
"@typescript-eslint/project-service@8.62.0_typescript@6.0.3": {
"integrity": "sha512-wexnCqiTg7BOGtbLDftYpRWlmLq4xfoMd7BKFR6Y75sZS3QmRKLdN3yWLhmIYgqMmP/OXWpj3H8odkb5nGURCQ==",
"dependencies": [
"@typescript-eslint/tsconfig-utils",
"@typescript-eslint/types",
@@ -959,21 +958,21 @@
"typescript"
]
},
"@typescript-eslint/scope-manager@8.61.1": {
"integrity": "sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==",
"@typescript-eslint/scope-manager@8.62.0": {
"integrity": "sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==",
"dependencies": [
"@typescript-eslint/types",
"@typescript-eslint/visitor-keys"
]
},
"@typescript-eslint/tsconfig-utils@8.61.1_typescript@6.0.3": {
"integrity": "sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==",
"@typescript-eslint/tsconfig-utils@8.62.0_typescript@6.0.3": {
"integrity": "sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==",
"dependencies": [
"typescript"
]
},
"@typescript-eslint/type-utils@8.61.1_eslint@10.5.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": {
"integrity": "sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==",
"@typescript-eslint/type-utils@8.62.0_eslint@10.6.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": {
"integrity": "sha512-+g5O3j0w2ldzC86Pv6fvbO/xhAonbJFIdf/MKQ1d30gndlsVzUOE83ldfSE15Qrl9fhFjK6AovHs5Wpp6vx86w==",
"dependencies": [
"@typescript-eslint/types",
"@typescript-eslint/typescript-estree",
@@ -984,11 +983,11 @@
"typescript"
]
},
"@typescript-eslint/types@8.61.1": {
"integrity": "sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA=="
"@typescript-eslint/types@8.62.0": {
"integrity": "sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg=="
},
"@typescript-eslint/typescript-estree@8.61.1_typescript@6.0.3": {
"integrity": "sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==",
"@typescript-eslint/typescript-estree@8.62.0_typescript@6.0.3": {
"integrity": "sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==",
"dependencies": [
"@typescript-eslint/project-service",
"@typescript-eslint/tsconfig-utils",
@@ -1002,8 +1001,8 @@
"typescript"
]
},
"@typescript-eslint/utils@8.61.1_eslint@10.5.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": {
"integrity": "sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==",
"@typescript-eslint/utils@8.62.0_eslint@10.6.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": {
"integrity": "sha512-82r66fi9zYwZ+mTq3vKgwjbZ1PVk/DJzrXFLpG6RnBbdvH8TEGVHIs9H4d2drhkOzf0syZuD/OZvvlu6GDbP4g==",
"dependencies": [
"@eslint-community/eslint-utils",
"@typescript-eslint/scope-manager",
@@ -1013,8 +1012,8 @@
"typescript"
]
},
"@typescript-eslint/visitor-keys@8.61.1": {
"integrity": "sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==",
"@typescript-eslint/visitor-keys@8.62.0": {
"integrity": "sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==",
"dependencies": [
"@typescript-eslint/types",
"eslint-visitor-keys@5.0.1"
@@ -1024,7 +1023,7 @@
"integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
"deprecated": true
},
"@vitejs/plugin-react-swc@4.3.1_vite@8.0.16__@types+node@26.0.0__jiti@2.7.0_@types+node@26.0.0_jiti@2.7.0": {
"@vitejs/plugin-react-swc@4.3.1_vite@8.1.0__@types+node@26.0.1__jiti@2.7.0_@types+node@26.0.1_jiti@2.7.0": {
"integrity": "sha512-PaeokKjAGraNN+s5SIApgsktnJprIyt3zgEIu7awnEdfn29QiB2crTcCzyi2XGpX9rUnTc0cKU07Wm0N0g7H2w==",
"dependencies": [
"@rolldown/pluginutils",
@@ -1264,7 +1263,7 @@
"escape-string-regexp@5.0.0": {
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="
},
"eslint-plugin-react-hooks@7.1.1_eslint@10.5.0__jiti@2.7.0_jiti@2.7.0": {
"eslint-plugin-react-hooks@7.1.1_eslint@10.6.0__jiti@2.7.0_jiti@2.7.0": {
"integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==",
"dependencies": [
"@babel/core",
@@ -1275,7 +1274,7 @@
"zod-validation-error"
]
},
"eslint-plugin-react-refresh@0.5.3_eslint@10.5.0__jiti@2.7.0_jiti@2.7.0": {
"eslint-plugin-react-refresh@0.5.3_eslint@10.6.0__jiti@2.7.0_jiti@2.7.0": {
"integrity": "sha512-5EMmLCV98Pi4o/f/3DP/v/tNqLHMIc9I8LKClNDWhZ9JTho89/kQcitCXQBMG7sAfVRK0Ie3T2EDOzp1YXYiVA==",
"dependencies": [
"eslint"
@@ -1296,8 +1295,8 @@
"eslint-visitor-keys@5.0.1": {
"integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="
},
"eslint@10.5.0_jiti@2.7.0": {
"integrity": "sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==",
"eslint@10.6.0_jiti@2.7.0": {
"integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==",
"dependencies": [
"@eslint-community/eslint-utils",
"@eslint-community/regexpp",
@@ -1442,8 +1441,8 @@
"is-glob"
]
},
"globals@17.6.0": {
"integrity": "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA=="
"globals@17.7.0": {
"integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg=="
},
"has-flag@4.0.0": {
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
@@ -2351,8 +2350,8 @@
"signal-exit"
]
},
"rolldown@1.0.3": {
"integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==",
"rolldown@1.1.3": {
"integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==",
"dependencies": [
"@oxc-project/types",
"@rolldown/pluginutils"
@@ -2500,8 +2499,8 @@
"prelude-ls"
]
},
"typescript-eslint@8.61.1_eslint@10.5.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": {
"integrity": "sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==",
"typescript-eslint@8.62.0_eslint@10.6.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": {
"integrity": "sha512-8QxXi+ZACKX0kaqO4gY8kn0RSD9gFfaHDWwjqtEN48aWCBkX4MJaufWN+c3BzlrXLOxfywDL8CaoqUwcRq4j4Q==",
"dependencies": [
"@typescript-eslint/eslint-plugin",
"@typescript-eslint/parser",
@@ -2592,8 +2591,8 @@
"vfile-message"
]
},
"vite@8.0.16_@types+node@26.0.0_jiti@2.7.0": {
"integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==",
"vite@8.1.0_@types+node@26.0.1_jiti@2.7.0": {
"integrity": "sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==",
"dependencies": [
"@types/node",
"jiti",
@@ -2661,17 +2660,17 @@
"npm:@lingui/core@6.4.0",
"npm:@lingui/format-po@6.4.0",
"npm:@lingui/react@6.4.0",
"npm:@lingui/swc-plugin@6.4.0",
"npm:@lingui/swc-plugin@6.5.0",
"npm:@lingui/vite-plugin@6.4.0",
"npm:@types/node@26",
"npm:@types/node@^26.0.1",
"npm:@types/react-dom@^19.2.3",
"npm:@types/react@^19.2.17",
"npm:@vitejs/plugin-react-swc@^4.3.1",
"npm:eslint-plugin-react-hooks@^7.1.1",
"npm:eslint-plugin-react-refresh@~0.5.3",
"npm:eslint@^10.5.0",
"npm:eslint@^10.6.0",
"npm:frimousse@0.3",
"npm:globals@^17.6.0",
"npm:globals@^17.7.0",
"npm:jiti@^2.7.0",
"npm:react-dom@^19.2.7",
"npm:react-hook-form@^7.80.0",
@@ -2679,9 +2678,9 @@
"npm:react-router@^8.0.1",
"npm:react@^19.2.7",
"npm:remark-gfm@^4.0.1",
"npm:typescript-eslint@^8.61.1",
"npm:typescript-eslint@^8.62.0",
"npm:typescript@~6.0.3",
"npm:vite@^8.0.16"
"npm:vite@^8.1.0"
]
}
}

View File

@@ -16,7 +16,7 @@
"@lingui/format-po": "6.4.0",
"@lingui/react": "6.4.0",
"@types/react": "^19.2.17",
"@lingui/swc-plugin": "6.4.0",
"@lingui/swc-plugin": "6.5.0",
"@vitejs/plugin-react-swc": "^4.3.1",
"frimousse": "^0.3.0",
"jiti": "^2.7.0",
@@ -30,14 +30,14 @@
"devDependencies": {
"@lingui/vite-plugin": "6.4.0",
"@eslint/js": "^10.0.1",
"@types/node": "^26.0.0",
"@types/node": "^26.0.1",
"@types/react-dom": "^19.2.3",
"eslint": "^10.5.0",
"eslint": "^10.6.0",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.3",
"globals": "^17.6.0",
"globals": "^17.7.0",
"typescript": "~6.0.3",
"typescript-eslint": "^8.61.1",
"vite": "^8.0.16"
"typescript-eslint": "^8.62.0",
"vite": "^8.1.0"
}
}

View File

@@ -2855,8 +2855,8 @@ body.has-fab .page-content {
.dump-card-preview,
.playlist-card-preview {
flex-shrink: 0;
width: 48px;
height: 48px;
width: 80px;
height: 80px;
display: flex;
align-items: center;
justify-content: center;
@@ -2875,7 +2875,7 @@ body.has-fab .page-content {
/* ── Shared card preview icon ── */
.dump-card-preview-icon,
.playlist-card-icon {
font-size: 1.4rem;
font-size: 1.8rem;
opacity: 0.7;
line-height: 1;
}
@@ -2889,14 +2889,14 @@ body.has-fab .page-content {
}
.dump-card-preview .rich-content-compact {
width: 48px;
height: 48px;
width: 80px;
height: 80px;
justify-content: center;
}
.dump-card-preview .rich-content-compact-thumbnail {
width: 48px;
height: 48px;
width: 80px;
height: 80px;
object-fit: contain;
border-radius: 0;
border: none;

View File

@@ -3,13 +3,12 @@ import { AppHeader } from "./AppHeader.tsx";
interface PageShellProps {
children: ReactNode;
feed?: ReactNode;
centered?: boolean;
centerSlot?: ReactNode;
}
export function PageShell(
{ children, feed, centered = false, centerSlot }: PageShellProps,
{ children, centered = false, centerSlot }: PageShellProps,
) {
return (
<div className="page-shell">
@@ -19,7 +18,6 @@ export function PageShell(
>
{children}
</main>
{feed}
</div>
);
}

View File

@@ -1,7 +1,16 @@
import { useCallback, useEffect, useRef } from "react";
import { useCallback, useEffect, useState } from "react";
/**
* Observe a sentinel element and call `onLoadMore` when it scrolls into view.
*
* The sentinel is tracked via a state-backed callback ref rather than a plain
* `useRef` so the observer re-attaches whenever the underlying DOM node
* changes — e.g. when the index page swaps the active feed tab and a fresh
* sentinel `<div>` mounts in place of the old one. A static ref would keep
* observing the now-detached node and silently stop loading more.
*/
export function useInfiniteScroll(onLoadMore: () => void, enabled: boolean) {
const sentinelRef = useRef<HTMLDivElement>(null);
const [sentinel, setSentinel] = useState<HTMLDivElement | null>(null);
const handleIntersect = useCallback(
(entries: IntersectionObserverEntry[]) => {
@@ -13,14 +22,13 @@ export function useInfiniteScroll(onLoadMore: () => void, enabled: boolean) {
);
useEffect(() => {
const el = sentinelRef.current;
if (!el) return;
if (!sentinel) return;
const observer = new IntersectionObserver(handleIntersect, {
rootMargin: "200px",
});
observer.observe(el);
observer.observe(sentinel);
return () => observer.disconnect();
}, [handleIntersect]);
}, [handleIntersect, sentinel]);
return sentinelRef;
return setSentinel;
}

View File

@@ -1,5 +1,5 @@
import {
type RefObject,
type Ref,
useCallback,
useEffect,
useLayoutEffect,
@@ -41,7 +41,7 @@ interface UseUserDumpFeedResult {
state: State;
setState: React.Dispatch<React.SetStateAction<State>>;
setItems: (fn: (prev: Dump[]) => Dump[]) => void;
sentinelRef: RefObject<HTMLDivElement | null>;
sentinelRef: Ref<HTMLDivElement>;
}
/**

View File

@@ -70,37 +70,7 @@ export function UserDumps() {
const { profileUser, items: dumps, hasMore, loadingMore } = state;
return (
<PageShell
feed={dumps.length > 0 && (
<>
<ul className="dump-feed">
{dumps.map((dump) => (
<DumpCard
key={dump.id}
dump={dump}
voteCount={voteCounts[dump.id] ?? dump.voteCount}
voted={myVotes.has(dump.id)}
canVote={!!me}
castVote={castVote}
removeVote={removeVote}
isOwner={isOwnProfile}
/>
))}
</ul>
{hasMore && <div ref={sentinelRef} />}
{loadingMore && (
<p className="feed-loading-more">
<Trans>Loading more</Trans>
</p>
)}
{!hasMore && (
<p className="feed-end">
<Trans>You've reached the end.</Trans>
</p>
)}
</>
)}
>
<PageShell>
<ProfileSubpageHeader
username={username!}
profileUser={profileUser}
@@ -120,11 +90,41 @@ export function UserDumps() {
{createModalOpen && (
<DumpCreateModal onClose={() => setCreateModalOpen(false)} />
)}
{dumps.length === 0 && (
<p className="empty-state">
<Trans>Nothing here yet.</Trans>
</p>
)}
{dumps.length === 0
? (
<p className="empty-state">
<Trans>Nothing here yet.</Trans>
</p>
)
: (
<>
<ul className="dump-feed">
{dumps.map((dump) => (
<DumpCard
key={dump.id}
dump={dump}
voteCount={voteCounts[dump.id] ?? dump.voteCount}
voted={myVotes.has(dump.id)}
canVote={!!me}
castVote={castVote}
removeVote={removeVote}
isOwner={isOwnProfile}
/>
))}
</ul>
{hasMore && <div ref={sentinelRef} />}
{loadingMore && (
<p className="feed-loading-more">
<Trans>Loading more</Trans>
</p>
)}
{!hasMore && (
<p className="feed-end">
<Trans>You've reached the end.</Trans>
</p>
)}
</>
)}
</PageShell>
);
}

View File

@@ -144,56 +144,56 @@ export function UserUpvoted() {
);
return (
<PageShell
feed={visibleDumps.length > 0 && (
<>
<ul className="dump-feed">
{visibleDumps.map((dump) => {
const phase = fading[dump.id];
const extraCls = phase === "cooldown"
? "dump-card--fading"
: phase === "dismissing"
? "dump-card--dismissing"
: undefined;
return (
<DumpCard
key={dump.id}
dump={dump}
voteCount={voteCounts[dump.id] ?? dump.voteCount}
voted={myVotes.has(dump.id)}
canVote={!!me}
castVote={castVote}
removeVote={removeVote}
className={extraCls}
isOwner={!!me && me.id === dump.userId}
/>
);
})}
</ul>
{hasMore && <div ref={sentinelRef} />}
{loadingMore && (
<p className="feed-loading-more">
<Trans>Loading more</Trans>
</p>
)}
{!hasMore && (
<p className="feed-end">
<Trans>You've reached the end.</Trans>
</p>
)}
</>
)}
>
<PageShell>
<ProfileSubpageHeader
username={username!}
profileUser={profileUser}
title={t`Upvoted`}
/>
{visibleDumps.length === 0 && (
<p className="empty-state">
<Trans>Nothing here yet.</Trans>
</p>
)}
{visibleDumps.length === 0
? (
<p className="empty-state">
<Trans>Nothing here yet.</Trans>
</p>
)
: (
<>
<ul className="dump-feed">
{visibleDumps.map((dump) => {
const phase = fading[dump.id];
const extraCls = phase === "cooldown"
? "dump-card--fading"
: phase === "dismissing"
? "dump-card--dismissing"
: undefined;
return (
<DumpCard
key={dump.id}
dump={dump}
voteCount={voteCounts[dump.id] ?? dump.voteCount}
voted={myVotes.has(dump.id)}
canVote={!!me}
castVote={castVote}
removeVote={removeVote}
className={extraCls}
isOwner={!!me && me.id === dump.userId}
/>
);
})}
</ul>
{hasMore && <div ref={sentinelRef} />}
{loadingMore && (
<p className="feed-loading-more">
<Trans>Loading more</Trans>
</p>
)}
{!hasMore && (
<p className="feed-end">
<Trans>You've reached the end.</Trans>
</p>
)}
</>
)}
</PageShell>
);
}

View File

@@ -1,4 +1,4 @@
import type { RefObject } from "react";
import type { Ref } from "react";
import type { Dump, User } from "../../model.ts";
export interface MainFeedProps {
@@ -7,7 +7,7 @@ export interface MainFeedProps {
error: string | null;
hasMore: boolean;
loadingMore: boolean;
sentinelRef: RefObject<HTMLDivElement | null>;
sentinelRef: Ref<HTMLDivElement>;
voteCounts: Record<string, number>;
myVotes: Set<string>;
user: User | null;