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

Contact app mobile #752

Draft
wants to merge 22 commits into
base: develop
Choose a base branch
from
Draft

Contact app mobile #752

wants to merge 22 commits into from

Conversation

RyanMG
Copy link
Contributor

@RyanMG RyanMG commented Mar 13, 2025

As an onboarding exercise, it was suggested that I would build a mobile version of the existing toolbox contacts app. I used this to

  1. Get familiar with how toolbox instantiates entrypoints / app routes
  2. Learn how to build UI / model data / bindings / etc within the hoist FE exosystem
  3. Get familar with as many interfaces as I could (typing, styling, property options, etc)

I pulled as much of the common parts of the desktop contact app as I could - specifically sharing SCSS definitions where overrides or replacements were not needed, and sharing the contact service, which as an API should behave the same between the versions.

However, sharing implementation code proved messier, and may. not be possible in any way where its complexity would be justified in the saved footprint. Currently if I try to make this "common" component builder step, the code fails to execute with an error that a mobile application is importing desktop components (and vice versa). This will mean that for now, any code updates may need to be done in two separate places, violating a pretty basic DRY principle.

I will provide inline comments to this code where specificity is merited, but a few top level notes here:

  1. Any new application entry point into toolbox appears to need a new entry to the OAuth provider (Auth0 in this case?) for a valid redirect URL. I am getting a 403 currently when the application refreshes and requests an updated accessToken (https://login.xh.io/oauth/token), and this appears to stem from it not liking my provided redirect_uri (in this case http://localhost:3000/mobileContacts/). I worked around this for my work by just disabling outh in my local hoist-core, but we will need to look into this if any new routes are merged
  2. Would like to pick someone's brain on the webpack dev server. I hit an issue at one point where I accidentally entered /contactsMobile (plural contactS) rather than /contactMobile, and webpack was serving me the chunkhash for the standard contact page. It seems the webpack server does some sort of pattern matcher for entry points where it finds the closest entry point matching the provided pathname left-to-right. So for instance, I can load /app as I would expect, but /apppppppp would also load the base app entry point. /ap however would not, since it does not make an exact match before the pathname ends. Not critical, just weird and I could not find the cause. Would be good to better understand.
  3. I'll need to do more reviewing / get some pointers on how persistence is set up. You will see that I ended up using localstorge to persist user favorites. I was not seeing that working in the existing contacts app (on dev, not just local). It works properly in both places with this change, but I am not doing it in what appears to be the prescribed way. It also appears that this persistance is something I will be using all the time here, so the sooner I get a handle on it the better.

Note:

The inline comments on this PR are almost all still valid, despite being noted as "outdated". Lee suggested reconfguring my folder choices to create a root "contact" folder which can house common / shared files, then within create "desktop" and "mobile" folders for respective, dedicated code. This was a full refactor of file structure, which broke the previous comments - hense them showing as outdated.

RyanMG added 16 commits March 11, 2025 13:03
 - implements grid and images view
 - scales grid view down to just name and location to better use narrow space
 - grid / image button group uses images to better use space
 - some styling work to prevent overflow of top bar UI in common widths
…mobile.

 - Prep for this to migrate to src/examples/
 - webpack seemed to want to load the base "contacts" every time any path was requested with "contact" in the name
 - contactMobile was always being loaded using the contact.js bunble
 - mobile seems not to have the same issue, so mobileContacts is loading with no issue
 - details page is by defaul flex none and width 0
 - selecting an option adds a class to show the details
 - X button added to details header which removes the class and hides the details
 - favorites button will toggle the record as favorite
 - edit button enabled to allow editing
 - save button will properly call to persist changes
 - favorite icon is present in grid list view
 - properly show all persisted contact data in details form
 - share SCSS files where between desktop / mobile where unchanged
 - import desktop SCSS into mobile SCSS file where we need to add defintions
 - some added classes to properly sync UI between desktop and mobile
@RyanMG RyanMG added the wip label Mar 13, 2025
RyanMG added 5 commits March 12, 2025 21:21
 - add specificity to a custom class to avoid the need for !important (lesser evil)
 - move an import to the UI comments rather than the model component where I had accidently placed it
…ance (WIP)

 - persisting in DB now works for view type and favorites
 - seems unnessessiary boilerplate, so likely this needs more iteration as I learn the process
 - css comment update where I needed to increate specificity on a class def
 - post-save contact update the record rather than resyncing / rebuilding the entire page
 - group grid view listing by isFavorite
 - remove custom isFavorite render (will not show when the column is the group directive)
 - remove now unneeded styling for isFavorite cell
@@ -52,6 +53,11 @@ export class ContactService extends HoistService {
const {userFaves} = this,
isFavorite = userFaves.includes(id);

XH.setPref(PERSIST_APP.prefKey, {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted above, I will need some help with persistence management.

Existing (desktop) contacts is saving grid / tile view, but is NOT saving favorites. I see the userFaves being wired up, but then the persistence code never gets called subsequently when the state changes on an item. I am unsure why.

So this resolves it - call persistence manually to save the value. But ultimately I should understand why its not working by default and resolve it that way.

@@ -0,0 +1 @@
@import '../contact/DirectoryPanel';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No change to CSS, but I do need it present / loaded.

setDisplayMode(value: 'grid' | 'tiles') {
this.displayMode = value;

XH.setPref(PERSIST_APP.prefKey, {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See https://github.com/xh/toolbox/pull/752/files#r1994240772. This was persisting correctly alone, but if I include the above fix to user favorites and NOT this, then saving a favorite will clobber the save of mode.

@@ -0,0 +1,24 @@
import {hoistCmp} from '@xh/hoist/core';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of these components are visually exactly the same as the desktop counterpoints - so ideally I share some of all of the execution to avoid repetition. However, the need for DESKTOP versions of components there and MOBILE version here means that this is not possible in my testing - the app would complain of any imports to a mobile app which themselves included imports of desktop items.

className: 'tb-contact-tile__bar',
// This inline style defintion keeps me from needing to fight with
// existing styles when making the mobile button want to center itself
style: {alignItems: 'center'},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a fair bit of fighting the styling while working on this.

We define some styles in SCSS files to customer a component or layout. Said component / layout will already have some styling present from existing styles from XH / TB / BP. Then additionally you can define some styles on an element component which are writing direct to the styles tag.
This ultimately meant for me that any styles I applied in an SCSS file were clobbered right off by either an existing style tag or - more often - existing styles with far greater specificity. In this case, using an inline style was of highest import, and so was allowing this align-items style to win over the native styles applied to a mobile button.

@@ -0,0 +1,48 @@
.tb-mobile-contact-details-panel {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is not entirely different from the desktop styles, but for mobile I needed CSS animations. Trying to inhert the common parts was proving troublesome due to the nesting.

const {currentRecord, visible} = model;

return panel({
className: `tb-mobile-contact-details-panel${visible ? '--details-visible' : ''}`,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hide it until needed, since its "off screen".

}
});

const contactProfile = hoistCmp.factory({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the definitions below here are another instance where they LOOK the same as the desktop counterparts, but use MOBILE versions of components.

const {readonly, isDirty} = formModel;

// Pulled from standard details page
// How could the form be "dirty" if it's readonly?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsure if I am missing something in the isDirty logic?

…rent

 - Shared components can now live in the root
 - name updates to mobile UI and model to align with the desktop names
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant