Skip to content

Commit

Permalink
[WIP] implement timetable hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
noriaki committed Jun 24, 2020
1 parent e930b2d commit d771563
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 0 deletions.
79 changes: 79 additions & 0 deletions __tests__/hooks/useTimetable.test.ts
@@ -0,0 +1,79 @@
import { renderHook, act } from '@testing-library/react-hooks';

import useTimetable, { TimetableProps } from '~/hooks/useTimetable';

describe('useTimetable hooks', () => {
it('set initial timetable', () => {
const timetable = {
schedule: [0, 1, 2, 3, 4, 5, 6],
// 06:00, 06:30, 07:10, 22:50, 23:30, 00:30
data: [7200, 9000, 11400, 67800, 70200, 73800],
};
const currentTime = new Date('2020-05-28T07:09:30').getTime();
const { result } = renderHook(() => useTimetable(timetable, currentTime));
expect(result.current.index).toBe(0);
expect(result.current.remaining).toBe(30);
});

describe('時間経過で保持する時刻表データも進む', () => {
let timetable: TimetableProps;
beforeEach(() => {
timetable = {
schedule: [0, 1, 2, 3, 4, 5, 6],
// 06:00, 06:30, 07:10, 22:50, 23:30, 00:30
data: [7200, 9000, 11400, 67800, 70200, 73800],
};
});

it('発車時刻ちょうどの場合は残り時間は`0`', () => {
const currentTime = new Date('2020-05-28T07:10:00').getTime();
const { result } = renderHook(() => useTimetable(timetable, currentTime));
expect(result.current.remaining).toBe(0);
});

it('発車済みの時刻は削除される', () => {
const currentTime = new Date('2020-05-28T06:29:59').getTime();
const elapsedTime = currentTime + 2000; // 2 seconds

const { result } = renderHook(() => useTimetable(timetable, currentTime));
expect(result.current.remaining).toBe(1);

act(() => {
result.current.tick(elapsedTime);
});
expect(result.current.remaining).toBe(2399);
});

it('発車時刻を越えたとき`index`が先頭`0`であれば維持', () => {
const currentTime = new Date('2020-05-28T06:29:59').getTime();
const elapsedTime = currentTime + 2000; // 2 seconds

const { result } = renderHook(() => useTimetable(timetable, currentTime));
expect(result.current.index).toBe(0);

act(() => {
result.current.tick(elapsedTime);
});
expect(result.current.index).toBe(0);
});

it('発車時刻を越えたとき`index`が先頭`1`以上であれば`-1`される', () => {
const currentTime = new Date('2020-05-28T06:29:59').getTime();
const elapsedTime = currentTime + 2000; // 2 seconds

const { result } = renderHook(() => useTimetable(timetable, currentTime));
expect(result.current.index).toBe(0);

act(() => {
result.current.next();
result.current.next();
});
expect(result.current.index).toBe(2);

act(() => {
result.current.tick(elapsedTime);
});
expect(result.current.index).toBe(1);
});
});
});
84 changes: 84 additions & 0 deletions src/hooks/useTimetable.ts
@@ -0,0 +1,84 @@
import { useState, useCallback, useRef } from 'react';

export type TimetableProps = {
schedule: number[];
data: number[];
};

export type TimetableHook = {
index: number;
value: number;
tick: (nextTime: number) => void;
next: () => void;
remaining: number;
};

const getSecondsSince4am: (time: number) => number = (time) => {
const d = new Date(time);
const hours = (d.getHours() - 4) * 60 * 60;
const minutes = d.getMinutes() * 60;
const seconds = d.getSeconds();
return hours + minutes + seconds;
};

const getSlicedData: (data: number[], time: number) => number[] = (
data,
time,
) => {
const currentTimeAsSeconds = getSecondsSince4am(time);
return data.filter((s) => s >= currentTimeAsSeconds);
};

const useTimetable = (
timetable: TimetableProps,
currentTime: number = Date.now(),
): TimetableHook => {
const time = useRef(currentTime);
const [currentIndex, setIndex] = useState(0);
const [currentData, setData] = useState(
getSlicedData(timetable.data, time.current),
);

const value = currentData[currentIndex];
const remaining = value - getSecondsSince4am(time.current);

const next = useCallback(() => {
setIndex((prevIndex) => {
if (prevIndex >= currentData.length - 1) {
return currentData.length - 1;
}
return prevIndex + 1;
});
}, [currentData.length]);

const prev = useCallback(() => {
setIndex((prevIndex) => {
if (prevIndex <= 0) {
return 0;
}
return prevIndex - 1;
});
}, []);

const tick = useCallback<TimetableHook['tick']>(
(nextTime) => {
time.current = nextTime;
const nextData = getSlicedData(timetable.data, time.current);
if (currentData.length !== nextData.length) {
prev();
}
setData(nextData);
},
[timetable.data, currentData.length, prev],
);

return {
index: currentIndex,
value,
tick,
next,
remaining,
};
};

export default useTimetable;

0 comments on commit d771563

Please sign in to comment.