-
Notifications
You must be signed in to change notification settings - Fork 2
Contact app mobile #752
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
base: develop
Are you sure you want to change the base?
Contact app mobile #752
Changes from all commits
83f74c3
0c20082
d3ae54f
bba655e
2d77257
aa19fd2
f098783
4df8677
1e1a5f8
8aa46fd
f03878b
ccc571a
79768c8
f8c2574
4bf46b0
b344237
4104855
e0fc1fb
8f4f800
45d7a4e
5ea7796
6b0ada2
75e2192
9bc654c
b735ac7
835f7c2
0733793
76b6ca3
efc4c7e
f4edffe
84af811
7923f43
6c7ce8a
75e84e1
06b92f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import '../Bootstrap'; | ||
|
||
import {XH} from '@xh/hoist/core'; | ||
import {AppContainer} from '@xh/hoist/mobile/appcontainer'; | ||
import {AppComponent} from '../examples/contact/mobile/AppComponent'; | ||
import AppModel from '../examples/contact/mobile/AppModel'; | ||
import {AuthModel} from '../core/AuthModel'; | ||
|
||
XH.renderApp({ | ||
clientAppCode: 'contactMobile', | ||
clientAppName: 'XH Contact Mobile', | ||
componentClass: AppComponent, | ||
modelClass: AppModel, | ||
containerClass: AppContainer, | ||
authModelClass: AuthModel, | ||
isMobileApp: true, | ||
enableLogout: true, | ||
checkAccess: () => true | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,18 +2,27 @@ import {hoistCmp, uses} from '@xh/hoist/core'; | |
import {appBar} from '@xh/hoist/desktop/cmp/appbar'; | ||
import {panel} from '@xh/hoist/desktop/cmp/panel'; | ||
import {Icon} from '@xh/hoist/icon'; | ||
import {a} from '@xh/hoist/cmp/layout'; | ||
import {AppModel} from './AppModel'; | ||
import {directoryPanel} from './DirectoryPanel'; | ||
import '../../core/Toolbox.scss'; | ||
import '../../../core/Toolbox.scss'; | ||
|
||
export const AppComponent = hoistCmp({ | ||
displayName: 'App', | ||
model: uses(AppModel), | ||
|
||
render() { | ||
const domain = window.location.origin; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unsure if there is a 'hoist' way to access the URI. |
||
return panel({ | ||
tbar: appBar({ | ||
icon: Icon.contact({size: '2x', prefix: 'fal'}), | ||
leftItems: [ | ||
a({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. New link to let users access mobile contacts from the existing toolbox UI |
||
item: 'View contacts for mobile', | ||
href: `${domain}/contactMobile`, | ||
target: '_blank' | ||
}) | ||
], | ||
appMenuButtonProps: {hideLogoutItem: false} | ||
}), | ||
item: directoryPanel() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,6 @@ | ||
import {XH} from '@xh/hoist/core'; | ||
import {ContactService} from './svc/ContactService'; | ||
import {BaseAppModel} from '../../BaseAppModel'; | ||
|
||
export const PERSIST_APP = {prefKey: 'contactAppState'}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As desktop AppModel is no longer the root for all of contacts, I moved this to here, which is common to both versions. |
||
import {ContactService} from '../svc/ContactService'; | ||
import {BaseAppModel} from '../../../BaseAppModel'; | ||
|
||
export class AppModel extends BaseAppModel { | ||
static instance: AppModel; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import {hoistCmp, uses, XH} from '@xh/hoist/core'; | ||
import {appBar} from '@xh/hoist/mobile/cmp/header'; | ||
import {panel} from '@xh/hoist/mobile/cmp/panel'; | ||
import {Icon} from '@xh/hoist/icon'; | ||
import AppModel from './AppModel'; | ||
import {navigator} from '@xh/hoist/mobile/cmp/navigator'; | ||
import '../../../core/Toolbox.scss'; | ||
|
||
export const AppComponent = hoistCmp({ | ||
displayName: 'App', | ||
model: uses(AppModel), | ||
|
||
render() { | ||
return panel({ | ||
tbar: appBar({ | ||
omit: XH.isLandscape, | ||
icon: Icon.boxFull({size: 'lg', prefix: 'fal'}), | ||
hideRefreshButton: false, | ||
appMenuButtonProps: { | ||
hideLogoutItem: false, | ||
hideThemeItem: true | ||
} | ||
}), | ||
item: navigator() | ||
}); | ||
} | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import {managed, PlainObject, XH} from '@xh/hoist/core'; | ||
import {ContactService} from '../svc/ContactService'; | ||
import {BaseAppModel} from '../../../BaseAppModel'; | ||
import {NavigatorModel} from '@xh/hoist/mobile/cmp/navigator'; | ||
import directoryPanel from './DirectoryPanel'; | ||
import detailsPanel from './details/DetailsPanel'; | ||
import DetailsPanelModel from './details/DetailsPanelModel'; | ||
import DirectoryPanelModel from './DirectoryPanelModel'; | ||
import {action, makeObservable, observable, runInAction} from '@xh/hoist/mobx'; | ||
import {castArray, isEmpty, uniq} from 'lodash'; | ||
|
||
import { | ||
autoRefreshAppOption, | ||
sizingModeAppOption, | ||
themeAppOption | ||
} from '@xh/hoist/mobile/cmp/appOption'; | ||
|
||
export default class AppModel extends BaseAppModel { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. App model, as the root model for mobile contacts, now does a. lot more management of its children - including storing the fetched contact data and instantiating the models for the child views. |
||
static instance: AppModel; | ||
|
||
@observable.ref tagList: string[] = []; | ||
@observable.ref contacts: PlainObject[] = []; | ||
|
||
@managed directoryPanelModel: DirectoryPanelModel; | ||
@managed detailsPanelModel: DetailsPanelModel; | ||
|
||
@managed | ||
navigatorModel: NavigatorModel = new NavigatorModel({ | ||
track: true, | ||
pages: [ | ||
{id: 'default', content: directoryPanel}, | ||
{id: 'details', content: detailsPanel} | ||
] | ||
}); | ||
|
||
override getRoutes() { | ||
return [ | ||
{ | ||
name: 'default', | ||
path: '/contactMobile', | ||
children: [ | ||
{ | ||
name: 'details', | ||
path: '/:id' | ||
} | ||
] | ||
} | ||
]; | ||
} | ||
|
||
constructor() { | ||
super(); | ||
makeObservable(this); | ||
this.directoryPanelModel = new DirectoryPanelModel(this); | ||
this.detailsPanelModel = new DetailsPanelModel(this); | ||
} | ||
|
||
async updateContactAsync(id: string, data: PlainObject) { | ||
if (!data || isEmpty(data)) return; | ||
// Mobile select only allows single selections, while desktop allows multiple | ||
// This means we receive an array on the desktop picker, and a single value here. | ||
// Convert single result into an array of one to keep data consistent. | ||
if (data.tags) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comment above explains this. At some point maybe mobile needs to be enhanced to support multi-select, but that seems out of scope here. |
||
data.tags = castArray(data.tags); | ||
} | ||
await XH.contactService.updateContactAsync(id, data); | ||
await this.refreshAsync(); | ||
} | ||
|
||
@action | ||
async toggleFavorite(id: string) { | ||
await XH.contactService.toggleFavorite(id); | ||
this.contacts = this.contacts.map(contact => | ||
contact.id === id ? {...contact, isFavorite: !contact.isFavorite} : contact | ||
); | ||
} | ||
|
||
override getAppOptions() { | ||
return [themeAppOption(), sizingModeAppOption(), autoRefreshAppOption()]; | ||
} | ||
|
||
override async initAsync() { | ||
await super.initAsync(); | ||
await XH.installServicesAsync(ContactService); | ||
await this.loadAsync(); | ||
} | ||
|
||
override async doLoadAsync() { | ||
try { | ||
const contacts = await XH.contactService.getContactsAsync(); | ||
runInAction(() => { | ||
this.contacts = contacts; | ||
this.tagList = uniq(contacts.flatMap(it => it.tags ?? [])).sort() as string[]; | ||
}); | ||
} catch (e) { | ||
XH.handleException(e); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import {hoistCmp, uses} from '@xh/hoist/core'; | ||
import {grid} from '@xh/hoist/cmp/grid'; | ||
import {hframe, filler} from '@xh/hoist/cmp/layout'; | ||
import {storeCountLabel, storeFilterField} from '@xh/hoist/cmp/store'; | ||
import {button} from '@xh/hoist/mobile/cmp/button'; | ||
import {buttonGroupInput} from '@xh/hoist/mobile/cmp/input'; | ||
import {panel} from '@xh/hoist/mobile/cmp/panel'; | ||
import {Icon} from '@xh/hoist/icon'; | ||
import {toolbar} from '@xh/hoist/mobile/cmp/toolbar'; | ||
|
||
import tileView from './cmp/TileView'; | ||
import DirectoryPanelModel from './DirectoryPanelModel'; | ||
import '../DirectoryPanel.scss'; | ||
|
||
const directoryPanel = hoistCmp.factory({ | ||
model: uses(DirectoryPanelModel), | ||
|
||
render({model}) { | ||
return panel({ | ||
className: 'tb-directory-panel', | ||
item: hframe( | ||
panel({ | ||
tbar: tbar(), | ||
item: model.displayMode === 'grid' ? grid() : tileView(), | ||
bbar: bbar() | ||
}) | ||
) | ||
}); | ||
} | ||
}); | ||
|
||
const tbar = hoistCmp.factory<DirectoryPanelModel>(({model}) => { | ||
return toolbar({ | ||
className: 'tb-directory-panel__tbar', | ||
items: [ | ||
storeFilterField({ | ||
leftIcon: Icon.search(), | ||
maxWidth: 400, | ||
minWidth: 200 | ||
}), | ||
filler(), | ||
buttonGroupInput({ | ||
outlined: true, | ||
bind: 'displayMode', | ||
value: model.displayMode, | ||
intent: 'primary', | ||
items: [ | ||
button({ | ||
icon: Icon.list(), | ||
value: 'grid', | ||
width: 50 | ||
}), | ||
button({ | ||
icon: Icon.users(), | ||
value: 'tiles', | ||
width: 50 | ||
}) | ||
] | ||
}) | ||
] | ||
}); | ||
}); | ||
|
||
const bbar = hoistCmp.factory<DirectoryPanelModel>(({model}) => { | ||
const { | ||
gridModel: {store} | ||
} = model; | ||
|
||
return toolbar(filler(), storeCountLabel({store, unit: 'contact'})); | ||
}); | ||
|
||
export default directoryPanel; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You will se this update across this PR.
After some back and forth, Lee suggested that I nest the new
mobile
contacts under the parent /examples/contacts, and do the same withdesktop
.This means that some of this PR will be just file path adjustments to accomodate this change.