From ebd4b76a85590a08445aec91c20018d5cc52939d Mon Sep 17 00:00:00 2001 From: rjwats Date: Fri, 29 Jan 2021 23:42:00 +0000 Subject: [PATCH] Reduce UI artefact size by removing moment.js Remove moment.js dependency --- interface/.env.development | 4 +-- interface/package-lock.json | 10 +++--- interface/package.json | 2 +- interface/src/ntp/NTPStatusForm.tsx | 37 +++++++++++----------- interface/src/ntp/TimeFormat.ts | 46 ++++++++++++++++++++++++++-- interface/src/ntp/types.ts | 6 ++-- lib/framework/NTPSettingsService.cpp | 4 +-- lib/framework/NTPStatus.cpp | 23 +++++++++++--- 8 files changed, 92 insertions(+), 40 deletions(-) diff --git a/interface/.env.development b/interface/.env.development index b12cfd0..949cce6 100644 --- a/interface/.env.development +++ b/interface/.env.development @@ -1,4 +1,4 @@ # Change the IP address to that of your ESP device to enable local development of the UI. # Remember to also enable CORS in platformio.ini before uploading the code to the device. -REACT_APP_HTTP_ROOT=http://192.168.0.88 -REACT_APP_WEB_SOCKET_ROOT=ws://192.168.0.88 +REACT_APP_HTTP_ROOT=http://192.168.0.24 +REACT_APP_WEB_SOCKET_ROOT=ws://192.168.0.24 diff --git a/interface/package-lock.json b/interface/package-lock.json index 19b41e6..a7a2438 100644 --- a/interface/package-lock.json +++ b/interface/package-lock.json @@ -10309,11 +10309,6 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, - "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" - }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -10984,6 +10979,11 @@ "lines-and-columns": "^1.1.6" } }, + "parse-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", + "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==" + }, "parse5": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", diff --git a/interface/package.json b/interface/package.json index 2cf702a..ff73548 100644 --- a/interface/package.json +++ b/interface/package.json @@ -16,8 +16,8 @@ "jwt-decode": "^3.1.2", "lodash": "^4.17.20", "mime-types": "^2.1.28", - "moment": "^2.29.1", "notistack": "^1.0.3", + "parse-ms": "^2.1.0", "react": "^17.0.1", "react-dom": "^17.0.1", "react-dropzone": "^11.2.4", diff --git a/interface/src/ntp/NTPStatusForm.tsx b/interface/src/ntp/NTPStatusForm.tsx index 6b277dd..30a3cd9 100644 --- a/interface/src/ntp/NTPStatusForm.tsx +++ b/interface/src/ntp/NTPStatusForm.tsx @@ -1,5 +1,4 @@ import React, { Component, Fragment } from 'react'; -import moment from 'moment'; import { WithTheme, withTheme } from '@material-ui/core/styles'; import { Avatar, Divider, List, ListItem, ListItemAvatar, ListItemText, Button } from '@material-ui/core'; @@ -14,7 +13,7 @@ import RefreshIcon from '@material-ui/icons/Refresh'; import { RestFormProps, FormButton, HighlightAvatar } from '../components'; import { isNtpActive, ntpStatusHighlight, ntpStatus } from './NTPStatus'; -import { formatIsoDateTime, formatLocalDateTime } from './TimeFormat'; +import { formatDuration, formatDateTime, formatLocalDateTime } from './TimeFormat'; import { NTPStatus, Time } from './types'; import { redirectingAuthorizedFetch, withAuthenticatedContext, AuthenticatedContextProps } from '../authentication'; import { TIME_ENDPOINT } from '../api'; @@ -39,34 +38,34 @@ class NTPStatusForm extends Component { } updateLocalTime = (event: React.ChangeEvent) => { - this.setState({ localTime: event.target.value }); + this.setState({ + localTime: event.target.value + }); } openSetTime = () => { - this.setState({ localTime: formatLocalDateTime(moment()), settingTime: true, }); + this.setState({ + localTime: formatLocalDateTime(new Date()), + settingTime: true + }); } closeSetTime = () => { - this.setState({ settingTime: false }); + this.setState({ + settingTime: false + }); } - createAdjustedTime = (): Time => { - const currentLocalTime = moment(this.props.data.time_local); - const newLocalTime = moment(this.state.localTime); - newLocalTime.subtract(currentLocalTime.utcOffset()) - newLocalTime.milliseconds(0); - newLocalTime.utc(); - return { - time_utc: newLocalTime.format() - } - } + createTime = (): Time => ({ + local_time: formatLocalDateTime(new Date(this.state.localTime)) + }); configureTime = () => { this.setState({ processing: true }); redirectingAuthorizedFetch(TIME_ENDPOINT, { method: 'POST', - body: JSON.stringify(this.createAdjustedTime()), + body: JSON.stringify(this.createTime()), headers: { 'Content-Type': 'application/json' } @@ -153,7 +152,7 @@ class NTPStatusForm extends Component { - + @@ -162,7 +161,7 @@ class NTPStatusForm extends Component { - + @@ -171,7 +170,7 @@ class NTPStatusForm extends Component { - + diff --git a/interface/src/ntp/TimeFormat.ts b/interface/src/ntp/TimeFormat.ts index 7e0bb82..8637711 100644 --- a/interface/src/ntp/TimeFormat.ts +++ b/interface/src/ntp/TimeFormat.ts @@ -1,5 +1,45 @@ -import moment, { Moment } from 'moment'; +import parseMilliseconds from 'parse-ms'; -export const formatIsoDateTime = (isoDateString: string) => moment.parseZone(isoDateString).format('ll @ HH:mm:ss'); +const LOCALE_FORMAT = new Intl.DateTimeFormat( + [...window.navigator.languages], + { + day: 'numeric', + month: 'short', + year: 'numeric', + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + hour12: false + } +); -export const formatLocalDateTime = (moment: Moment) => moment.format('YYYY-MM-DDTHH:mm'); +export const formatDateTime = (dateTime: string) => { + return LOCALE_FORMAT.format(new Date(dateTime.substr(0, 19))); +} + +export const formatLocalDateTime = (date: Date) => { + return new Date(date.getTime() - date.getTimezoneOffset() * 60000) + .toISOString() + .slice(0, -1) + .substr(0, 19); +} + +export const formatDuration = (duration: number) => { + const { days, hours, minutes, seconds } = parseMilliseconds(duration * 1000); + var formatted = ''; + if (days) { + formatted += pluralize(days, 'day'); + } + if (formatted || hours) { + formatted += pluralize(hours, 'hour'); + } + if (formatted || minutes) { + formatted += pluralize(minutes, 'minute'); + } + if (formatted || seconds) { + formatted += pluralize(seconds, 'second'); + } + return formatted; +} + +const pluralize = (count: number, noun: string, suffix: string = 's') => ` ${count} ${noun}${count !== 1 ? suffix : ''} `; diff --git a/interface/src/ntp/types.ts b/interface/src/ntp/types.ts index a266d12..06c87ef 100644 --- a/interface/src/ntp/types.ts +++ b/interface/src/ntp/types.ts @@ -5,8 +5,8 @@ export enum NTPSyncStatus { export interface NTPStatus { status: NTPSyncStatus; - time_utc: string; - time_local: string; + utc_time: string; + local_time: string; server: string; uptime: number; } @@ -19,5 +19,5 @@ export interface NTPSettings { } export interface Time { - time_utc: string; + local_time: string; } diff --git a/lib/framework/NTPSettingsService.cpp b/lib/framework/NTPSettingsService.cpp index 420db7f..8940a73 100644 --- a/lib/framework/NTPSettingsService.cpp +++ b/lib/framework/NTPSettingsService.cpp @@ -73,9 +73,9 @@ void NTPSettingsService::configureNTP() { void NTPSettingsService::configureTime(AsyncWebServerRequest* request, JsonVariant& json) { if (!sntp_enabled() && json.is()) { - String timeUtc = json["time_utc"]; struct tm tm = {0}; - char* s = strptime(timeUtc.c_str(), "%Y-%m-%dT%H:%M:%SZ", &tm); + String timeLocal = json["local_time"]; + char* s = strptime(timeLocal.c_str(), "%Y-%m-%dT%H:%M:%S", &tm); if (s != nullptr) { time_t time = mktime(&tm); struct timeval now = {.tv_sec = time}; diff --git a/lib/framework/NTPStatus.cpp b/lib/framework/NTPStatus.cpp index 2275d2f..00dd31c 100644 --- a/lib/framework/NTPStatus.cpp +++ b/lib/framework/NTPStatus.cpp @@ -7,12 +7,25 @@ NTPStatus::NTPStatus(AsyncWebServer* server, SecurityManager* securityManager) { AuthenticationPredicates::IS_AUTHENTICATED)); } -String toISOString(tm* time, bool incOffset) { +/* + * Formats the time using the format provided. + * + * Uses a 25 byte buffer, large enough to fit an ISO time string with offset. + */ +String formatTime(tm* time, const char* format) { char time_string[25]; - strftime(time_string, 25, incOffset ? "%FT%T%z" : "%FT%TZ", time); + strftime(time_string, 25, format, time); return String(time_string); } +String toUTCTimeString(tm* time) { + return formatTime(time, "%FT%TZ"); +} + +String toLocalTimeString(tm* time) { + return formatTime(time, "%FT%T"); +} + void NTPStatus::ntpStatus(AsyncWebServerRequest* request) { AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_NTP_STATUS_SIZE); JsonObject root = response->getRoot(); @@ -24,10 +37,10 @@ void NTPStatus::ntpStatus(AsyncWebServerRequest* request) { root["status"] = sntp_enabled() ? 1 : 0; // the current time in UTC - root["time_utc"] = toISOString(gmtime(&now), false); + root["utc_time"] = toUTCTimeString(gmtime(&now)); - // local time as ISO String with TZ - root["time_local"] = toISOString(localtime(&now), true); + // local time with offset + root["local_time"] = toLocalTimeString(localtime(&now)); // the sntp server name root["server"] = sntp_getservername(0);