Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Implements front-end looping via query param #24

Merged
merged 2 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/Http/Middleware/HandleInertiaRequests.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ public function share(Request $request): array
{
$index = intval(request()->input('index'));
$progress = request()->input('progress');
$loop = intval(request()->input('loop'));

return [
...parent::share($request),
'index' => $index,
'progress' => $progress,
'loop' => $loop,
'auth' => [
'user' => $request->user(),
],
Expand Down
40 changes: 35 additions & 5 deletions resources/js/Components/SlideView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import Keys from '@/constants/keys.ts';
import dataStore from '@/store/dataStore.ts'
import slideStore from '@/store/slideStore.ts'

const content = computed(() => {
return dataStore.data[slideStore.index];
});

let loopInterval: null | ReturnType<typeof setInterval> = null;
let fontLoadInterval: null | ReturnType<typeof setInterval> = null;
const fontLoaded = ref(false);
const FONT = '16px Montserrat';

const content = computed(() => {
return dataStore.data[slideStore.index];
});

const showProgressLabel = computed<boolean>(() => {
return slideStore.progress === ProgressType.Label;
});
Expand All @@ -35,6 +36,14 @@ const buildQueryParams = () : QueryParams => {
return query;
};

const checkAndClearLoopInterval = () : void => {
if (!loopInterval) {
return;
}

clearInterval(Number(loopInterval));
};

const incrementContent = (count: number) : void => {
slideStore.increment(count);

Expand All @@ -58,6 +67,10 @@ const bindKeyDown = (event: KeyboardEvent): void => {
}
}

if (Keys.ALL_APP_KEYS.includes(key)) {
checkAndClearLoopInterval();
}

if (Keys.INCREMENTORS.includes(key)) {
incrementContent(1);
} else if (Keys.DECREMENTORS.includes(key)) {
Expand Down Expand Up @@ -86,8 +99,25 @@ onMounted(() => {
fontLoaded.value = true;
clearInterval(Number(fontLoadInterval));
}, 50);

if (!slideStore.canLoop()) {
return;
}

loopInterval = setInterval(() => {
if (slideStore.isEnd()) {
incrementContent(-1 * dataStore.data.length);
return;
}

incrementContent(1)
}, slideStore.loop * 1000);
});

onUnmounted(() => {
window.removeEventListener('keydown', bindKeyDown);
checkAndClearLoopInterval();
});
onUnmounted(() => window.removeEventListener('keydown', bindKeyDown));
</script>

<template>
Expand Down
2 changes: 2 additions & 0 deletions resources/js/Pages/Slides.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import slideStore from '@/store/slideStore.ts'

const props = defineProps<{
index?: number,
loop?: number,
progress?: ProgressType,
slides?: string
content?: string
}>();

const processQueryParams = (): void => {
slideStore.index = props.index ?? 0;
slideStore.loop = props.loop ?? 0;
slideStore.progress = props.progress ?? ProgressType.Bar;
};

Expand Down
10 changes: 10 additions & 0 deletions resources/js/constants/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ export const LARGE_DECREMENTORS = [
'B',
];

export const ALL_APP_KEYS = [
...INCREMENTORS,
...DECREMENTORS,
...LARGE_INCREMENTORS,
...LARGE_DECREMENTORS,
DOLLAR_SIGN,
ZERO,
];

export default {
ENTER,
SPACE,
Expand All @@ -43,4 +52,5 @@ export default {
DECREMENTORS,
LARGE_INCREMENTORS,
LARGE_DECREMENTORS,
ALL_APP_KEYS
};
13 changes: 13 additions & 0 deletions resources/js/store/slideStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import dataStore from './dataStore.ts'

const slideStore = reactive({
index: 0,
loop: 0,
progress: ProgressType.Bar,

getNewIndex(count: number) : number {
Expand All @@ -29,6 +30,18 @@ const slideStore = reactive({
slideStore.index = newIndex;
},

isEnd() : boolean {
return this.index === dataStore.data.length - 1;
},

canLoop() : boolean {
if (this.loop < 2) {
return false;
}

return true;
},

reset() {
this.index = 0;
this.progress = ProgressType.Bar;
Expand Down
53 changes: 53 additions & 0 deletions resources/js/test/components/SlideView.loop.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { mount, VueWrapper } from '@vue/test-utils'

import SlideView from '@/Components/SlideView.vue'
import Keys from '@/constants/keys.ts';
import ProgressType from '@/enums/progressType.ts'
import dataStore from '@/store/dataStore.ts'
import slideStore from '@/store/slideStore.ts'

// Incrementors

const mountWrapper = () : VueWrapper<any> => {
dataStore.data = ['foo', 'bar', 'baz', 'foo', 'bar', 'baz', 'foo', 'bar'];
slideStore.index = 1;
slideStore.loop = 5;
slideStore.progress = ProgressType.Bar;

return mount(SlideView);
};

test('loopInterval is not null with valid loop set', async () => {
const wrapper = mountWrapper();

expect(wrapper.vm.loopInterval).not.toBe(null);
});

test('loopInterval is cleared with valid loop set', async () => {
const wrapper = mountWrapper();
const spy = vi.spyOn(global, 'clearInterval');

wrapper.vm.checkAndClearLoopInterval();

expect(spy).toHaveBeenCalledTimes(1);
expect(spy.mock.lastCall?.[0]).toBe(Number(wrapper.vm.loopInterval));
});

test('loopInterval is cleared on valid key press', async () => {
const wrapper = mountWrapper();
const spy = vi.spyOn(global, 'clearInterval');

wrapper.vm.bindKeyDown({ key: Keys.ENTER });

expect(spy).toHaveBeenCalledTimes(1);
expect(spy.mock.lastCall?.[0]).toBe(Number(wrapper.vm.loopInterval));
});

test('loopInterval is not cleared on invalid key press', async () => {
const wrapper = mountWrapper();
const spy = vi.spyOn(global, 'clearInterval');

wrapper.vm.bindKeyDown({ key: 'x' });

expect(spy).toHaveBeenCalledTimes(0);
});
32 changes: 30 additions & 2 deletions resources/js/test/store/slideStore.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import ProgressType from '@/enums/progressType.ts'
import dataStore from '@/store/dataStore.ts'
import slideStore from '@/store/slideStore.ts'

afterEach(() => {
slideStore.reset()
})
dataStore.reset();
slideStore.reset();
});

test('gets the right initial values', async () => {
expect(slideStore.index).toBe(0);
Expand All @@ -30,3 +32,29 @@ test('will not change slide store if no new index', async () => {

expect(slideStore.index).toBe(0);
});

test('returns true for isEnd if slide index is at the end', async () => {
dataStore.data = ['a', 'b', 'c'];
slideStore.index = 2;

expect(slideStore.isEnd()).toBe(true);
});

test('returns false for isEnd if slide index is not at the end', async () => {
dataStore.data = ['a', 'b', 'c'];
slideStore.index = 1;

expect(slideStore.isEnd()).toBe(false);
});

test('returns true for canLoop if loop value is valid', async () => {
slideStore.loop = 5;

expect(slideStore.canLoop()).toBe(true);
});

test('returns false for canLoop if loop value is invalid', async () => {
slideStore.loop = 1;

expect(slideStore.canLoop()).toBe(false);
});
Loading