From 41b6d3f762af9ccbd5f4125c6a36c92f02192a86 Mon Sep 17 00:00:00 2001 From: "Ng Wing Tat, David" Date: Fri, 21 Jul 2023 15:41:04 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=8E=A8=20Improve=20restore=20session?= =?UTF-8?q?=20flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/root-store/setup-root-store.ts | 7 ------ .../auth-loading-screen.tsx | 24 ++++++++++++------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/app/models/root-store/setup-root-store.ts b/app/models/root-store/setup-root-store.ts index 17ef0735d..f40159c17 100644 --- a/app/models/root-store/setup-root-store.ts +++ b/app/models/root-store/setup-root-store.ts @@ -78,13 +78,6 @@ export async function setupRootStore() { // load data from storage data = await storage.load(storageKey) || {} rootStore = createRootStore(env, data) - - if (rootStore.userStore.currentUser) { - rootStore.userStore.authCore.resume().then(() => { - const address = rootStore.userStore.authCore.primaryCosmosAddress - rootStore.chainStore.setupWallet(address) - }) - } } catch (e) { // if there's any problems loading, then let's at least fallback to an empty state // instead of crashing. diff --git a/app/screens/auth-loading-screen/auth-loading-screen.tsx b/app/screens/auth-loading-screen/auth-loading-screen.tsx index 966240fe6..7d8df7134 100644 --- a/app/screens/auth-loading-screen/auth-loading-screen.tsx +++ b/app/screens/auth-loading-screen/auth-loading-screen.tsx @@ -10,13 +10,15 @@ import { UserStore } from "../../models/user-store" import { translate } from "../../i18n" import { logError } from "../../utils/error" +import { ChainStore } from "../../models/chain-store" export interface AuthLoadingScreenProps extends NavigationStackScreenProps<{}> { + chainStore: ChainStore deepLinkHandleStore: DeepLinkHandleStore userStore: UserStore } -@inject("deepLinkHandleStore", "userStore") +@inject("deepLinkHandleStore", "userStore", "chainStore") @observer export class AuthLoadingScreen extends React.Component { componentDidMount() { @@ -26,17 +28,23 @@ export class AuthLoadingScreen extends React.Component { + if (this.props.userStore.isSigningIn) { + const address = this.props.userStore.authCore.primaryCosmosAddress + this.props.chainStore.setupWallet(address) + } else { + // Reset Authcore login state if user is currently not signing in + this.props.userStore.authCore.setHasSignedIn(false) + } + }) try { await this.props.deepLinkHandleStore.handleAppReferrer() await this.props.deepLinkHandleStore.openBranchDeepLink() @@ -60,8 +68,8 @@ export class AuthLoadingScreen extends React.Component Date: Fri, 21 Jul 2023 15:44:14 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=A8=20Add=20red=20dot=20for=20unseen?= =?UTF-8?q?=20notification?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 3 + .../main-tab-bar/main-tab-bar.props.ts | 1 + app/components/main-tab-bar/main-tab-bar.tsx | 31 ++- app/components/nft-web-view/nft-web-view.tsx | 7 +- app/models/authcore-store/authcore-store.ts | 3 + app/models/environment.ts | 23 ++- app/models/user-store/user-store.ts | 149 ++++++++++++++ .../auth-loading-screen.tsx | 3 + .../nft-notification-screen.tsx | 13 +- .../post-sign-in-screen.tsx | 2 + app/services/api/index.ts | 2 + app/services/api/likecoin-chain-api.ts | 186 ++++++++++++++++++ app/services/api/liker-land-api.ts | 115 +++++++++++ app/services/app-config/app-config.keys.ts | 6 + ios/Podfile.lock | 2 +- tsconfig.json | 2 +- 16 files changed, 541 insertions(+), 7 deletions(-) create mode 100644 app/services/api/likecoin-chain-api.ts create mode 100644 app/services/api/liker-land-api.ts diff --git a/.env b/.env index d3cc0bfe4..03d947809 100644 --- a/.env +++ b/.env @@ -24,7 +24,10 @@ MIN_VERSION_FOR_WALLET=300 LIKECO_API_URL=https://like.co/api LIKECOIN_API_URL=https://api.like.co +LIKECOIN_CHAIN_API_URL=https://mainnet-node.like.co +LIKECOIN_NFT_API_WALLET=like17m4vwrnhjmd20uu7tst7nv0kap6ee7js69jfrs LIKERLAND_URL=https://liker.land +LIKERLAND_LOGIN_MESSAGE=Login LIKECOIN_BUTTON_BASE_URL=https://button.like.co SUPERLIKE_BASE_URL=https://s.like.co diff --git a/app/components/main-tab-bar/main-tab-bar.props.ts b/app/components/main-tab-bar/main-tab-bar.props.ts index b35ac6b93..4a9e5a112 100644 --- a/app/components/main-tab-bar/main-tab-bar.props.ts +++ b/app/components/main-tab-bar/main-tab-bar.props.ts @@ -6,4 +6,5 @@ export interface MainTabBarIconProps { horizontal?: boolean routeName: string user?: User + unseenEventCount?: number } diff --git a/app/components/main-tab-bar/main-tab-bar.tsx b/app/components/main-tab-bar/main-tab-bar.tsx index cc9b7010d..fe978d036 100644 --- a/app/components/main-tab-bar/main-tab-bar.tsx +++ b/app/components/main-tab-bar/main-tab-bar.tsx @@ -1,4 +1,6 @@ import * as React from "react" +import { View } from "react-native" +import styled from "styled-components/native" import { inject, observer } from "mobx-react" import { MainTabBarIconProps } from "./main-tab-bar.props" @@ -10,13 +12,27 @@ import { RootStore } from "../../models/root-store" import { color } from "../../theme" +const NotificationIconWrapper = styled(View)` + position: relative; +` +const NotificationDot = styled(View)` + position: absolute; + top: 0; + right: 0; + width: 8; + height: 8; + borderRadius: 4; + background-color: #e35050; +` + @inject((rootStore: RootStore) => ({ user: rootStore.userStore.currentUser, + unseenEventCount: rootStore.userStore.unseenEventCount, })) @observer export class MainTabBarIcon extends React.Component { render() { - const { focused, routeName, user } = this.props + const { focused, routeName, user, unseenEventCount } = this.props let name: IconTypes const size = 24 switch (routeName) { @@ -52,7 +68,7 @@ export class MainTabBarIcon extends React.Component { } } const fill = focused ? color.palette.likeCyan : color.palette.lightGrey - return ( + const icon = ( { fill={fill} /> ) + + if (routeName === "Notification" && unseenEventCount > 0) { + return ( + + + {icon} + + ) + } + + return icon } } diff --git a/app/components/nft-web-view/nft-web-view.tsx b/app/components/nft-web-view/nft-web-view.tsx index f772bf4e9..71982f679 100644 --- a/app/components/nft-web-view/nft-web-view.tsx +++ b/app/components/nft-web-view/nft-web-view.tsx @@ -22,7 +22,11 @@ const ControlBar = styled.View` border-color: ${({ theme }) => theme.color.separator}; ` -export function NFTWebView({ style, ...props }: WebViewProps) { +export interface NFTWebViewProps extends WebViewProps { + onPressRefresh?: () => void +} + +export function NFTWebView({ style, onPressRefresh, ...props }: NFTWebViewProps) { const webViewRef = React.useRef(null) const [webViewKey, setWebViewKey] = React.useState(0); @@ -46,6 +50,7 @@ export function NFTWebView({ style, ...props }: WebViewProps) { const handleRefreshButtonPress = () => { webViewRef.current?.reload() + onPressRefresh?.() } const handleHardwareBackButtonPress = () => { diff --git a/app/models/authcore-store/authcore-store.ts b/app/models/authcore-store/authcore-store.ts index 16b6a883b..00938957d 100644 --- a/app/models/authcore-store/authcore-store.ts +++ b/app/models/authcore-store/authcore-store.ts @@ -181,6 +181,9 @@ export const AuthCoreStoreModel = types ) self.profile = AuthCoreUserModel.create(currentUser) }), + waitForInit: flow(function*() { + yield pendingInitPromise + }), }, } }) diff --git a/app/models/environment.ts b/app/models/environment.ts index 75e782799..456b8f33b 100644 --- a/app/models/environment.ts +++ b/app/models/environment.ts @@ -3,7 +3,12 @@ import { AuthCoreAPI } from "../services/authcore" import { CosmosAPI } from "../services/cosmos" import { Mintscan } from "../services/mintscan" import { Reactotron } from "../services/reactotron" -import { LikeCoAPI, LikeCoinAPI } from "../services/api" +import { + LikeCoAPI, + LikeCoinAPI, + LikeCoinChainAPI, + LikerLandAPI, +} from "../services/api" import { BranchIO } from "../services/branch-io" import { initSentry } from "../utils/sentry" @@ -19,6 +24,8 @@ export class Environment { this.authCoreAPI = new AuthCoreAPI() this.likeCoAPI = new LikeCoAPI() this.likeCoinAPI = new LikeCoinAPI() + this.likeCoinChainAPI = new LikeCoinChainAPI() + this.likerLandAPI = new LikerLandAPI() this.cosmosAPI = new CosmosAPI() this.mintscan = new Mintscan() this.branchIO = new BranchIO() @@ -36,6 +43,8 @@ export class Environment { COSMOS_ADDRESS_PREFIX, LIKECO_API_URL, LIKECOIN_API_URL, + LIKECOIN_CHAIN_API_URL, + LIKERLAND_URL, MINTSCAN_URL, SENTRY_DSN, SENTRY_ENV, @@ -46,6 +55,8 @@ export class Environment { this.authCoreAPI.setup(AUTHCORE_ROOT_URL, COSMOS_CHAIN_ID, COSMOS_ADDRESS_PREFIX) this.likeCoAPI.setup(LIKECO_API_URL) this.likeCoinAPI.setup(LIKECOIN_API_URL) + this.likeCoinChainAPI.setup(LIKECOIN_CHAIN_API_URL) + this.likerLandAPI.setup(LIKERLAND_URL) this.cosmosAPI.setup(COSMOS_LCD_URL, this.appConfig.getGasLimits()) this.mintscan.setup(MINTSCAN_URL) } @@ -75,6 +86,16 @@ export class Environment { */ likeCoinAPI: LikeCoinAPI + /** + * LikeCoin chain API. + */ + likeCoinChainAPI: LikeCoinChainAPI + + /** + * Like Land API. + */ + likerLandAPI: LikerLandAPI + /** * AuthCore API. */ diff --git a/app/models/user-store/user-store.ts b/app/models/user-store/user-store.ts index 35e30f6b3..2ade708c5 100644 --- a/app/models/user-store/user-store.ts +++ b/app/models/user-store/user-store.ts @@ -39,6 +39,10 @@ import { UserResult, UserRegisterParams, SuperLikeStatusResult, + LikerLandUserFolloweeListResult, + NFTClassListResult, + NFTEvent, + LikerLandUserInfoResult, } from "../../services/api" import { throwProblem } from "../../services/api/api-problem" @@ -64,6 +68,7 @@ export const UserStoreModel = types isSigningIn: false, isSigningOut: false, trackingStatus: "not-determined" as TrackingStatus, + unseenEventCount: 0, })) .extend(withEnvironment) .views(self => ({ @@ -417,6 +422,150 @@ export const UserStoreModel = types yield self.appMeta.postResume(); }), })) + .actions(self => ({ + loginLikerLand: flow(function * () { + yield self.authCore.waitForInit() + const address = self.authCore.primaryCosmosAddress + const signingPayload = { + /* eslint-disable @typescript-eslint/camelcase */ + chain_id: self.env.appConfig.getValue('COSMOS_CHAIN_ID'), + memo: [ + `${self.env.appConfig.getValue('LIKERLAND_LOGIN_MESSAGE')}:`, + JSON.stringify({ + ts: Date.now(), + address, + }), + ].join(' '), + msgs: [], + fee: { + gas: '0', + amount: [ + { + denom: self.env.appConfig.getValue('COSMOS_FRACTION_DENOM'), + amount: '0', + }, + ], + }, + sequence: '0', + account_number: '0', + /* eslint-enable @typescript-eslint/camelcase */ + }; + const { + signed, + signature: { + signature, + pub_key: publicKey + }, + }: any = yield self.env.authCoreAPI.signAmino(signingPayload, address) + const data = { + signature, + publicKey: publicKey.value, + message: stringify(signed), + from: address, + signMethod: 'memo', + }; + yield self.env.likerLandAPI.login(data) + }), + fetchLikerLandFollowees: flow(function * () { + const result: LikerLandUserFolloweeListResult = yield self.env.likerLandAPI.fetchUserFolloweeList() + switch (result.kind) { + case "ok": { + return result.data + } + default: + throwProblem(result) + return undefined + } + }), + })) + .actions(self => { + async function fetchFolloweeNewClassEvents(followee: string) { + const result: NFTClassListResult = await self.env.likeCoinChainAPI.fetchNFTClassList({ + iscnOwner: followee, + reverse: true, + }) + switch (result.kind) { + case "ok": + return result.data.map(({ id, created_at: timestamp }) => ({ + /* eslint-disable @typescript-eslint/camelcase */ + action: 'new_class', + class_id: id, + nft_id: '', + sender: followee, + receiver: '', + tx_hash: '', + timestamp, + memo: '', + /* eslint-enable @typescript-eslint/camelcase */ + } as NFTEvent)) + + default: + return [] + } + } + + function getEventType(event: NFTEvent) { + const user = self.authCore.primaryCosmosAddress + let eventType; + if (event.action === "new_class") { + eventType = "mint_nft" + } else if (event.action === "buy_nft" || event.action === "sell_nft") { + if (event.receiver === user) { + eventType = "purchase_nft" + } else { + eventType = "nft_sale" + } + } else if (event.sender === self.env.appConfig.getValue('LIKECOIN_NFT_API_WALLET')) { + if (event.receiver === user) { + eventType = "purchase_nft" + } else { + eventType = "nft_sale" + } + } else if (event.receiver === user) { + eventType = "receive_nft" + } else if (event.sender === user) { + eventType = "send_nft" + } else { + eventType = "transfer_nft" + } + return eventType; + } + + return { + loadUnseenEventCount: flow(function * () { + const userInfoResult: LikerLandUserInfoResult = yield self.env.likerLandAPI.fetchUserInfo() + if (userInfoResult.kind !== "ok") return + + const { eventLastSeenTs } = userInfoResult.data + + const { followees }: { followees: string[] } = yield self.fetchLikerLandFollowees(); + + const eventResponses: NFTEvent[][] = yield Promise.all([ + self.env.likeCoinChainAPI.fetchNFTEvents({ + involver: self.authCore.primaryCosmosAddress, + limit: 100, + actionType: ["/cosmos.nft.v1beta1.MsgSend", "buy_nft"], + ignoreToList: self.env.appConfig.getValue('LIKECOIN_NFT_API_WALLET'), + reverse: true, + }).then((result) => result.kind === 'ok' ? result.data : []).catch(() => []), + ...followees.map(fetchFolloweeNewClassEvents) + ]) + const events = eventResponses.flat() + self.unseenEventCount = events.reduce((count, event) => { + if ( + eventLastSeenTs < new Date(event.timestamp).getTime() && + ["nft_sale", "receive_nft", "mint_nft"].includes(getEventType(event)) + ) { + return count + 1 + } + return count + }, 0) + }), + clearUnseenEventCount() { + self.unseenEventCount = 0 + }, + } + }); type UserStoreType = Instance export interface UserStore extends UserStoreType {} diff --git a/app/screens/auth-loading-screen/auth-loading-screen.tsx b/app/screens/auth-loading-screen/auth-loading-screen.tsx index 7d8df7134..425dc78d3 100644 --- a/app/screens/auth-loading-screen/auth-loading-screen.tsx +++ b/app/screens/auth-loading-screen/auth-loading-screen.tsx @@ -55,6 +55,7 @@ export class AuthLoadingScreen extends React.Component { + componentDidMount(): void { + this.props.userStore.clearUnseenEventCount() + } + get webViewURL() { const baseURL = this.props.walletConnectStore.localizedLikerLandBaseURL return `${baseURL}/notifications?in_app=1` } + handlePressRefresh() { + this.props.userStore.loadUnseenEventCount() + } + render() { return ( @@ -42,6 +52,7 @@ export class NFTNotificationScreen extends React.Component ) diff --git a/app/screens/post-sign-in-screen/post-sign-in-screen.tsx b/app/screens/post-sign-in-screen/post-sign-in-screen.tsx index 5759b3ca8..cdc804e9c 100644 --- a/app/screens/post-sign-in-screen/post-sign-in-screen.tsx +++ b/app/screens/post-sign-in-screen/post-sign-in-screen.tsx @@ -52,8 +52,10 @@ export class PostSignInScreen extends React.Component< await Promise.all([ this.props.userStore.fetchUserInfo(), this.props.userStore.appMeta.fetch(), + this.props.userStore.loginLikerLand(), ]) await this.props.userStore.postResume() + this.props.userStore.loadUnseenEventCount() this.props.navigation.navigate("App") // Try to open the deferred deep link URL after sign in diff --git a/app/services/api/index.ts b/app/services/api/index.ts index 3f56a36c5..1aba026a4 100644 --- a/app/services/api/index.ts +++ b/app/services/api/index.ts @@ -1,3 +1,5 @@ +export * from "./likecoin-chain-api" export * from "./likeco-api" export * from "./likecoin-api" +export * from "./liker-land-api" export * from "./api.types" diff --git a/app/services/api/likecoin-chain-api.ts b/app/services/api/likecoin-chain-api.ts new file mode 100644 index 000000000..d71559ca9 --- /dev/null +++ b/app/services/api/likecoin-chain-api.ts @@ -0,0 +1,186 @@ +import { ApisauceInstance, create, ApiResponse } from "apisauce" + +import { GeneralApiProblem, getGeneralApiProblem } from "./api-problem" +import { ApiConfig, COMMON_API_CONFIG } from "./api-config" + +export interface NFTClass { + id: string + name: string + description: string + symbol: string + uri: string + uri_hash: string + config: { + burnable: boolean + max_supply: string + blind_box_config: any + }, + metadata: { + image: string + message: string + external_url: string + is_custom_image: string + nft_meta_collection_id: string + nft_meta_collection_name: string + nft_meta_collection_descrption: string + }, + parent: { + type: string + iscn_id_prefix: string + account: string + }, + created_at: string + owner: string +} +export interface NFTClassListResponse { + classes: NFTClass[] + pagination: { + next_key: number + count: number + } +} +export type NFTClassListResult = { kind: "ok", data: NFTClass[] } | GeneralApiProblem + +export interface NFTEvent { + action: string + class_id: string + nft_id: string + sender: string + receiver: string + tx_hash: string + timestamp: string + memo: string +} +export interface NFTEventListResponse { events: NFTEvent[] } +export type NFTEventListResult = { kind: "ok", data: NFTEvent[] } | GeneralApiProblem + +/** + * LikeCoin chain API. + */ +export class LikeCoinChainAPI { + /** + * The underlying apisauce instance which performs the requests. + */ + apisauce: ApisauceInstance + + /** + * Configurable options. + */ + config: ApiConfig + + /** + * Creates the api. + * + * @param config The configuration to use. + */ + constructor(config: ApiConfig = COMMON_API_CONFIG) { + this.config = config + } + + /** + * Sets up the API. This will be called during the bootup + * sequence and will happen before the first React component + * is mounted. + * + * Be as quick as possible in here. + */ + setup(url: string) { + // construct the apisauce instance + this.apisauce = create({ + baseURL: url, + timeout: this.config.timeout, + headers: { + Accept: "application/json", + "User-Agent": this.config.userAgent, + "X-Device-Id": this.config.deviceId + }, + }) + } + + async fetchNFTClassList({ + iscnOwner, + nftOwner, + expand, + reverse, + limit, + key, + }: { + iscnOwner?: string + nftOwner?: string + expand?: string + reverse?: boolean + limit?: string + key?: string + } = {}): Promise { + const params: any = {} + // eslint-disable-next-line @typescript-eslint/camelcase + if (iscnOwner) params.iscn_owner = iscnOwner + if (nftOwner) params.owner = nftOwner + if (expand) params.expand = expand + if (reverse) params['pagination.reverse'] = reverse + if (limit) params['pagination.limit'] = limit + if (key) params['pagination.key'] = key + const response: ApiResponse = await this.apisauce.get("/likechain/likenft/v1/class", params) + + if (!response.ok) { + const problem = getGeneralApiProblem(response) + if (problem) return problem + } + + const { classes } = response.data + return { kind: "ok", data: classes } + } + + async fetchNFTEvents({ + classId, + nftId, + sender, + receiver, + creator, + involver, + limit, + key, + actionType, + ignoreToList, + ignoreFromList, + reverse, + }: { + classId?: string + nftId?: string + sender?: string + receiver?: string + creator?: string + involver?: string + limit?: number + key?: string + actionType?: string[] + ignoreToList?: string + ignoreFromList?: string + reverse?: boolean + }): Promise { + const params: any = {} + /* eslint-disable @typescript-eslint/camelcase */ + if (classId) params.class_id = classId + if (nftId) params.nft_id = nftId + if (sender) params.sender = sender + if (creator) params.creator = creator + if (receiver) params.receiver = receiver + if (involver) params.involver = involver + if (actionType) params.action_type = actionType + if (ignoreToList) params.ignore_to_list = ignoreToList + if (ignoreFromList) params.ignore_from_list = ignoreFromList + if (key) params['pagination.key'] = key + if (limit) params['pagination.limit'] = limit + if (reverse) params['pagination.reverse'] = reverse + /* eslint-enable @typescript-eslint/camelcase */ + const response: ApiResponse = await this.apisauce.get("/likechain/likenft/v1/event", params) + + if (!response.ok) { + const problem = getGeneralApiProblem(response) + if (problem) return problem + } + + const { events } = response.data + return { kind: "ok", data: events } + } +} diff --git a/app/services/api/liker-land-api.ts b/app/services/api/liker-land-api.ts new file mode 100644 index 000000000..25e93a8c5 --- /dev/null +++ b/app/services/api/liker-land-api.ts @@ -0,0 +1,115 @@ +import { ApisauceInstance, create, ApiResponse } from "apisauce" + +import { GeneralApiProblem, getGeneralApiProblem } from "./api-problem" +import { ApiConfig, COMMON_API_CONFIG } from "./api-config" +import * as Types from "./api.types" + +export interface LikerLandUserFolloweeListResponse { + followees: string[] + pastFollowees: string[] +} + +export type LikerLandUserFolloweeListResult = { + kind: "ok" + data: LikerLandUserFolloweeListResponse +} | GeneralApiProblem + +export interface LikerLandUserInfoResponse { + user: string + email: string + eventLastSeenTs: number + locale: string +} + +export type LikerLandUserInfoResult = { + kind: "ok" + data: LikerLandUserInfoResponse +} + +/** + * liker.land API. + */ +export class LikerLandAPI { + /** + * The underlying apisauce instance which performs the requests. + */ + apisauce: ApisauceInstance + + /** + * Configurable options. + */ + config: ApiConfig + + /** + * Creates the api. + * + * @param config The configuration to use. + */ + constructor(config: ApiConfig = COMMON_API_CONFIG) { + this.config = config + } + + /** + * Sets up the API. This will be called during the bootup + * sequence and will happen before the first React component + * is mounted. + * + * Be as quick as possible in here. + */ + setup(url: string) { + // construct the apisauce instance + this.apisauce = create({ + baseURL: url, + timeout: this.config.timeout, + headers: { + Accept: "application/json", + "User-Agent": this.config.userAgent, + "X-Device-Id": this.config.deviceId + }, + }) + } + + async login(body: any): Promise { + const response: ApiResponse = await this.apisauce.post("/api/v2/users/login", body) + + if (!response.ok) { + const problem = getGeneralApiProblem(response) + if (problem) return problem + } + + return { kind: "ok" } + } + + async logout(): Promise { + const response: ApiResponse = await this.apisauce.post("/api/v2/users/logout") + + if (!response.ok) { + const problem = getGeneralApiProblem(response) + if (problem) return problem + } + + return { kind: "ok" } + } + + async fetchUserInfo() { + const response: ApiResponse = await this.apisauce.get("/api/v2/users/self") + + if (!response.ok) { + const problem = getGeneralApiProblem(response) + if (problem) return problem + } + + return { kind: "ok", data: response.data } + } + + async fetchUserFolloweeList(): Promise { + const response: ApiResponse = await this.apisauce.get("/api/v2/users/followees") + + if (!response.ok) { + const problem = getGeneralApiProblem(response) + if (problem) return problem + } + + return { kind: "ok", data: response.data } + } +} diff --git a/app/services/app-config/app-config.keys.ts b/app/services/app-config/app-config.keys.ts index acf224ecb..36ec63b6e 100644 --- a/app/services/app-config/app-config.keys.ts +++ b/app/services/app-config/app-config.keys.ts @@ -25,7 +25,10 @@ import { APP_REFERRAL_ENABLE, LIKECO_API_URL, LIKECOIN_API_URL, + LIKECOIN_CHAIN_API_URL, + LIKECOIN_NFT_API_WALLET, LIKERLAND_URL, + LIKERLAND_LOGIN_MESSAGE, LIKECOIN_BUTTON_BASE_URL, SUPERLIKE_BASE_URL, MIN_VERSION, @@ -73,7 +76,10 @@ export default { LIKECO_API_URL, LIKECOIN_API_URL, LIKERLAND_URL, + LIKERLAND_LOGIN_MESSAGE, + LIKECOIN_CHAIN_API_URL, LIKECOIN_BUTTON_BASE_URL, + LIKECOIN_NFT_API_WALLET, SUPERLIKE_BASE_URL, MIN_VERSION, MIN_VERSION_FOR_WALLET, diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 1b0adc04d..89a838eb4 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -898,7 +898,7 @@ SPEC CHECKSUMS: OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 Protobuf: 0cde852566359049847168e51bd1c690e0f70056 - RCT-Folly: a21c126816d8025b547704b777a2ba552f3d9fa9 + RCT-Folly: b9d9fe1fc70114b751c076104e52f3b1b5e5a95a RCTRequired: 412e994c1e570cf35378a32c18fd46e50634938b RCTTypeSafety: ef27340c728e6d673af345ed69e479a010c8a2d8 React: 36b9f5116572e5b80f01e562bb1f1451e8848e47 diff --git a/tsconfig.json b/tsconfig.json index 9ed5cafa5..7c5c0b912 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,7 @@ "noUnusedLocals": true, "sourceMap": true, "target": "es2015", - "lib": ["es6"], + "lib": ["es6", "es2019"], "skipLibCheck": true }, "exclude": ["node_modules"], From 183ae31f2ff9431f377d2753e47e8e1baae71547 Mon Sep 17 00:00:00 2001 From: "Ng Wing Tat, David" Date: Sat, 29 Jul 2023 18:36:35 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20Update=20return=20t?= =?UTF-8?q?ype=20from=20chain=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/services/api/likecoin-chain-api.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/services/api/likecoin-chain-api.ts b/app/services/api/likecoin-chain-api.ts index d71559ca9..ce1c55462 100644 --- a/app/services/api/likecoin-chain-api.ts +++ b/app/services/api/likecoin-chain-api.ts @@ -17,20 +17,20 @@ export interface NFTClass { }, metadata: { image: string - message: string - external_url: string - is_custom_image: string - nft_meta_collection_id: string - nft_meta_collection_name: string - nft_meta_collection_descrption: string + message?: string + external_url?: string + is_custom_image?: string + nft_meta_collection_id?: string + nft_meta_collection_name?: string + nft_meta_collection_descrption?: string }, parent: { type: string iscn_id_prefix: string account: string }, - created_at: string - owner: string + created_at?: string + owner?: string } export interface NFTClassListResponse { classes: NFTClass[]