diff --git a/.changeset/fresh-cows-repair.md b/.changeset/fresh-cows-repair.md new file mode 100644 index 000000000..84a87137d --- /dev/null +++ b/.changeset/fresh-cows-repair.md @@ -0,0 +1,5 @@ +--- +"@onflow/fcl-core": minor +--- + +Add getStorageProvider to currentUser configuration diff --git a/.changeset/pre.json b/.changeset/pre.json index b4d2a197a..aeefa8799 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -26,11 +26,15 @@ "@onflow/util-uid": "1.2.2" }, "changesets": [ + "fresh-cows-repair", + "lovely-bikes-applaud", "odd-actors-ring", "pink-students-divide", "shaggy-snakes-vanish", "slow-lies-fix", + "slow-peaches-vanish", "soft-tomatoes-brake", + "stupid-turkeys-hope", "tasty-ducks-mix", "witty-pants-argue" ] diff --git a/.changeset/slow-peaches-vanish.md b/.changeset/slow-peaches-vanish.md new file mode 100644 index 000000000..ac02855d1 --- /dev/null +++ b/.changeset/slow-peaches-vanish.md @@ -0,0 +1,5 @@ +--- +"@onflow/fcl": minor +--- + +Use localStorage as default & export LOCAL_STORAGE/SESSION_STORAGE as helpers for fcl.storage.default configuration key diff --git a/.changeset/stupid-turkeys-hope.md b/.changeset/stupid-turkeys-hope.md new file mode 100644 index 000000000..b7f1e70f1 --- /dev/null +++ b/.changeset/stupid-turkeys-hope.md @@ -0,0 +1,5 @@ +--- +"@onflow/fcl-react-native": patch +--- + +Pass getStorageProvider to currentUser configuration diff --git a/package-lock.json b/package-lock.json index 90a1870b9..1fecea7e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28713,13 +28713,13 @@ }, "packages/fcl": { "name": "@onflow/fcl", - "version": "1.12.4-alpha.5", + "version": "1.13.0-alpha.6", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.25.7", "@onflow/config": "1.5.1-alpha.0", - "@onflow/fcl-core": "1.13.0-alpha.3", - "@onflow/fcl-wc": "5.4.1-alpha.3", + "@onflow/fcl-core": "1.13.0-alpha.4", + "@onflow/fcl-wc": "5.4.1-alpha.4", "@onflow/interaction": "0.0.11", "@onflow/rlp": "1.2.3-alpha.0", "@onflow/sdk": "1.5.4-alpha.1", @@ -28786,7 +28786,7 @@ }, "packages/fcl-core": { "name": "@onflow/fcl-core", - "version": "1.13.0-alpha.3", + "version": "1.13.0-alpha.4", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.25.7", @@ -28834,12 +28834,12 @@ }, "packages/fcl-react-native": { "name": "@onflow/fcl-react-native", - "version": "1.9.7-alpha.3", + "version": "1.9.7-alpha.4", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.25.7", "@onflow/config": "1.5.1-alpha.0", - "@onflow/fcl-core": "1.13.0-alpha.3", + "@onflow/fcl-core": "1.13.0-alpha.4", "@onflow/interaction": "0.0.11", "@onflow/rlp": "1.2.3-alpha.0", "@onflow/sdk": "1.5.4-alpha.1", @@ -28886,7 +28886,7 @@ }, "packages/fcl-wc": { "name": "@onflow/fcl-wc", - "version": "5.4.1-alpha.3", + "version": "5.4.1-alpha.4", "license": "Apache-2.0", "dependencies": { "@babel/plugin-proposal-class-properties": "^7.18.6", @@ -28916,7 +28916,7 @@ "jest": "^29.7.0" }, "peerDependencies": { - "@onflow/fcl-core": "1.13.0-alpha.3" + "@onflow/fcl-core": "1.13.0-alpha.4" } }, "packages/fcl/node_modules/typescript": { diff --git a/packages/fcl-core/CHANGELOG.md b/packages/fcl-core/CHANGELOG.md index ea928ce35..49ba41661 100644 --- a/packages/fcl-core/CHANGELOG.md +++ b/packages/fcl-core/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/fcl +## 1.13.0-alpha.4 + +### Minor Changes + +- [#2001](https://github.com/onflow/fcl-js/pull/2001) [`bac8c54db1b6821a2158923544aa537885d5a0e7`](https://github.com/onflow/fcl-js/commit/bac8c54db1b6821a2158923544aa537885d5a0e7) Thanks [@jribbink](https://github.com/jribbink)! - Add getStorageProvider to currentUser configuration + ## 1.13.0-alpha.3 ### Patch Changes diff --git a/packages/fcl-core/package.json b/packages/fcl-core/package.json index 31f55352e..6594c3f16 100644 --- a/packages/fcl-core/package.json +++ b/packages/fcl-core/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/fcl-core", - "version": "1.13.0-alpha.3", + "version": "1.13.0-alpha.4", "description": "Flow Client Library", "license": "Apache-2.0", "author": "Dapper Labs ", diff --git a/packages/fcl-core/src/current-user/index.js b/packages/fcl-core/src/current-user/index.js index 4e3a83e2f..58248ccbc 100644 --- a/packages/fcl-core/src/current-user/index.js +++ b/packages/fcl-core/src/current-user/index.js @@ -1,6 +1,6 @@ import "../default-config" import * as t from "@onflow/types" -import {account, arg} from "@onflow/sdk" +import {arg} from "@onflow/sdk" import {config} from "@onflow/config" import {spawn, send, INIT, SUBSCRIBE, UNSUBSCRIBE} from "@onflow/util-actor" import {withPrefix, sansPrefix} from "@onflow/util-address" @@ -46,53 +46,61 @@ const getStoredUser = async storage => { return stored || fallback } -const HANDLERS = { - [INIT]: async ctx => { - if (typeof window === "undefined") { - console.warn( - ` +const makeHandlers = cfg => { + // Wrapper for backwards compatibility + const getStorageProvider = async () => { + if (cfg.getStorageProvider) return await cfg.getStorageProvider() + return await config.first(["fcl.storage", "fcl.storage.default"]) + } + + return { + [INIT]: async ctx => { + if (typeof window === "undefined") { + console.warn( + ` %cFCL Warning ============================ "currentUser" is only available in the browser. For more info, please see the docs: https://docs.onflow.org/fcl/ ============================ `, - "font-weight:bold;font-family:monospace;" - ) - } + "font-weight:bold;font-family:monospace;" + ) + } - ctx.merge(JSON.parse(DATA)) - const storage = await config.first(["fcl.storage", "fcl.storage.default"]) - if (storage.can) { - const user = await getStoredUser(storage) - if (notExpired(user)) ctx.merge(user) - } - }, - [SUBSCRIBE]: (ctx, letter) => { - ctx.subscribe(letter.from) - ctx.send(letter.from, UPDATED, {...ctx.all()}) - }, - [UNSUBSCRIBE]: (ctx, letter) => { - ctx.unsubscribe(letter.from) - }, - [SNAPSHOT]: async (ctx, letter) => { - letter.reply({...ctx.all()}) - }, - [SET_CURRENT_USER]: async (ctx, letter, data) => { - ctx.merge(data) - const storage = await config.first(["fcl.storage", "fcl.storage.default"]) - if (storage.can) storage.put(NAME, ctx.all()) - ctx.broadcast(UPDATED, {...ctx.all()}) - }, - [DEL_CURRENT_USER]: async (ctx, letter) => { - ctx.merge(JSON.parse(DATA)) - const storage = await config.first(["fcl.storage", "fcl.storage.default"]) - if (storage.can) storage.put(NAME, ctx.all()) - ctx.broadcast(UPDATED, {...ctx.all()}) - }, + ctx.merge(JSON.parse(DATA)) + const storage = await getStorageProvider() + if (storage.can) { + const user = await getStoredUser(storage) + if (notExpired(user)) ctx.merge(user) + } + }, + [SUBSCRIBE]: (ctx, letter) => { + ctx.subscribe(letter.from) + ctx.send(letter.from, UPDATED, {...ctx.all()}) + }, + [UNSUBSCRIBE]: (ctx, letter) => { + ctx.unsubscribe(letter.from) + }, + [SNAPSHOT]: async (ctx, letter) => { + letter.reply({...ctx.all()}) + }, + [SET_CURRENT_USER]: async (ctx, letter, data) => { + ctx.merge(data) + const storage = await getStorageProvider() + if (storage.can) storage.put(NAME, ctx.all()) + ctx.broadcast(UPDATED, {...ctx.all()}) + }, + [DEL_CURRENT_USER]: async (ctx, letter) => { + ctx.merge(JSON.parse(DATA)) + const storage = await getStorageProvider() + if (storage.can) storage.put(NAME, ctx.all()) + ctx.broadcast(UPDATED, {...ctx.all()}) + }, + } } -const spawnCurrentUser = () => spawn(HANDLERS, NAME) +const spawnCurrentUser = cfg => spawn(makeHandlers(cfg), NAME) function notExpired(user) { return ( @@ -148,18 +156,14 @@ const makeConfig = async ({ /** * @description - Factory function to get the authenticate method - * @param {object} [opts] - Options - * @param {object} [opts.platform] - platform that runs the function - * @param {object} [opts.service] - Optional service to use for authentication - * @param {object} [opts.discovery] - Optional discovery options + * @param {CurrentUserConfig} config - Current User Configuration */ const getAuthenticate = - ({platform, discovery}) => + config => /** * @description - Authenticate a user * @param {object} [opts] - Options * @param {object} [opts.service] - Optional service to use for authentication - * @param {object} [opts.user] - Optional user object * @param {boolean} [opts.redir] - Optional redirect flag * @returns */ @@ -174,9 +178,9 @@ const getAuthenticate = } return new Promise(async (resolve, reject) => { - spawnCurrentUser() + spawnCurrentUser(config) const opts = {redir} - const user = await snapshot() + const user = await getSnapshot(config)() const discoveryService = await getDiscoveryService(service) const refreshService = serviceOfType(user.services, "authn-refresh") let accountProofData @@ -188,7 +192,7 @@ const getAuthenticate = service: refreshService, msg: accountProofData, opts, - platform, + platform: config.platform, user, }) send(NAME, SET_CURRENT_USER, await buildUser(response)) @@ -199,7 +203,7 @@ const getAuthenticate = level: LEVELS.error, }) } finally { - return resolve(await snapshot()) + return resolve(await getSnapshot(config)()) } } else { return resolve(user) @@ -223,8 +227,8 @@ const getAuthenticate = msg: accountProofData, config: await makeConfig(discoveryService), opts, - platform, - execStrategy: discovery?.execStrategy, + platform: config.platform, + execStrategy: config.discovery?.execStrategy, user, }) @@ -236,18 +240,23 @@ const getAuthenticate = level: LEVELS.error, }) } finally { - resolve(await snapshot()) + resolve(await getSnapshot(config)()) } }) } /** - * @description - Unauthenticate a user - * @returns {void} + * @description - Factory function to get the unauthenticate method + * @param {CurrentUserConfig} config - Current User Configuration */ -function unauthenticate() { - spawnCurrentUser() - send(NAME, DEL_CURRENT_USER) +function getUnauthenticate(config) { + /** + * @description - Unauthenticate a user + */ + return function unauthenticate() { + spawnCurrentUser(config) + send(NAME, DEL_CURRENT_USER) + } } const normalizePreAuthzResponse = authz => ({ @@ -258,8 +267,12 @@ const normalizePreAuthzResponse = authz => ({ authorization: (authz || {}).authorization || [], }) +/** + * @description - Factory function to get the resolvePreAuthz method + * @param {CurrentUserConfig} config - Current User Configuration + */ const getResolvePreAuthz = - ({platform}) => + config => (authz, {user}) => { const resp = normalizePreAuthzResponse(authz) const axs = [] @@ -276,7 +289,7 @@ const getResolvePreAuthz = return execService({ service: az, msg: signable, - platform, + platform: config.platform, user, }) }, @@ -290,36 +303,36 @@ const getResolvePreAuthz = } /** - * @description - * Produces the needed authorization details for the current user to submit transactions to Flow - * It defines a signing function that connects to a user's wallet provider to produce signatures to submit transactions. + * @description - Factory function to get the authorization method * - * @param {object} opts - running options - * @param {string} opts.platform - platform that runs the function - * @param {object} [opts.discovery] - discovery options - * @param {object} account - Account object - * @returns {Promise} - Account object with signing function + * @param {CurrentUserConfig} config - Current User Configuration */ const getAuthorization = - ({platform, discovery}) => + config => + /** + * @description - Produces the needed authorization details for the current user to submit transactions to Flow + * It defines a signing function that connects to a user's wallet provider to produce signatures to submit transactions. + * + * @param {object} account - Account object + * @returns {Promise} - Account object with signing function + * */ async account => { - spawnCurrentUser() + spawnCurrentUser(config) return { ...account, tempId: "CURRENT_USER", async resolve(account, preSignable) { - const user = await getAuthenticate({platform, discovery})({redir: true}) + const user = await getAuthenticate(config)({redir: true}) const authz = serviceOfType(user.services, "authz") const preAuthz = serviceOfType(user.services, "pre-authz") if (preAuthz) - return getResolvePreAuthz({platform, discovery, user})( + return getResolvePreAuthz(config)( await execService({ service: preAuthz, msg: preSignable, - user, - platform, + platform: config.platform, user, }), { @@ -343,7 +356,7 @@ const getAuthorization = opts: { includeOlderJsonRpcCall: true, }, - platform, + platform: config.platform, user, }) ) @@ -358,59 +371,59 @@ const getAuthorization = } /** - * @description - * The callback passed to subscribe will be called when the user authenticates and un-authenticates, making it easy to update the UI accordingly. - * - * @param {Function} callback - Callback function - * @returns {Function} - Unsubscribe function + * @description - Factory function to get the subscribe method + * @param {CurrentUserConfig} config - Current User Configuration */ -function subscribe(callback) { - spawnCurrentUser() - const EXIT = "@EXIT" - const self = spawn(async ctx => { - ctx.send(NAME, SUBSCRIBE) - while (1) { - const letter = await ctx.receive() - if (letter.tag === EXIT) { - ctx.send(NAME, UNSUBSCRIBE) - return +function getSubscribe(config) { + /** + * @description + * The callback passed to subscribe will be called when the user authenticates and un-authenticates, making it easy to update the UI accordingly. + * + * @param {Function} callback - Callback function + * @returns {Function} - Unsubscribe function + */ + return function subscribe(callback) { + spawnCurrentUser(config) + const EXIT = "@EXIT" + const self = spawn(async ctx => { + ctx.send(NAME, SUBSCRIBE) + while (1) { + const letter = await ctx.receive() + if (letter.tag === EXIT) { + ctx.send(NAME, UNSUBSCRIBE) + return + } + callback(letter.data) } - callback(letter.data) - } - }) - return () => send(self, EXIT) + }) + return () => send(self, EXIT) + } } /** - * @description - Gets the current user - * @returns {Promise} - User object + * @description - Factory function to get the snapshot method + * @param {CurrentUserConfig} config - Current User Configuration */ -function snapshot() { - spawnCurrentUser() - return send(NAME, SNAPSHOT, null, {expectReply: true, timeout: 0}) -} - -async function info() { - spawnCurrentUser() - const {addr} = await snapshot() - if (addr == null) throw new Error("No Flow Address for Current User") - return account(addr) +function getSnapshot(config) { + /** + * @description - Gets the current user + * @returns {Promise} - User object + */ + return function snapshot() { + spawnCurrentUser(config) + return send(NAME, SNAPSHOT, null, {expectReply: true, timeout: 0}) + } } /** - * @description - Resolves the current user as an argument + * Resolves the current user as an argument + * @param {CurrentUserConfig} config - Current User Configuration * - * @param {object} opts - running options - * @param {string} opts.platform - platform that runs the function - * @param {object} [opts.discovery] - discovery options - * @returns {Promise} */ -const getResolveArgument = - ({platform, discovery}) => - async () => { - const {addr} = await getAuthenticate({platform, discovery})() - return arg(withPrefix(addr), t.Address) - } +const getResolveArgument = config => async () => { + const {addr} = await getAuthenticate(config)() + return arg(withPrefix(addr), t.Address) +} const makeSignable = msg => { invariant(/^[0-9a-f]+$/i.test(msg), "Message must be a hex string") @@ -422,21 +435,18 @@ const makeSignable = msg => { /** * @description - Factory function to get the signUserMessage method - * @param {object} opts - running options - * @param {string} opts.platform - platform that runs the function - * @param {object} [opts.discovery] - discovery options - * @returns {function(string): Promise} + * @param {CurrentUserConfig} config - Current User Configuration */ const getSignUserMessage = - ({platform, discovery}) => + config => /** * @description - A method to use allowing the user to personally sign data via FCL Compatible Wallets/Services. * @param {string} msg - Message to sign * @returns {Promise} - Array of CompositeSignatures */ async msg => { - spawnCurrentUser() - const user = await getAuthenticate({platform, discovery})({ + spawnCurrentUser(config) + const user = await getAuthenticate(config)({ redir: true, }) @@ -451,7 +461,7 @@ const getSignUserMessage = const response = await execService({ service: signingService, msg: makeSignable(msg), - platform, + platform: config.platform, user, }) if (Array.isArray(response)) { @@ -464,45 +474,36 @@ const getSignUserMessage = } } +/** + * @typedef {object} CurrentUserConfig - Current User Configuration + * @property {string} platform - Platform + * @property {object} [discovery] - FCL Discovery Configuration + * @property {() => Promise} [getStorageProvider] - Storage Provider Getter + */ + /** * @description * Creates the Current User object * - * @param {object} opts - Configuration Options - * @param {string} opts.platform - Platform - * @param {object} [opts.discovery] - Discovery Config Resolver for additional configuration - */ -const getCurrentUser = ({platform, discovery}) => { - let currentUser = () => { - return { - authenticate: getAuthenticate({platform, discovery}), - unauthenticate, - authorization: getAuthorization({platform, discovery}), - signUserMessage: getSignUserMessage({platform, discovery}), - subscribe, - snapshot, - resolveArgument: getResolveArgument({platform, discovery}), - } + * @param {CurrentUserConfig} config - Current User Configuration + * */ +const getCurrentUser = config => { + const currentUser = { + authenticate: getAuthenticate(config), + unauthenticate: getUnauthenticate(config), + authorization: getAuthorization(config), + signUserMessage: getSignUserMessage(config), + subscribe: getSubscribe(config), + snapshot: getSnapshot(config), + resolveArgument: getResolveArgument(config), } - currentUser.authenticate = getAuthenticate({ - platform, - discovery, - }) - currentUser.unauthenticate = unauthenticate - currentUser.authorization = getAuthorization({ - platform, - discovery, - }) - currentUser.signUserMessage = getSignUserMessage({ - platform, - discovery, - }) - currentUser.subscribe = subscribe - currentUser.snapshot = snapshot - currentUser.resolveArgument = getResolveArgument({platform, discovery}) - - return currentUser + return Object.assign( + () => { + return {...currentUser} + }, + {...currentUser} + ) } export {getCurrentUser} diff --git a/packages/fcl-core/src/exec/mutate.js b/packages/fcl-core/src/exec/mutate.js index 552dbd417..b2ad5cea1 100644 --- a/packages/fcl-core/src/exec/mutate.js +++ b/packages/fcl-core/src/exec/mutate.js @@ -7,13 +7,11 @@ import {isNumber} from "../utils/is" /** * @description - * Factory function that returns a mutate function. + * Factory function that returns a mutate function for a given currentUser. * - * @param {object} opts - Configuration Options - * @param {string} opts.platform - Platform - * @param {object} [opts.discovery] - Discovery options + * @param {ReturnType | import("../current-user").CurrentUserConfig} currentUserOrConfig - CurrentUser actor or configuration */ -export const getMutate = ({platform, discovery}) => { +export const getMutate = currentUserOrConfig => { /** * @description * Allows you to submit transactions to the blockchain to potentially mutate the state. @@ -66,10 +64,12 @@ export const getMutate = ({platform, discovery}) => { try { await preMutate(opts) opts = await prepTemplateOpts(opts) - const currentUser = getCurrentUser({platform, discovery}) // Allow for a config to overwrite the authorization function. // prettier-ignore - const authz = await sdk.config().get("fcl.authz", currentUser().authorization) + const currentUser = typeof currentUserOrConfig === "function" ? currentUserOrConfig : getCurrentUser(currentUserOrConfig) + const authz = await sdk + .config() + .get("fcl.authz", currentUser().authorization) txid = sdk .send([ diff --git a/packages/fcl-core/src/fcl-core.ts b/packages/fcl-core/src/fcl-core.ts index ef6579566..a031b2ca5 100644 --- a/packages/fcl-core/src/fcl-core.ts +++ b/packages/fcl-core/src/fcl-core.ts @@ -148,3 +148,5 @@ export { } from "./utils/constants" export {execStrategy} from "./current-user/exec-service" + +export type {StorageProvider} from "./utils/storage" diff --git a/packages/fcl-core/src/utils/storage.ts b/packages/fcl-core/src/utils/storage.ts new file mode 100644 index 000000000..a477becfd --- /dev/null +++ b/packages/fcl-core/src/utils/storage.ts @@ -0,0 +1,5 @@ +export type StorageProvider = { + can: boolean + get: (key: string) => Promise + put: (key: string, value: any) => Promise +} diff --git a/packages/fcl-react-native/CHANGELOG.md b/packages/fcl-react-native/CHANGELOG.md index d4035d7d5..00c0df3c4 100644 --- a/packages/fcl-react-native/CHANGELOG.md +++ b/packages/fcl-react-native/CHANGELOG.md @@ -1,5 +1,14 @@ # @onflow/fcl-react-native +## 1.9.7-alpha.4 + +### Patch Changes + +- [#2001](https://github.com/onflow/fcl-js/pull/2001) [`bac8c54db1b6821a2158923544aa537885d5a0e7`](https://github.com/onflow/fcl-js/commit/bac8c54db1b6821a2158923544aa537885d5a0e7) Thanks [@jribbink](https://github.com/jribbink)! - Pass getStorageProvider to currentUser configuration + +- Updated dependencies [[`bac8c54db1b6821a2158923544aa537885d5a0e7`](https://github.com/onflow/fcl-js/commit/bac8c54db1b6821a2158923544aa537885d5a0e7)]: + - @onflow/fcl-core@1.13.0-alpha.4 + ## 1.9.7-alpha.3 ### Patch Changes diff --git a/packages/fcl-react-native/package.json b/packages/fcl-react-native/package.json index af723f9c1..2b1937acd 100644 --- a/packages/fcl-react-native/package.json +++ b/packages/fcl-react-native/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/fcl-react-native", - "version": "1.9.7-alpha.3", + "version": "1.9.7-alpha.4", "description": "Flow Client Library", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -48,7 +48,7 @@ "dependencies": { "@babel/runtime": "^7.25.7", "@onflow/config": "1.5.1-alpha.0", - "@onflow/fcl-core": "1.13.0-alpha.3", + "@onflow/fcl-core": "1.13.0-alpha.4", "@onflow/interaction": "0.0.11", "@onflow/rlp": "1.2.3-alpha.0", "@onflow/sdk": "1.5.4-alpha.1", diff --git a/packages/fcl-react-native/src/fcl-react-native.ts b/packages/fcl-react-native/src/fcl-react-native.ts index 061a07096..2b87edc33 100644 --- a/packages/fcl-react-native/src/fcl-react-native.ts +++ b/packages/fcl-react-native/src/fcl-react-native.ts @@ -69,11 +69,14 @@ import { initServiceRegistry, setIsReactNative, } from "@onflow/fcl-core" -export const mutate = getMutate({platform: "react-native"}) -const currentUser = getCurrentUser({platform: "react-native"}) - -export {currentUser} +export const currentUser = getCurrentUser({ + platform: "react-native", + getStorageProvider: async () => { + return (await config().get("fcl.storage")) || getAsyncStorage() + }, +}) +export const mutate = getMutate(currentUser) export const authenticate = (opts = {}) => currentUser().authenticate(opts) export const unauthenticate = () => currentUser().unauthenticate() @@ -93,6 +96,7 @@ import { useServiceDiscovery, ServiceDiscovery, } from "./utils/react-native" +import {getAsyncStorage} from "./utils/react-native/storage" config(getDefaultConfig()) diff --git a/packages/fcl-react-native/src/utils/react-native/default-config.js b/packages/fcl-react-native/src/utils/react-native/default-config.js index 8c487b152..5edeca7de 100644 --- a/packages/fcl-react-native/src/utils/react-native/default-config.js +++ b/packages/fcl-react-native/src/utils/react-native/default-config.js @@ -1,22 +1,5 @@ -import AsyncStorage from "@react-native-async-storage/async-storage" - -const getAsyncStorage = () => { - try { - const ASYNC_STORAGE = { - can: true, - get: async key => JSON.parse(await AsyncStorage.getItem(key)), - put: async (key, value) => - await AsyncStorage.setItem(key, JSON.stringify(value)), - } - return ASYNC_STORAGE - } catch (error) { - return null - } -} - export const getDefaultConfig = () => { return { "discovery.wallet.method.default": "DEEPLINK/RPC", - "fcl.storage.default": getAsyncStorage(), } } diff --git a/packages/fcl-react-native/src/utils/react-native/storage.ts b/packages/fcl-react-native/src/utils/react-native/storage.ts new file mode 100644 index 000000000..e835cf1e0 --- /dev/null +++ b/packages/fcl-react-native/src/utils/react-native/storage.ts @@ -0,0 +1,25 @@ +import AsyncStorage from "@react-native-async-storage/async-storage" + +const safeParseJSON = (str?: string | null) => { + if (str == null) return null + try { + return JSON.parse(str) + } catch (error) { + return null + } +} + +export const getAsyncStorage = () => { + try { + const ASYNC_STORAGE = { + can: true, + get: async (key: string) => + safeParseJSON(await AsyncStorage.getItem(key)), + put: async (key: string, value: any) => + await AsyncStorage.setItem(key, JSON.stringify(value)), + } + return ASYNC_STORAGE + } catch (error) { + return null + } +} diff --git a/packages/fcl-wc/CHANGELOG.md b/packages/fcl-wc/CHANGELOG.md index c9aa7cae0..dcdc28097 100644 --- a/packages/fcl-wc/CHANGELOG.md +++ b/packages/fcl-wc/CHANGELOG.md @@ -1,5 +1,12 @@ # @onflow/fcl-wc +## 5.4.1-alpha.4 + +### Patch Changes + +- Updated dependencies [[`bac8c54db1b6821a2158923544aa537885d5a0e7`](https://github.com/onflow/fcl-js/commit/bac8c54db1b6821a2158923544aa537885d5a0e7)]: + - @onflow/fcl-core@1.13.0-alpha.4 + ## 5.4.1-alpha.3 ### Patch Changes diff --git a/packages/fcl-wc/package.json b/packages/fcl-wc/package.json index a833f8146..d657d1a4a 100644 --- a/packages/fcl-wc/package.json +++ b/packages/fcl-wc/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/fcl-wc", - "version": "5.4.1-alpha.3", + "version": "5.4.1-alpha.4", "description": "WalletConnect adapter for FCL", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -55,6 +55,6 @@ "tailwindcss": "^3.4.14" }, "peerDependencies": { - "@onflow/fcl-core": "1.13.0-alpha.3" + "@onflow/fcl-core": "1.13.0-alpha.4" } } diff --git a/packages/fcl/CHANGELOG.md b/packages/fcl/CHANGELOG.md index 47bd65547..363b7ad3e 100644 --- a/packages/fcl/CHANGELOG.md +++ b/packages/fcl/CHANGELOG.md @@ -1,5 +1,17 @@ # @onflow/fcl +## 1.13.0-alpha.6 + +### Minor Changes + +- [#2001](https://github.com/onflow/fcl-js/pull/2001) [`bac8c54db1b6821a2158923544aa537885d5a0e7`](https://github.com/onflow/fcl-js/commit/bac8c54db1b6821a2158923544aa537885d5a0e7) Thanks [@jribbink](https://github.com/jribbink)! - Use localStorage as default & export LOCAL_STORAGE/SESSION_STORAGE as helpers for fcl.storage.default configuration key + +### Patch Changes + +- Updated dependencies [[`bac8c54db1b6821a2158923544aa537885d5a0e7`](https://github.com/onflow/fcl-js/commit/bac8c54db1b6821a2158923544aa537885d5a0e7)]: + - @onflow/fcl-core@1.13.0-alpha.4 + - @onflow/fcl-wc@5.4.1-alpha.4 + ## 1.12.4-alpha.5 ### Patch Changes diff --git a/packages/fcl/package.json b/packages/fcl/package.json index 0b5be9b36..be4393af6 100644 --- a/packages/fcl/package.json +++ b/packages/fcl/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/fcl", - "version": "1.12.4-alpha.5", + "version": "1.13.0-alpha.6", "description": "Flow Client Library", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -49,8 +49,8 @@ "dependencies": { "@babel/runtime": "^7.25.7", "@onflow/config": "1.5.1-alpha.0", - "@onflow/fcl-core": "1.13.0-alpha.3", - "@onflow/fcl-wc": "5.4.1-alpha.3", + "@onflow/fcl-core": "1.13.0-alpha.4", + "@onflow/fcl-wc": "5.4.1-alpha.4", "@onflow/interaction": "0.0.11", "@onflow/rlp": "1.2.3-alpha.0", "@onflow/sdk": "1.5.4-alpha.1", diff --git a/packages/fcl/src/fcl.ts b/packages/fcl/src/fcl.ts index 79f5936a4..6f6b5e97a 100644 --- a/packages/fcl/src/fcl.ts +++ b/packages/fcl/src/fcl.ts @@ -63,18 +63,28 @@ export { nodeVersionInfo, } from "@onflow/fcl-core" -import {getMutate, getCurrentUser, initServiceRegistry} from "@onflow/fcl-core" +import { + getMutate, + getCurrentUser, + initServiceRegistry, + StorageProvider, +} from "@onflow/fcl-core" import {execStrategyHook} from "./discovery/exec-hook" const discoveryOpts = { execStrategy: execStrategyHook, } -export const mutate = getMutate({platform: "web", discovery: discoveryOpts}) export const currentUser = getCurrentUser({ platform: "web", discovery: discoveryOpts, + getStorageProvider: async () => { + return ( + (await config.get("fcl.storage")) || LOCAL_STORAGE + ) + }, }) +export const mutate = getMutate(currentUser) export const authenticate = (opts = {}) => currentUser().authenticate(opts) export const unauthenticate = () => currentUser().unauthenticate() @@ -88,7 +98,7 @@ export const logIn = (opts = {}) => currentUser().authenticate(opts) export const authz = currentUser().authorization import {config} from "@onflow/config" -import {getDefaultConfig, coreStrategies} from "./utils/web" +import {getDefaultConfig, coreStrategies, LOCAL_STORAGE} from "./utils/web" import {initFclWcLoader} from "./utils/walletconnect/loader" config(getDefaultConfig()) @@ -98,3 +108,5 @@ initServiceRegistry({coreStrategies}) // Automatically load fcl-wc plugin // Based on the user's config initFclWcLoader() + +export {LOCAL_STORAGE, SESSION_STORAGE} from "./utils/web" diff --git a/packages/fcl/src/utils/web/default-config.js b/packages/fcl/src/utils/web/default-config.js index 02c783eac..5127b7128 100644 --- a/packages/fcl/src/utils/web/default-config.js +++ b/packages/fcl/src/utils/web/default-config.js @@ -1,22 +1,5 @@ -const isServerSide = () => typeof window === "undefined" - -const getSessionStorage = () => { - try { - const SESSION_STORAGE = { - can: !isServerSide(), - get: async key => JSON.parse(sessionStorage.getItem(key)), - put: async (key, value) => - sessionStorage.setItem(key, JSON.stringify(value)), - } - return SESSION_STORAGE - } catch (error) { - return null - } -} - export const getDefaultConfig = () => { return { "discovery.wallet.method.default": "IFRAME/RPC", - "fcl.storage.default": getSessionStorage(), } } diff --git a/packages/fcl/src/utils/web/index.js b/packages/fcl/src/utils/web/index.js index 86d47b3c9..7c943c9f8 100644 --- a/packages/fcl/src/utils/web/index.js +++ b/packages/fcl/src/utils/web/index.js @@ -3,3 +3,4 @@ export {renderPop} from "./render-pop" export {renderTab} from "./render-tab" export {getDefaultConfig} from "./default-config" export {coreStrategies} from "./coreStrategies" +export {LOCAL_STORAGE, SESSION_STORAGE} from "./storage" diff --git a/packages/fcl/src/utils/web/storage.ts b/packages/fcl/src/utils/web/storage.ts new file mode 100644 index 000000000..88da9a5c9 --- /dev/null +++ b/packages/fcl/src/utils/web/storage.ts @@ -0,0 +1,25 @@ +import {StorageProvider} from "@onflow/fcl-core" + +const isServerSide = () => typeof window === "undefined" +const safeParseJSON = (str?: string | null) => { + if (str == null) return null + try { + return JSON.parse(str) + } catch (error) { + return null + } +} + +export const SESSION_STORAGE = { + can: !isServerSide() && !!window.sessionStorage, + get: async (key: string) => safeParseJSON(sessionStorage.getItem(key)), + put: async (key: string, value: any) => + sessionStorage.setItem(key, JSON.stringify(value)), +} as StorageProvider + +export const LOCAL_STORAGE = { + can: !isServerSide() && !!window.localStorage, + get: async (key: string) => safeParseJSON(localStorage.getItem(key)), + put: async (key: string, value: any) => + localStorage.setItem(key, JSON.stringify(value)), +} as StorageProvider