Fix date formatting on language change
This commit is contained in:
		
							parent
							
								
									ed3433f51b
								
							
						
					
					
						commit
						11bbad49fd
					
				@ -46,6 +46,7 @@
 | 
			
		||||
    "react-dev-utils": "^10.2.1",
 | 
			
		||||
    "react-dom": "^16.13.1",
 | 
			
		||||
    "react-i18next": "^11.7.0",
 | 
			
		||||
    "react-moment": "^0.9.7",
 | 
			
		||||
    "react-redux": "^7.2.0",
 | 
			
		||||
    "react-router-dom": "^5.2.0",
 | 
			
		||||
    "redux": "^4.0.5",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										42
									
								
								src/app.tsx
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								src/app.tsx
									
									
									
									
									
								
							@ -1,23 +1,27 @@
 | 
			
		||||
import React, { Dispatch, HTMLProps, useEffect } from 'react';
 | 
			
		||||
import React, { HTMLProps, useEffect } from 'react';
 | 
			
		||||
import { Link, Route, Switch } from "react-router-dom"
 | 
			
		||||
import moment from "moment";
 | 
			
		||||
import { route, routes } from "@/routing";
 | 
			
		||||
import { useDispatch, useSelector } from "react-redux";
 | 
			
		||||
import { AppState } from "@/state/reducer";
 | 
			
		||||
import { StudentAction, StudentActions } from "@/state/actions/student";
 | 
			
		||||
import { useSelector } from "react-redux";
 | 
			
		||||
import { AppState, isReady } from "@/state/reducer";
 | 
			
		||||
import { StudentActions } from "@/state/actions/student";
 | 
			
		||||
import { sampleStudent } from "@/provider/dummy/student";
 | 
			
		||||
import { Trans, useTranslation } from "react-i18next";
 | 
			
		||||
import { Student } from "@/data";
 | 
			
		||||
import '@/styles/overrides.scss'
 | 
			
		||||
import '@/styles/header.scss'
 | 
			
		||||
import classNames from "classnames";
 | 
			
		||||
import { EditionAction, EditionActions } from "@/state/actions/edition";
 | 
			
		||||
import { EditionActions } from "@/state/actions/edition";
 | 
			
		||||
import { sampleEdition } from "@/provider/dummy/edition";
 | 
			
		||||
import { Edition } from "@/data/edition";
 | 
			
		||||
import { SettingActions } from "@/state/actions/settings";
 | 
			
		||||
import { useDispatch } from "@/state/actions";
 | 
			
		||||
import { getLocale, Locale } from "@/state/reducer/settings";
 | 
			
		||||
import i18n from "@/i18n";
 | 
			
		||||
import moment from "moment";
 | 
			
		||||
 | 
			
		||||
const UserMenu = (props: HTMLProps<HTMLUListElement>) => {
 | 
			
		||||
    const student = useSelector<AppState, Student>(state => state.student as Student);
 | 
			
		||||
    const dispatch = useDispatch<Dispatch<StudentAction>>();
 | 
			
		||||
    const dispatch = useDispatch();
 | 
			
		||||
    const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
    const handleUserLogin = () => {
 | 
			
		||||
@ -47,17 +51,17 @@ const UserMenu = (props: HTMLProps<HTMLUListElement>) => {
 | 
			
		||||
const LanguageSwitcher = ({ className, ...props }: HTMLProps<HTMLUListElement>) => {
 | 
			
		||||
    const { i18n } = useTranslation();
 | 
			
		||||
 | 
			
		||||
    const handleLanguageChange = (language: string) => () => {
 | 
			
		||||
        i18n.changeLanguage(language);
 | 
			
		||||
        document.documentElement.lang = language;
 | 
			
		||||
        moment.locale(language)
 | 
			
		||||
    const dispatch = useDispatch();
 | 
			
		||||
 | 
			
		||||
    const handleLanguageChange = (language: Locale) => () => {
 | 
			
		||||
        dispatch({ type: SettingActions.SetLocale, locale: language })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const isActive = (language: string) => language.toLowerCase() === i18n.language.toLowerCase();
 | 
			
		||||
 | 
			
		||||
    return <ul className={ classNames(className, "language-switcher") } { ...props }>
 | 
			
		||||
        { ['pl', 'en'].map(language => <li key={ language }>
 | 
			
		||||
            <Link to="#" onClick={ handleLanguageChange(language) }
 | 
			
		||||
            <Link to="#" onClick={ handleLanguageChange(language as Locale) }
 | 
			
		||||
                  className={ classNames("language-switcher__language", isActive(language) && "language-switcher__language--active") }>
 | 
			
		||||
                { language }
 | 
			
		||||
            </Link>
 | 
			
		||||
@ -66,16 +70,24 @@ const LanguageSwitcher = ({ className, ...props }: HTMLProps<HTMLUListElement>)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function App() {
 | 
			
		||||
    const dispatch = useDispatch<Dispatch<EditionAction>>();
 | 
			
		||||
    const dispatch = useDispatch();
 | 
			
		||||
    const edition = useSelector<AppState, Edition | null>(state => state.edition);
 | 
			
		||||
 | 
			
		||||
    const locale = useSelector<AppState, Locale>(state => getLocale(state.settings));
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        if (!edition) {
 | 
			
		||||
            dispatch({ type: EditionActions.Set, edition: sampleEdition });
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const isReady = !!edition;
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        i18n.changeLanguage(locale);
 | 
			
		||||
        document.documentElement.lang = locale;
 | 
			
		||||
        moment.locale(locale)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const ready = useSelector(isReady);
 | 
			
		||||
 | 
			
		||||
    return <>
 | 
			
		||||
        <header className="header">
 | 
			
		||||
@ -96,7 +108,7 @@ function App() {
 | 
			
		||||
                </nav>
 | 
			
		||||
            </div>
 | 
			
		||||
        </header>
 | 
			
		||||
        { isReady && <Switch>{ routes.map(({ name, content, ...route }) => <Route { ...route } key={ name }>{ content() }</Route>) }</Switch> }
 | 
			
		||||
        { ready && <Switch>{ routes.map(({ name, content, ...route }) => <Route { ...route } key={ name }>{ content() }</Route>) }</Switch> }
 | 
			
		||||
    </>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								src/i18n.ts
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								src/i18n.ts
									
									
									
									
									
								
							@ -2,10 +2,9 @@ import i18n from "i18next";
 | 
			
		||||
import { initReactI18next } from "react-i18next";
 | 
			
		||||
import I18nextBrowserLanguageDetector from "i18next-browser-languagedetector";
 | 
			
		||||
 | 
			
		||||
import moment from "moment";
 | 
			
		||||
 | 
			
		||||
import "moment/locale/pl"
 | 
			
		||||
import "moment/locale/en-gb"
 | 
			
		||||
import moment, { isDuration, isMoment } from "moment";
 | 
			
		||||
 | 
			
		||||
const resources = {
 | 
			
		||||
    en: {
 | 
			
		||||
@ -21,9 +20,20 @@ i18n
 | 
			
		||||
    .use(initReactI18next)
 | 
			
		||||
    .init({
 | 
			
		||||
        resources,
 | 
			
		||||
        fallbackLng: "en",
 | 
			
		||||
        fallbackLng: "pl",
 | 
			
		||||
        interpolation: {
 | 
			
		||||
            escapeValue: false
 | 
			
		||||
            escapeValue: false,
 | 
			
		||||
            format: (value, format, lng) => {
 | 
			
		||||
                if (isMoment(value)) {
 | 
			
		||||
                    return value.locale(lng || "pl").format(format || "DD MMM YYYY");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (isDuration(value)) {
 | 
			
		||||
                    return value.locale(lng || "pl").humanize();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return value;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -31,10 +31,10 @@ const Step = ({ until, label, completedOn, children, completed, ...props }: Step
 | 
			
		||||
            { label }
 | 
			
		||||
            { until && <Box>
 | 
			
		||||
                <Typography variant="subtitle2" color="textSecondary">
 | 
			
		||||
                    { t('until', { date: until.format("DD MMMM YYYY") }) }
 | 
			
		||||
                    { t('until', { date: until }) }
 | 
			
		||||
                    { isLate && <Typography color="error" display="inline"
 | 
			
		||||
                                            variant="body2"> - { t('late', { by: moment.duration(now.diff(until)).humanize() }) }</Typography> }
 | 
			
		||||
                    { !isLate && !completed && <Typography display="inline" variant="body2"> - { t('left', { left: left.humanize() }) }</Typography> }
 | 
			
		||||
                                            variant="body2"> - { t('late', { by: moment.duration(now.diff(until)) }) }</Typography> }
 | 
			
		||||
                    { !isLate && !completed && <Typography display="inline" variant="body2"> - { t('left', { left: left }) }</Typography> }
 | 
			
		||||
                </Typography>
 | 
			
		||||
            </Box> }
 | 
			
		||||
        </StepLabel>
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,20 @@
 | 
			
		||||
import { StudentAction, StudentActions } from "@/state/actions/student";
 | 
			
		||||
import { EditionAction, EditionActions } from "@/state/actions/edition";
 | 
			
		||||
import { SettingActions, SettingsAction } from "@/state/actions/settings";
 | 
			
		||||
import { Dispatch } from "react";
 | 
			
		||||
 | 
			
		||||
import { useDispatch as useReduxDispatch } from "react-redux";
 | 
			
		||||
 | 
			
		||||
export * from "./base"
 | 
			
		||||
export * from "./edition"
 | 
			
		||||
export * from "./settings"
 | 
			
		||||
export * from "./student"
 | 
			
		||||
 | 
			
		||||
export type Action = StudentAction | EditionAction | SettingsAction;
 | 
			
		||||
 | 
			
		||||
export const Actions = { ...StudentActions, ...EditionActions, ...SettingActions }
 | 
			
		||||
export type Actions = typeof Actions;
 | 
			
		||||
 | 
			
		||||
export const useDispatch = () => useReduxDispatch<Dispatch<Action>>()
 | 
			
		||||
 | 
			
		||||
export default Actions;
 | 
			
		||||
							
								
								
									
										12
									
								
								src/state/actions/settings.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/state/actions/settings.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
import { Action } from "@/state/actions/base";
 | 
			
		||||
import { Locale } from "@/state/reducer/settings";
 | 
			
		||||
 | 
			
		||||
export enum SettingActions {
 | 
			
		||||
    SetLocale = "SET_LOCALE",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface SetLocaleAction extends Action<SettingActions.SetLocale> {
 | 
			
		||||
    locale: Locale
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type SettingsAction = SetLocaleAction;
 | 
			
		||||
@ -2,12 +2,16 @@ import { combineReducers } from "redux";
 | 
			
		||||
 | 
			
		||||
import studentReducer from "./student"
 | 
			
		||||
import editionReducer from "@/state/reducer/edition";
 | 
			
		||||
import settingsReducer from "@/state/reducer/settings";
 | 
			
		||||
 | 
			
		||||
const rootReducer = combineReducers({
 | 
			
		||||
    student: studentReducer,
 | 
			
		||||
    edition: editionReducer,
 | 
			
		||||
    settings: settingsReducer,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export type AppState = ReturnType<typeof rootReducer>;
 | 
			
		||||
 | 
			
		||||
export default rootReducer;
 | 
			
		||||
 | 
			
		||||
export const isReady = (state: AppState) => !!state.edition;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								src/state/reducer/settings.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/state/reducer/settings.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
import { SettingActions, SettingsAction } from "@/state/actions/settings";
 | 
			
		||||
 | 
			
		||||
export type Locale = "pl" | "en"
 | 
			
		||||
 | 
			
		||||
export type SettingsState = {
 | 
			
		||||
    locale: Locale
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const defaultSettingsState: SettingsState = {
 | 
			
		||||
    locale: "pl",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const settingsReducer = (state: SettingsState = defaultSettingsState, action: SettingsAction): SettingsState => {
 | 
			
		||||
    switch (action.type) {
 | 
			
		||||
        case SettingActions.SetLocale:
 | 
			
		||||
            return { ...state, locale: action.locale }
 | 
			
		||||
        default:
 | 
			
		||||
            return state;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default settingsReducer;
 | 
			
		||||
 | 
			
		||||
export const getLocale = (state: SettingsState): Locale => state.locale;
 | 
			
		||||
@ -3,9 +3,9 @@ login: login
 | 
			
		||||
logout: logout
 | 
			
		||||
logged-in-as: logged in as <1>{{ name }}</1>
 | 
			
		||||
 | 
			
		||||
until: until {{ date }}
 | 
			
		||||
late: late by {{ by }}
 | 
			
		||||
left: '{{ left }} left'
 | 
			
		||||
until: until {{ date, DD MMMM YYYY }}
 | 
			
		||||
late: late by {{ by, humanize }}
 | 
			
		||||
left: '{{ left, humanize }} left'
 | 
			
		||||
 | 
			
		||||
dropzone: "Drag and drop a file here or click to choose"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,9 +3,9 @@ login: zaloguj się
 | 
			
		||||
logout: wyloguj się
 | 
			
		||||
logged-in-as: zalogowany jako <1>{{ name }}</1>
 | 
			
		||||
 | 
			
		||||
until: do {{ date }}
 | 
			
		||||
late: '{{ by }} spóźnienia'
 | 
			
		||||
left: jeszcze {{ left }}
 | 
			
		||||
until: do {{ date, DD MMMM YYYY }}
 | 
			
		||||
late: '{{ by, humanize }} spóźnienia'
 | 
			
		||||
left: jeszcze {{ left, humanize }}
 | 
			
		||||
 | 
			
		||||
confirm: zatwierdź
 | 
			
		||||
go-back: wstecz
 | 
			
		||||
 | 
			
		||||
@ -7408,6 +7408,11 @@ react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
 | 
			
		||||
  integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
 | 
			
		||||
 | 
			
		||||
react-moment@^0.9.7:
 | 
			
		||||
  version "0.9.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/react-moment/-/react-moment-0.9.7.tgz#ca570466595b1aa4f7619e62da18b3bb2de8b6f3"
 | 
			
		||||
  integrity sha512-ifzUrUGF6KRsUN2pRG5k56kO0mJBr8kRkWb0wNvtFIsBIxOuPxhUpL1YlXwpbQCbHq23hUu6A0VEk64HsFxk9g==
 | 
			
		||||
 | 
			
		||||
react-redux@^7.2.0:
 | 
			
		||||
  version "7.2.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d"
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user