Small calendar type dedupe (#4116)

* Small calendar type dedupe

* Additional cleanups
This commit is contained in:
Jaco Greeff 2020-11-30 20:53:38 +01:00 committed by GitHub
parent 31c1a887a4
commit 8881e459ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 72 additions and 68 deletions

View File

@ -1,7 +1,7 @@
// Copyright 2017-2020 @polkadot/app-calendar authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { EntryInfo } from './types';
import type { EntryInfoTyped } from './types';
import React, { useMemo } from 'react';
import styled from 'styled-components';
@ -14,23 +14,25 @@ interface Props {
hour: number;
index: number;
minutes: number;
scheduled: EntryInfo[];
scheduled: EntryInfoTyped[];
}
const MN_TO_MS = 60 * 1000;
const HR_TO_MS = 60 * MN_TO_MS;
function filterEntries (date: Date, minutes: number, index: number, scheduled: EntryInfoTyped[]): EntryInfoTyped[] {
const start = date.getTime() + (index * HR_TO_MS);
const end = start + HR_TO_MS;
const explicit = start + (minutes * MN_TO_MS);
return scheduled
.filter(({ dateTime }) => dateTime >= explicit && dateTime < end)
.sort((a, b) => (a.dateTime - b.dateTime) || a.type.localeCompare(b.type));
}
function DayHour ({ className = '', date, hour, index, minutes, scheduled }: Props): React.ReactElement<Props> | null {
const filtered = useMemo(
(): EntryInfo[] => {
const start = date.getTime() + (index * HR_TO_MS);
const end = start + HR_TO_MS;
const explicit = start + (minutes * MN_TO_MS);
return scheduled
.filter(({ dateTime }) => dateTime >= explicit && dateTime < end)
.sort((a, b) => (a.dateTime - b.dateTime) || a.type.localeCompare(b.type));
},
() => filterEntries(date, minutes, index, scheduled),
[date, index, minutes, scheduled]
);

View File

@ -1,7 +1,7 @@
// Copyright 2017-2020 @polkadot/app-calendar authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { EntryInfo } from './types';
import type { EntryInfoTyped } from './types';
import React, { useMemo, useState, useCallback } from 'react';
import styled from 'styled-components';
@ -13,7 +13,7 @@ import { useTranslation } from './translate';
interface Props {
className?: string;
item: EntryInfo;
item: EntryInfoTyped;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@ -21,41 +21,43 @@ function assertUnreachable (x: never): never {
throw new Error('We cannot get here');
}
function exportCalendar (date: Date, description: string): void {
const startDate = dateCalendarFormat(date);
// For now just add 1 hour for each event
const endDate = dateCalendarFormat(new Date(new Date(date).setHours(new Date(date).getHours() + 1)));
const calData =
'BEGIN:VCALENDAR\n' +
'CALSCALE:GREGORIAN\n' +
'METHOD:PUBLISH\n' +
'PRODID:-//Test Cal//EN\n' +
'VERSION:2.0\n' +
'BEGIN:VEVENT\n' +
'UID:test-1\n' +
'DTSTART;VALUE=DATE:' + startDate + '\n' +
'DTEND;VALUE=DATE:' + endDate + '\n' +
'SUMMARY:' + description + '\n' +
'DESCRIPTION:' + description + '\n' +
'END:VEVENT\n' +
'END:VCALENDAR';
const fileNameIcs = encodeURI(description) + '.ics';
const data = new File([calData], fileNameIcs, { type: 'text/plain' });
const anchor = window.document.createElement('a');
anchor.href = window.URL.createObjectURL(data);
anchor.download = fileNameIcs;
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
window.URL.revokeObjectURL(anchor.href);
}
function DayItem ({ className, item: { date, info, type } }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [description, setDescription] = useState<string>('');
const _exportCal = useCallback(
() => {
const startDate = dateCalendarFormat(date);
// For now just add 1 hour for each event
const endDate = dateCalendarFormat(new Date(new Date(date).setHours(new Date(date).getHours() + 1)));
const calData =
'BEGIN:VCALENDAR\n' +
'CALSCALE:GREGORIAN\n' +
'METHOD:PUBLISH\n' +
'PRODID:-//Test Cal//EN\n' +
'VERSION:2.0\n' +
'BEGIN:VEVENT\n' +
'UID:test-1\n' +
'DTSTART;VALUE=DATE:' + startDate + '\n' +
'DTEND;VALUE=DATE:' + endDate + '\n' +
'SUMMARY:' + description + '\n' +
'DESCRIPTION:' + description + '\n' +
'END:VEVENT\n' +
'END:VCALENDAR';
const fileNameIcs = encodeURI(description) + '.ics';
const data = new File([calData], fileNameIcs, { type: 'text/plain' });
const anchor = window.document.createElement('a');
anchor.href = window.URL.createObjectURL(data);
anchor.download = fileNameIcs;
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
window.URL.revokeObjectURL(anchor.href);
},
() => exportCalendar(date, description),
[description, date]
);
@ -180,7 +182,7 @@ export default React.memo(styled(DayItem)`
padding: 0;
position: absolute;
right: 1.5rem;
.ui--Icon {
width: 0.7rem;
height: 0.7rem;

View File

@ -1,7 +1,7 @@
// Copyright 2017-2020 @polkadot/app-calendar authors & contributors
// SPDX-License-Identifier: Apache-2.0
import BN from 'bn.js';
import type BN from 'bn.js';
export type EntryType = 'councilElection' | 'councilMotion' | 'democracyDispatch' | 'democracyLaunch' | 'referendumDispatch' | 'referendumVote' | 'scheduler' | 'societyChallenge' | 'societyRotate'| 'stakingEpoch' | 'stakingEra' | 'stakingSlash' | 'treasurySpend';
@ -12,6 +12,9 @@ export interface EntryInfo {
dateTime: number;
info: string | BN | null;
isPending?: boolean;
}
export interface EntryInfoTyped extends EntryInfo {
type: EntryType;
}

View File

@ -4,7 +4,7 @@
import type { DeriveCollectiveProposal, DeriveDispatch, DeriveReferendumExt, DeriveSessionProgress } from '@polkadot/api-derive/types';
import type { Option } from '@polkadot/types';
import type { BlockNumber, EraIndex, Scheduled, UnappliedSlash } from '@polkadot/types/interfaces';
import type { EntryInfo, EntryType } from './types';
import type { EntryInfo, EntryInfoTyped, EntryType } from './types';
import BN from 'bn.js';
import { useEffect, useState } from 'react';
@ -38,8 +38,7 @@ function createConstDurations (bestNumber: BlockNumber, blockTime: number, items
...newDate(blocks, blockTime),
blockNumber: bestNumber.add(blocks),
blocks,
info: null,
type
info: null
}]];
});
}
@ -58,8 +57,7 @@ function createCouncilMotions (bestNumber: BlockNumber, blockTime: number, motio
...newDate(blocks, blockTime),
blockNumber: votes.end,
blocks,
info: `${hashStr.substr(0, 6)}…${hashStr.substr(-4)}`,
type: 'councilMotion'
info: `${hashStr.substr(0, 6)}…${hashStr.substr(-4)}`
};
})
.filter((item): item is EntryInfo => !!item)
@ -74,8 +72,7 @@ function createDispatches (bestNumber: BlockNumber, blockTime: number, dispatche
...newDate(blocks, blockTime),
blockNumber: at,
blocks,
info: index,
type: 'democracyDispatch'
info: index
}]];
});
}
@ -89,16 +86,14 @@ function createReferendums (bestNumber: BlockNumber, blockTime: number, referend
...newDate(voteBlocks, blockTime),
blockNumber: bestNumber.add(voteBlocks),
blocks: voteBlocks,
info: index,
type: 'referendumVote'
info: index
}]]);
result.push(['referendumDispatch', [{
...newDate(enactBlocks, blockTime),
blockNumber: bestNumber.add(enactBlocks),
blocks: enactBlocks,
info: index,
isPending: true,
type: 'referendumDispatch'
isPending: true
}]]);
return result;
@ -121,8 +116,7 @@ function createStakingInfo (bestNumber: BlockNumber, blockTime: number, sessionI
...newDate(blocks, blockTime),
blockNumber: bestNumber.add(blocks),
blocks,
info: eraIndex,
type: 'stakingSlash'
info: eraIndex
};
})
: [];
@ -132,15 +126,13 @@ function createStakingInfo (bestNumber: BlockNumber, blockTime: number, sessionI
...newDate(blocksSes, blockTime),
blockNumber: bestNumber.add(blocksSes),
blocks: blocksSes,
info: sessionInfo.currentIndex.add(BN_ONE),
type: 'stakingEpoch'
info: sessionInfo.currentIndex.add(BN_ONE)
}]],
['stakingEra', [{
...newDate(blocksEra, blockTime),
blockNumber: bestNumber.add(blocksEra),
blocks: blocksEra,
info: sessionInfo.activeEra.add(BN_ONE),
type: 'stakingEra'
info: sessionInfo.activeEra.add(BN_ONE)
}]],
['stakingSlash', slashEras]
];
@ -167,8 +159,7 @@ function createScheduled (bestNumber: BlockNumber, blockTime: number, scheduled:
? idOrNull.isAscii
? idOrNull.toUtf8()
: idOrNull.toHex()
: null,
type: 'scheduler'
: null
});
return items;
@ -176,10 +167,16 @@ function createScheduled (bestNumber: BlockNumber, blockTime: number, scheduled:
}, [])]];
}
function addFiltered (state: EntryInfo[], types: [EntryType, EntryInfo[]][]): EntryInfo[] {
return types.reduce((state: EntryInfo[], [typeFilter, items]): EntryInfo[] =>
state.filter(({ type }) => type !== typeFilter).concat(...items), state
);
function addFiltered (state: EntryInfoTyped[], types: [EntryType, EntryInfo[]][]): EntryInfoTyped[] {
return types.reduce((state: EntryInfoTyped[], [typeFilter, items]): EntryInfoTyped[] => {
return state
.filter(({ type }) => type !== typeFilter)
.concat(...items.map((item): EntryInfoTyped => {
(item as EntryInfoTyped).type = typeFilter;
return item as EntryInfoTyped;
}));
}, state);
}
// TODO council votes, tips closing
@ -193,7 +190,7 @@ export default function useScheduled (): EntryInfo[] {
const scheduled = useCall<ScheduleEntry[]>(api.query.scheduler?.agenda.entries);
const sessionInfo = useCall<DeriveSessionProgress>(api.query.staking && api.derive.session?.progress);
const slashes = useCall<SlashEntry[]>(api.query.staking?.unappliedSlashes.entries);
const [state, setState] = useState<EntryInfo[]>([]);
const [state, setState] = useState<EntryInfoTyped[]>([]);
useEffect((): void => {
bestNumber && dispatches && setState((state) =>