Skip to content

Commit

Permalink
initial
Browse files Browse the repository at this point in the history
  • Loading branch information
vkozio committed Sep 27, 2024
1 parent 42b2804 commit e4c7a71
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 147 deletions.
25 changes: 25 additions & 0 deletions .cursorignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)
__fixtures__
__mocks__
__test__
./**/*.fixture.tsx
./**/fixture/*.*
.github/workflows/run_e2e_tests.yml
.helpers
*.fixture.{ts,tsx}
**/*.test.tsx
appconfig.js
configs/*
cosmos**
e2e/**
jest.config.js
jest.setup.js
playwright**
postcss.config.ts
public
scripts
server
template.config.js
vite.config.ts
vite.proxy.ts
i18next-scanner.config.cjs
25 changes: 25 additions & 0 deletions src/core/api/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { configRepo } from '~core/config';
import { apiClient } from '~core/apiClientInstance';
import type { Event } from '~core/types';

export interface EventListParams {
feed?: string;
bbox?: string;
}

export function getEventsList(params: EventListParams, abortController: AbortController) {
return apiClient
.get<Event[]>(
'/events',
{
appId: configRepo.get().id,
...params,
},
true,
{
signal: abortController.signal,
errorsConfig: { hideErrors: true },
},
)
.then((response) => response ?? []); // Ensure we always return an array
}
89 changes: 57 additions & 32 deletions src/core/shared_state/currentEvent.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,63 @@
import { createAtom, createBooleanAtom } from '~utils/atoms';
import { atom, action } from '@reatom/core';
import { v3toV2 } from '~utils/atoms/v3tov2';
import { focusedGeometryAtom } from '../focused_geometry/model';

// * CurrentEventAtomState *
// null represents the initial state of event - we need that state for cases of autoselecting event
// { id: null } represents event reset was caused by user actions
export type CurrentEventAtomState = {
id: string | null;
} | null;

export const currentEventAtom = createAtom(
{
setCurrentEventId: (eventId: string | null) => eventId,
focusedGeometryAtom,
},
({ onAction, onChange }, state: CurrentEventAtomState = null) => {
onChange('focusedGeometryAtom', (focusedGeometry) => {
const currentGeometrySource = focusedGeometry?.source;
if (
currentGeometrySource &&
currentGeometrySource.type !== 'event' &&
currentGeometrySource.type !== 'episode'
) {
// if focused geometry is no longer represents event, user stopped work with events
// following state specifies that
state = { id: null };
// reatom v2 imports mapped to reatom v3
const __v3_imports = {
focusedGeometryAtom: focusedGeometryAtom.v3atom,
};

function __create_v3() {
const { focusedGeometryAtom } = __v3_imports;
// v3 definitions section

// * CurrentEventAtomState *
// null represents the initial state of event - we need that state for cases of autoselecting event
// { id: null } represents event reset was caused by user actions
type CurrentEventAtomState = { id: string | null } | null;

const currentEventAtom = atom<CurrentEventAtomState>(null, 'currentEventAtom');
const scheduledAutoSelect = atom(false, 'scheduledAutoSelect');
const scheduledAutoFocus = atom(false, 'scheduledAutoFocus');

const setCurrentEventId = action((ctx, eventId: string | null) => {
currentEventAtom(ctx, { id: eventId });
}, 'setCurrentEventId');

// Stateless computed atom that updates currentEventAtom based on focusedGeometryAtom
const computedCurrentEventAtom = atom((ctx) => {
const currentEvent = ctx.spy(currentEventAtom);
const focusedGeometry = ctx.spy(focusedGeometryAtom);
const currentGeometrySource = focusedGeometry?.source;

if (
currentGeometrySource &&
currentGeometrySource.type !== 'event' &&
currentGeometrySource.type !== 'episode'
) {
if (currentEvent?.id !== null) {
currentEventAtom(ctx, { id: null });
}
});
}
}, 'computedCurrentEventAtom');

onAction('setCurrentEventId', (eventId) => (state = { id: eventId }));
// v3 exports object
return {
currentEventAtom,
scheduledAutoSelect,
scheduledAutoFocus,
setCurrentEventId,
computedCurrentEventAtom,
};
}

return state;
},
'[Shared state] currentEventAtom',
);
const v3 = __create_v3();
// v3 exports as default
export default v3;

export const scheduledAutoSelect = createBooleanAtom(false, 'scheduledAutoSelect');
export const scheduledAutoFocus = createBooleanAtom(false, 'scheduledAutoFocus');
// v2 compatible exports keeping the same names
export const currentEventAtom = v3toV2(v3.currentEventAtom, {
setCurrentEventId: v3.setCurrentEventId,
});
export const scheduledAutoSelect = v3toV2(v3.scheduledAutoSelect);
export const scheduledAutoFocus = v3toV2(v3.scheduledAutoFocus);
122 changes: 76 additions & 46 deletions src/core/shared_state/currentEventFeed.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,85 @@
import { createAtom } from '~utils/atoms';
import { atom, action, type Ctx } from '@reatom/core';
import { v3toV2 } from '~utils/atoms/v3tov2';
import { configRepo } from '~core/config';
import { currentEventAtom, scheduledAutoSelect } from './currentEvent';
import type { EventFeedConfig } from '~core/config/types';

type CurrentEventFeedAtomState = {
id: string;
} | null;

export const currentEventFeedAtom = createAtom(
{
setCurrentFeed: (feedId: string) => feedId,
setFeedForExistingEvent: (feedId: string) => feedId,
resetCurrentFeed: () => null,
syncFeed: (eventFeeds) => eventFeeds,
},
(
{ onAction, onChange, schedule, getUnlistedState },
state: CurrentEventFeedAtomState = { id: configRepo.get().defaultFeed },
) => {
onAction('setCurrentFeed', (feedId) => {
if (state?.id !== feedId) {
state = { id: feedId };
}
schedule((dispatch) => dispatch(currentEventAtom.setCurrentEventId(null)));
});
// reatom v2 imports mapped to reatom v3
const __v3_imports = {
currentEventAtom: currentEventAtom.v3atom,
scheduledAutoSelect: scheduledAutoSelect.v3atom,
};

onAction('resetCurrentFeed', () => {
if (state) {
state = null;
}
});

onAction('syncFeed', (eventFeeds) => {
if (eventFeeds && eventFeeds.data.length && !eventFeeds.loading) {
const newFeed = checkFeed(eventFeeds.data, state?.id);
if (newFeed !== undefined && newFeed !== state?.id) {
state = { id: newFeed };
const currentEvent = getUnlistedState(currentEventAtom);
if (currentEvent !== null)
schedule((dispatch) => dispatch(scheduledAutoSelect.setTrue()));
}
function __create_v3() {
const { currentEventAtom, scheduledAutoSelect } = __v3_imports;
// v3 definitions section

type CurrentEventFeedAtomState = {
id: string;
} | null;

const currentEventFeedAtom = atom<CurrentEventFeedAtomState>(
{ id: configRepo.get().defaultFeed },
'currentEventFeedAtom',
);

const setCurrentFeed = action((ctx, feedId: string) => {
updateFeed(ctx, feedId);
}, 'setCurrentFeed');

const setFeedForExistingEvent = action((ctx, feedId: string) => {
updateFeed(ctx, feedId);
}, 'setFeedForExistingEvent');

const resetCurrentFeed = action((ctx) => {
updateFeed(ctx, null);
}, 'resetCurrentFeed');

const syncFeed = action(
(ctx, eventFeeds: { data: EventFeedConfig[]; loading: boolean }) => {
if (eventFeeds?.data?.length && !eventFeeds.loading) {
const currentFeed = ctx.get(currentEventFeedAtom);
const newFeed = checkFeed(eventFeeds.data, currentFeed?.id);
updateFeed(ctx, newFeed);
}
});
},
'syncFeed',
);

function updateFeed(ctx: Ctx, newFeedId: string | null) {
const currentFeed = ctx.get(currentEventFeedAtom);
if (currentFeed?.id !== newFeedId) {
currentEventFeedAtom(ctx, newFeedId ? { id: newFeedId } : null);
// deselect current event
currentEventAtom(ctx, { id: null });
scheduledAutoSelect(ctx, false);
}
}

return state;
},
'[Shared state] currentEventFeedAtom',
);
function checkFeed(eventFeeds: EventFeedConfig[], feedId?: string) {
if (!feedId) return configRepo.get().defaultFeed;
const feed = eventFeeds?.find((fd) => fd.feed === feedId);
return feed ? feed.feed : configRepo.get().defaultFeed;
}

function checkFeed(eventFeeds: EventFeedConfig[], feedId?: string) {
if (!feedId) return configRepo.get().defaultFeed;
const feed = eventFeeds?.find((fd) => fd.feed === feedId);
return feed ? feed.feed : configRepo.get().defaultFeed;
// v3 exports object
return {
currentEventFeedAtom,
setCurrentFeed,
setFeedForExistingEvent,
resetCurrentFeed,
syncFeed,
};
}

const v3 = __create_v3();
// v3 exports as default
export default v3;

// v2 compatible exports keeping the same names
export const currentEventFeedAtom = v3toV2(v3.currentEventFeedAtom, {
setCurrentFeed: v3.setCurrentFeed,
setFeedForExistingEvent: v3.setFeedForExistingEvent,
resetCurrentFeed: v3.resetCurrentFeed,
syncFeed: v3.syncFeed,
});
104 changes: 51 additions & 53 deletions src/features/events_list/atoms/autoSelectEvent.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,58 @@
import { createAtom } from '~utils/atoms';
import { atom } from '@reatom/core';
import { currentNotificationAtom } from '~core/shared_state';
import {
currentEventAtom,
scheduledAutoFocus,
scheduledAutoSelect,
} from '~core/shared_state/currentEvent';
import { currentEventAtom, scheduledAutoSelect } from '~core/shared_state/currentEvent';
import { i18n } from '~core/localization';
import { v3toV2 } from '~utils/atoms/v3tov2';
import { eventListResourceAtom } from './eventListResource';

export const autoSelectEvent = createAtom(
{
eventListResourceAtom,
},
({ getUnlistedState, schedule, onChange }, state = {}) => {
onChange('eventListResourceAtom', (eventListResource) => {
const autoSelectWasScheduled = getUnlistedState(scheduledAutoSelect);
if (
autoSelectWasScheduled &&
eventListResource &&
!eventListResource.loading &&
!eventListResource.error &&
eventListResource.data &&
eventListResource.data.length
) {
const currentEvent = getUnlistedState(currentEventAtom);
const currentEventNotInTheList = !eventListResource.data.some(
(e) => e.eventId === currentEvent?.id,
);
// reatom v2 imports mapped to reatom v3
const __v3_imports = {
eventListResourceAtom: eventListResourceAtom.v3atom,
scheduledAutoSelect: scheduledAutoSelect.v3atom,
currentEventAtom: currentEventAtom.v3atom,
};
function __create_v3() {
const { eventListResourceAtom, scheduledAutoSelect, currentEventAtom } = __v3_imports;
// v3 definitions section
const autoSelectEvent = atom((ctx) => {
const eventListResource = ctx.spy(eventListResourceAtom);
const autoSelectWasScheduled = ctx.spy(scheduledAutoSelect);
const currentEvent = ctx.spy(currentEventAtom);

if (!currentEventNotInTheList) return state;
if (
!autoSelectWasScheduled ||
!eventListResource?.data?.length ||
eventListResource.loading ||
eventListResource.error
) {
return;
}

if (currentEvent?.id) {
// This case happens when call for event by provided eventId didn't return event
schedule((dispatch) =>
dispatch(
currentNotificationAtom.showNotification(
'warning',
{ title: i18n.t('event_list.no_event_in_feed') },
5,
),
),
);
} else {
const firstEventInList = eventListResource.data[0];
schedule((dispatch) => {
dispatch([
scheduledAutoSelect.setFalse(),
scheduledAutoFocus.setTrue(),
currentEventAtom.setCurrentEventId(firstEventInList.eventId),
]);
});
}
}
});
const currentEventNotInTheList = !eventListResource.data.some(
(e) => e.eventId === currentEvent?.id,
);

return state;
},
'autoSelectEvent',
);
if (currentEventNotInTheList && currentEvent?.id) {
currentNotificationAtom.showNotification.v3action(
ctx,
'warning',
{ title: i18n.t('event_list.no_event_in_feed') },
5,
);
currentEventAtom(ctx, { id: null });
}

scheduledAutoSelect(ctx, false);
}, 'autoSelectEvent');

// v3 exports object
return {
autoSelectEvent,
};
}
const v3 = __create_v3();
// v3 exports as default
export default v3;

// v2 compatible exports keeping the same names
export const autoSelectEvent = v3toV2(v3.autoSelectEvent);
Loading

0 comments on commit e4c7a71

Please sign in to comment.