From 28d709e6e1c3d9d084ba60b2e31577b0d41d83d6 Mon Sep 17 00:00:00 2001 From: dsrkafuu Date: Fri, 18 Mar 2022 17:27:30 +0800 Subject: [PATCH] feat: new battle history list --- README.md | 4 ++- package.json | 6 ++-- pnpm-lock.yaml | 22 ++++++------ src/store/modules/API.ts | 42 +++++++++++----------- src/views/Settings.scss | 41 ++++++++++++---------- src/views/SettingsHistory.tsx | 65 ++++++++++++++++++++--------------- 6 files changed, 97 insertions(+), 83 deletions(-) diff --git a/README.md b/README.md index d0bd090..971cf65 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ [![](https://img.shields.io/github/license/dsrkafuu/skyline-overlay)](https://github.com/dsrkafuu/skyline-overlay/blob/main/LICENSE) [![](https://img.shields.io/lgtm/grade/javascript/github/dsrkafuu/skyline-overlay)](https://lgtm.com/projects/g/dsrkafuu/skyline-overlay/context:javascript) -An extended horizon overlay like [horizoverlay](https://github.com/bsides/horizoverlay/) with some of [ikegami](https://github.com/hibiyasleep/ikegami)'s features. The project is based on React with MobX & Vite, and the dedicated [ffxiv-overlay-api](https://github.com/dsrkafuu/ffxiv-overlay-api). +Features: Basic overlay, extended details, customizable display/data settings, battle history, and more. The project is based on React with MobX & Vite, and the dedicated [ffxiv-overlay-api](https://github.com/dsrkafuu/ffxiv-overlay-api). Skyline Preview @@ -27,6 +27,8 @@ Check [ngld/OverlayPlugin](https://github.com/ngld/OverlayPlugin). For WebSocket 2K+ (WQHD+) screen with 1.1x scale is recommended for better experience in FFXIV. +Click the encounter can ends current battle and start a new one; zone name will be fully displayed when hover; click the DPS meter on encounter bar can switch to show HPS. + By default the overlay follows the pet-merging policy set in the FFXIV ACT Plugin. When playing on global servers with a custom language patch which causes the plugin's pet-merging not working, you can set your ID in an overlay config to manually merge your pets' data. Local server: diff --git a/package.json b/package.json index 9e6523e..beee744 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": true, "type": "module", "name": "skyline-overlay", - "version": "3.3.2", + "version": "3.4.0", "description": "A modern customizable horizon FFXIV miniparse overlay.", "license": "Apache-2.0", "author": "DSRKafuU (https://dsrkafuu.net)", @@ -25,7 +25,7 @@ "@sentry/tracing": "~6.18.2", "clsx": "~1.1.1", "ffxiv-overlay-api": "~4.2.0", - "mobx": "~6.4.2", + "mobx": "~6.5.0", "mobx-react-lite": "~3.3.0", "normalize.css": "~8.0.1", "react": "~17.0.2", @@ -47,7 +47,7 @@ "eslint-plugin-react-hooks": "~4.3.0", "glob": "~7.2.0", "npm-run-all": "~4.1.5", - "prettier": "~2.5.1", + "prettier": "~2.6.0", "rollup-plugin-visualizer": "~5.6.0", "sass": "~1.49.9", "serve": "~13.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 26fa890..1501713 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,11 +19,11 @@ specifiers: eslint-plugin-react-hooks: ~4.3.0 ffxiv-overlay-api: ~4.2.0 glob: ~7.2.0 - mobx: ~6.4.2 + mobx: ~6.5.0 mobx-react-lite: ~3.3.0 normalize.css: ~8.0.1 npm-run-all: ~4.1.5 - prettier: ~2.5.1 + prettier: ~2.6.0 react: ~17.0.2 react-dom: ~17.0.2 rollup-plugin-visualizer: ~5.6.0 @@ -39,8 +39,8 @@ dependencies: '@sentry/tracing': 6.18.2 clsx: 1.1.1 ffxiv-overlay-api: 4.2.0 - mobx: 6.4.2 - mobx-react-lite: 3.3.0_4e589064ce40a783661f96d46f38f574 + mobx: 6.5.0 + mobx-react-lite: 3.3.0_3b110ddd052df537d55bc762cdd4d0b8 normalize.css: 8.0.1 react: 17.0.2 react-dom: 17.0.2_react@17.0.2 @@ -61,7 +61,7 @@ devDependencies: eslint-plugin-react-hooks: 4.3.0_eslint@8.11.0 glob: 7.2.0 npm-run-all: 4.1.5 - prettier: 2.5.1 + prettier: 2.6.0 rollup-plugin-visualizer: 5.6.0 sass: 1.49.9 serve: 13.0.2 @@ -2285,7 +2285,7 @@ packages: resolution: {integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==} dev: true - /mobx-react-lite/3.3.0_4e589064ce40a783661f96d46f38f574: + /mobx-react-lite/3.3.0_3b110ddd052df537d55bc762cdd4d0b8: resolution: {integrity: sha512-U/kMSFtV/bNVgY01FuiGWpRkaQVHozBq5CEBZltFvPt4FcV111hEWkgwqVg9GPPZSEuEdV438PEz8mk8mKpYlA==} peerDependencies: mobx: ^6.1.0 @@ -2298,13 +2298,13 @@ packages: react-native: optional: true dependencies: - mobx: 6.4.2 + mobx: 6.5.0 react: 17.0.2 react-dom: 17.0.2_react@17.0.2 dev: false - /mobx/6.4.2: - resolution: {integrity: sha512-b4xQJYiH8sb0sEbfq/Ws3N77DEJtSihUFD1moeiz2jNoJ5B+mqJutt54ouO9iEfkp7Wk4jQDsVUOh7DPEW3wEw==} + /mobx/6.5.0: + resolution: {integrity: sha512-pHZ/cySF00FVENDWIDzJyoObFahK6Eg4d0papqm6d7yMkxWTZ/S/csqJX1A3PsYy4t5k3z2QnlwuCfMW5lSEwA==} dev: false /ms/2.0.0: @@ -2571,8 +2571,8 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /prettier/2.5.1: - resolution: {integrity: sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==} + /prettier/2.6.0: + resolution: {integrity: sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A==} engines: {node: '>=10.13.0'} hasBin: true dev: true diff --git a/src/store/modules/API.ts b/src/store/modules/API.ts index f0cf4df..45f47d6 100644 --- a/src/store/modules/API.ts +++ b/src/store/modules/API.ts @@ -2,7 +2,6 @@ import OverlayAPI, { ExtendData } from 'ffxiv-overlay-api'; import { makeAutoObservable } from 'mobx'; import { Store } from '..'; import { cloneDeep } from '../../utils/lodash'; -import { getLS, setLS } from '../../utils/storage'; const cleanData: ExtendData = { isActive: false, @@ -25,21 +24,24 @@ class API { overlay = new OverlayAPI(); data: ExtendData = cleanData; historys: HistoryData[] = []; - history: HistoryData | null = null; + history = { + idx: -1, // mark current history index for active comparsion + data: null as HistoryData | null, + }; /** @mobx computed */ get active() { - return (this.history || this.data).isActive; + return (this.history.data || this.data).isActive; } get encounter() { - return (this.history || this.data).encounter; + return (this.history.data || this.data).encounter; } get lb() { - return (this.history || this.data).limitBreak; + return (this.history.data || this.data).limitBreak; } get combatant() { - return (this.history || this.data).combatant; + return (this.history.data || this.data).combatant; } /** @@ -48,12 +50,6 @@ class API { constructor(rootStore: Store) { this.rootStore = rootStore; - // load historys from storage - const historys = getLS('historys'); - if (historys && Array.isArray(historys) && historys.length <= 5) { - this.historys = historys; - } - // add overlay callback this.overlay.addListener('CombatData', (rawData) => { const data = rawData.extendData; @@ -74,34 +70,36 @@ class API { /** * show a history data (-1 to disable) */ - showHistory(index: number) { - if (index < 0 || index >= 5 || !this.historys[index]) { - this.history = null; + showHistory(idx: number) { + if (idx < 0 || idx >= 5 || !this.historys[idx]) { + this.history.idx = -1; + this.history.data = null; return; } - // direct set reference instead of deep clone, - // for Object.is() comparsion - this.history = this.historys[index]; + this.history.idx = idx; + this.history.data = this.historys[idx]; } /** * update new combat data */ updateCombat(payload: ExtendData) { this.data = payload; - this.history && (this.history = null); + // clear current history display if new data appears + if (this.history.data) { + this.history.idx = -1; + this.history.data = null; + } this.rootStore.settings.toggleShowCombatants(true); } /** * push a history (active must be false) (5 max) */ tryPushHistory(payload: ExtendData) { - // if last data (false) this data (true) which indicates a new encounter + // if last data (false) this data (true) which indicates a new battle, // push last data (false) into a new history if (lastData && !lastData.isActive && payload.isActive) { this.historys.length >= 5 && this.historys.pop(); this.historys.unshift({ time: Date.now(), ...lastData }); - // save to storage - setLS('historys', this.historys); } // record data for future use lastData = cloneDeep(payload); diff --git a/src/views/Settings.scss b/src/views/Settings.scss index fc18357..0cf7bf5 100644 --- a/src/views/Settings.scss +++ b/src/views/Settings.scss @@ -175,30 +175,35 @@ white-space: nowrap; overflow: hidden; flex: 0 0 auto; - margin-left: $padding-md; + margin-left: $padding-md; &:last-child { margin-right: $padding-md; } + } - &:first-child, - &:nth-child(3) { - flex: 0 0 auto; - width: 0.4rem; - text-align: center; - } + &-duration { + flex: 0 0 auto; + width: 0.38rem; + text-align: center; + } - &:nth-child(2) { - flex: 0 0 auto; - width: 0.6rem; - text-align: center; - } + &-time { + flex: 0 0 auto; + width: 0.58rem; + text-align: center; + } - &:last-child { - flex: 1 1 auto; - min-width: 0; - text-overflow: ellipsis; - padding: 0 $padding-sm; - } + &-zone { + flex: 1 1 auto; + text-overflow: ellipsis; + min-width: 0; + padding: 0 $padding-sm; + } + + &-dps { + display: flex; + justify-content: flex-end; + padding-right: $padding-lg; } } diff --git a/src/views/SettingsHistory.tsx b/src/views/SettingsHistory.tsx index 081976e..5f53694 100644 --- a/src/views/SettingsHistory.tsx +++ b/src/views/SettingsHistory.tsx @@ -2,23 +2,9 @@ import { observer } from 'mobx-react-lite'; import { useStore } from '../hooks'; import clsx from 'clsx'; import { fmtDuration, fmtZoneName } from '../utils/formatters'; +import { useEffect, useState } from 'react'; -function parseDate(time?: number) { - if (!time) { - return ''; - } - const d = new Date(time); - const mon = d.getMonth() + 1; - const day = d.getDate(); - const MM = mon < 10 ? `0${mon}` : mon; - const DD = day < 10 ? `0${day}` : day; - return `${MM}-${DD}`; -} - -function parseTime(time?: number) { - if (!time) { - return ''; - } +function parseTime(time: number) { const d = new Date(time); const hour = d.getHours(); const min = d.getMinutes(); @@ -32,7 +18,8 @@ function parseTime(time?: number) { interface SettingsHistoryRowProps { current: boolean; duration: string; - zone: string; + dps: number; + zoneName: string; time?: number; onClick?: () => void; } @@ -40,10 +27,17 @@ interface SettingsHistoryRowProps { function SettingsHistoryRow({ current, duration, - zone, + dps, + zoneName, time, onClick, }: SettingsHistoryRowProps) { + const [now, setNow] = useState(Date.now()); + useEffect(() => { + const timer = setInterval(() => setNow(Date.now()), 1000); + return () => clearInterval(timer); + }, []); + return (
- {parseDate(time)} - {parseTime(time)} - {fmtDuration(duration)} - {fmtZoneName(zone)} +
+ {parseTime(time || now)} +
+
+ {fmtDuration(duration)} +
+
+ {fmtZoneName(zoneName)} +
+
+ {dps} + DPS +
); } function SettingsHistory() { const { api } = useStore(); + // do not use getters here, + // since getter may returns history data when selected + const { duration, dps, zoneName } = api.data.encounter; return (
api.showHistory(-1)} /> {api.historys.map((item, idx) => { + const { duration, dps, zoneName } = item.encounter; return ( api.showHistory(idx)} /> );