Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

refactor: add debug logging and improve some technical things #133

Merged
merged 1 commit into from
Jul 7, 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
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"**/*.code-search": true,
"**/.cache": true,
"**/dist": true,
"**/.angular": true,
"**/coverage": true,
"pnpm-lock.yaml": true
},
Expand Down
3 changes: 2 additions & 1 deletion src/client/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"project": "./tsconfig.json"
},
"rules": {
"@angular-eslint/prefer-on-push-component-change-detection": "error"
"@angular-eslint/prefer-on-push-component-change-detection": "error",
"no-console": "error"
}
}
5 changes: 3 additions & 2 deletions src/client/src/app/+state/action-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ export type HttpActionCreator<
error: _ActionCreatorWithResponse<`[${TScope}] [Error] ${TName}`, TProps, unknown>;
};

type x = HttpActionCreator<'a', 'b', { a: string }, { b: number }>;

export function createHttpAction<TProps extends object, TSuccess = unknown>() {
return <TScope extends string, TName extends string>(scope: TScope, name: TName) => {
const action = createAction(`[${scope}] ${name}`, creator<TProps>());
Expand All @@ -97,6 +95,7 @@ export function handleHttpAction<
TActionStateName extends TInferredState extends { actionStates: Record<string, ActionState> }
? keyof TInferredState['actionStates']
: never,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
TAction extends HttpActionCreator<string, string, any, any>,
TProps = Parameters<TAction>[0],
TInferredState = TState,
Expand Down Expand Up @@ -134,12 +133,14 @@ export function handleHttpAction<
} else if (props.type === action.success.type) {
actionStates[actionStateName as string] = successActionState;
} else if (props.type === action.error.type) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
actionStates[actionStateName as string] = errorActionState((props as any).response);
}
})
);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function onHttpAction<T extends HttpActionCreator<string, string, any, any>>(
action: T,
actionStateSelector?: Selector<object, ActionState>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ export const loadEventsReducers: Reducers<EventsFeatureState> = [
})
)
),
handleHttpAction('load', loadEventsAction, { condition: (s, p) => !p.silent }),
handleHttpAction('load', loadEventsAction, {
condition: (s, p) => !p.silent,
startCondition: (s, p) => s.actionStates.load.state === 'none' || p.reload === true,
}),
];

export const loadEventsEffects: Effects = {
Expand Down
15 changes: 11 additions & 4 deletions src/client/src/app/+state/events/events.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,19 @@ export function selectEventsActionState(action: keyof EventsFeatureState['action
return createDistinctSelector(selectEventsActionStates, state => state[action]);
}

export function selectEvent(id: string) {
return createDistinctSelector(selectEventsFeature, state => state.entities[id]);
export function selectEvent(id: string | null | undefined) {
return createDistinctSelector(selectEventsFeature, state =>
id ? state.entities[id] ?? null : null
);
}

export function selectEventTimeslot(eventId: string, timeslotId: string) {
export function selectEventTimeslot(
eventId: string | null | undefined,
timeslotId: string | null | undefined
) {
return createDistinctSelector(selectEventsFeature, state =>
state.entities[eventId]?.timeslots.find(x => x.id === timeslotId)
eventId && timeslotId
? state.entities[eventId]?.timeslots.find(x => x.id === timeslotId) ?? null
: null
);
}
30 changes: 22 additions & 8 deletions src/client/src/app/+state/events/events.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,35 @@ export function keepEventsLoaded(options?: OptionalInjector) {
.subscribe(() => store.dispatch(loadEventsAction({ reload: true, silent: true })));
}

export function keepEventLoaded(eventId: Signal<string>, options?: OptionalInjector) {
export function keepEventLoaded(
eventId: Signal<string | null | undefined>,
options?: OptionalInjector
) {
const store = injectEx(Store, options);

effect(() => store.dispatch(loadEventAction({ eventId: eventId(), reload: false })), {
...options,
allowSignalWrites: true,
});
effect(
() => {
const id = eventId();
if (id) {
store.dispatch(loadEventAction({ eventId: id, reload: false }));
}
},
{
...options,
allowSignalWrites: true,
}
);

store
.select(selectEventsActionState('loadOne'))
.pipe(
filter(x => x.state === 'none'),
takeUntilDestroyed(injectEx(DestroyRef, options))
)
.subscribe(() =>
store.dispatch(loadEventAction({ eventId: eventId(), reload: true, silent: true }))
);
.subscribe(() => {
const id = eventId();
if (id) {
store.dispatch(loadEventAction({ eventId: id, reload: true, silent: true }));
}
});
}
2 changes: 2 additions & 0 deletions src/client/src/app/+state/functional-effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ function _createFunctionalEffect<Source extends () => Observable<unknown>>(
return createEffect(source, actualConfig);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type EffectResult<OT> = Observable<OT> | ((...args: any[]) => Observable<OT>);
type ConditionallyDisallowActionCreator<DT, Result> = DT extends false
? unknown
Expand All @@ -46,6 +47,7 @@ export type CreateFunctionalEffectFunction = typeof _createFunctionalEffect & {
};

export const createFunctionalEffect: CreateFunctionalEffectFunction = (() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(_createFunctionalEffect as any).dispatching = _createDispatchingFunctionalEffect;
return _createFunctionalEffect as CreateFunctionalEffectFunction;
})();
Expand Down
5 changes: 4 additions & 1 deletion src/client/src/app/+state/maps/actions/load-maps.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ export const loadMapsReducers: Reducers<MapsFeatureState> = [
props.reload ? mapsEntityAdapter.removeAll(state) : state
)
),
handleHttpAction('load', loadMapsAction, { condition: (s, p) => !p.silent }),
handleHttpAction('load', loadMapsAction, {
condition: (s, p) => !p.silent,
startCondition: (s, p) => s.actionStates.load.state === 'none' || p.reload === true,
}),
];

export const loadMapsEffects: Effects = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ export const loadPlayerEventsReducers: Reducers<PlayerEventsFeatureState> = [
})
)
),
handleHttpAction('load', loadPlayerEventsAction, { condition: (s, p) => !p.silent }),
handleHttpAction('load', loadPlayerEventsAction, {
condition: (s, p) => !p.silent,
startCondition: (s, p) => s.actionStates.load.state === 'none' || p.reload === true,
}),
];

export const loadPlayerEventsEffects: Effects = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { switchMap } from 'rxjs';

import { UserSettingsService } from '../../../api/services';
import { UserSettings } from '../../../models/parsed-models';
import { RealtimeEventsService } from '../../../services/realtime-events.service';
import { isEmptyObject, removeUndefinedProperties } from '../../../utils/common.utils';
import { createHttpAction, handleHttpAction, onHttpAction, toHttpAction } from '../../action-state';
import { createFunctionalEffect } from '../../functional-effect';
Expand Down Expand Up @@ -35,19 +34,18 @@ export const updateUserSettingsReducers: Reducers<UserSettingsFeatureState> = [
];

export const updateUserSettingsEffects: Effects = {
updateUserSettings$: createFunctionalEffect.dispatching(
(api = inject(UserSettingsService), events = inject(RealtimeEventsService)) =>
onHttpAction(updateUserSettingsAction, selectUserSettingsActionState('update')).pipe(
switchMap(({ props }) =>
toHttpAction(
updateUserSettings(api, props),
updateUserSettingsAction,
props
// , () =>
// events.skipEvent('userSettingsChanged')
)
updateUserSettings$: createFunctionalEffect.dispatching((api = inject(UserSettingsService)) =>
onHttpAction(updateUserSettingsAction, selectUserSettingsActionState('update')).pipe(
switchMap(({ props }) =>
toHttpAction(
updateUserSettings(api, props),
updateUserSettingsAction,
props
// , () =>
// events.skipEvent('userSettingsChanged')
)
)
)
),
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { DestroyRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Store } from '@ngrx/store';
import { filter, map } from 'rxjs';
import { filter } from 'rxjs';

import { loadUserSettingsAction } from './user-settings.actions';
import { selectUserSettingsActionState } from './user-settings.selectors';
import { injectEx, OptionalInjector } from '../../utils/angular.utils';

export function keepUserSettingsLoaded(options?: OptionalInjector) {
const store = injectEx(Store, options);
store.dispatch(loadUserSettingsAction({ reload: false }));
store
.select(selectUserSettingsActionState('load'))
.pipe(
filter(x => x.state === 'none'),
map((_, index) => index),
takeUntilDestroyed(injectEx(DestroyRef, options))
)
.subscribe(i => store.dispatch(loadUserSettingsAction({ reload: true, silent: i > 0 })));
.subscribe(() => store.dispatch(loadUserSettingsAction({ reload: true, silent: true })));
}
4 changes: 2 additions & 2 deletions src/client/src/app/+state/users/actions/update-user.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ export const updateUserEffects = {
(store = inject(Store), api = inject(UserAdministrationService)) =>
onHttpAction(updateUserAction, selectUsersActionState('update')).pipe(
concatLatestFrom(({ props }) => store.select(selectUser(props.id))),
filter(([, oldUser]) => !!oldUser),
filter((x): x is [ReturnType<typeof updateUserAction>, User] => !!x[1]),
switchMap(([{ props }, oldUser]) =>
toHttpAction(updateUser(api, props, oldUser!), updateUserAction, props)
toHttpAction(updateUser(api, props, oldUser), updateUserAction, props)
)
)
),
Expand Down
6 changes: 5 additions & 1 deletion src/client/src/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ export const appConfig: ApplicationConfig = {
router: routerReducer,
}),
provideRouterStore(),
provideStoreDevtools({ name: `Minigolf Friday (${Math.random().toString(16).substring(2)})` }),
provideStoreDevtools({
name: `Minigolf Friday (${Math.random().toString(16).substring(2)})`,
autoPause: true,
maxAge: 1000,
}),
provideAppState(),
provideUsersState(),
provideMapsState(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,10 @@ export class EventDetailsComponent {
() => this.canBuildInstances() && this.event() && !this.event()?.startedAt
);

protected readonly canCommit = computed(
() =>
this.event() &&
this.event()?.staged &&
this.event()?.timeslots &&
this.event()!.timeslots.length > 0
);
protected readonly canCommit = computed(() => {
const event = this.event();
return event && event.staged && event.timeslots && event.timeslots.length > 0;
});

protected readonly allowToStart = computed(
() => this.hasInstances() && this.allTimeslotsHaveMaps()
Expand Down Expand Up @@ -138,10 +135,13 @@ export class EventDetailsComponent {
}

protected deleteEvent() {
const event = this.event();
if (!event) return;

this._confirmationService.confirm({
header: this.translations.events_deleteDialog_title(),
message: interpolate(this.translations.events_deleteDialog_text(), {
date: formatDate(this.event()!.date, 'mediumDate', this.locale()),
date: formatDate(event.date, 'mediumDate', this.locale()),
}),
acceptLabel: this.translations.shared_delete(),
acceptButtonStyleClass: 'p-button-danger',
Expand All @@ -151,18 +151,21 @@ export class EventDetailsComponent {
accept: () => {
this._store.dispatch(
removeEventAction({
eventId: this.eventId()!,
eventId: event.id,
})
);
},
});
}

protected startEvent() {
const event = this.event();
if (!event) return;

this._confirmationService.confirm({
header: this.translations.events_startDialog_title(),
message: interpolate(this.translations.events_startDialog_text(), {
date: formatDate(this.event()!.date, 'mediumDate', this.locale()),
date: formatDate(event.date, 'mediumDate', this.locale()),
}),
acceptLabel: this.translations.shared_start(),
acceptButtonStyleClass: 'p-button-success',
Expand All @@ -172,18 +175,21 @@ export class EventDetailsComponent {
accept: () => {
this._store.dispatch(
startEventAction({
eventId: this.eventId()!,
eventId: event.id,
})
);
},
});
}

protected commitEvent() {
const event = this.event();
if (!event) return;

this._confirmationService.confirm({
header: this.translations.events_commitDialog_title(),
message: interpolate(this.translations.events_commitDialog_text(), {
date: formatDate(this.event()!.date, 'mediumDate', this.locale()),
date: formatDate(event.date, 'mediumDate', this.locale()),
}),
acceptLabel: this.translations.shared_commit(),
acceptButtonStyleClass: 'p-button-success',
Expand All @@ -193,7 +199,7 @@ export class EventDetailsComponent {
accept: () => {
this._store.dispatch(
commitEventAction({
eventId: this.eventId()!,
eventId: event.id,
commit: true,
})
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ export class EventTimeslotDialogComponent {

const timeslot = untracked(() => this.timeslot());
if (this.visible() && timeslot) {
console.log('timeslot', timeslot);
untracked(() =>
this.form.setValue({
time: dateWithTime(new Date(), timeslot.time),
Expand Down
Loading
Loading