lostcarpark / conclar

ConClár Programme Guide in ReactJS
MIT License
11 stars 12 forks source link

Update program when changes take place on back end #39

Open lostcarpark opened 2 years ago

lostcarpark commented 2 years ago

Goal: Make ConClár load program changes during convention, if internet connection available.

Steps to achieve this:

lostcarpark commented 2 years ago

Logging an issue, partly so the plan is clear in my head.

mcdemarco commented 2 years ago

As of task 1, I get a error on the console and a blank page when navigating directly to http://localhost:3000/myschedule. I happen to have some schedule items selected. I can get to them with the internal nav.

lostcarpark commented 2 years ago

Any details on the error you get?

mcdemarco commented 2 years ago
ProgramList.js:14 Uncaught TypeError: Cannot read properties of null (reading 'length')
    at ProgramList (ProgramList.js:14:1)
    at renderWithHooks (react-dom.development.js:14985:1)
    at mountIndeterminateComponent (react-dom.development.js:17811:1)
    at beginWork (react-dom.development.js:19049:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:3945:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:3994:1)
    at invokeGuardedCallback (react-dom.development.js:4056:1)
    at beginWork$1 (react-dom.development.js:23964:1)
    at performUnitOfWork (react-dom.development.js:22776:1)
    at workLoopSync (react-dom.development.js:22707:1)
ProgramList @ ProgramList.js:14
renderWithHooks @ react-dom.development.js:14985
mountIndeterminateComponent @ react-dom.development.js:17811
beginWork @ react-dom.development.js:19049
callCallback @ react-dom.development.js:3945
invokeGuardedCallbackDev @ react-dom.development.js:3994
invokeGuardedCallback @ react-dom.development.js:4056
beginWork$1 @ react-dom.development.js:23964
performUnitOfWork @ react-dom.development.js:22776
workLoopSync @ react-dom.development.js:22707
renderRootSync @ react-dom.development.js:22670
performSyncWorkOnRoot @ react-dom.development.js:22293
(anonymous) @ react-dom.development.js:11327
unstable_runWithPriority @ scheduler.development.js:468
runWithPriority$1 @ react-dom.development.js:11276
flushSyncCallbackQueueImpl @ react-dom.development.js:11322
flushSyncCallbackQueue @ react-dom.development.js:11309
scheduleUpdateOnFiber @ react-dom.development.js:21893
dispatchAction @ react-dom.development.js:16139
processData @ App.js:187
(anonymous) @ App.js:204
Promise.then (async)
(anonymous) @ App.js:200
invokePassiveEffectCreate @ react-dom.development.js:23487
callCallback @ react-dom.development.js:3945
invokeGuardedCallbackDev @ react-dom.development.js:3994
invokeGuardedCallback @ react-dom.development.js:4056
flushPassiveEffectsImpl @ react-dom.development.js:23574
unstable_runWithPriority @ scheduler.development.js:468
runWithPriority$1 @ react-dom.development.js:11276
flushPassiveEffects @ react-dom.development.js:23447
(anonymous) @ react-dom.development.js:23324
workLoop @ scheduler.development.js:417
flushWork @ scheduler.development.js:390
performWorkUntilDeadline @ scheduler.development.js:157
react-dom.development.js:20085 The above error occurred in the <ProgramList> component:

    at ProgramList (http://localhost:3000/static/js/bundle.js:2426:5)
    at Outlet (http://localhost:3000/static/js/bundle.js:44614:26)
    at Routes (http://localhost:3000/static/js/bundle.js:44706:5)
    at div
    at ScrollToTop (http://localhost:3000/static/js/bundle.js:2591:77)
    at Router (http://localhost:3000/static/js/bundle.js:44639:15)
    at BrowserRouter (http://localhost:3000/static/js/bundle.js:44119:5)
    at App (http://localhost:3000/static/js/bundle.js:62:74)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
logCapturedError @ react-dom.development.js:20085
update.callback @ react-dom.development.js:20118
callCallback @ react-dom.development.js:12318
commitUpdateQueue @ react-dom.development.js:12339
commitLifeCycles @ react-dom.development.js:20736
commitLayoutEffects @ react-dom.development.js:23426
callCallback @ react-dom.development.js:3945
invokeGuardedCallbackDev @ react-dom.development.js:3994
invokeGuardedCallback @ react-dom.development.js:4056
commitRootImpl @ react-dom.development.js:23151
unstable_runWithPriority @ scheduler.development.js:468
runWithPriority$1 @ react-dom.development.js:11276
commitRoot @ react-dom.development.js:22990
performSyncWorkOnRoot @ react-dom.development.js:22329
(anonymous) @ react-dom.development.js:11327
unstable_runWithPriority @ scheduler.development.js:468
runWithPriority$1 @ react-dom.development.js:11276
flushSyncCallbackQueueImpl @ react-dom.development.js:11322
flushSyncCallbackQueue @ react-dom.development.js:11309
scheduleUpdateOnFiber @ react-dom.development.js:21893
dispatchAction @ react-dom.development.js:16139
processData @ App.js:187
(anonymous) @ App.js:204
Promise.then (async)
(anonymous) @ App.js:200
invokePassiveEffectCreate @ react-dom.development.js:23487
callCallback @ react-dom.development.js:3945
invokeGuardedCallbackDev @ react-dom.development.js:3994
invokeGuardedCallback @ react-dom.development.js:4056
flushPassiveEffectsImpl @ react-dom.development.js:23574
unstable_runWithPriority @ scheduler.development.js:468
runWithPriority$1 @ react-dom.development.js:11276
flushPassiveEffects @ react-dom.development.js:23447
(anonymous) @ react-dom.development.js:23324
workLoop @ scheduler.development.js:417
flushWork @ scheduler.development.js:390
performWorkUntilDeadline @ scheduler.development.js:157
ProgramList.js:14 Uncaught (in promise) TypeError: Cannot read properties of null (reading 'length')
    at ProgramList (ProgramList.js:14:1)
    at renderWithHooks (react-dom.development.js:14985:1)
    at mountIndeterminateComponent (react-dom.development.js:17811:1)
    at beginWork (react-dom.development.js:19049:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:3945:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:3994:1)
    at invokeGuardedCallback (react-dom.development.js:4056:1)
    at beginWork$1 (react-dom.development.js:23964:1)
    at performUnitOfWork (react-dom.development.js:22776:1)
    at workLoopSync (react-dom.development.js:22707:1)
ProgramList @ ProgramList.js:14
renderWithHooks @ react-dom.development.js:14985
mountIndeterminateComponent @ react-dom.development.js:17811
beginWork @ react-dom.development.js:19049
callCallback @ react-dom.development.js:3945
invokeGuardedCallbackDev @ react-dom.development.js:3994
invokeGuardedCallback @ react-dom.development.js:4056
beginWork$1 @ react-dom.development.js:23964
performUnitOfWork @ react-dom.development.js:22776
workLoopSync @ react-dom.development.js:22707
renderRootSync @ react-dom.development.js:22670
performSyncWorkOnRoot @ react-dom.development.js:22293
(anonymous) @ react-dom.development.js:11327
unstable_runWithPriority @ scheduler.development.js:468
runWithPriority$1 @ react-dom.development.js:11276
flushSyncCallbackQueueImpl @ react-dom.development.js:11322
flushSyncCallbackQueue @ react-dom.development.js:11309
scheduleUpdateOnFiber @ react-dom.development.js:21893
dispatchAction @ react-dom.development.js:16139
processData @ App.js:187
(anonymous) @ App.js:204
Promise.then (async)
(anonymous) @ App.js:200
invokePassiveEffectCreate @ react-dom.development.js:23487
callCallback @ react-dom.development.js:3945
invokeGuardedCallbackDev @ react-dom.development.js:3994
invokeGuardedCallback @ react-dom.development.js:4056
flushPassiveEffectsImpl @ react-dom.development.js:23574
unstable_runWithPriority @ scheduler.development.js:468
runWithPriority$1 @ react-dom.development.js:11276
flushPassiveEffects @ react-dom.development.js:23447
(anonymous) @ react-dom.development.js:23324
workLoop @ scheduler.development.js:417
flushWork @ scheduler.development.js:390
performWorkUntilDeadline @ scheduler.development.js:157
lostcarpark commented 2 years ago

I can't seem to reproduce the error, but I can see it's caused by a null getting passed into the ProgramList, so it seems sensible to have a check for null there. Hopefully the latest pull request will fix it.

mcdemarco commented 2 years ago

That seems to have fixed it. So should selections not be chock full of null?

localStorage.selections
'[null,true,null,null,null,false,null,true,false,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,true,null,null,null,null,null,null,true,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,true,null,null,null,null,null,null,null,null,null,null,null,true,null,null,null,null,null,null,null,null,null,null,null,null,true,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,true]'
lostcarpark commented 2 years ago

It's not really a problem, but I might change it to an associative array, which should stop elements that haven't been explicitly set from being stored. The nulls are elements that haven't been set to true or false.

mcdemarco commented 2 years ago

Why not just the selected IDs?

lostcarpark commented 2 years ago

That's a very sensible way to do it. :-)

lostcarpark commented 2 years ago

Okay, so merged pull request #44, which is a huge change. Not much has changed in the display, but a lot has changed behind the scenes. The major change is it uses Easy Peasy, which is a layer on top of Redux, for state management. I was looking at using the built in Context, but this wasn't workable as when context changes, it updates the entire display (which would be fine for occasional updates like refreshing the program, but not workable for things like selecting program items). This means that a lot few properties are passed between components, and leaves me ready to move on to the actual task of making it load program changes. I've also changed how program selections are stored. First, it now just stores an array of the selected IDs. I've also added an "APP_ID" property, which is used when storing the selection. This will be useful for multi-year events. It doesn't really matter what it's set to, but it should change for each year's event, otherwise, if the previous year's selection is still in local storage, it will cause this year's items with matching ideas to be set. Finally, I've added "Expand All" and "Collapse All" buttons. These could really do with styling. Not sure if buttons are the best control to use. I'd be happy to change to links or something else if it would make styling easier.

mcdemarco commented 2 years ago

Does "APP_ID" have to be an integer?

lostcarpark commented 2 years ago

No, it can be any string. I've used "O2021" for last year's Octocon. It's only purpose (for now at least) is to prevent items with the same IDs as the previous year from being selected if you still have them in your local storage.

mcdemarco commented 2 years ago

Ah, I thought it was 02021...

lostcarpark commented 2 years ago

Yes, easy mistake to make. On my to-do list is create a set of fictional sample programme data. The key isn't very important in itself, it's just there to differentiate selections, so that if the con site gets pointed at another convention, the selections don't get carried over (as has happened when I've used different datasets in testing). Anything specific to the current convention should have the App ID appended to the key when storing in local storage. However, settings (like 12 hour clock) shouldn't since it's reasonable to expect that users will want the same settings for another event. Note that local storage is only accessible by the app when running on the same server, so there won't be any conflict between the Capricon instance and the Boskone instance (for example). At the moment, the only convention specific stored is the "My Schedule" selections. However, something I'd quite like to add is an option for members to add personal comments to items.

mcdemarco commented 2 years ago

I've always wanted to add my own personal items, like lunch plans or party info.

lostcarpark commented 2 years ago

That's a cool idea. Maybe for the future.

lostcarpark commented 2 years ago

I was going to use a dependency, react-hooks-timer, but it seems overkill and at present a regular JavaScript setInterval timer appears to be sufficient.

Also, I've done most of my testing on FireFox, so I didn't realise that Chrome is caching the data sent to Fetch(), even after a page reload. Changing the call to fetch(url, { cache: "reload" }) forces it to ignore cached data.

mcdemarco commented 2 years ago

Also, I've done most of my testing on FireFox, so I didn't realise that Chrome is caching the data sent to Fetch(), even after a page reload. Changing the call to fetch(url, { cache: "reload" }) forces it to ignore cached data.

Can you check that bit in now? It sounds useful.

lostcarpark commented 2 years ago

I've now pulled the changes into main. It will count down a timer, then refresh the program and people data, with the above setting to force a reload. At present it's a bit "brute force", refreshing the entire program on every download. I would like to make it compare program entries with the previously stored data, so it would only refresh changed items that have changed. However, that will probably make minimal difference from a user perspective.

mcdemarco commented 2 years ago

What is the user experience when the data refreshes?

lostcarpark commented 2 years ago

In my testing, if a program item has changed, the item just gets replaced with the new version. It will also hide any items that have passed their start time (plus ADJUST_MINUTES). There's no noticable change to unchanged items (even though they are replaced under the covers).