From 1d87171808ed2f4d39ffd5970154b576839b48f0 Mon Sep 17 00:00:00 2001 From: Pig Fang Date: Fri, 31 Jan 2020 15:58:37 +0800 Subject: [PATCH] rewrite user dashboard with React --- app/Http/Controllers/UserController.php | 4 +- package.json | 3 + .../assets/src/scripts/hooks/useTween.ts | 24 ++ resources/assets/src/scripts/route.ts | 2 +- resources/assets/src/styles/_breakpoints.scss | 27 +++ resources/assets/src/views/user/Dashboard.vue | 212 ------------------ .../src/views/user/Dashboard/InfoBox.tsx | 33 +++ .../src/views/user/Dashboard/SignButton.tsx | 38 ++++ .../assets/src/views/user/Dashboard/index.tsx | 134 +++++++++++ .../src/views/user/Dashboard/score.scss | 23 ++ .../src/views/user/Dashboard/scoreUtils.ts | 34 +++ resources/assets/tests/setup.ts | 4 +- .../assets/tests/views/user/Dashboard.test.ts | 180 --------------- .../tests/views/user/Dashboard.test.tsx | 161 +++++++++++++ .../views/user/widgets/dashboard/usage.twig | 2 +- webpack.config.js | 3 +- yarn.lock | 20 +- 17 files changed, 504 insertions(+), 400 deletions(-) create mode 100644 resources/assets/src/scripts/hooks/useTween.ts create mode 100644 resources/assets/src/styles/_breakpoints.scss delete mode 100644 resources/assets/src/views/user/Dashboard.vue create mode 100644 resources/assets/src/views/user/Dashboard/InfoBox.tsx create mode 100644 resources/assets/src/views/user/Dashboard/SignButton.tsx create mode 100644 resources/assets/src/views/user/Dashboard/index.tsx create mode 100644 resources/assets/src/views/user/Dashboard/score.scss create mode 100644 resources/assets/src/views/user/Dashboard/scoreUtils.ts delete mode 100644 resources/assets/tests/views/user/Dashboard.test.ts create mode 100644 resources/assets/tests/views/user/Dashboard.test.tsx diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 3dcff87f..939e311f 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -100,8 +100,8 @@ class UserController extends Controller 'players' => $this->calculatePercentageUsed($user->players->count(), option('score_per_player')), 'storage' => $this->calculatePercentageUsed($this->getStorageUsed($user), option('score_per_storage')), ], - 'signAfterZero' => option('sign_after_zero'), - 'signGapTime' => option('sign_gap_time'), + 'signAfterZero' => (bool) option('sign_after_zero'), + 'signGapTime' => (int) option('sign_gap_time'), ]); } diff --git a/package.json b/package.json index b7876cb8..815d0554 100644 --- a/package.json +++ b/package.json @@ -45,8 +45,10 @@ "@types/echarts": "^4.4.2", "@types/jest": "^24.0.25", "@types/jquery": "^3.3.29", + "@types/js-yaml": "^3.12.2", "@types/react": "^16.9.17", "@types/react-dom": "^16.9.4", + "@types/tween.js": "^17.2.0", "@types/webpack": "^4.41.2", "@typescript-eslint/eslint-plugin": "^2.8.0", "@typescript-eslint/parser": "^2.8.0", @@ -64,6 +66,7 @@ "file-loader": "^5.0.2", "jest": "^24.9.0", "jest-extended": "^0.11.2", + "js-yaml": "^3.13.1", "mini-css-extract-plugin": "^0.9.0", "optimize-css-assets-webpack-plugin": "^5.0.3", "postcss-loader": "^3.0.0", diff --git a/resources/assets/src/scripts/hooks/useTween.ts b/resources/assets/src/scripts/hooks/useTween.ts new file mode 100644 index 00000000..c9c961f7 --- /dev/null +++ b/resources/assets/src/scripts/hooks/useTween.ts @@ -0,0 +1,24 @@ +import React, { useState, useEffect, useRef } from 'react' +import TWEEN from '@tweenjs/tween.js' + +export default function useTween( + initialValue: T, +): [T, React.Dispatch>] { + const [value, setValue] = useState(initialValue) + const ref = useRef(value) + const [dest, setDest] = useState(initialValue) + + useEffect(() => { + function animate() { + requestAnimationFrame(animate) + TWEEN.update() + setValue(ref.current) + } + + const tween = new TWEEN.Tween(ref) + tween.to({ current: dest }, 1000).start() + animate() + }, [dest]) + + return [value, setDest] +} diff --git a/resources/assets/src/scripts/route.ts b/resources/assets/src/scripts/route.ts index 5d253166..193c107f 100644 --- a/resources/assets/src/scripts/route.ts +++ b/resources/assets/src/scripts/route.ts @@ -10,7 +10,7 @@ export default [ }, { path: 'user', - component: () => import('../views/user/Dashboard.vue'), + react: () => import('../views/user/Dashboard'), el: '#usage-box', }, { diff --git a/resources/assets/src/styles/_breakpoints.scss b/resources/assets/src/styles/_breakpoints.scss new file mode 100644 index 00000000..0be643f5 --- /dev/null +++ b/resources/assets/src/styles/_breakpoints.scss @@ -0,0 +1,27 @@ +@use 'sass:map'; + +$breakpoints: ( + xs: 0, + sm: 576px, + md: 768px, + lg: 992px, + xl: 1200px, +); + +@mixin less-than($breakpoint) { + @media (max-width: map-get($breakpoints, $breakpoint)) { + @content; + } +} + +@mixin between($down, $up) { + @media (max-width: map-get($breakpoints, $down)) and (min-width: map-get($breakpoints, $up)) { + @content; + } +} + +@mixin greater-than($breakpoint) { + @media (min-width: map-get($breakpoints, $breakpoint)) { + @content; + } +} diff --git a/resources/assets/src/views/user/Dashboard.vue b/resources/assets/src/views/user/Dashboard.vue deleted file mode 100644 index e83f54ce..00000000 --- a/resources/assets/src/views/user/Dashboard.vue +++ /dev/null @@ -1,212 +0,0 @@ - - - - - diff --git a/resources/assets/src/views/user/Dashboard/InfoBox.tsx b/resources/assets/src/views/user/Dashboard/InfoBox.tsx new file mode 100644 index 00000000..6fcb7143 --- /dev/null +++ b/resources/assets/src/views/user/Dashboard/InfoBox.tsx @@ -0,0 +1,33 @@ +import React from 'react' + +interface Props { + name: string + icon: string + color: string + used: number + total: number + unit: string +} + +const InfoBox: React.FC = props => { + const percentage = (props.used / props.total) * 100 + + return ( +
+ + + +
+ {props.name} + + {props.used} / {props.total} {props.unit} + +
+
+
+
+
+ ) +} + +export default React.memo(InfoBox) diff --git a/resources/assets/src/views/user/Dashboard/SignButton.tsx b/resources/assets/src/views/user/Dashboard/SignButton.tsx new file mode 100644 index 00000000..43e61b4e --- /dev/null +++ b/resources/assets/src/views/user/Dashboard/SignButton.tsx @@ -0,0 +1,38 @@ +import React, { useMemo } from 'react' +import { trans } from '../../../scripts/i18n' +import * as scoreUtils from './scoreUtils' + +interface Props { + isLoading: boolean + lastSign: Date + canSignAfterZero: boolean + signGap: number + onClick: React.MouseEventHandler +} + +const SignButton: React.FC = props => { + const { lastSign, signGap, canSignAfterZero } = props + const remainingTime = useMemo( + () => scoreUtils.remainingTime(lastSign, signGap, canSignAfterZero), + [lastSign, signGap, canSignAfterZero], + ) + const remainingTimeText = useMemo( + () => scoreUtils.remainingTimeText(remainingTime), + [remainingTime], + ) + const canSign = remainingTime <= 0 + + return ( +