Skip to content

Commit

Permalink
v1.2.0 - Fixes and new feature (#2)
Browse files Browse the repository at this point in the history
* fixes for localstorage, hydrate-view request, auto-refresh, react bad iframe with maximum z-index

* fix fetch data using Storage API, new feature for `Utils.promisify`

* fix issue with hidrateViewer where the storage update observable was being used to notify

* v1.2.0
  • Loading branch information
wpdas authored Apr 7, 2023
1 parent ab29eac commit affd70b
Show file tree
Hide file tree
Showing 14 changed files with 327 additions and 35 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ dist
temp

# build
/components
/hooks
/auth
/bridge
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,12 @@ const getRoomsListHandler = (request, response) => {

In the Widget side, the handler is going to provide 3 props: `request` with its type and payload, `response` that is the way the app send a answer back to the React App and `utils` that provides some useful features like the `promisify`.

The `promisify` needs 3 parameters: `caller` which is going to request something, `resolve`, a method that is going to be called as soon as the _caller_ find an answer and `reject`, method that will be called when the service times out.
The `promisify` needs 4 parameters: `caller` which is going to request something, `resolve`, a method that is going to be called as soon as the _caller_ find an answer, `reject`, method that will be called when the service times out and `timeout`, a optional parameter where you can set the timeout for this promise. The default timeout is 10 seconds.

So, promisify implementation is

```ts
promisify(caller: () => any, resolve: () => void, reject: () => void)
promisify(caller: () => any, resolve: () => void, reject: () => void, timeout: number)
```

Example of using the promisify feature inside the Widget:
Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"name": "near-social-bridge",
"version": "1.1.0",
"version": "1.2.0",
"description": "This library allows you to create a common application using ReactJS and inject it in a controlled way into a Widget on Near Social. Therefore, the Widget talks to the React application and vice versa, making it possible to consume Discovery API resources within the React application.",
"main": "./dist/cjs/index.js",
"module": "./index.js",
"types": "./index.d.ts",
"scripts": {
"clean": "rm -rf dist hooks auth bridge navigation request services session-storage utils constants.d.ts constants.js index.d.ts index.js",
"build": "npm run clean; tsc -p tsconfig.prod.json; npm run build:cjs",
"clean": "rm -rf dist components hooks auth bridge navigation request services session-storage utils constants.d.ts constants.js index.d.ts index.js",
"copy-files": "copyfiles -u 1 src/components/spinner.css src/bridge/contexts/fixBadIframe.css ./",
"build": "npm run clean; tsc -p tsconfig.prod.json; npm run copy-files; npm run build:cjs",
"build:esm": "tsc",
"build:cjs": "tsc --module commonjs --outDir dist/cjs",
"prepack": "npm install; npm run build",
Expand Down Expand Up @@ -35,6 +36,7 @@
"@types/react": "^18.0.12",
"@typescript-eslint/eslint-plugin": "^5.55.0",
"@typescript-eslint/parser": "^5.55.0",
"copyfiles": "^2.4.1",
"eslint": "^8.36.0",
"eslint-config-prettier": "^8.7.0",
"eslint-plugin-prettier": "^4.2.1",
Expand Down
14 changes: 11 additions & 3 deletions src/bridge/contexts/NearSocialBridgeProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import isLocalDev from '../../utils/isLocalDev'
import React, { createContext, useCallback, useEffect, useState } from 'react'
import AuthProvider from '../../auth/contexts/AuthProvider'
import {
Expand All @@ -8,6 +7,10 @@ import {
postMessage as postMessageService,
} from '../../services/bridge-service'
import { GetMessageCallBack, NearSocialBridgeProps } from '../types'
import isDevelopment from '../../utils/isDevelopment'
import { initRefreshService } from '../../utils/refresh'
import Spinner from '../../components/Spinner'
import './fixBadIframe.css' // DON'T REMOVE

const defaultValue: NearSocialBridgeProps = {
postMessage: () => {
Expand Down Expand Up @@ -92,9 +95,9 @@ const NearSocialBridgeProvider: React.FC<Props> = ({ children, fallback }) => {
}
}, [])

if (!isConnected && !isLocalDev) {
if (!isConnected) {
if (fallback) return <>{fallback}</>
return null
return <Spinner />
}

return (
Expand All @@ -105,3 +108,8 @@ const NearSocialBridgeProvider: React.FC<Props> = ({ children, fallback }) => {
}

export default NearSocialBridgeProvider

// DEV Utils features
if (isDevelopment) {
initRefreshService()
}
12 changes: 12 additions & 0 deletions src/bridge/contexts/fixBadIframe.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
This is a palliative feature to fix a bug that happens with react.
When content inside an iframe is updated, react adds an iframe covering the entire
screen using the maximum z-index, thus making any interaction with the application impossible.
Using "react-error-overlay": "6.0.9" didn't fix the issue.
Source: https://stackoverflow.com/questions/69051008/react-injecting-iframe-with-max-z-index-on-reload-after-changes-development
*/
body > iframe[style*='2147483647']{
display: none;
}
21 changes: 21 additions & 0 deletions src/components/Spinner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react'
import './spinner.css'

const Spinner = () => (
<div style={{ margin: 'auto', paddingTop: '236px', width: '100%' }}>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
<div className="lds-ripple">
<div></div>
<div></div>
</div>
</div>
</div>
)

export default Spinner
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Spinner } from './Spinner'
47 changes: 47 additions & 0 deletions src/components/spinner.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* Spinner */
.lds-ripple {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
}
.lds-ripple div {
position: absolute;
border: 4px solid #000000;
opacity: 1;
border-radius: 50%;
animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}
.lds-ripple div:nth-child(2) {
animation-delay: -0.5s;
}
@keyframes lds-ripple {
0% {
top: 36px;
left: 36px;
width: 0;
height: 0;
opacity: 0;
}
4.9% {
top: 36px;
left: 36px;
width: 0;
height: 0;
opacity: 0;
}
5% {
top: 36px;
left: 36px;
width: 0;
height: 0;
opacity: 1;
}
100% {
top: 0px;
left: 0px;
width: 72px;
height: 72px;
opacity: 0;
}
}
9 changes: 1 addition & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import isDevelopment from './utils/isDevelopment'
import { initRefreshService } from './utils/refresh'

export * from './bridge'
export * from './navigation'
export * from './request'
export * from './services'
export * from './session-storage'
export * from './auth'
export * from './hooks'

// DEV Utils features
if (isDevelopment) {
initRefreshService()
}
export * from './components'
3 changes: 2 additions & 1 deletion src/navigation/createStackNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import useNavigation from './hooks/useNavigation'
import { syncContentHeight } from './syncContentHeight'
import { ParamListBase, Route } from './types'
import { useAuth } from '../auth'
import Spinner from '../components/Spinner'

/**
* Create and provides a Navigator (Routes controler) and Screen (Route component).
Expand Down Expand Up @@ -173,7 +174,7 @@ const createStackNavigator = function <T extends ParamListBase>(fallback?: React

// Shows the Fallback component while waiting for the connection and
// userInfo
if (!isReady || !auth.ready) return fallback ? <>{fallback}</> : null
if (!isReady || !auth.ready) return fallback ? <>{fallback}</> : <Spinner />

return <>{currentScreen}</>
}
Expand Down
15 changes: 12 additions & 3 deletions src/session-storage/persistStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@ import { getConnectionStatus } from '../services/bridge-service'
import isLocalDev from '../utils/isLocalDev'
import sessionStorage, { sessionStorageUpdateObservable } from './sessionStorage'

let isLocalStorageAccessible = true

try {
// just try to read it
window.localStorage
} catch (error) {
isLocalStorageAccessible = false
}

const setItem = (key: string, value: any) => {
return new Promise<void>((resolve) => {
if (isLocalDev && localStorage) {
if (isLocalDev && isLocalStorageAccessible) {
resolve(localStorage.setItem(key, value))
} else {
resolve(sessionStorage.setItem(key, value))
Expand All @@ -15,7 +24,7 @@ const setItem = (key: string, value: any) => {
const getItem = (key: string) => {
return new Promise((resolve) => {
// Local host: localStorage
if (isLocalDev && localStorage) {
if (isLocalDev && isLocalStorageAccessible) {
resolve(localStorage.getItem(key))
return
}
Expand All @@ -36,7 +45,7 @@ const getItem = (key: string) => {

const removeItem = (key: string) => {
return new Promise<void>((resolve) => {
if (isLocalDev && localStorage) {
if (isLocalDev && isLocalStorageAccessible) {
resolve(localStorage.removeItem(key))
} else {
resolve(sessionStorage.removeItem(key))
Expand Down
5 changes: 2 additions & 3 deletions src/session-storage/sessionStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ const keys = () => Object.keys(_storage)
*/
const hydrateViewer = async () => {
// Hydrate the Viewer "sessionStorageClone" state with External App _storage object
await request(REQUEST_KEYS.SESSION_STORAGE_HYDRATE_VIEWER, _storage)
sessionStorageUpdateObservable.notify(_storage)
await request(REQUEST_KEYS.SESSION_STORAGE_HYDRATE_VIEWER, _storage, { forceTryAgain: true })
}

/**
Expand All @@ -53,7 +52,7 @@ const hydrateViewer = async () => {
const hydrate = async () => {
// Request to hydrate the external app
const view_sessionStorageClone = await request(REQUEST_KEYS.SESSION_STORAGE_HYDRATE_APP, undefined, {
forceTryAgain: true,
forceTryAgain: false,
})

// Hydrate _storage with data stored in the Viewer "sessionStorageClone" state
Expand Down
28 changes: 21 additions & 7 deletions widget/NearSocialBridgeCore.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,14 @@ const Utils = {
/**
* Call resolve or reject for a given caller
* E.g:
* Utils.promisify(() => getCachedObject(), (res) => console.log(res), (err) => console.log(err))
* var timeout = 5000 // 5sec
* Utils.promisify(() => getCachedObject(), (res) => console.log(res), (err) => console.log(err), timeout)
*
* Default timeout is 10 seconds
*/
promisify: (caller, resolve, reject) => {
promisify: (caller, resolve, reject, _timeout) => {
const timer = 1000
const timeout = timer * 10
const timeout = _timeout || timer * 10
let timeoutCheck = 0

const find = () => {
Expand Down Expand Up @@ -192,10 +195,21 @@ const sessionStorageHydrateViewer = (requestType, payload) => {

// Retrieve stored data
const sessionStorageHydrateApp = (requestType) => {
// get stored data
const storageData = Storage.privateGet(CORE_STORAGE_KEY)
const responseBody = buildAnswer(requestType, storageData)
Utils.sendMessage(responseBody)
Utils.promisify(
// get stored data
() => Storage.privateGet(CORE_STORAGE_KEY),
(storageData) => {
const responseBody = buildAnswer(requestType, storageData)
Utils.sendMessage(responseBody)
},
() => {
// After 3 seconds, if no data is found, just send
// an empty answer
const responseBody = buildAnswer(requestType)
Utils.sendMessage(responseBody)
},
3000
)
}

// Set thew new iFrame height based on the new screen/route
Expand Down
Loading

0 comments on commit affd70b

Please sign in to comment.