Cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Handling complex object with AdditiveList

ANSWERED

Hi,

 

If I want to use the Setting API to produce an ordered list of complex objects, say 

 

 

{
   firstName:string,
   lastName:string
}

 

 

 

Since the AdditiveList is the only Setting UI that can be used to sort the order of a list of values, I think that's the only option I have. AdditiveList takes care of ordering values and deleting values, so far so good.

 

When it comes to adding a new value, it depends on an "addAction". The document mentioned, "addAction: a component that will be used to render the input style for this list. Should be a component that allows for input, such as Select or TextInput." However, I cannot find any component that allows input of a complex object - I don't want to give user a text input and ask them to input JSON 🙂

 

Then it looks like there's two possible solutions.

 

The first one, somehow make a component produce a complex object. Perhaps somehow allow a <Section> behave like an input to produce a complex object, while acceptable by <AdditiveList> as addAction? The problem is I cannot figure out how.

 

The second solution is what I'm working towards. The solution is not to use AdditiveList to add new object, only to sort the order and delete an object. Use other UI to allow user add a new complex object.

 

Then AdditiveList natively shows an "Add" button working with "addAction". I tried to make it disappear, not to mislead user. So far, I tried to give "addAction" a <Button>, a <Text>, and null. None of the usage is documented in API, so I'm taking some risk. The result is:

 

<Button> - does not show up

<Text> - displayed

null - runtime error

 

So my solution is, give the AdditiveList a Button or Text, anyway disable its add function, and add new object from elsewhere. 

 

My question is: is there any better way to achieve this?

 

Thanks

 

Bing

Best Answer
0 Votes
1 BEST ANSWER

Accepted Solutions

I forgot to show what the settings screen looks like when displaying the <select>:

select.png

 

Peter McLennan
Gondwana Software

View best answer in original post

Best Answer
0 Votes
14 REPLIES 14

I wonder if there might be another way to do this: React.

 

In this, I used a <select> to display a list. The list items can be added to, deleted or reordered. This was done by defining the <select> options with

options={slideSelectOptions}

 

slideSelectOptions is just a JS array. Whenever the array is manipulated in code, React takes care of redisplaying the <select>.

 

Obviously your .jsx has to provide an interface to allow users to indicate what they want done with the list, and those wishes are effected in JS by changing slideSelectOptions.

 

If you want a promo code for that app so you can see if the approach might work for you, let me know. But you can't have the source code!

Peter McLennan
Gondwana Software
Best Answer
0 Votes

I forgot to show what the settings screen looks like when displaying the <select>:

select.png

 

Peter McLennan
Gondwana Software
Best Answer
0 Votes

Hello Bing,

 

I haven't needed to sort in JSX, but I there are some more complex things you can do with the HTML generation, so you should be able to add sorting fairly easily to whatever control you want to use.

 

If you are using the basic structure of a Fitbit JSX file, it should look something like:

 

function mySettings(props) {
  const { settings } = props;

  return (
  <Page>
    <Section title={<Text bold align="center">My Title</Text>}>

<!-- rest of page here -->

  </Page>)
}

 

You can nest useful functions within the mySettings function like

 

function mySettings(props) {
  const { settings } = props;
  function tgl(na){
    var test = ["basket","triangle","apple"]
    console.log(test.sort())
    return <Toggle
             settingsKey={`${na}`}
           />
  }
  return (
  <Page>
    <Section title={<Text>Test Section</Text>} >
      {tgl("shSteps")}
    </Section>
  </Page>)
}

 

As shown, the array sort function will work here.

Regards, 

Best Answer
0 Votes

Hi Peter,

 

Thanks for the tip mate!

 

<Select> would display the list, and the list can be manipulated. However it still needs other UI to allow user add the item, and re-ordering the item, cause <Select> itself does not do this. 

I guess my best shot is still <AdditiveList>. At least it allows delete and re-ordering out of box.

 

Nevertheless, thanks anyway!

 

Bing

Best Answer
0 Votes

Hi morningReign,

 

Thanks for your reply. Sorry I didn't express myself clear enough. What I want is to allow user manage the order of the array of the complex objects, much like AdditiveList allow user to manage an array of simple texts, or manage an array of complex objects from selections. 

 

My problem however is to allow user add a complex object with text input (like 'firstName' & 'lastName'), and able to reorder the objects. The fitbit API document only gave examples of handling simple text, or handling complex objects - but the complex objects can only be selected from a list of existing objects. 

 

What I want to achieve is to allow user click "add" and input firstName and lastName, also allow user to re-order the list, and delete any of the input. 

 

Anyway, thanks for the reply!

 

Best regards

Bing

Best Answer
0 Votes

The app I offered you maintains a list of items, each of which comprises about nine fields (including an image). The settings UI allows those complex items to be created, edited, resequenced and deleted.

 

You're right that ability to do those things required explicit coding in the companion and settings, because no out-of-the-box settings element can do it all. Half of the trick is trusting React to update the UI when the underlying source data (eg, array on which a <select> is based) changed. The other half is dynamic Settings code that appears and disappears depending on what the user wants to do. morningReign provided an example of this.

Peter McLennan
Gondwana Software
Best Answer
0 Votes

Settings main menuSettings main menuAdd/edit an entryAdd/edit an entryReorder entriesReorder entries

Peter McLennan
Gondwana Software
Best Answer
0 Votes

Hi Peter,

 

Woo that's a very complex setting, inspired me to do something similar. Thanks!

 

How do you get an image in Setting API? Nowhere in the documentation has any mention of allowing user uploading something or selecing an image. I have found out from other posts in this forum that fitbit has some undocumented features, such as a <Webconfig> tag, that some developers rely upon. How do I find those features? Where, if possible, can I find some documents about them? 

 

Thanks again!

 

Bing

Best Answer
0 Votes

I didn't use any undocumented features. I used the <ImagePicker> to let the user select an image for the current slide ( = object = item). That component automatically puts the image into settings as a Base64-encoded string. You can read that string and setItem() it to an item-specific key in settings. That way, you can have multiple images in your settings, accessible via item number or whatever key you want.

Redisplaying the image later, including within a <TextImageRow>, involves reading the image strings back out of settingsStorage and associating them with the relevant settings components.

React takes care of updating settings components when the underlying data source (settingsStorage) changes.

Peter McLennan
Gondwana Software
Best Answer
0 Votes

Thanks for the reply. I realised <ImagePicker> is indeed in the Setting API document. It's just not listed in the guides document (https://dev.fitbit.com/build/guides/settings/) so that's why I missed it. Thanks for the tips again!

Best Answer

So, finally I got it. I worked out an example handling adding/removing/updating/reordering an array of "Person" objects, with two "pages", a list page and an add/update page.

 

Here's the code in typescript (in index.tsx)

 

 

interface Person {
  name:string,
  address?:string,
  uuid:string
}

function uuidv4():string {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}

function settingsComponent(props: Parameters<Parameters<typeof registerSettingsPage>[0]>[0]) {
  const persons: Person[] = props.settings.persons?JSON.parse(props.settings.persons):[]
  console.log("Rendering, "+JSON.stringify(props.settings))
  if (props.settings.addPage === 'true') 
    return (
      <Page>
        <Section title={props.settings.curItem === undefined?"Add a Person":"Update Person"}>
        <TextInput label="Name" settingsKey="newPersonName"
        ></TextInput>
        <TextInput label="Address" settingsKey="newPersonAddress"
        ></TextInput>
        {props.settings.curItem === undefined?
        ([<Button 
        label="Add"
        onClick={()=>{
          persons.push({
            name: JSON.parse(props.settings.newPersonName).name as string,
            address: JSON.parse(props.settings.newPersonAddress).name as string,
            uuid: uuidv4()
          })
          props.settingsStorage.setItem('persons', JSON.stringify(persons))   
          props.settingsStorage.setItem('addPage', "false")           
        }}/>,
        <Button 
        label="Cancel"
        onClick={()=>{
          props.settingsStorage.setItem('addPage', "false")
        }}/>        
        ])
        :
        ([
          <Button 
        label="Update"
        onClick={()=>{
          const original:Person = persons.find((p)=>p.uuid===props.settings.curItem);
          if (original) {
            original.name = JSON.parse(props.settings.newPersonName).name as string
            original.address = JSON.parse(props.settings.newPersonAddress).name as string
          }
          props.settingsStorage.setItem('persons', JSON.stringify(persons))   
          props.settingsStorage.setItem('addPage', "false")           
        }}/>,
        <Button 
        label="Cancel"
        onClick={()=>{
          props.settingsStorage.setItem('addPage', "false")
        }}/>        
        ])
        }
        
      </Section>
      </Page>
    )
  else
    return (
    <Page>
      <AdditiveList
        title="Person"
        description="Description of additive list"
        key="w"
        addAction={
          <Button label="Add Person" onClick={()=>{
            props.settingsStorage.setItem("addPage","true")
            props.settingsStorage.removeItem("curItem")
            props.settingsStorage.removeItem('newPersonName')
            props.settingsStorage.removeItem('newPersonAddress')
          }}/>
        }
        settingsKey="persons"
        renderItem={
          ({name, address, uuid})=>
          <Button label={name+","+address}
          onClick={()=>{
            props.settingsStorage.setItem('addPage', "true")
            props.settingsStorage.setItem('curItem', uuid)
            props.settingsStorage.setItem('newPersonName', JSON.stringify({name:name}))
            props.settingsStorage.setItem('newPersonAddress', JSON.stringify({name:address}))
          }}
          />
        }

      />
    </Page>
    )
}

registerSettingsPage(settingsComponent);

 

Best Answer

Excellent work @BingRen 

 

I am continually impressed with how dynamic the settings page can be.

Best Answer

Hi. I was wondering what to do if this error happens?

uncaught-syntaxerror-unexpected-token-u-json

 

Best Answer
0 Votes

I think that can happen when trying to use JSON.stringify() on an undefined object.

Peter McLennan
Gondwana Software
Best Answer
0 Votes