Skip to content

Commit

Permalink
fix(ui): 🐛 improve reactivity of contacts & qr code
Browse files Browse the repository at this point in the history
  • Loading branch information
jojobyte committed Oct 26, 2023
1 parent d66c99f commit 778d8c8
Show file tree
Hide file tree
Showing 10 changed files with 645 additions and 288 deletions.
12 changes: 8 additions & 4 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@
- [ ] Dialogs
- [x] Send
- [x] Request
- [ ] QR Code
- [ ] Share
- [ ] Scan
- [ ] Add a Contact
- [x] QR Code
- [x] Share
- [x] Scan
- [x] Add a Contact
- [x] Share QR Code not getting up to date name for Profile
- [x] Changing alias field removes wallet from localForage contact
- [x] `Finish pairing` contact in contacts list after save.
- should just be named contact
- [ ] Send & Request Funds to / from Contact
- [ ] Fiat balance from:
- https://rates2.dashretail.org/rates?source=dashretail&%7B%7D=
Expand Down
36 changes: 22 additions & 14 deletions src/components/contacts-list.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { lit as html } from '../helpers/lit.js'
import { envoy, restate, } from '../helpers/utils.js'
import {
envoy,
restate,
sortContactsByAlias,
} from '../helpers/utils.js'
// import { updateAllFunds, } from '../helpers/wallet.js'

function getAvatar(info) {
Expand Down Expand Up @@ -68,7 +72,7 @@ const initialState = {
${state.footer(state)}
`,
item: c => {
let user = c.info?.preferred_username
let user = c.alias || c.info?.preferred_username
let name = c.info?.name

let itemAlias = user
Expand Down Expand Up @@ -110,16 +114,16 @@ const initialState = {
)
},
handleContactsChange: (newState, oldState) => {
console.log(
'handle contacts update',
{newState, oldState}
)
// if (
// newState?.walletFunds?.balance !==
// oldState?.walletFunds?.balance
// ) {
// newState.elements.figure.innerHTML = newState.content(newState)
// }
if (newState.contacts !== oldState.contacts) {
console.log(
'handle contacts update',
{newState, oldState}
)

newState.render?.({
contacts: newState.contacts?.sort(sortContactsByAlias),
})
}
}
},
}
Expand All @@ -132,6 +136,10 @@ let state = envoy(
export async function setupContactsList(
el, setupState = {}
) {
console.log(
'setupContactsList state.contacts',
state.contacts
)
restate(state, setupState)
// if (setupState?.events?.handleContactsChange) {
// state._listeners = [
Expand Down Expand Up @@ -188,7 +196,7 @@ export async function setupContactsList(

state.removeAllListeners = removeAllListeners

async function render (
async function render(
renderState = {},
position = 'afterbegin',
) {
Expand All @@ -211,7 +219,7 @@ export async function setupContactsList(
return {
element: section,
render,
restate,
restate: async newState => await restate(state, newState),
}
}

Expand Down
31 changes: 31 additions & 0 deletions src/helpers/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

export const STOREAGE_SALT = 'b9f4088bd3a93783147e3d78aa10cc911a2449a0d79a226ae33a5957b368cc18'

export const DUFFS = 100000000;

export const OIDC_CLAIMS = {
preferred_username: '',
name: '', // [given_name,middle_name,family_name].join(' ')
given_name: '',
family_name: '',
middle_name: '',
nickname: '',
website: '',
address: {},
email: '',
email_verified: false,
phone_number: '',
phone_number_verified: false,
profile: '', // 'https://imgur.com/gallery/y6sSvCr.json',
picture: '', // 'https://i.imgur.com/y6sSvCr.jpeg', // url to avatar img
sub: '',
xpub: '',
updated_at: (new Date()).toISOString(),
}

export const PHRASE_REGEX = new RegExp(
/^([a-zA-Z]+\s){11,}([a-zA-Z]+)$/
)
export const ALIAS_REGEX = new RegExp(
/^[a-zA-Z0-9]{1,}$/
)
236 changes: 232 additions & 4 deletions src/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {
DashPhrase,
} from '../imports.js'

import { DUFFS } from './constants.js'

// export async function walletSchema(
// phrase = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong',
// accountIndex = 0
Expand Down Expand Up @@ -223,8 +225,6 @@ export function phraseToEl(phrase, el = 'span', cls = 'tag') {
)?.join(' ')
}

export const DUFFS = 100000000;

/**
* @param {Number} duffs - ex: 00000000
* @param {Number} [fix] - value for toFixed - ex: 8
Expand Down Expand Up @@ -312,6 +312,38 @@ export function setClipboard(event) {
);
}

/**
* Creates a `Proxy` wrapped object with optional listeners
* that react to changes
*
* @example
* let fooHistory = []
*
* let kung = envoy(
* { foo: 'bar' },
* function firstListener(state, oldState) {
* if (state.foo !== oldState.foo) {
* localStorage.foo = state.foo
* },
* },
* async function secondListener(state, oldState) {
* if (state.foo !== oldState.foo) {
* fooHistory.push(oldState.foo)
* }
* }
* )
* kung.foo = 'baz'
* console.log(localStorage.foo) // 'baz'
* kung.foo = 'boo'
* console.log(fooHistory) // ['bar','baz']
*
* @param {Object} obj
* @param {...(
* state: any, oldState: any, prop: string | symbol
* ) => void | Promise<void>?} [initListeners]
*
* @returns {obj}
*/
export function envoy(obj, ...initListeners) {
let _listeners = [...initListeners]
return new Proxy(obj, {
Expand All @@ -332,7 +364,8 @@ export function envoy(obj, ...initListeners) {
_listeners.forEach(
fn => fn(
{...obj, [prop]: value},
obj
obj,
prop
)
)

Expand Down Expand Up @@ -460,4 +493,199 @@ export function parseAddressField(uri) {
// }

return result
}
}

export function isEmpty(value) {
if (value === null) {
return true
}
// if (typeof value === 'boolean' && value === false) {
// return true
// }
if (typeof value === 'string' && value?.length === 0) {
return true
}
if (typeof value === 'object' && Object.keys(value)?.length === 0) {
return true
}
if (Array.isArray(value) && value.length === 0) {
return true
}
return false;
}

export function generateShareURI(state, protocol = 'web+dash') {
let claims = [
["xpub", state.wallet?.xpub || ''],
["sub", state.wallet?.xkeyId || ''],
]

if (state.userInfo) {
let filteredInfo = Array.from(Object.entries(state.userInfo))
.filter(p => {
let [key, val] = p
if (
![
'updated_at',
'email_verified',
'phone_number_verified',
].includes(key) &&
!isEmpty(val)
) {
return true
}
})

claims = [
...claims,
...filteredInfo,
]
}

let scope = claims.map(p => p[0]).join(',')

console.log('Generate QR claims', claims, scope)

return new URL(
`${protocol}://?${
new URLSearchParams([
...claims,
['scope', scope]
])
}`
)
}

export async function loadStore(store, callback) {
// let storeLen = await store.length()
let result = []

return await store.iterate((v, k, i) => {
result.push(v)

// if (i === storeLen) {
// return result
// }
})
.then(() => callback(result))
.catch(err => {
console.error('loadStore', err)
return null
});
}

export async function loadStoreObject(store, callback) {
// let storeLen = await store.length()
let result = {}

return await store.iterate((v, k, i) => {
result[k] = v

// if (i === storeLen) {
// return result
// }
})
.then(() => callback(result))
.catch(err => {
console.error('loadStoreObject', err)
return null
});
}

/**
* promise debounce changes
*
* https://www.freecodecamp.org/news/javascript-debounce-example/
*
* @example
* const change = debounce((a) => console.log('Saving data', a));
* change('b');change('c');change('d');
* 'Saving data d'
*
* @param {(...args) => void} callback
* @param {number} [delay]
*
* @returns {Promise<any>}
*/
export async function debouncePromise(callback, delay = 300) {
let timer

return await new Promise(resolve => async (...args) => {
clearTimeout(timer)

timer = setTimeout(() => {
resolve(callback.apply(this, args))
}, delay)
})

// return async (...args) => {
// clearTimeout(timer)

// timer = resolve => setTimeout(() => {
// resolve(callback.apply(this, args))
// }, delay)

// return await new Promise(timer)
// }
}

/**
* debounce changes
*
* https://www.freecodecamp.org/news/javascript-debounce-example/
*
* @example
* const change = debounce((a) => console.log('Saving data', a));
* change('b');change('c');change('d');
* 'Saving data d'
*
* @param {(...args) => void} callback
* @param {number} [delay]
*
* @returns {(...args) => void}
*/
export function debounce(callback, delay = 300) {
let timer

return (...args) => {
clearTimeout(timer)

timer = setTimeout(() => {
return callback.apply(this, args)
}, delay)

return timer
}
}

/**
* debounce that immediately triggers and black holes any extra
* executions within the time delay
*
* https://www.freecodecamp.org/news/javascript-debounce-example/
*
* @example
* const dry = nobounce((a) => console.log('Saving data', a));
* dry('b');dry('c');dry('d');
* 'Saving data b'
*
* @param {(...args) => void} callback
* @param {number} [delay]
*
* @returns {(...args) => void}
*/
export function nobounce(callback, delay = 300) {
let timer

return (...args) => {
if (!timer) {
callback.apply(this, args)
}

clearTimeout(timer)

timer = setTimeout(() => {
timer = undefined
}, delay)
}
}
Loading

0 comments on commit 778d8c8

Please sign in to comment.