From d46c16c4758d8f0af70c876de634a8565bac81f2 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Wed, 17 Aug 2022 16:19:00 +0200 Subject: [PATCH 01/89] (feat) live metrics --- package.json | 3 +- src/common/components/FeedList.tsx | 160 ++++++++++----------- src/common/services/socket.service.ts | 56 ++++---- src/newsfeed/ActivityModel.ts | 61 +++++++- src/newsfeed/activity/Activity.tsx | 190 ++++++++++++------------- yarn.lock | 195 +++++++------------------- 6 files changed, 315 insertions(+), 350 deletions(-) diff --git a/package.json b/package.json index cda1a4150..84fc2fb85 100644 --- a/package.json +++ b/package.json @@ -148,7 +148,7 @@ "rn-update-apk": "^4.5.0", "rne-modal-tooltip": "gist:b28c003d87c619674def0878473338a0", "rxjs": "^6.5.5", - "socket.io-client": "^2.3.0", + "socket.io-client": "^4.5.1", "styled-components": "^5.3.3", "styled-system": "^5.1.5", "tinycolor2": "^1.4.2", @@ -169,6 +169,7 @@ "@types/react-native": "^0.68.2", "@types/react-native-snap-carousel": "^3.8.1", "@types/react-test-renderer": "17.0.1", + "@types/socket.io-client": "^3.0.0", "@typescript-eslint/eslint-plugin": "^4.22.1", "@typescript-eslint/parser": "^4.22.1", "@wdio/browserstack-service": "^7.19.5", diff --git a/src/common/components/FeedList.tsx b/src/common/components/FeedList.tsx index c638cf488..4c45db6ee 100644 --- a/src/common/components/FeedList.tsx +++ b/src/common/components/FeedList.tsx @@ -6,7 +6,12 @@ import { Dimensions, RefreshControl, } from 'react-native'; -import { FlashList, FlashListProps, ListRenderItem } from '@shopify/flash-list'; +import { + FlashList, + FlashListProps, + ListRenderItem, + ViewToken, +} from '@shopify/flash-list'; import Animated from 'react-native-reanimated'; import { observer } from 'mobx-react'; @@ -171,74 +176,6 @@ export class FeedList extends Component< return null; } - /** - * Render component - */ - render() { - let renderRow: ListRenderItem; - - const { - feedStore, - renderTileActivity, - renderActivity, - header, - ...passThroughProps - } = this.props; - - if (feedStore.isTiled) { - renderRow = renderTileActivity || this.renderTileActivity; - } else { - renderRow = renderActivity || this.renderActivity; - } - - const items: Array = !this.props.hideItems - ? feedStore.entities.slice() - : []; - - const AnimatedFlashList = this.AnimatedFlashList; - - return ( - - - } - disableAutoLayout={true} - onEndReached={this.loadMore} - getItemType={this.getType} - onEndReachedThreshold={5} // 5 times the visible list height - numColumns={feedStore.isTiled ? 3 : 1} - ListEmptyComponent={!this.props.hideItems ? this.empty : null} - viewabilityConfig={this.viewOpts} - onViewableItemsChanged={this.onViewableItemsChanged} - scrollEventThrottle={16} - keyboardShouldPersistTaps="always" - testID="feedlistCMP" - {...passThroughProps} - keyboardDismissMode="on-drag" - onScroll={this.props.onScroll} - /> - {this.props.bottomComponent} - - ); - } - getType(item: ActivityModel | InjectItem) { const isActivity = item instanceof ActivityModel; return item instanceof InjectItem @@ -308,18 +245,15 @@ export class FeedList extends Component< * On viewable item changed */ onViewableItemsChanged = (change: { - viewableItems: any[]; - changed: any[]; + viewableItems: ViewToken[]; + changed: ViewToken[]; }) => { - change.viewableItems.forEach((item: { item: any }) => { - if (item && item.item && item.item.sendViewed) item.item.sendViewed(); - }); + change.viewableItems.forEach((item: { item: any }) => + item?.item?.sendViewed?.(), + ); change.changed.forEach( - (c: { item: { setVisible: (arg0: any) => void }; isViewable: any }) => { - if (c.item && c.item.setVisible) { - c.item.setVisible(c.isViewable); - } - }, + (c: { item: { setVisible: (arg0: any) => void }; isViewable: any }) => + c.item?.setVisible?.(c.isViewable), ); }; @@ -403,6 +337,74 @@ export class FeedList extends Component< /> ); }; + + /** + * Render component + */ + render() { + let renderRow: ListRenderItem; + + const { + feedStore, + renderTileActivity, + renderActivity, + header, + ...passThroughProps + } = this.props; + + if (feedStore.isTiled) { + renderRow = renderTileActivity || this.renderTileActivity; + } else { + renderRow = renderActivity || this.renderActivity; + } + + const items: Array = !this.props.hideItems + ? feedStore.entities.slice() + : []; + + const AnimatedFlashList = this.AnimatedFlashList; + + return ( + + + } + disableAutoLayout={true} + onEndReached={this.loadMore} + getItemType={this.getType} + onEndReachedThreshold={5} // 5 times the visible list height + numColumns={feedStore.isTiled ? 3 : 1} + ListEmptyComponent={!this.props.hideItems ? this.empty : null} + viewabilityConfig={this.viewOpts} + onViewableItemsChanged={this.onViewableItemsChanged} + scrollEventThrottle={16} + keyboardShouldPersistTaps="always" + testID="feedlistCMP" + {...passThroughProps} + keyboardDismissMode="on-drag" + onScroll={this.props.onScroll} + /> + {this.props.bottomComponent} + + ); + } } const footerStyle = ThemedStyles.combine('centered', 'padding3x'); diff --git a/src/common/services/socket.service.ts b/src/common/services/socket.service.ts index 0c274fb63..c263b4bd1 100644 --- a/src/common/services/socket.service.ts +++ b/src/common/services/socket.service.ts @@ -1,8 +1,5 @@ -//@ts-nocheck -import SocketIOClient from 'socket.io-client'; - +import SocketIOClient, { Socket } from 'socket.io-client'; import { SOCKET_URI } from '../../config/Config'; - import sessionService from './session.service'; /** @@ -11,9 +8,9 @@ import sessionService from './session.service'; export class SocketService { LIVE_ROOM_NAME = 'live'; - socket = false; + socket?: Socket; registered = false; - rooms = []; + rooms: string[] = []; constructor() { sessionService.onSession(token => { @@ -26,17 +23,14 @@ export class SocketService { } setUp() { - if (this.socket) { - this.socket.destroy(); - } + this.socket?.close(); console.log('connecting to ', SOCKET_URI); this.socket = SocketIOClient(SOCKET_URI, { - reconnect: true, + // reconnect: true, reconnection: true, timeout: 30000, - pingTimeout: 30000, autoConnect: false, transports: ['websocket'], // importat with RN }); @@ -51,34 +45,36 @@ export class SocketService { } setUpDefaultListeners() { - if (!this.socket.on) return; + if (!this.socket?.on) { + return; + } // connect - this.socket.on('connect', () => { + this.socket?.on('connect', () => { //console.log(`[ws]::connected to ${SOCKET_URI}`); this.registerWithAccessToken(); this.join(`${this.LIVE_ROOM_NAME}:${sessionService.guid}`); }); // disconnect - this.socket.on('disconnect', () => { + this.socket?.on('disconnect', () => { //console.log(`[ws]::disconnected from ${SOCKET_URI}`); this.registered = false; }); // registered - this.socket.on('registered', guid => { + this.socket?.on('registered', () => { this.registered = true; - this.socket.emit('join', this.rooms); + this.socket?.emit('join', this.rooms); }); // error - this.socket.on('error', e => { + this.socket?.on('error', _e => { //console.log('[ws]::error', e); }); // rooms - this.socket.on('rooms', rooms => { + this.socket?.on('rooms', rooms => { if (!this.registered) { return; } @@ -88,13 +84,13 @@ export class SocketService { }); // joined - this.socket.on('joined', (room, rooms) => { + this.socket?.on('joined', (room, rooms) => { //console.log(`[ws]::joined`, room, rooms); this.rooms = rooms; }); // left - this.socket.on('left', (room, rooms) => { + this.socket?.on('left', (room, rooms) => { //console.log(`[ws]::left`, room, rooms); this.rooms = rooms; }); @@ -104,8 +100,8 @@ export class SocketService { //console.log('[ws]::reconnect'); this.registered = false; - this.socket.disconnect(); - this.socket.connect(); + this.socket?.disconnect(); + this.socket?.connect(); return this; } @@ -114,35 +110,33 @@ export class SocketService { //console.log('[ws]::disconnect'); this.registered = false; - this.socket.disconnect(); + this.socket?.disconnect(); return this; } emit(...args) { - if (!this.socket) return; - this.socket.emit.apply(this.socket, args); + // @ts-ignore + this.socket?.emit.apply(this.socket, args); return this; } subscribe(name, callback) { - if (!this.socket) return; - this.socket.on(name, callback); + this.socket?.on(name, callback); return this; } unsubscribe(name, callback) { - if (!this.socket) return; - this.socket.off(name, callback); + this.socket?.off(name, callback); return this; } - join(room) { + join(room: string) { if (!room) { return this; } - if (!this.registered || !this.socket.connected) { + if (!this.registered || !this.socket?.connected) { this.rooms.push(room); return this; } diff --git a/src/newsfeed/ActivityModel.ts b/src/newsfeed/ActivityModel.ts index b3ae5459c..a68b900e2 100644 --- a/src/newsfeed/ActivityModel.ts +++ b/src/newsfeed/ActivityModel.ts @@ -27,6 +27,7 @@ import mindsService from '../common/services/minds-config.service'; import NavigationService from '../navigation/NavigationService'; import { showNotification } from '../../AppMessages'; import mediaProxyUrl from '../common/helpers/media-proxy-url'; +import socketService from '~/common/services/socket.service'; type Thumbs = Record | Record[]; @@ -326,10 +327,16 @@ export default class ActivityModel extends BaseModel { }; @action - setVisible(value: boolean) { - this.is_visible = value; + setVisible(visible: boolean) { + this.is_visible = visible; if (this.remind_object) { - this.remind_object.is_visible = value; + this.remind_object.is_visible = visible; + } + + if (visible) { + this.listenForMetrics(); + } else { + this.unlistenFromMetrics(); } } @@ -556,6 +563,48 @@ export default class ActivityModel extends BaseModel { return false; } + + private get metricsRoom() { + return `entity:metrics:${this.guid}`; + } + + private onMetricsUpdate(event: string) { + console.log('[ActivityModel] metrics update ============>', event); + + try { + const metricsEvent: MetricsChangedEvent = JSON.parse(event); + + runInAction(() => { + if (typeof metricsEvent['thumbs:up:count'] === 'number') { + this['thumbs:up:count'] = metricsEvent['thumbs:up:count']; + } + if (typeof metricsEvent['thumbs:down:count'] === 'number') { + this['thumbs:down:count'] = metricsEvent['thumbs:down:count']; + } + }); + } catch (e) { + console.error(e, event); + return; + } + } + + /** + * listens to metrics updates + */ + private listenForMetrics(): void { + console.log('listenForMetrics'); + + socketService.join(this.metricsRoom); + socketService.subscribe(this.metricsRoom, this.onMetricsUpdate); + } + + /** + * unlistens from metrics updates + */ + private unlistenFromMetrics(): void { + socketService.leave(this.metricsRoom); + socketService.unsubscribe(this.metricsRoom, this.onMetricsUpdate); + } } /** @@ -569,3 +618,9 @@ decorate(ActivityModel, { 'thumbs:down:user_guids': observable, 'thumbs:up:user_guids': observable, }); + +// Parsed metrics changed event. +type MetricsChangedEvent = { + 'thumbs:up:count'?: number; + 'thumbs:down:count'?: number; +}; diff --git a/src/newsfeed/activity/Activity.tsx b/src/newsfeed/activity/Activity.tsx index 622f4a224..e6467c8e3 100644 --- a/src/newsfeed/activity/Activity.tsx +++ b/src/newsfeed/activity/Activity.tsx @@ -194,6 +194,101 @@ export default class Activity extends Component { showNotification(i18n.t('copied'), 'info'); }; + setMediaViewRef = o => { + this.mediaView = o; + }; + + /** + * Pause video if exist + */ + pauseVideo() { + this.mediaView?.pauseVideo(); + } + + /** + * Play video if exist + */ + playVideo(sound) { + this.mediaView?.playVideo(sound); + } + + /** + * Show translation + */ + showTranslate = async () => { + if (this.translate.current) { + //@ts-ignore + const lang = await this.translate.current?.show(); + if (this.remind && lang) this.remind.showTranslate(); + } else { + if (this.remind) this.remind.showTranslate(); + } + }; + + /** + * Show Owner + */ + showOwner() { + const rightToolbar: React.ReactNode = ( + + ); + return ( + + ); + } + + /** + * Show remind activity + */ + showRemind() { + const remind_object = this.props.entity.remind_object; + + if (remind_object && !this.props.hideRemind) { + if (blockListService.has(remind_object.owner_guid)) { + return ( + + + {i18n.t('activity.remindBlocked')} + + {' '} + @{remind_object.ownerObj.username} + + + + ); + } + + return ( + + + + ); + } + } + /** * Render */ @@ -298,99 +393,4 @@ export default class Activity extends Component { ); } - - setMediaViewRef = o => { - this.mediaView = o; - }; - - /** - * Pause video if exist - */ - pauseVideo() { - this.mediaView?.pauseVideo(); - } - - /** - * Play video if exist - */ - playVideo(sound) { - this.mediaView?.playVideo(sound); - } - - /** - * Show translation - */ - showTranslate = async () => { - if (this.translate.current) { - //@ts-ignore - const lang = await this.translate.current?.show(); - if (this.remind && lang) this.remind.showTranslate(); - } else { - if (this.remind) this.remind.showTranslate(); - } - }; - - /** - * Show Owner - */ - showOwner() { - const rightToolbar: React.ReactNode = ( - - ); - return ( - - ); - } - - /** - * Show remind activity - */ - showRemind() { - const remind_object = this.props.entity.remind_object; - - if (remind_object && !this.props.hideRemind) { - if (blockListService.has(remind_object.owner_guid)) { - return ( - - - {i18n.t('activity.remindBlocked')} - - {' '} - @{remind_object.ownerObj.username} - - - - ); - } - - return ( - - - - ); - } - } } diff --git a/yarn.lock b/yarn.lock index 852dec896..1a1504b16 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4176,6 +4176,11 @@ resolved "https://registry.yarnpkg.com/@snowplow/react-native-tracker/-/react-native-tracker-1.2.1.tgz#881f06e2a56cac8961142e32641784db11697297" integrity sha512-5JX976UuCSX+QPu7+ph4Tg3WJd+DYP1SdNre09l9I+4onEtaQau5cdxAKUFPThW1oedV+fznoT/t5tSqwo7Gfg== +"@socket.io/component-emitter@~3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" + integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== + "@styled-system/background@^5.1.2": version "5.1.2" resolved "https://registry.yarnpkg.com/@styled-system/background/-/background-5.1.2.tgz#75c63d06b497ab372b70186c0bf608d62847a2ba" @@ -4647,6 +4652,13 @@ dependencies: "@types/node" "*" +"@types/socket.io-client@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/socket.io-client/-/socket.io-client-3.0.0.tgz#d0b8ea22121b7c1df68b6a923002f9c8e3cefb42" + integrity sha512-s+IPvFoEIjKA3RdJz/Z2dGR4gLgysKi8owcnrVwNjgvc01Lk68LJDDsG2GRqegFITcxmvCMYM7bhMpwEMlHmDg== + dependencies: + socket.io-client "*" + "@types/stack-utils@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" @@ -5249,11 +5261,6 @@ aes-js@^3.1.2: resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ== -after@0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" - integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= - agent-base@5: version "5.1.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" @@ -5581,11 +5588,6 @@ array.prototype.flat@^1.2.3: define-properties "^1.1.3" es-abstract "^1.18.0-next.1" -arraybuffer.slice@~0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" - integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== - arrify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" @@ -6001,11 +6003,6 @@ babel-preset-jest@^26.6.2: babel-plugin-jest-hoist "^26.6.2" babel-preset-current-node-syntax "^1.0.0" -backo2@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" - integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= - backoff@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" @@ -6030,11 +6027,6 @@ base-x@^3.0.2, base-x@^3.0.8: dependencies: safe-buffer "^5.0.1" -base64-arraybuffer@0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812" - integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI= - base64-js@^1.0.2, base64-js@^1.1.2, base64-js@^1.2.3, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -6108,11 +6100,6 @@ blakejs@^1.1.0: resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5" integrity sha1-ad+S75U6qIylGjLfarHFShVfx6U= -blob@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" - integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== - bluebird@^3.5.0, bluebird@^3.5.4: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" @@ -7131,21 +7118,11 @@ compare-versions@^3.4.0: resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== -component-bind@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" - integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= - -component-emitter@^1.2.1, component-emitter@^1.3.0, component-emitter@~1.3.0: +component-emitter@^1.2.1, component-emitter@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== -component-inherit@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" - integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= - component-type@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-type/-/component-type-1.2.1.tgz#8a47901700238e4fc32269771230226f24b415a9" @@ -7589,7 +7566,7 @@ debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: dependencies: ms "2.1.2" -debug@4.3.4, debug@^4.3.2: +debug@4.3.4, debug@^4.3.2, debug@~4.3.1, debug@~4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -7610,13 +7587,6 @@ debug@^4.3.1: dependencies: ms "2.1.2" -debug@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - decamelize@^1.1.1, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -8172,33 +8142,21 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -engine.io-client@~3.5.0: - version "3.5.1" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.5.1.tgz#b500458a39c0cd197a921e0e759721a746d0bdb9" - integrity sha512-oVu9kBkGbcggulyVF0kz6BV3ganqUeqXvD79WOFKa+11oK692w1NyFkuEj4xrkFRpZhn92QOqTk4RQq5LiBXbQ== - dependencies: - component-emitter "~1.3.0" - component-inherit "0.0.3" - debug "~3.1.0" - engine.io-parser "~2.2.0" - has-cors "1.1.0" - indexof "0.0.1" - parseqs "0.0.6" - parseuri "0.0.6" - ws "~7.4.2" - xmlhttprequest-ssl "~1.5.4" - yeast "0.1.2" - -engine.io-parser@~2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.1.tgz#57ce5611d9370ee94f99641b589f94c97e4f5da7" - integrity sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg== +engine.io-client@~6.2.1: + version "6.2.2" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.2.2.tgz#c6c5243167f5943dcd9c4abee1bfc634aa2cbdd0" + integrity sha512-8ZQmx0LQGRTYkHuogVZuGSpDqYZtCM/nv8zQ68VZ+JkOpazJ7ICdsSpaO6iXwvaU30oFg5QJOJWj8zWqhbKjkQ== dependencies: - after "0.8.2" - arraybuffer.slice "~0.0.7" - base64-arraybuffer "0.1.4" - blob "0.0.5" - has-binary2 "~1.0.2" + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + engine.io-parser "~5.0.3" + ws "~8.2.3" + xmlhttprequest-ssl "~2.0.0" + +engine.io-parser@~5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0" + integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg== enquirer@^2.3.5: version "2.3.6" @@ -10225,18 +10183,6 @@ has-bigints@^1.0.0: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== -has-binary2@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" - integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== - dependencies: - isarray "2.0.1" - -has-cors@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" - integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= - has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -10619,11 +10565,6 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" - integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= - infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -11161,11 +11102,6 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= -isarray@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" - integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= - isarray@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" @@ -14673,16 +14609,6 @@ parse5@6.0.1, parse5@^6.0.0, parse5@^6.0.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== -parseqs@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.6.tgz#8e4bb5a19d1cdc844a08ac974d34e273afa670d5" - integrity sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w== - -parseuri@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.6.tgz#e1496e829e3ac2ff47f39a4dd044b32823c4a25a" - integrity sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow== - parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -17243,31 +17169,23 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -socket.io-client@^2.3.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.4.0.tgz#aafb5d594a3c55a34355562fc8aea22ed9119a35" - integrity sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ== - dependencies: - backo2 "1.0.2" - component-bind "1.0.0" - component-emitter "~1.3.0" - debug "~3.1.0" - engine.io-client "~3.5.0" - has-binary2 "~1.0.2" - indexof "0.0.1" - parseqs "0.0.6" - parseuri "0.0.6" - socket.io-parser "~3.3.0" - to-array "0.1.4" - -socket.io-parser@~3.3.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.2.tgz#ef872009d0adcf704f2fbe830191a14752ad50b6" - integrity sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg== +socket.io-client@*, socket.io-client@^4.5.1: + version "4.5.1" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.5.1.tgz#cab8da71976a300d3090414e28c2203a47884d84" + integrity sha512-e6nLVgiRYatS+AHXnOnGi4ocOpubvOUCGhyWw8v+/FxW8saHkinG6Dfhi9TU0Kt/8mwJIAASxvw6eujQmjdZVA== dependencies: - component-emitter "~1.3.0" - debug "~3.1.0" - isarray "2.0.1" + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.2" + engine.io-client "~6.2.1" + socket.io-parser "~4.2.0" + +socket.io-parser@~4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.1.tgz#01c96efa11ded938dcb21cbe590c26af5eff65e5" + integrity sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" socks-proxy-agent@5, socks-proxy-agent@^5.0.0: version "5.0.1" @@ -18108,11 +18026,6 @@ tmpl@1.0.x: resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= -to-array@0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" - integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= - to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" @@ -19377,7 +19290,7 @@ ws@^6.1.4: dependencies: async-limiter "~1.0.0" -ws@^7, ws@^7.4.4, ws@~7.4.2: +ws@^7, ws@^7.4.4: version "7.4.4" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== @@ -19392,6 +19305,11 @@ ws@^7.5.1: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== +ws@~8.2.3: + version "8.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" + integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== + xcode@3.0.1, xcode@^3.0.0, xcode@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/xcode/-/xcode-3.0.1.tgz#3efb62aac641ab2c702458f9a0302696146aa53c" @@ -19492,10 +19410,10 @@ xmldom@0.1.x: resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== -xmlhttprequest-ssl@~1.5.4: - version "1.5.5" - resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" - integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= +xmlhttprequest-ssl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67" + integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A== xregexp@2.0.0: version "2.0.0" @@ -19699,11 +19617,6 @@ yazl@^2.5.1: dependencies: buffer-crc32 "~0.2.3" -yeast@0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" - integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= - yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" From 756760ba70e32ca57bfb3b73c9e5a3759e520b06 Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Fri, 2 Sep 2022 16:14:35 -0300 Subject: [PATCH 02/89] (feat) supermind post --- src/common/components/GradientBorderView.tsx | 33 ++++++++++++++ .../supermind/SupermindBorderView.tsx | 43 +++++++++++++++++++ .../components/supermind/SupermindLabel.tsx | 33 ++++++++++++++ .../v2/viewer/ActivityFullScreen.tsx | 40 +++++++++++------ src/newsfeed/ActivityModel.ts | 5 +++ src/newsfeed/activity/Activity.tsx | 14 +++++- .../activity/metrics/ActivityMetrics.tsx | 2 + src/styles/Colors.ts | 2 + 8 files changed, 158 insertions(+), 14 deletions(-) create mode 100644 src/common/components/GradientBorderView.tsx create mode 100644 src/common/components/supermind/SupermindBorderView.tsx create mode 100644 src/common/components/supermind/SupermindLabel.tsx diff --git a/src/common/components/GradientBorderView.tsx b/src/common/components/GradientBorderView.tsx new file mode 100644 index 000000000..c69abc734 --- /dev/null +++ b/src/common/components/GradientBorderView.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { View, ViewProps } from 'react-native'; +import { LinearGradient, LinearGradientProps } from 'expo-linear-gradient'; + +type PropsType = { + borderWidth: number; + colors: LinearGradientProps['colors']; + start: LinearGradientProps['start']; + end: LinearGradientProps['end']; +} & ViewProps; + +export default function GradientBorderView({ + borderWidth, + colors, + children, + style, + start, + end, + ...other +}: PropsType) { + const containerStyle = React.useMemo(() => [style, { margin: borderWidth }], [ + style, + borderWidth, + ]); + + return ( + + + {children} + + + ); +} diff --git a/src/common/components/supermind/SupermindBorderView.tsx b/src/common/components/supermind/SupermindBorderView.tsx new file mode 100644 index 000000000..36db62f74 --- /dev/null +++ b/src/common/components/supermind/SupermindBorderView.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import GradientBorderView from '../GradientBorderView'; +import { SupermindGradient } from '~/styles/Colors'; +import { View } from 'react-native'; +import ThemedStyles from '~/styles/ThemedStyles'; + +type PropsType = { + children: React.ReactNode; +}; + +/** + * Supermind border view + */ +export default function SupermindBorderView({ children }: PropsType) { + return ( + + + {children} + + + ); +} + +const start = { x: 0, y: 0 }; +const end = { x: 1, y: 0 }; + +const styles = ThemedStyles.create({ + outerStyle: [ + { + borderRadius: 5, + overflow: 'hidden', + }, + 'margin3x', + ], + innerStyle: { + borderRadius: 4, + overflow: 'hidden', + }, +}); diff --git a/src/common/components/supermind/SupermindLabel.tsx b/src/common/components/supermind/SupermindLabel.tsx new file mode 100644 index 000000000..40d2605d6 --- /dev/null +++ b/src/common/components/supermind/SupermindLabel.tsx @@ -0,0 +1,33 @@ +import { View } from 'react-native'; +import React from 'react'; +import ThemedStyles from '~/styles/ThemedStyles'; +import { LinearGradient } from 'expo-linear-gradient'; +import { B2 } from '~/common/ui'; +import { SupermindGradient } from '~/styles/Colors'; + +export default function SupermindLabel() { + return ( + + + + Supermind + + + + ); +} + +const start = { x: 0, y: 0 }; +const end = { x: 1, y: 0 }; +const locations = [0, 0.3, 1]; +const styles = ThemedStyles.create({ + outerStyle: { + height: 20, + borderRadius: 3, + overflow: 'hidden', + }, +}); diff --git a/src/discovery/v2/viewer/ActivityFullScreen.tsx b/src/discovery/v2/viewer/ActivityFullScreen.tsx index 98882ee64..d23c4cac9 100644 --- a/src/discovery/v2/viewer/ActivityFullScreen.tsx +++ b/src/discovery/v2/viewer/ActivityFullScreen.tsx @@ -37,6 +37,7 @@ import InteractionsBar from '../../../common/components/interactions/Interaction import InteractionsBottomSheet from '../../../common/components/interactions/InteractionsBottomSheet'; import { GroupContext } from '~/groups/GroupViewScreen'; import { remindContainerStyle } from '~/newsfeed/activity/styles'; +import SupermindBorderView from '~/common/components/supermind/SupermindBorderView'; type ActivityRoute = RouteProp; @@ -232,6 +233,28 @@ const ActivityFullScreen = observer((props: PropsType) => { y: 0, }; + let remind: null | React.ReactNode = null; + + if (hasRemind) { + const Container: any = + props.entity.supermind && props.entity.supermind.isReply + ? SupermindBorderView + : QuoteContainer; + + remind = hasRemind ? ( + + + + ) : null; + } + const ownerBlockShadow = React.useMemo( () => Platform.select({ @@ -312,18 +335,7 @@ const ActivityFullScreen = observer((props: PropsType) => { )} - {hasRemind && ( - - - - )} + {hasRemind && remind} )} @@ -433,3 +445,7 @@ const contentFitStyle = ThemedStyles.combine( styles.content, ); const contentNotFitStyle = ThemedStyles.combine('fullWidth', styles.content); + +const QuoteContainer = ({ children }) => ( + {children} +); diff --git a/src/newsfeed/ActivityModel.ts b/src/newsfeed/ActivityModel.ts index b3ae5459c..fe185c9b0 100644 --- a/src/newsfeed/ActivityModel.ts +++ b/src/newsfeed/ActivityModel.ts @@ -43,6 +43,11 @@ export default class ActivityModel extends BaseModel { @observable edited: '0' | '1' = '0'; @observable paywall: true | '1' | '' = ''; + supermind?: { + request_guid: string; + isReply: boolean; + }; + time_updated: string = ''; // decorated observables 'is:following': boolean; diff --git a/src/newsfeed/activity/Activity.tsx b/src/newsfeed/activity/Activity.tsx index 04a8cf2a7..3a465ba83 100644 --- a/src/newsfeed/activity/Activity.tsx +++ b/src/newsfeed/activity/Activity.tsx @@ -36,6 +36,7 @@ import { textStyle, } from './styles'; import MText from '../../common/components/MText'; +import SupermindBorderView from '~/common/components/supermind/SupermindBorderView'; const FONT_THRESHOLD = 300; @@ -377,8 +378,13 @@ export default class Activity extends Component { ); } + const Container: any = + this.props.entity.supermind && this.props.entity.supermind.isReply + ? SupermindBorderView + : QuoteContainer; + return ( - + { hydrateOnNav={true} showOnlyContent={this.props.showOnlyContent} /> - + ); } } } + +const QuoteContainer = ({ children }) => ( + {children} +); diff --git a/src/newsfeed/activity/metrics/ActivityMetrics.tsx b/src/newsfeed/activity/metrics/ActivityMetrics.tsx index 22a70e6af..cf3f632c7 100644 --- a/src/newsfeed/activity/metrics/ActivityMetrics.tsx +++ b/src/newsfeed/activity/metrics/ActivityMetrics.tsx @@ -13,6 +13,7 @@ import LockTag from '../../../wire/v2/lock/LockTag'; import type { SupportTiersType } from '../../../wire/WireTypes'; import { getLockType } from '../../../wire/v2/lock/Lock'; import MText from '../../../common/components/MText'; +import SupermindLabel from '~/common/components/supermind/SupermindLabel'; type PropsType = { entity: ActivityModel; @@ -71,6 +72,7 @@ export default class ActivityMetrics extends Component { ) : undefined} {lockType !== null && } + {Boolean(this.props.entity.supermind) && } ); } diff --git a/src/styles/Colors.ts b/src/styles/Colors.ts index 04785105c..e9fc45eaf 100644 --- a/src/styles/Colors.ts +++ b/src/styles/Colors.ts @@ -74,3 +74,5 @@ export const DARK_THEME: ColorsType = { AvatarActive: '#ECDA51', AvatarCircled: '#AEB0B8', }; + +export const SupermindGradient = ['#2B52E8', '#5238ED', '#9B0FA8']; From cfd5e1ecade4477b024dcd9e37328298d8ab9e0c Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Tue, 6 Sep 2022 11:19:50 -0300 Subject: [PATCH 03/89] (feat) fork vector icons and add new icons --- ios/Minds.xcodeproj/project.pbxproj | 422 +++++++++++++--------------- ios/Podfile.lock | 10 +- package.json | 2 +- yarn.lock | 49 +--- 4 files changed, 210 insertions(+), 273 deletions(-) diff --git a/ios/Minds.xcodeproj/project.pbxproj b/ios/Minds.xcodeproj/project.pbxproj index 86c26db42..a84592f16 100644 --- a/ios/Minds.xcodeproj/project.pbxproj +++ b/ios/Minds.xcodeproj/project.pbxproj @@ -8,12 +8,12 @@ /* Begin PBXBuildFile section */ 00E356F31AD99517003FC87E /* MindsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* MindsTests.m */; }; - 016B8573F8D13369EEE74DC4 /* libPods-Minds-tvOSTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 12B72A1CE7CA9CAE7ABA20AC /* libPods-Minds-tvOSTests.a */; }; - 0C285D14B0508F9EA3369E01 /* libPods-Minds-MindsTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AA3EF46C56904249231CFEFC /* libPods-Minds-MindsTests.a */; }; + 05549C42F2CFEAB571280F82 /* libPods-Minds-tvOSTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 646D299545D01B3044B2A262 /* libPods-Minds-tvOSTests.a */; }; 0EA47AA13B1C4DFC8DCD3912 /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BFA6449C294D4B2FA6E84139 /* Roboto-Regular.ttf */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 16EFCC804FEDC55F16D692D9 /* libPods-Minds-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 437636C45B87E97E15407CF4 /* libPods-Minds-tvOS.a */; }; 1AA59E37AB3047CFBD86BE17 /* Roboto-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1130D1F2C2E04310B09E2143 /* Roboto-Italic.ttf */; }; 286EDEC924F96D240054AA85 /* ReactShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286EDEC824F96D240054AA85 /* ReactShareViewController.swift */; }; 28B61F20242C070D00550028 /* BootSplash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 28B61F1F242C070D00550028 /* BootSplash.storyboard */; }; @@ -24,12 +24,13 @@ 2F0CB44F2DED46AD90ACAB77 /* Roboto-BlackItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 729486A08ED94889AEF5E356 /* Roboto-BlackItalic.ttf */; }; 3DA2BBCAEB564997AFD1C084 /* Roboto-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 8B643CFCAA3F4577B09A2534 /* Roboto-Medium.ttf */; }; 48CAA0973AD04D1A9053E464 /* Roboto-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 75FAB4811732472D8C17DC7B /* Roboto-Bold.ttf */; }; + 4A506091CF72A135B053F1E6 /* libPods-Minds-MindsTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CA580A6B8762BDA710050667 /* libPods-Minds-MindsTests.a */; }; + 7628DCE0F0C77F5C8509F42B /* libPods-Share.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2AB99F719BB96F09DE9EBBF5 /* libPods-Share.a */; }; 7E995000E76319A71DCCD0F0 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9362380F89F5D347440F1C3 /* ExpoModulesProvider.swift */; }; 85BE2BC5C8A0313ED7E37322 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F6EF68D15D4B9C75C0FF0F /* ExpoModulesProvider.swift */; }; + 9832F26700332C780A1F3794 /* libPods-Minds.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 800103CD75B82B3E6C71F45B /* libPods-Minds.a */; }; A50188A89F8B46A8A07DF06C /* Roboto-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 09A1906E337F47559DC79E93 /* Roboto-BoldItalic.ttf */; }; B080D11FCFB44B31B8AA0E13 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 37DF9BB306F046229D6C90DA /* libz.tbd */; }; - BFD89474CBE1D6336C2CC163 /* libPods-Share.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 963121A276F1F1F8D81B63C4 /* libPods-Share.a */; }; - C68C800CC068F28AE0EE105C /* libPods-Minds.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9044D1906C468B7A77FE7A5E /* libPods-Minds.a */; }; D72BE5F925EEFB5C00C10FD7 /* mute.aiff in Resources */ = {isa = PBXBuildFile; fileRef = D72BE5F825EEFB5C00C10FD7 /* mute.aiff */; }; D7AF6A2A265FEDF800AE0794 /* NotificationService.m in Sources */ = {isa = PBXBuildFile; fileRef = D7AF6A29265FEDF800AE0794 /* NotificationService.m */; }; D7AF6A2E265FEDF800AE0794 /* ImageNotification.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D7AF6A26265FEDF800AE0794 /* ImageNotification.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -40,7 +41,6 @@ D7E64A4924F5C266005C259F /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D7E64A4724F5C266005C259F /* MainInterface.storyboard */; }; D7E64A4D24F5C266005C259F /* Share.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D7E64A4324F5C266005C259F /* Share.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; D8D2687AD1684A69B25EABFB /* Roboto-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = AF49184C3B7B4F3880BE848F /* Roboto-Light.ttf */; }; - DB0699A040AD2213B2366303 /* libPods-Minds-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0C39552DD333C9C98D92DA8 /* libPods-Minds-tvOS.a */; }; DDCDA3D26013483999518551 /* Roboto-ThinItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5E29162356AA4F08BB78B216 /* Roboto-ThinItalic.ttf */; }; F5974A9182A84FDFBB21A8F3 /* Roboto-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1FDD531A5CCB46F89DED8B17 /* Roboto-Black.ttf */; }; F82709059E5746EF863ADD14 /* Roboto-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3C3FF91BE82D4802BE675819 /* Roboto-Thin.ttf */; }; @@ -100,54 +100,43 @@ 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* MindsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MindsTests.m; sourceTree = ""; }; 09A1906E337F47559DC79E93 /* Roboto-BoldItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-BoldItalic.ttf"; path = "../src/assets/fonts/Roboto/Roboto-BoldItalic.ttf"; sourceTree = ""; }; - 0BC3435CAF630FAB2A0870A6 /* Pods-Minds-MindsTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds-MindsTests.release.xcconfig"; path = "Target Support Files/Pods-Minds-MindsTests/Pods-Minds-MindsTests.release.xcconfig"; sourceTree = ""; }; + 0FE934761AD8AFE7ED72ED60 /* Pods-Share.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Share.release.xcconfig"; path = "Target Support Files/Pods-Share/Pods-Share.release.xcconfig"; sourceTree = ""; }; 1130D1F2C2E04310B09E2143 /* Roboto-Italic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-Italic.ttf"; path = "../src/assets/fonts/Roboto/Roboto-Italic.ttf"; sourceTree = ""; }; - 120C7EC6D7CD1964EE88B79A /* Pods-Minds-tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds-tvOSTests.release.xcconfig"; path = "Target Support Files/Pods-Minds-tvOSTests/Pods-Minds-tvOSTests.release.xcconfig"; sourceTree = ""; }; - 129023260E8A42FF9C601292 /* MaterialIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = MaterialIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf"; sourceTree = ""; }; - 12B72A1CE7CA9CAE7ABA20AC /* libPods-Minds-tvOSTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Minds-tvOSTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07F961A680F5B00A75B9A /* Minds.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Minds.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Minds/AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.mm; path = Minds/AppDelegate.mm; sourceTree = ""; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Minds/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Minds/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Minds/main.m; sourceTree = ""; }; + 1C0F356DF4B50F92B99F2D24 /* Pods-Minds.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds.release.xcconfig"; path = "Target Support Files/Pods-Minds/Pods-Minds.release.xcconfig"; sourceTree = ""; }; 1FDD531A5CCB46F89DED8B17 /* Roboto-Black.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-Black.ttf"; path = "../src/assets/fonts/Roboto/Roboto-Black.ttf"; sourceTree = ""; }; - 267E732FD84A4504BA57C548 /* Fontisto.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Fontisto.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf"; sourceTree = ""; }; 286EDEC824F96D240054AA85 /* ReactShareViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReactShareViewController.swift; path = "../../node_modules/react-native-share-menu/ios/ReactShareViewController.swift"; sourceTree = ""; }; 28B61F1F242C070D00550028 /* BootSplash.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = BootSplash.storyboard; path = Minds/BootSplash.storyboard; sourceTree = ""; }; - 2A27DBF55829C2D2E125AE26 /* Pods-Share.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Share.release.xcconfig"; path = "Target Support Files/Pods-Share/Pods-Share.release.xcconfig"; sourceTree = ""; }; + 2AB99F719BB96F09DE9EBBF5 /* libPods-Share.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Share.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 2CD2622D204E4B0286943196 /* Roboto-MediumItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-MediumItalic.ttf"; path = "../src/assets/fonts/Roboto/Roboto-MediumItalic.ttf"; sourceTree = ""; }; 2D02E47B1E0B4A5D006451C7 /* Minds-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Minds-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 2D02E4901E0B4A5D006451C7 /* Minds-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Minds-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 37DF9BB306F046229D6C90DA /* libz.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; - 3AA7A7F064A2E9E925FE0331 /* Pods-Minds-tvOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds-tvOSTests.debug.xcconfig"; path = "Target Support Files/Pods-Minds-tvOSTests/Pods-Minds-tvOSTests.debug.xcconfig"; sourceTree = ""; }; - 3B12186C131C4F45A20FC8D8 /* FontAwesome.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf"; sourceTree = ""; }; 3C3FF91BE82D4802BE675819 /* Roboto-Thin.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-Thin.ttf"; path = "../src/assets/fonts/Roboto/Roboto-Thin.ttf"; sourceTree = ""; }; - 3C5E8F6E4B334FFBB16040A2 /* AntDesign.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = AntDesign.ttf; path = "../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf"; sourceTree = ""; }; - 4AEB0062FAE04A63B6114F7F /* EvilIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = EvilIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"; sourceTree = ""; }; - 4FECC68832D9FE3A396C0A53 /* Pods-Minds-MindsTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds-MindsTests.debug.xcconfig"; path = "Target Support Files/Pods-Minds-MindsTests/Pods-Minds-MindsTests.debug.xcconfig"; sourceTree = ""; }; - 507ED6F83A904030B363B7BF /* FontAwesome5_Solid.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome5_Solid.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf"; sourceTree = ""; }; - 5BE104A3F5C06F4FEAEDFBBB /* Pods-Minds.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds.debug.xcconfig"; path = "Target Support Files/Pods-Minds/Pods-Minds.debug.xcconfig"; sourceTree = ""; }; + 437636C45B87E97E15407CF4 /* libPods-Minds-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Minds-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 45ABB9EE4F0B4D2242FB3533 /* Pods-Minds-tvOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds-tvOSTests.debug.xcconfig"; path = "Target Support Files/Pods-Minds-tvOSTests/Pods-Minds-tvOSTests.debug.xcconfig"; sourceTree = ""; }; + 505D26485E487398614622BB /* Pods-Minds-MindsTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds-MindsTests.release.xcconfig"; path = "Target Support Files/Pods-Minds-MindsTests/Pods-Minds-MindsTests.release.xcconfig"; sourceTree = ""; }; 5E29162356AA4F08BB78B216 /* Roboto-ThinItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-ThinItalic.ttf"; path = "../src/assets/fonts/Roboto/Roboto-ThinItalic.ttf"; sourceTree = ""; }; - 6554BD510CFC4D63834F7561 /* Pods-Minds-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds-tvOS.release.xcconfig"; path = "Target Support Files/Pods-Minds-tvOS/Pods-Minds-tvOS.release.xcconfig"; sourceTree = ""; }; + 646D299545D01B3044B2A262 /* libPods-Minds-tvOSTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Minds-tvOSTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 7238FCAFBE18D80FF1A92EA9 /* Pods-Minds-tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds-tvOSTests.release.xcconfig"; path = "Target Support Files/Pods-Minds-tvOSTests/Pods-Minds-tvOSTests.release.xcconfig"; sourceTree = ""; }; 729486A08ED94889AEF5E356 /* Roboto-BlackItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-BlackItalic.ttf"; path = "../src/assets/fonts/Roboto/Roboto-BlackItalic.ttf"; sourceTree = ""; }; 73818081AF2E4726B1E7FBBC /* Roboto-LightItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-LightItalic.ttf"; path = "../src/assets/fonts/Roboto/Roboto-LightItalic.ttf"; sourceTree = ""; }; 75FAB4811732472D8C17DC7B /* Roboto-Bold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-Bold.ttf"; path = "../src/assets/fonts/Roboto/Roboto-Bold.ttf"; sourceTree = ""; }; - 787E0BE22C954FC5AA66A9C9 /* Feather.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Feather.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Feather.ttf"; sourceTree = ""; }; - 83F383987437412091619595 /* Foundation.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Foundation.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Foundation.ttf"; sourceTree = ""; }; + 77BBB11785808A45C9B19C4A /* Pods-Minds-MindsTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds-MindsTests.debug.xcconfig"; path = "Target Support Files/Pods-Minds-MindsTests/Pods-Minds-MindsTests.debug.xcconfig"; sourceTree = ""; }; + 794217F01A7524786C6B69D1 /* Pods-Minds.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds.debug.xcconfig"; path = "Target Support Files/Pods-Minds/Pods-Minds.debug.xcconfig"; sourceTree = ""; }; + 7D8668E8D9BDD805B03AB31E /* Pods-Share.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Share.debug.xcconfig"; path = "Target Support Files/Pods-Share/Pods-Share.debug.xcconfig"; sourceTree = ""; }; + 800103CD75B82B3E6C71F45B /* libPods-Minds.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Minds.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 8B643CFCAA3F4577B09A2534 /* Roboto-Medium.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-Medium.ttf"; path = "../src/assets/fonts/Roboto/Roboto-Medium.ttf"; sourceTree = ""; }; - 8C7076874E08CF99F2CBA6E5 /* Pods-Minds-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-Minds-tvOS/Pods-Minds-tvOS.debug.xcconfig"; sourceTree = ""; }; - 9044D1906C468B7A77FE7A5E /* libPods-Minds.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Minds.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 94F69A3BC26857996772658E /* Pods-Minds.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds.release.xcconfig"; path = "Target Support Files/Pods-Minds/Pods-Minds.release.xcconfig"; sourceTree = ""; }; - 963121A276F1F1F8D81B63C4 /* libPods-Share.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Share.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - A19E1A09322941E3A3DEB34B /* FontAwesome5_Brands.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome5_Brands.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf"; sourceTree = ""; }; - AA3EF46C56904249231CFEFC /* libPods-Minds-MindsTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Minds-MindsTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 90AC11008DC9E1E8EAEBB1AA /* Pods-Minds-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-Minds-tvOS/Pods-Minds-tvOS.debug.xcconfig"; sourceTree = ""; }; + 97E7D10F6734E2B08E17812C /* Pods-Minds-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minds-tvOS.release.xcconfig"; path = "Target Support Files/Pods-Minds-tvOS/Pods-Minds-tvOS.release.xcconfig"; sourceTree = ""; }; AF49184C3B7B4F3880BE848F /* Roboto-Light.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-Light.ttf"; path = "../src/assets/fonts/Roboto/Roboto-Light.ttf"; sourceTree = ""; }; - AF88048DF32544AF9BBF87EF /* Entypo.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Entypo.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Entypo.ttf"; sourceTree = ""; }; - B370BDA97DD845B5BDA08834 /* Octicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Octicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = ""; }; BFA6449C294D4B2FA6E84139 /* Roboto-Regular.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-Regular.ttf"; path = "../src/assets/fonts/Roboto/Roboto-Regular.ttf"; sourceTree = ""; }; - C0C39552DD333C9C98D92DA8 /* libPods-Minds-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Minds-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - C8BCF958B14D4858AB34647A /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = SimpleLineIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = ""; }; + CA580A6B8762BDA710050667 /* libPods-Minds-MindsTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Minds-MindsTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; D72521D82367A1FF00F95DC2 /* Minds.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Minds.entitlements; path = Minds/Minds.entitlements; sourceTree = ""; }; D72BE5F825EEFB5C00C10FD7 /* mute.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; name = mute.aiff; path = "../node_modules/react-native-silent-switch/mute.aiff"; sourceTree = ""; }; D7AF6A26265FEDF800AE0794 /* ImageNotification.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ImageNotification.appex; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -165,11 +154,6 @@ D7E64A4A24F5C266005C259F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D7E64A5524F5C445005C259F /* Share.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Share.entitlements; sourceTree = ""; }; D8F6EF68D15D4B9C75C0FF0F /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Minds-MindsTests/ExpoModulesProvider.swift"; sourceTree = ""; }; - D9E376C2870E44E28C59FC94 /* Zocial.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Zocial.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Zocial.ttf"; sourceTree = ""; }; - DA530E8C62EFB5A2FDFC8EAB /* Pods-Share.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Share.debug.xcconfig"; path = "Target Support Files/Pods-Share/Pods-Share.debug.xcconfig"; sourceTree = ""; }; - DE2209E216E44B24A4EA55AC /* MaterialCommunityIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = MaterialCommunityIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf"; sourceTree = ""; }; - E8EE368DB28048A4805CD7A9 /* Ionicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Ionicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf"; sourceTree = ""; }; - EA396FA4D42D40E1B2959EF0 /* FontAwesome5_Regular.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome5_Regular.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; }; F9362380F89F5D347440F1C3 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Minds/ExpoModulesProvider.swift"; sourceTree = ""; }; @@ -180,7 +164,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 0C285D14B0508F9EA3369E01 /* libPods-Minds-MindsTests.a in Frameworks */, + 4A506091CF72A135B053F1E6 /* libPods-Minds-MindsTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -189,7 +173,7 @@ buildActionMask = 2147483647; files = ( B080D11FCFB44B31B8AA0E13 /* libz.tbd in Frameworks */, - C68C800CC068F28AE0EE105C /* libPods-Minds.a in Frameworks */, + 9832F26700332C780A1F3794 /* libPods-Minds.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -197,7 +181,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - DB0699A040AD2213B2366303 /* libPods-Minds-tvOS.a in Frameworks */, + 16EFCC804FEDC55F16D692D9 /* libPods-Minds-tvOS.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -205,7 +189,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 016B8573F8D13369EEE74DC4 /* libPods-Minds-tvOSTests.a in Frameworks */, + 05549C42F2CFEAB571280F82 /* libPods-Minds-tvOSTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -220,7 +204,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BFD89474CBE1D6336C2CC163 /* libPods-Share.a in Frameworks */, + 7628DCE0F0C77F5C8509F42B /* libPods-Share.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -267,11 +251,11 @@ ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED2971642150620600B7C4FE /* JavaScriptCore.framework */, 37DF9BB306F046229D6C90DA /* libz.tbd */, - 9044D1906C468B7A77FE7A5E /* libPods-Minds.a */, - AA3EF46C56904249231CFEFC /* libPods-Minds-MindsTests.a */, - C0C39552DD333C9C98D92DA8 /* libPods-Minds-tvOS.a */, - 12B72A1CE7CA9CAE7ABA20AC /* libPods-Minds-tvOSTests.a */, - 963121A276F1F1F8D81B63C4 /* libPods-Share.a */, + 800103CD75B82B3E6C71F45B /* libPods-Minds.a */, + CA580A6B8762BDA710050667 /* libPods-Minds-MindsTests.a */, + 437636C45B87E97E15407CF4 /* libPods-Minds-tvOS.a */, + 646D299545D01B3044B2A262 /* libPods-Minds-tvOSTests.a */, + 2AB99F719BB96F09DE9EBBF5 /* libPods-Share.a */, ); name = Frameworks; sourceTree = ""; @@ -293,22 +277,6 @@ BFA6449C294D4B2FA6E84139 /* Roboto-Regular.ttf */, 3C3FF91BE82D4802BE675819 /* Roboto-Thin.ttf */, 5E29162356AA4F08BB78B216 /* Roboto-ThinItalic.ttf */, - 3C5E8F6E4B334FFBB16040A2 /* AntDesign.ttf */, - AF88048DF32544AF9BBF87EF /* Entypo.ttf */, - 4AEB0062FAE04A63B6114F7F /* EvilIcons.ttf */, - 787E0BE22C954FC5AA66A9C9 /* Feather.ttf */, - 3B12186C131C4F45A20FC8D8 /* FontAwesome.ttf */, - A19E1A09322941E3A3DEB34B /* FontAwesome5_Brands.ttf */, - EA396FA4D42D40E1B2959EF0 /* FontAwesome5_Regular.ttf */, - 507ED6F83A904030B363B7BF /* FontAwesome5_Solid.ttf */, - 267E732FD84A4504BA57C548 /* Fontisto.ttf */, - 83F383987437412091619595 /* Foundation.ttf */, - E8EE368DB28048A4805CD7A9 /* Ionicons.ttf */, - DE2209E216E44B24A4EA55AC /* MaterialCommunityIcons.ttf */, - 129023260E8A42FF9C601292 /* MaterialIcons.ttf */, - B370BDA97DD845B5BDA08834 /* Octicons.ttf */, - C8BCF958B14D4858AB34647A /* SimpleLineIcons.ttf */, - D9E376C2870E44E28C59FC94 /* Zocial.ttf */, ); name = Resources; sourceTree = ""; @@ -404,16 +372,16 @@ FE1DBD94E1B49F8F110400B4 /* Pods */ = { isa = PBXGroup; children = ( - 5BE104A3F5C06F4FEAEDFBBB /* Pods-Minds.debug.xcconfig */, - 94F69A3BC26857996772658E /* Pods-Minds.release.xcconfig */, - 4FECC68832D9FE3A396C0A53 /* Pods-Minds-MindsTests.debug.xcconfig */, - 0BC3435CAF630FAB2A0870A6 /* Pods-Minds-MindsTests.release.xcconfig */, - 8C7076874E08CF99F2CBA6E5 /* Pods-Minds-tvOS.debug.xcconfig */, - 6554BD510CFC4D63834F7561 /* Pods-Minds-tvOS.release.xcconfig */, - 3AA7A7F064A2E9E925FE0331 /* Pods-Minds-tvOSTests.debug.xcconfig */, - 120C7EC6D7CD1964EE88B79A /* Pods-Minds-tvOSTests.release.xcconfig */, - DA530E8C62EFB5A2FDFC8EAB /* Pods-Share.debug.xcconfig */, - 2A27DBF55829C2D2E125AE26 /* Pods-Share.release.xcconfig */, + 794217F01A7524786C6B69D1 /* Pods-Minds.debug.xcconfig */, + 1C0F356DF4B50F92B99F2D24 /* Pods-Minds.release.xcconfig */, + 77BBB11785808A45C9B19C4A /* Pods-Minds-MindsTests.debug.xcconfig */, + 505D26485E487398614622BB /* Pods-Minds-MindsTests.release.xcconfig */, + 90AC11008DC9E1E8EAEBB1AA /* Pods-Minds-tvOS.debug.xcconfig */, + 97E7D10F6734E2B08E17812C /* Pods-Minds-tvOS.release.xcconfig */, + 45ABB9EE4F0B4D2242FB3533 /* Pods-Minds-tvOSTests.debug.xcconfig */, + 7238FCAFBE18D80FF1A92EA9 /* Pods-Minds-tvOSTests.release.xcconfig */, + 7D8668E8D9BDD805B03AB31E /* Pods-Share.debug.xcconfig */, + 0FE934761AD8AFE7ED72ED60 /* Pods-Share.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -425,12 +393,12 @@ isa = PBXNativeTarget; buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "MindsTests" */; buildPhases = ( - 6D374E1DE73054824AB7763A /* [CP] Check Pods Manifest.lock */, + 7CECF075AB2C916E4236A0BB /* [CP] Check Pods Manifest.lock */, 00E356EA1AD99517003FC87E /* Sources */, 00E356EB1AD99517003FC87E /* Frameworks */, 00E356EC1AD99517003FC87E /* Resources */, - D63496A695119D33F5C5C1C7 /* [CP] Embed Pods Frameworks */, - 97B1A346E11B778ED7BCD398 /* [CP] Copy Pods Resources */, + 62402BB2C436DA825C156C1C /* [CP] Embed Pods Frameworks */, + 4F53AF050A952B359858C357 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -446,7 +414,7 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Minds" */; buildPhases = ( - 328EA261F4A902FF82A3788E /* [CP] Check Pods Manifest.lock */, + 121C9F22480077F6730CC375 /* [CP] Check Pods Manifest.lock */, FD10A7F022414F080027D42C /* Start Packager */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, @@ -454,8 +422,8 @@ 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, DF23D9AE37694D50863E721D /* Upload Debug Symbols to Sentry */, D7E64A4E24F5C266005C259F /* Embed App Extensions */, - 908D1AD13413B9B75D44CF3E /* [CP] Embed Pods Frameworks */, - 566C6B5977664F82962A1792 /* [CP] Copy Pods Resources */, + 16D70F8E55DCBDA8F3897CB4 /* [CP] Embed Pods Frameworks */, + 310D9FFF7056863501963E1E /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -472,7 +440,7 @@ isa = PBXNativeTarget; buildConfigurationList = 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "Minds-tvOS" */; buildPhases = ( - 6CA1ECB50432AF69328B4990 /* [CP] Check Pods Manifest.lock */, + 479D76E5A2B6DAB2E08821F6 /* [CP] Check Pods Manifest.lock */, FD10A7F122414F3F0027D42C /* Start Packager */, 2D02E4771E0B4A5D006451C7 /* Sources */, 2D02E4781E0B4A5D006451C7 /* Frameworks */, @@ -492,7 +460,7 @@ isa = PBXNativeTarget; buildConfigurationList = 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "Minds-tvOSTests" */; buildPhases = ( - 51115C26B334E6C399F15058 /* [CP] Check Pods Manifest.lock */, + 84ED279D8DF6B50D7F53BCDA /* [CP] Check Pods Manifest.lock */, 2D02E48C1E0B4A5D006451C7 /* Sources */, 2D02E48D1E0B4A5D006451C7 /* Frameworks */, 2D02E48E1E0B4A5D006451C7 /* Resources */, @@ -528,12 +496,12 @@ isa = PBXNativeTarget; buildConfigurationList = D7E64A5124F5C266005C259F /* Build configuration list for PBXNativeTarget "Share" */; buildPhases = ( - E427C3545FE157F94C5AC6D9 /* [CP] Check Pods Manifest.lock */, + CB3CD3D553412D113D2A7543 /* [CP] Check Pods Manifest.lock */, D7E64A3F24F5C266005C259F /* Sources */, D7E64A4024F5C266005C259F /* Frameworks */, D7E64A4124F5C266005C259F /* Resources */, 286EDECA24F96E530054AA85 /* Bundle React Native code and images */, - 0D74B2FC0638B2F7D97EEBBE /* [CP] Copy Pods Resources */, + 47F736D7A81B0A21F6477659 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -693,13 +661,86 @@ shellPath = /bin/sh; shellScript = "export NODE_BINARY=node\nif [ \"${CONFIGURATION}\" = \"Release\" ]; then\n export SENTRY_PROPERTIES=sentry.properties\n\n ../node_modules/@sentry/cli/bin/sentry-cli react-native xcode ../node_modules/react-native/scripts/react-native-xcode.sh\nelse \n ../node_modules/react-native/scripts/react-native-xcode.sh\nfi\n"; }; - 0D74B2FC0638B2F7D97EEBBE /* [CP] Copy Pods Resources */ = { + 121C9F22480077F6730CC375 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Share/Pods-Share-resources.sh", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Minds-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 16D70F8E55DCBDA8F3897CB4 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Minds/Pods-Minds-frameworks.sh", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/hermes.framework/hermes", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Minds/Pods-Minds-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 286EDECA24F96E530054AA85 /* Bundle React Native code and images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Bundle React Native code and images"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nexport NODE_BINARY=\"node\"\n\nexport ENTRY_FILE=index.share.js\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; + }; + 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Bundle React Native Code And Images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export SENTRY_PROPERTIES=sentry.properties\nexport NODE_BINARY=node\n../node_modules/@sentry/cli/bin/sentry-cli react-native xcode ../node_modules/react-native/scripts/react-native-xcode.sh"; + }; + 310D9FFF7056863501963E1E /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Minds/Pods-Minds-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf", @@ -714,6 +755,7 @@ "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIconsOutlined.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf", @@ -734,6 +776,7 @@ ); name = "[CP] Copy Pods Resources"; outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf", @@ -748,6 +791,7 @@ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIconsOutlined.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf", @@ -767,64 +811,10 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Share/Pods-Share-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 286EDECA24F96E530054AA85 /* Bundle React Native code and images */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Bundle React Native code and images"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nexport NODE_BINARY=\"node\"\n\nexport ENTRY_FILE=index.share.js\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; - }; - 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Bundle React Native Code And Images"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "export SENTRY_PROPERTIES=sentry.properties\nexport NODE_BINARY=node\n../node_modules/@sentry/cli/bin/sentry-cli react-native xcode ../node_modules/react-native/scripts/react-native-xcode.sh"; - }; - 328EA261F4A902FF82A3788E /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Minds-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Minds/Pods-Minds-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 51115C26B334E6C399F15058 /* [CP] Check Pods Manifest.lock */ = { + 479D76E5A2B6DAB2E08821F6 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -839,21 +829,20 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Minds-tvOSTests-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Minds-tvOS-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 566C6B5977664F82962A1792 /* [CP] Copy Pods Resources */ = { + 47F736D7A81B0A21F6477659 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Minds/Pods-Minds-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", + "${PODS_ROOT}/Target Support Files/Pods-Share/Pods-Share-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf", @@ -868,6 +857,7 @@ "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIconsOutlined.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf", @@ -888,7 +878,6 @@ ); name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf", @@ -903,6 +892,7 @@ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIconsOutlined.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf", @@ -922,72 +912,10 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Minds/Pods-Minds-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 6CA1ECB50432AF69328B4990 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Minds-tvOS-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 6D374E1DE73054824AB7763A /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Minds-MindsTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 908D1AD13413B9B75D44CF3E /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Minds/Pods-Minds-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/hermes.framework/hermes", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Minds/Pods-Minds-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Share/Pods-Share-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 97B1A346E11B778ED7BCD398 /* [CP] Copy Pods Resources */ = { + 4F53AF050A952B359858C357 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1009,6 +937,7 @@ "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf", + "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIconsOutlined.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf", @@ -1044,6 +973,7 @@ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIconsOutlined.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf", @@ -1066,7 +996,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Minds-MindsTests/Pods-Minds-MindsTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; - D63496A695119D33F5C5C1C7 /* [CP] Embed Pods Frameworks */ = { + 62402BB2C436DA825C156C1C /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1084,21 +1014,51 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Minds-MindsTests/Pods-Minds-MindsTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - DF23D9AE37694D50863E721D /* Upload Debug Symbols to Sentry */ = { + 7CECF075AB2C916E4236A0BB /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "Upload Debug Symbols to Sentry"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Minds-MindsTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\n\nif [ \"${CONFIGURATION}\" = \"Release\" ]; then\n export SENTRY_PROPERTIES=sentry.properties\n ../node_modules/@sentry/cli/bin/sentry-cli upload-dsym\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; }; - E427C3545FE157F94C5AC6D9 /* [CP] Check Pods Manifest.lock */ = { + 84ED279D8DF6B50D7F53BCDA /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Minds-tvOSTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + CB3CD3D553412D113D2A7543 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1120,6 +1080,20 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + DF23D9AE37694D50863E721D /* Upload Debug Symbols to Sentry */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Upload Debug Symbols to Sentry"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\n\nif [ \"${CONFIGURATION}\" = \"Release\" ]; then\n export SENTRY_PROPERTIES=sentry.properties\n ../node_modules/@sentry/cli/bin/sentry-cli upload-dsym\nfi\n"; + }; FD10A7F022414F080027D42C /* Start Packager */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1254,7 +1228,7 @@ /* Begin XCBuildConfiguration section */ 00E356F61AD99517003FC87E /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4FECC68832D9FE3A396C0A53 /* Pods-Minds-MindsTests.debug.xcconfig */; + baseConfigurationReference = 77BBB11785808A45C9B19C4A /* Pods-Minds-MindsTests.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; @@ -1279,7 +1253,7 @@ }; 00E356F71AD99517003FC87E /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0BC3435CAF630FAB2A0870A6 /* Pods-Minds-MindsTests.release.xcconfig */; + baseConfigurationReference = 505D26485E487398614622BB /* Pods-Minds-MindsTests.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; @@ -1301,7 +1275,7 @@ }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5BE104A3F5C06F4FEAEDFBBB /* Pods-Minds.debug.xcconfig */; + baseConfigurationReference = 794217F01A7524786C6B69D1 /* Pods-Minds.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -1334,7 +1308,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 94F69A3BC26857996772658E /* Pods-Minds.release.xcconfig */; + baseConfigurationReference = 1C0F356DF4B50F92B99F2D24 /* Pods-Minds.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -1368,7 +1342,7 @@ }; 2D02E4971E0B4A5E006451C7 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 8C7076874E08CF99F2CBA6E5 /* Pods-Minds-tvOS.debug.xcconfig */; + baseConfigurationReference = 90AC11008DC9E1E8EAEBB1AA /* Pods-Minds-tvOS.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; @@ -1397,7 +1371,7 @@ }; 2D02E4981E0B4A5E006451C7 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 6554BD510CFC4D63834F7561 /* Pods-Minds-tvOS.release.xcconfig */; + baseConfigurationReference = 97E7D10F6734E2B08E17812C /* Pods-Minds-tvOS.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; @@ -1426,7 +1400,7 @@ }; 2D02E4991E0B4A5E006451C7 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3AA7A7F064A2E9E925FE0331 /* Pods-Minds-tvOSTests.debug.xcconfig */; + baseConfigurationReference = 45ABB9EE4F0B4D2242FB3533 /* Pods-Minds-tvOSTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -1454,7 +1428,7 @@ }; 2D02E49A1E0B4A5E006451C7 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 120C7EC6D7CD1964EE88B79A /* Pods-Minds-tvOSTests.release.xcconfig */; + baseConfigurationReference = 7238FCAFBE18D80FF1A92EA9 /* Pods-Minds-tvOSTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -1655,7 +1629,7 @@ }; D7E64A4F24F5C266005C259F /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = DA530E8C62EFB5A2FDFC8EAB /* Pods-Share.debug.xcconfig */; + baseConfigurationReference = 7D8668E8D9BDD805B03AB31E /* Pods-Share.debug.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -1692,7 +1666,7 @@ }; D7E64A5024F5C266005C259F /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2A27DBF55829C2D2E125AE26 /* Pods-Share.release.xcconfig */; + baseConfigurationReference = 0FE934761AD8AFE7ED72ED60 /* Pods-Share.release.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index bd7e20ce1..80972830b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -480,7 +480,7 @@ PODS: - React-Core - RNPermissions (3.6.1): - React-Core - - RNPhotoEditor (1.0.12): + - RNPhotoEditor (1.0.13): - iOSPhotoEditor - React - RNReanimated (2.8.0): @@ -525,8 +525,8 @@ PODS: - SnowplowTracker (~> 3.0) - RNSVG (12.1.0): - React - - RNVectorIcons (7.1.0): - - React + - RNVectorIcons (9.2.0): + - React-Core - SDWebImage (5.11.1): - SDWebImage/Core (= 5.11.1) - SDWebImage/Core (5.11.1) @@ -959,7 +959,7 @@ SPEC CHECKSUMS: RNInAppBrowser: 3ff3a3b8f458aaf25aaee879d057352862edf357 RNLocalize: 7c7aeda16c01db7a0918981c14875c0a53be9b79 RNPermissions: dcdb7b99796bbeda6975a6e79ad519c41b251b1c - RNPhotoEditor: 2a4c8501057612652c0badcd5e23c435d5c2c8cd + RNPhotoEditor: dae3b766e42ff5c184e74be0a95ff6af107a5ecc RNReanimated: 64573e25e078ae6bec03b891586d50b9ec284393 RNScreens: d6da2b9e29cf523832c2542f47bf1287318b1868 RNSentry: a2b02b326ae4fce91ce7c57d3f7dcf8df4f72269 @@ -967,7 +967,7 @@ SPEC CHECKSUMS: RNShareMenu: cb9dac548c8bf147d06f0bf07296ad51ea9f5fc3 RNSnowplowTracker: 88c65c2132ed97c0732473948c00c8f4d6473b12 RNSVG: ce9d996113475209013317e48b05c21ee988d42e - RNVectorIcons: bc69e6a278b14842063605de32bec61f0b251a59 + RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8 SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d SDWebImageWebPCoder: f93010f3f6c031e2f8fb3081ca4ee6966c539815 Sentry: 89d26e036063b9cb9caa59b6951dd2f8277aa13b diff --git a/package.json b/package.json index 29431bb6c..d83b6bea2 100644 --- a/package.json +++ b/package.json @@ -143,7 +143,7 @@ "react-native-switch-pro": "^1.0.4", "react-native-system-setting": "^1.7.6", "react-native-tab-view": "^3.1.1", - "react-native-vector-icons": "^7.1.0", + "react-native-vector-icons": "msantang78/react-native-vector-icons", "react-native-vision-camera": "^2.13.1", "react-native-webview": "^11.14.0", "rn-update-apk": "^4.5.0", diff --git a/yarn.lock b/yarn.lock index 2b47699e1..caa50ff3a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12743,11 +12743,6 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= - lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" @@ -12788,11 +12783,6 @@ lodash.flattendeep@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= -lodash.frompairs@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.frompairs/-/lodash.frompairs-4.0.1.tgz#bc4e5207fa2757c136e573614e9664506b2b1bd2" - integrity sha1-vE5SB/onV8E25XNhTpZkUGsrG9I= - lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" @@ -12843,11 +12833,6 @@ lodash.merge@^4.6.1, lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.omit@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60" - integrity sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA= - lodash.omitby@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.omitby/-/lodash.omitby-4.6.0.tgz#5c15ff4754ad555016b53c041311e8f079204791" @@ -12873,21 +12858,6 @@ lodash.range@^3.2.0: resolved "https://registry.yarnpkg.com/lodash.range/-/lodash.range-3.2.0.tgz#f461e588f66683f7eadeade513e38a69a565a15d" integrity sha1-9GHliPZmg/fq3q3lE+OKaaVloV0= -lodash.template@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" - integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.templatesettings "^4.0.0" - -lodash.templatesettings@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" - integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.throttle@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" @@ -16142,19 +16112,12 @@ react-native-tab-view@^3.1.1: resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-3.1.1.tgz#1f8d7a835ab4f5b1b1407ec8dddc1053b53fa3c6" integrity sha512-M5pRN6utQfytKWoKlKVzg5NbkYu308qNoW1khGTtEOTs1k14p2dHJ/BWOJoJYHKbPVUyZldbG9MFT7gUl4YHnw== -react-native-vector-icons@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/react-native-vector-icons/-/react-native-vector-icons-7.1.0.tgz#145487d617b2a81d395d2cf64e6e065fcab3a454" - integrity sha512-V2a1zJ4i+kS8O4j183gIwX14St9AxxXabxwYpFBgRhvr2NDXyFcjHDEAgrOYYlt2W57e20aN1tBDU/I+wn9WtQ== +react-native-vector-icons@msantang78/react-native-vector-icons: + version "9.2.0" + resolved "https://codeload.github.com/msantang78/react-native-vector-icons/tar.gz/2edf434d52ff1b79d1902c96565b0aa0a5aa42b6" dependencies: - lodash.frompairs "^4.0.1" - lodash.isequal "^4.5.0" - lodash.isstring "^4.0.1" - lodash.omit "^4.5.0" - lodash.pick "^4.4.0" - lodash.template "^4.5.0" prop-types "^15.7.2" - yargs "^15.0.2" + yargs "^16.1.1" react-native-vision-camera@^2.13.1: version "2.13.1" @@ -19665,7 +19628,7 @@ yargs@^13.2.4: y18n "^4.0.0" yargs-parser "^13.1.2" -yargs@^15.0.2, yargs@^15.1.0, yargs@^15.3.1, yargs@^15.4.1: +yargs@^15.1.0, yargs@^15.3.1, yargs@^15.4.1: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== @@ -19682,7 +19645,7 @@ yargs@^15.0.2, yargs@^15.1.0, yargs@^15.3.1, yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^16.0.3, yargs@^16.2.0: +yargs@^16.0.3, yargs@^16.1.1, yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== From 9f90b868ea798a041f2ff31e9da394bb7f836d20 Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Tue, 6 Sep 2022 11:20:11 -0300 Subject: [PATCH 04/89] (feat) add supermind action icon --- src/common/ui/icons/map.ts | 4 ++++ src/newsfeed/activity/Actions.tsx | 3 +++ src/newsfeed/activity/actions/Supermind.tsx | 25 +++++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 src/newsfeed/activity/actions/Supermind.tsx diff --git a/src/common/ui/icons/map.ts b/src/common/ui/icons/map.ts index 822dcf228..5b03c9324 100644 --- a/src/common/ui/icons/map.ts +++ b/src/common/ui/icons/map.ts @@ -19,6 +19,10 @@ const ICON_MAP = { font: 'MaterialIcons', name: 'info-outline', }, + supermind: { + font: 'MaterialIcons', + name: 'tips-and-updates', + }, warning: { font: 'MaterialIcons', name: 'warning', diff --git a/src/newsfeed/activity/Actions.tsx b/src/newsfeed/activity/Actions.tsx index 7d19d0acb..2e40f254d 100644 --- a/src/newsfeed/activity/Actions.tsx +++ b/src/newsfeed/activity/Actions.tsx @@ -15,6 +15,7 @@ import type ActivityModel from '../ActivityModel'; import { useNavigation } from '@react-navigation/native'; import ThemedStyles from '../../styles/ThemedStyles'; import ShareAction from './actions/ShareAction'; +import Supermind from './actions/Supermind'; type PropsType = { entity: ActivityModel; @@ -63,6 +64,8 @@ export const Actions = observer((props: PropsType) => { {isOwner && !isScheduled && ( )} + + {!isOwner && } )} diff --git a/src/newsfeed/activity/actions/Supermind.tsx b/src/newsfeed/activity/actions/Supermind.tsx new file mode 100644 index 000000000..c53a566e2 --- /dev/null +++ b/src/newsfeed/activity/actions/Supermind.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { IconButtonNext } from '~/common/ui'; +import { actionsContainerStyle } from './styles'; + +/** + * Supermind activity action + */ +export default function Supermind({ entity }) { + return ( + { + if (!entity.supermind) { + console.log('Call the composer here'); + } + }} + /> + ); +} From c7091fdca26dcc3d88a73e11f371ffb7f078b40b Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Tue, 6 Sep 2022 14:23:28 -0300 Subject: [PATCH 05/89] (chore) move the share action to the ellipsis menu --- src/newsfeed/activity/Actions.tsx | 3 - src/newsfeed/activity/ActivityActionSheet.tsx | 86 +++++++++++++++++-- src/newsfeed/activity/actions/ShareAction.tsx | 1 - 3 files changed, 81 insertions(+), 9 deletions(-) diff --git a/src/newsfeed/activity/Actions.tsx b/src/newsfeed/activity/Actions.tsx index 2e40f254d..27a17b250 100644 --- a/src/newsfeed/activity/Actions.tsx +++ b/src/newsfeed/activity/Actions.tsx @@ -14,7 +14,6 @@ import BaseModel from '../../common/BaseModel'; import type ActivityModel from '../ActivityModel'; import { useNavigation } from '@react-navigation/native'; import ThemedStyles from '../../styles/ThemedStyles'; -import ShareAction from './actions/ShareAction'; import Supermind from './actions/Supermind'; type PropsType = { @@ -55,8 +54,6 @@ export const Actions = observer((props: PropsType) => { /> - - {!isOwner && hasWire && ( )} diff --git a/src/newsfeed/activity/ActivityActionSheet.tsx b/src/newsfeed/activity/ActivityActionSheet.tsx index 53b3fc235..71a2a9212 100644 --- a/src/newsfeed/activity/ActivityActionSheet.tsx +++ b/src/newsfeed/activity/ActivityActionSheet.tsx @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react'; import { Alert, Linking } from 'react-native'; import { IconButtonNext } from '~ui/icons'; -import { IS_IOS, MINDS_URI } from '../../config/Config'; +import { ANDROID_CHAT_APP, IS_IOS, MINDS_URI } from '../../config/Config'; import { isFollowing } from '../NewsfeedService'; import shareService from '../../share/ShareService'; import i18n from '../../common/services/i18n.service'; @@ -19,6 +19,8 @@ import { import { GroupContext } from '~/groups/GroupViewScreen'; import { withChannelContext } from '~/channel/v2/ChannelContext'; import type UserModel from '~/channel/UserModel'; +import SendIntentAndroid from 'react-native-send-intent'; +import logService from '~/common/services/log.service'; type PropsType = { entity: ActivityModel; @@ -35,6 +37,7 @@ type StateType = { options: Array; userBlocked: boolean; shown: boolean; + shareMenuShown: boolean; }; /** @@ -43,11 +46,13 @@ type StateType = { class ActivityActionSheet extends PureComponent { static contextType = GroupContext; ref = React.createRef(); + shareMenuRef = React.createRef(); deleteOption: React.ReactNode; state: StateType = { options: [], userBlocked: false, shown: false, + shareMenuShown: false, }; /** @@ -272,10 +277,17 @@ class ActivityActionSheet extends PureComponent { title: i18n.t('share'), onPress: () => { this.hideActionSheet(); - shareService.share( - this.props.entity.text, - MINDS_URI + 'newsfeed/' + this.props.entity.guid, - ); + if (IS_IOS) { + this.share(); + } else { + if (!this.state.shareMenuShown) { + this.setState({ shareMenuShown: true }); + return; + } + if (this.shareMenuRef.current) { + this.shareMenuRef.current?.present(); + } + } }, }); @@ -363,6 +375,38 @@ class ActivityActionSheet extends PureComponent { } } + /** + * Hide the share menu + */ + hideShareMenu = () => { + this.shareMenuRef.current?.dismiss(); + }; + + /** + * Send link to a user in chat + */ + sendTo = async () => { + this.hideShareMenu(); + try { + const installed = await SendIntentAndroid.isAppInstalled( + ANDROID_CHAT_APP, + ); + if (installed) { + SendIntentAndroid.sendText({ + title: '', + text: MINDS_URI + 'newsfeed/' + this.props.entity.guid, + type: SendIntentAndroid.TEXT_PLAIN, + package: ANDROID_CHAT_APP, + }); + } else { + Linking.openURL('market://details?id=com.minds.chat'); + } + } catch (error) { + logService.exception(error); + console.log(error); + } + }; + /** * Show an error message */ @@ -374,6 +418,17 @@ class ActivityActionSheet extends PureComponent { ); } + /** + * Share the link to the post + */ + share = () => { + this.hideShareMenu(); + shareService.share( + this.props.entity.text, + MINDS_URI + 'newsfeed/' + this.props.entity.guid, + ); + }; + /** * Render Header */ @@ -399,6 +454,27 @@ class ActivityActionSheet extends PureComponent { /> )} + {this.state.shareMenuShown && ( + + + + + + + )} ); } diff --git a/src/newsfeed/activity/actions/ShareAction.tsx b/src/newsfeed/activity/actions/ShareAction.tsx index fb0c62941..8939f1be4 100644 --- a/src/newsfeed/activity/actions/ShareAction.tsx +++ b/src/newsfeed/activity/actions/ShareAction.tsx @@ -37,7 +37,6 @@ export default observer(function ShareAction({ entity }: PropsType) { return; } if (ref.current) { - console.log('present'); ref.current?.present(); } } From 4dd23fb90c42a80a34686b1f9acae31a8c8ce956 Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Tue, 6 Sep 2022 14:25:37 -0300 Subject: [PATCH 06/89] (chore) update snapshots --- .../__snapshots__/ActivityScreen.js.snap | 14 ++++++++++---- .../actions/__snapshots__/BoostAction.js.snap | 1 + __tests__/auth/__snapshots__/LoginForm.js.snap | 1 + __tests__/auth/__snapshots__/LoginScreen.js.snap | 2 ++ __tests__/blogs/__snapshots__/BlogCard.js.snap | 1 + .../blogs/__snapshots__/BlogsViewScreen.js.snap | 13 +++++++++---- .../item-partials/__snapshots__/Partials.js.snap | 1 + .../__snapshots__/NotificationsScreen.js.snap | 8 ++++++++ 8 files changed, 33 insertions(+), 8 deletions(-) diff --git a/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap b/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap index 8d5f0aaab..0bdcc08b5 100644 --- a/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap +++ b/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap @@ -144,6 +144,7 @@ exports[`Activity screen component renders correctly 1`] = ` > -  +  diff --git a/__tests__/activity/components/actions/__snapshots__/BoostAction.js.snap b/__tests__/activity/components/actions/__snapshots__/BoostAction.js.snap index 5317673bf..b356e5d79 100644 --- a/__tests__/activity/components/actions/__snapshots__/BoostAction.js.snap +++ b/__tests__/activity/components/actions/__snapshots__/BoostAction.js.snap @@ -103,6 +103,7 @@ exports[`Boost action component renders correctly 1`] = ` > -  +  diff --git a/__tests__/discovery/v2/trends/item-partials/__snapshots__/Partials.js.snap b/__tests__/discovery/v2/trends/item-partials/__snapshots__/Partials.js.snap index abd538013..b25a67d99 100644 --- a/__tests__/discovery/v2/trends/item-partials/__snapshots__/Partials.js.snap +++ b/__tests__/discovery/v2/trends/item-partials/__snapshots__/Partials.js.snap @@ -394,6 +394,7 @@ exports[`Partials tests renders correctly TrendingHashtagPartial 1`] = ` > Date: Tue, 6 Sep 2022 14:27:09 -0300 Subject: [PATCH 07/89] (chore) add types for refs --- src/newsfeed/activity/ActivityActionSheet.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/newsfeed/activity/ActivityActionSheet.tsx b/src/newsfeed/activity/ActivityActionSheet.tsx index 71a2a9212..69f4ed2a9 100644 --- a/src/newsfeed/activity/ActivityActionSheet.tsx +++ b/src/newsfeed/activity/ActivityActionSheet.tsx @@ -1,5 +1,8 @@ import React, { PureComponent } from 'react'; import { Alert, Linking } from 'react-native'; +import { BottomSheetModal as BottomSheetModalType } from '@gorhom/bottom-sheet'; +import { withSafeAreaInsets } from 'react-native-safe-area-context'; + import { IconButtonNext } from '~ui/icons'; import { ANDROID_CHAT_APP, IS_IOS, MINDS_URI } from '../../config/Config'; import { isFollowing } from '../NewsfeedService'; @@ -10,7 +13,6 @@ import sessionService from '../../common/services/session.service'; import NavigationService from '../../navigation/NavigationService'; import type ActivityModel from '../ActivityModel'; import { showNotification } from '../../../AppMessages'; -import { withSafeAreaInsets } from 'react-native-safe-area-context'; import { BottomSheetButton, BottomSheetModal, @@ -45,8 +47,8 @@ type StateType = { */ class ActivityActionSheet extends PureComponent { static contextType = GroupContext; - ref = React.createRef(); - shareMenuRef = React.createRef(); + ref = React.createRef(); + shareMenuRef = React.createRef(); deleteOption: React.ReactNode; state: StateType = { options: [], From c221e5291a26c89ac995bb5ec763271795ca7ce5 Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Tue, 6 Sep 2022 14:38:48 -0300 Subject: [PATCH 08/89] (feat) allow custom text for supermind label --- src/common/components/supermind/SupermindLabel.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/common/components/supermind/SupermindLabel.tsx b/src/common/components/supermind/SupermindLabel.tsx index 40d2605d6..3cefc8ac8 100644 --- a/src/common/components/supermind/SupermindLabel.tsx +++ b/src/common/components/supermind/SupermindLabel.tsx @@ -5,7 +5,11 @@ import { LinearGradient } from 'expo-linear-gradient'; import { B2 } from '~/common/ui'; import { SupermindGradient } from '~/styles/Colors'; -export default function SupermindLabel() { +type Props = { + text?: string; +}; + +export default function SupermindLabel({ text }: Props) { return ( - Supermind + {text || 'Supermind'} From 06b537baa231481a5a75b3b87c71ebecc5c45c5a Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Tue, 6 Sep 2022 14:53:30 -0300 Subject: [PATCH 09/89] (chore) rename supermind action --- src/newsfeed/activity/Actions.tsx | 4 ++-- .../activity/actions/{Supermind.tsx => SupermindAction.tsx} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/newsfeed/activity/actions/{Supermind.tsx => SupermindAction.tsx} (82%) diff --git a/src/newsfeed/activity/Actions.tsx b/src/newsfeed/activity/Actions.tsx index 27a17b250..6c99580fd 100644 --- a/src/newsfeed/activity/Actions.tsx +++ b/src/newsfeed/activity/Actions.tsx @@ -14,7 +14,7 @@ import BaseModel from '../../common/BaseModel'; import type ActivityModel from '../ActivityModel'; import { useNavigation } from '@react-navigation/native'; import ThemedStyles from '../../styles/ThemedStyles'; -import Supermind from './actions/Supermind'; +import SupermindAction from './actions/SupermindAction'; type PropsType = { entity: ActivityModel; @@ -62,7 +62,7 @@ export const Actions = observer((props: PropsType) => { )} - {!isOwner && } + {!isOwner && } )} diff --git a/src/newsfeed/activity/actions/Supermind.tsx b/src/newsfeed/activity/actions/SupermindAction.tsx similarity index 82% rename from src/newsfeed/activity/actions/Supermind.tsx rename to src/newsfeed/activity/actions/SupermindAction.tsx index c53a566e2..c64493613 100644 --- a/src/newsfeed/activity/actions/Supermind.tsx +++ b/src/newsfeed/activity/actions/SupermindAction.tsx @@ -5,7 +5,7 @@ import { actionsContainerStyle } from './styles'; /** * Supermind activity action */ -export default function Supermind({ entity }) { +export default function SupermindAction({ entity }) { return ( { if (!entity.supermind) { console.log('Call the composer here'); From 9d94aa5956f25fff465b0fa3d266a11258c4763f Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Wed, 7 Sep 2022 07:59:15 -0300 Subject: [PATCH 10/89] (chore) supermind button behind FF --- src/newsfeed/activity/Actions.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/newsfeed/activity/Actions.tsx b/src/newsfeed/activity/Actions.tsx index 6c99580fd..159259146 100644 --- a/src/newsfeed/activity/Actions.tsx +++ b/src/newsfeed/activity/Actions.tsx @@ -15,6 +15,8 @@ import type ActivityModel from '../ActivityModel'; import { useNavigation } from '@react-navigation/native'; import ThemedStyles from '../../styles/ThemedStyles'; import SupermindAction from './actions/SupermindAction'; +import { useFeature } from '@growthbook/growthbook-react'; +import ShareAction from './actions/ShareAction'; type PropsType = { entity: ActivityModel; @@ -26,6 +28,8 @@ type PropsType = { export const Actions = observer((props: PropsType) => { const navigation = useNavigation(); + const supermindFeatureFlag = useFeature('mobile-supermind'); + if (props.hideTabs) { return null; } @@ -54,6 +58,8 @@ export const Actions = observer((props: PropsType) => { /> + {supermindFeatureFlag.off && } + {!isOwner && hasWire && ( )} @@ -62,7 +68,9 @@ export const Actions = observer((props: PropsType) => { )} - {!isOwner && } + {supermindFeatureFlag.on && !isOwner && ( + + )} )} From 640c76d0e949dfcb268c9fee3fb41d9aca47cb02 Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Wed, 7 Sep 2022 07:59:33 -0300 Subject: [PATCH 11/89] (chore) supermind label height --- src/common/components/supermind/SupermindLabel.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/common/components/supermind/SupermindLabel.tsx b/src/common/components/supermind/SupermindLabel.tsx index 3cefc8ac8..94b8d7f07 100644 --- a/src/common/components/supermind/SupermindLabel.tsx +++ b/src/common/components/supermind/SupermindLabel.tsx @@ -13,6 +13,7 @@ export default function SupermindLabel({ text }: Props) { return ( Date: Wed, 7 Sep 2022 08:01:55 -0300 Subject: [PATCH 12/89] (chore) update snapshots --- .../components/__snapshots__/ActivityScreen.js.snap | 7 ++++--- __tests__/blogs/__snapshots__/BlogsViewScreen.js.snap | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap b/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap index 0bdcc08b5..7dfae845c 100644 --- a/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap +++ b/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap @@ -1172,7 +1172,6 @@ exports[`Activity screen component renders correctly 1`] = ` }, ] } - testID="supermind button" > -  +  diff --git a/__tests__/blogs/__snapshots__/BlogsViewScreen.js.snap b/__tests__/blogs/__snapshots__/BlogsViewScreen.js.snap index b15de49c7..d65837b00 100644 --- a/__tests__/blogs/__snapshots__/BlogsViewScreen.js.snap +++ b/__tests__/blogs/__snapshots__/BlogsViewScreen.js.snap @@ -1701,7 +1701,6 @@ exports[`blog view screen component should renders correctly 1`] = ` }, ] } - testID="supermind button" > -  +  From 4f3c102f047992feaa4bb837d7aec1cc5590ca6a Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Wed, 7 Sep 2022 09:03:57 -0300 Subject: [PATCH 13/89] (chore) fix supermind data format --- src/discovery/v2/viewer/ActivityFullScreen.tsx | 2 +- src/newsfeed/ActivityModel.ts | 2 +- src/newsfeed/activity/Activity.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/discovery/v2/viewer/ActivityFullScreen.tsx b/src/discovery/v2/viewer/ActivityFullScreen.tsx index d23c4cac9..deab9fdb7 100644 --- a/src/discovery/v2/viewer/ActivityFullScreen.tsx +++ b/src/discovery/v2/viewer/ActivityFullScreen.tsx @@ -237,7 +237,7 @@ const ActivityFullScreen = observer((props: PropsType) => { if (hasRemind) { const Container: any = - props.entity.supermind && props.entity.supermind.isReply + props.entity.supermind && props.entity.supermind.is_reply ? SupermindBorderView : QuoteContainer; diff --git a/src/newsfeed/ActivityModel.ts b/src/newsfeed/ActivityModel.ts index fe185c9b0..fc3471fac 100644 --- a/src/newsfeed/ActivityModel.ts +++ b/src/newsfeed/ActivityModel.ts @@ -45,7 +45,7 @@ export default class ActivityModel extends BaseModel { supermind?: { request_guid: string; - isReply: boolean; + is_reply: boolean; }; time_updated: string = ''; diff --git a/src/newsfeed/activity/Activity.tsx b/src/newsfeed/activity/Activity.tsx index 3a465ba83..09d9a17ba 100644 --- a/src/newsfeed/activity/Activity.tsx +++ b/src/newsfeed/activity/Activity.tsx @@ -379,7 +379,7 @@ export default class Activity extends Component { } const Container: any = - this.props.entity.supermind && this.props.entity.supermind.isReply + this.props.entity.supermind && this.props.entity.supermind.is_reply ? SupermindBorderView : QuoteContainer; From 0d0d8344aa0d787f7e1715e8c772da1a43ecd3b6 Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Wed, 7 Sep 2022 12:45:41 -0300 Subject: [PATCH 14/89] (fix) alignment in supermind and activity metrics --- src/common/components/supermind/SupermindBorderView.tsx | 1 + src/newsfeed/activity/metrics/ActivityMetrics.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/components/supermind/SupermindBorderView.tsx b/src/common/components/supermind/SupermindBorderView.tsx index 36db62f74..171070404 100644 --- a/src/common/components/supermind/SupermindBorderView.tsx +++ b/src/common/components/supermind/SupermindBorderView.tsx @@ -35,6 +35,7 @@ const styles = ThemedStyles.create({ overflow: 'hidden', }, 'margin3x', + 'marginHorizontal4x', ], innerStyle: { borderRadius: 4, diff --git a/src/newsfeed/activity/metrics/ActivityMetrics.tsx b/src/newsfeed/activity/metrics/ActivityMetrics.tsx index cf3f632c7..81c99bfc5 100644 --- a/src/newsfeed/activity/metrics/ActivityMetrics.tsx +++ b/src/newsfeed/activity/metrics/ActivityMetrics.tsx @@ -81,7 +81,7 @@ export default class ActivityMetrics extends Component { const containerStyle = ThemedStyles.combine( 'rowJustifySpaceBetween', 'padding2x', - 'paddingLeft4x', + 'paddingHorizontal4x', ); const textStyle = ThemedStyles.combine( From a3c93c7e13f68549b4cf9bddf3d5011f50455ee9 Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Wed, 7 Sep 2022 12:48:17 -0300 Subject: [PATCH 15/89] (chore) snapshots --- .../activity/components/__snapshots__/ActivityMetrics.js.snap | 2 +- .../activity/components/__snapshots__/ActivityScreen.js.snap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/__tests__/activity/components/__snapshots__/ActivityMetrics.js.snap b/__tests__/activity/components/__snapshots__/ActivityMetrics.js.snap index 211eb8baa..010695c9d 100644 --- a/__tests__/activity/components/__snapshots__/ActivityMetrics.js.snap +++ b/__tests__/activity/components/__snapshots__/ActivityMetrics.js.snap @@ -12,7 +12,7 @@ exports[`activity metrics component renders correctly 1`] = ` "padding": 8, }, Object { - "paddingLeft": 16, + "paddingHorizontal": 16, }, ] } diff --git a/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap b/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap index 7dfae845c..41fca708e 100644 --- a/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap +++ b/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap @@ -512,7 +512,7 @@ exports[`Activity screen component renders correctly 1`] = ` "padding": 8, }, Object { - "paddingLeft": 16, + "paddingHorizontal": 16, }, ] } From 0026644e1e0281c68738d1599f703770a13a97b7 Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Wed, 7 Sep 2022 13:30:24 -0300 Subject: [PATCH 16/89] (chore) version bump --- android/gradle.properties | 6 +++--- ios/ImageNotification/Info.plist | 2 +- ios/Minds-tvOS/Info.plist | 2 +- ios/Minds-tvOSTests/Info.plist | 2 +- ios/Minds/Info.plist | 2 +- ios/MindsTests/Info.plist | 2 +- ios/Share/Info.plist | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/android/gradle.properties b/android/gradle.properties index 710b86b8d..2d403378b 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -49,10 +49,10 @@ newArchEnabled=false # Disable vision camera frame processors disableFrameProcessors=true -versionName=4.28.0 +versionName=4.29.0 # CUSTOM -versionCode=1050000148 +versionCode=1050000150 # PLAY STORE (Keep double hash for the CI) -## versionCode=310148 +## versionCode=310150 diff --git a/ios/ImageNotification/Info.plist b/ios/ImageNotification/Info.plist index 754969bf3..4549eeb91 100644 --- a/ios/ImageNotification/Info.plist +++ b/ios/ImageNotification/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 4.28.0 + 4.29.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSExtension diff --git a/ios/Minds-tvOS/Info.plist b/ios/Minds-tvOS/Info.plist index ecbe71cd2..d01c3e444 100644 --- a/ios/Minds-tvOS/Info.plist +++ b/ios/Minds-tvOS/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 4.28.0 + 4.29.0 CFBundleSignature ???? CFBundleVersion diff --git a/ios/Minds-tvOSTests/Info.plist b/ios/Minds-tvOSTests/Info.plist index 4907df8d8..36589930b 100644 --- a/ios/Minds-tvOSTests/Info.plist +++ b/ios/Minds-tvOSTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 4.28.0 + 4.29.0 CFBundleSignature ???? CFBundleVersion diff --git a/ios/Minds/Info.plist b/ios/Minds/Info.plist index af1f3a9d8..7a9807d0e 100644 --- a/ios/Minds/Info.plist +++ b/ios/Minds/Info.plist @@ -21,7 +21,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 4.28.0 + 4.29.0 CFBundleSignature ???? UIStatusBarStyle diff --git a/ios/MindsTests/Info.plist b/ios/MindsTests/Info.plist index 1aa527872..2057f146f 100644 --- a/ios/MindsTests/Info.plist +++ b/ios/MindsTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 4.28.0 + 4.29.0 CFBundleSignature ???? CFBundleVersion diff --git a/ios/Share/Info.plist b/ios/Share/Info.plist index 24486a570..da6129316 100644 --- a/ios/Share/Info.plist +++ b/ios/Share/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 4.28.0 + 4.29.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) HostAppBundleIdentifier From aa963d28b97a429de0db68ac4b33ce5273e0ace8 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Wed, 7 Sep 2022 14:22:37 +0200 Subject: [PATCH 17/89] (feat) supermind composer --- locales/en.json | 9 +- .../components/AutoComplete/AutoComplete.tsx | 2 +- .../AutoComplete/ChannelAutoCompleteList.tsx | 4 +- .../AutoComplete/ChannelSelectScreen.tsx | 54 ++++ .../AutoComplete/useAutoComplete.ts | 41 ++- .../AutoComplete/useChannelSuggestion.ts | 2 +- src/common/components/Input.tsx | 17 +- src/common/components/InputBase.tsx | 79 +++++ src/common/components/InputSelectorV2.tsx | 68 ++++ src/common/components/SelectorV2.tsx | 17 +- .../user-typeahead/UserTypeahead.tsx | 3 + src/common/ui/icons/map.ts | 12 + src/common/ui/screen/ModalFullScreen.tsx | 6 +- src/common/ui/screen/ScreenHeader.tsx | 3 + src/compose/ComposeBottomBar.tsx | 12 +- src/compose/ComposeScreen.tsx | 10 + .../PosterOptions/PosterStackNavigator.tsx | 6 + src/compose/SupermindComposeScreen.tsx | 295 ++++++++++++++++++ src/compose/createComposeStore.js | 46 ++- src/config/Config.ts | 8 +- src/navigation/NavigationStack.tsx | 13 + src/navigation/NavigationTypes.ts | 10 + src/wire/methods/v2/StripeCardSelector.tsx | 144 +++++---- 23 files changed, 761 insertions(+), 100 deletions(-) create mode 100644 src/common/components/AutoComplete/ChannelSelectScreen.tsx create mode 100644 src/common/components/InputBase.tsx create mode 100644 src/common/components/InputSelectorV2.tsx create mode 100644 src/compose/SupermindComposeScreen.tsx diff --git a/locales/en.json b/locales/en.json index 79fe093cb..a51772255 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1266,6 +1266,7 @@ "wire": { "customDonation": "Custom Donation", "selectCredit": "Select a credit card.", + "addCard": "Add card.", "amountMonth": "{{amount}} / month", "noAddress": "This channel has not configured their {{type}} address yet", "amountMonthDescription": "THIS POST CAN ONLY BE SEEN BY SUPPORTERS WHO WIRE {{amount}}/MONTH TO @{{name}}", @@ -1618,5 +1619,11 @@ "newMoment": "New Moment", "recommendedChannels": "Recommended Channels", "seeMore": "See more", - "removeFromFeed": "Remove from feed" + "removeFromFeed": "Remove from feed", + "channelSelect": { + "title": "Select a channel" + }, + "supermind": { + "requestSubmitted": "Supermind request has been sent." + } } diff --git a/src/common/components/AutoComplete/AutoComplete.tsx b/src/common/components/AutoComplete/AutoComplete.tsx index 6a781dd37..e38d2236a 100644 --- a/src/common/components/AutoComplete/AutoComplete.tsx +++ b/src/common/components/AutoComplete/AutoComplete.tsx @@ -7,7 +7,7 @@ import HashtagAutoCompleteList from './HashtagAutoCompleteList'; import useAutoComplete, { AutoCompleteInput } from './useAutoComplete'; interface AutoCompleteProps extends AutoCompleteInput { - onVisible: (visible: boolean) => void; + onVisible?: (visible: boolean) => void; /** * a custom component to use instead of flat list */ diff --git a/src/common/components/AutoComplete/ChannelAutoCompleteList.tsx b/src/common/components/AutoComplete/ChannelAutoCompleteList.tsx index 6faeb8154..2d26eff36 100644 --- a/src/common/components/AutoComplete/ChannelAutoCompleteList.tsx +++ b/src/common/components/AutoComplete/ChannelAutoCompleteList.tsx @@ -19,7 +19,7 @@ export interface ChannelAutoCompleteListProps { /** * when a channel is selected, **/ - onSelect?: (selectedText: string) => void; + onSelect?: (selectedText: string, channel: UserModel) => void; /** * a custom component to use instead of flat list */ @@ -45,7 +45,7 @@ function ChannelAutoCompleteList({ onSelect?.(user.username)} + onPress={user => onSelect?.(user.username, user)} /> ), [onSelect], diff --git a/src/common/components/AutoComplete/ChannelSelectScreen.tsx b/src/common/components/AutoComplete/ChannelSelectScreen.tsx new file mode 100644 index 000000000..a5f856f27 --- /dev/null +++ b/src/common/components/AutoComplete/ChannelSelectScreen.tsx @@ -0,0 +1,54 @@ +import { RouteProp } from '@react-navigation/core'; +import React, { useCallback, useState } from 'react'; +import UserModel from '../../../channel/UserModel'; +import NavigationService from '../../../navigation/NavigationService'; +import { RootStackParamList } from '../../../navigation/NavigationTypes'; +import i18nService from '../../services/i18n.service'; +import { ModalFullScreen } from '../../ui'; +import InputContainer from '../InputContainer'; +import AutoComplete from './AutoComplete'; + +type ChannelSelectScreenRoute = RouteProp< + RootStackParamList, + 'ChannelSelectScreen' +>; + +interface ChannelSelectScreenProps { + route?: ChannelSelectScreenRoute; +} + +export default function ChannelSelectScreen({ + route, +}: ChannelSelectScreenProps) { + const [username, setUsername] = useState(route?.params?.username || ''); + + const onSelect = useCallback( + (channel: UserModel) => { + NavigationService.goBack(); + route?.params?.onSelect(channel); + }, + [route], + ); + + return ( + + + null} + /> + + ); +} diff --git a/src/common/components/AutoComplete/useAutoComplete.ts b/src/common/components/AutoComplete/useAutoComplete.ts index 9365efdb1..387b29e96 100644 --- a/src/common/components/AutoComplete/useAutoComplete.ts +++ b/src/common/components/AutoComplete/useAutoComplete.ts @@ -2,15 +2,17 @@ import { useKeyboard, useDimensions } from '@react-native-community/hooks'; import { useState, useEffect, useCallback } from 'react'; import { InteractionManager } from 'react-native'; import useDebouncedCallback from '~/common/hooks/useDebouncedCallback'; +import UserModel from '../../../channel/UserModel'; export interface AutoCompleteInput { text: string; - selection: { start: number; end: number }; + selection?: { start: number; end: number }; textHeight?: number; scrollOffset?: number; onScrollToOffset?: (offset: number) => void; onTextChange: (text: string) => void; - onSelectionChange: (selection: { start: number; end: number }) => void; + onChannelSelect?: (channel: UserModel) => void; + onSelectionChange?: (selection: { start: number; end: number }) => void; onTextInputFocus?: () => void; } @@ -21,6 +23,7 @@ const useAutoComplete = ({ scrollOffset = 0, onScrollToOffset, onTextChange, + onChannelSelect, onSelectionChange, onTextInputFocus, }: AutoCompleteInput) => { @@ -31,7 +34,7 @@ const useAutoComplete = ({ const keyboard = useKeyboard(); useEffect(() => { - const substr = text.substr(0, selection.start) || ''; + const substr = (selection ? text.substr(0, selection.start) : text) || ''; /** * Distance from top within which we don't have to move the scroll position. @@ -77,17 +80,19 @@ const useAutoComplete = ({ [], ); const handleAutoCompleteSelect = useCallback( - (selectedText: string) => { + (selectedText: string, selectedChannel?: UserModel) => { let endword: RegExpMatchArray | null = [''], - matchText = text.substr(0, selection.end); + matchText = selection ? text.substr(0, selection.end) : text; - // search end of word - if (text.length > selection.end) { - endword = text.substr(selection.end).match(/^([a-zA-Z0-9])+\b/); - if (endword) { - matchText += endword[0]; - } else { - endword = ['']; + if (selection) { + // search end of word + if (text.length > selection.end) { + endword = text.substr(selection.end).match(/^([a-zA-Z0-9])+\b/); + if (endword) { + matchText += endword[0]; + } else { + endword = ['']; + } } } @@ -97,11 +102,16 @@ const useAutoComplete = ({ new RegExp(`${tag}[a-zA-Z0-9]+$`), tag + selectedText + ' ', ); - const postText = text.substr(selection.end + endword[0].length); + const postText = selection + ? text.substr(selection.end + endword[0].length) + : ''; onTextChange(preText + postText); + if (selectedChannel) { + onChannelSelect?.(selectedChannel); + } - onSelectionChange({ + onSelectionChange?.({ start: preText.length, end: preText.length, }); @@ -112,12 +122,13 @@ const useAutoComplete = ({ } }, [ + selection, text, - selection.end, query, onTextChange, onSelectionChange, onTextInputFocus, + onChannelSelect, ], ); diff --git a/src/common/components/AutoComplete/useChannelSuggestion.ts b/src/common/components/AutoComplete/useChannelSuggestion.ts index b4b69ab14..36f95d002 100644 --- a/src/common/components/AutoComplete/useChannelSuggestion.ts +++ b/src/common/components/AutoComplete/useChannelSuggestion.ts @@ -33,7 +33,7 @@ export default function useChannelSuggestion( useEffect(() => { if (!query) return; - debouncedFetch(query); + debouncedFetch(query.replace('@', '')); }, [debouncedFetch, query]); return fetchStore; diff --git a/src/common/components/Input.tsx b/src/common/components/Input.tsx index 32db7e3af..6c203d5d3 100644 --- a/src/common/components/Input.tsx +++ b/src/common/components/Input.tsx @@ -17,7 +17,14 @@ export interface PropsType extends TextInputProps { autofocus?: boolean; dateFormat?: string; labelStyle?: TextStyle | Array; + /** + * the label for the input + */ placeholder?: string; + /** + * the placeholder text for the TextInput + */ + placeholderText?: string; value?: string; testID?: string; keyboardType?: string; @@ -103,12 +110,17 @@ export default class Input extends Component { this.props.onChangeText(value.replace(/[^0-9]/g, '')) + : this.props.onChangeText + } ref={this.inputRef} - placeholder="" + placeholder={this.props.placeholderText} /> ); }; @@ -208,7 +220,6 @@ const styles = StyleSheet.create({ errorContainer: { alignContent: 'flex-end', flexGrow: 1, - paddingRight: 10, }, optional: { fontSize: 14, diff --git a/src/common/components/InputBase.tsx b/src/common/components/InputBase.tsx new file mode 100644 index 000000000..f36b47264 --- /dev/null +++ b/src/common/components/InputBase.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import { StyleProp, TextStyle, View } from 'react-native'; +import { Tooltip } from 'react-native-elements'; +import ThemedStyles from '../../styles/ThemedStyles'; +import { Column, Icon, Row } from '../ui'; +import MPressable from './MPressable'; +import MText from './MText'; + +interface InputBaseProps { + style?: any; + labelStyle?: StyleProp; + label: string; + value: string; + onPress?: () => void; + info?: string; + error?: string; + icon?: React.ReactNode; +} + +export default function InputBase({ + style, + labelStyle, + label, + value, + onPress, + info, + icon, + error, +}: InputBaseProps) { + const theme = ThemedStyles.style; + + return ( + + + + + {label} + + {!!error && ( + + {error} + + )} + + + {value} + + + + + {info && ( + {info}}> + + + )} + {icon ? icon : null} + + + ); +} diff --git a/src/common/components/InputSelectorV2.tsx b/src/common/components/InputSelectorV2.tsx new file mode 100644 index 000000000..1c7e8f944 --- /dev/null +++ b/src/common/components/InputSelectorV2.tsx @@ -0,0 +1,68 @@ +import React, { useCallback, useRef } from 'react'; +import { StyleProp, TextStyle, ViewStyle } from 'react-native'; +import { Icon } from '../ui'; +import InputBase from './InputBase'; +import Selector from './SelectorV2'; + +type PropsType = { + data: Array; + valueExtractor: (value: any) => any; + keyExtractor: (value: any) => any; + onSelected: (value: any) => any; + selectTitle?: string; + label: string; + selected: any; + textStyle?: TextStyle | TextStyle[]; + backdropOpacity?: number; + containerStyle?: StyleProp; + labelStyle?: StyleProp; + mainContainerStyle?: StyleProp; + info?: string; + error?: string; +}; + +const InputSelector = (props: PropsType) => { + let selectorRef = useRef(null); + + const onSelected = useCallback( + item => { + props.onSelected(props.keyExtractor(item)); + }, + [props], + ); + + const getValueOf = useCallback( + key => { + const selected = props.data.find(i => props.keyExtractor(i) === key); + return props.valueExtractor(selected); + }, + [props], + ); + + return ( + + {show => ( + show(props.selected)} + style={props.containerStyle} + label={props.label} + labelStyle={props.labelStyle} + info={props.info} + error={props.error} + value={getValueOf(props.selected)} + icon={} + /> + )} + + ); +}; + +export default InputSelector; diff --git a/src/common/components/SelectorV2.tsx b/src/common/components/SelectorV2.tsx index 44bb123e3..51fe08133 100644 --- a/src/common/components/SelectorV2.tsx +++ b/src/common/components/SelectorV2.tsx @@ -150,7 +150,11 @@ const SelectorV2: ForwardRefRenderFunction = ( const isSelected = item => selected === keyExtractor(item); const onMenuItemPress = () => { - onItemSelect(item); + if (item.onPress) { + item.onPress(); + } else { + onItemSelect(item); + } close(); }; @@ -163,12 +167,21 @@ const SelectorV2: ForwardRefRenderFunction = ( key={keyExtractor(item)} onPress={onMenuItemPress} textStyle={textStyle} + iconName={item.iconName} title={valueExtractor(item)} style={styles.menuItem} /> ); }, - [selected, onItemSelect], + [ + theme.colorLink, + theme.colorPrimaryText, + keyExtractor, + valueExtractor, + selected, + close, + onItemSelect, + ], ); /** diff --git a/src/common/components/user-typeahead/UserTypeahead.tsx b/src/common/components/user-typeahead/UserTypeahead.tsx index 3a3acb6e7..e914dc037 100644 --- a/src/common/components/user-typeahead/UserTypeahead.tsx +++ b/src/common/components/user-typeahead/UserTypeahead.tsx @@ -21,6 +21,9 @@ import ThemedStyles from '../../../styles/ThemedStyles'; import TextInput from '../TextInput'; import MText from '../MText'; +/** + * @deprecated please use ChannelSelectScreen + */ export default class UserTypeahead extends PureComponent { textInput = void 0; diff --git a/src/common/ui/icons/map.ts b/src/common/ui/icons/map.ts index 5b03c9324..72738b8be 100644 --- a/src/common/ui/icons/map.ts +++ b/src/common/ui/icons/map.ts @@ -136,6 +136,14 @@ const ICON_MAP = { font: 'MaterialCommunityIcons', name: 'check', }, + 'checkbox-marked': { + font: 'MaterialCommunityIcons', + name: 'checkbox-marked', + }, + 'checkbox-blank': { + font: 'MaterialCommunityIcons', + name: 'checkbox-blank-outline', + }, 'plus-circle-outline': { font: 'MaterialIcons', name: 'add-circle-outline', @@ -261,6 +269,10 @@ const ICON_MAP = { font: 'MaterialCommunityIcons', name: 'arrow-up', }, + supermind: { + font: 'MaterialCommunityIcons', + name: 'lightbulb-on', + }, } as const; export type IconNameType = keyof typeof ICON_MAP; diff --git a/src/common/ui/screen/ModalFullScreen.tsx b/src/common/ui/screen/ModalFullScreen.tsx index baa3466b1..021b13656 100644 --- a/src/common/ui/screen/ModalFullScreen.tsx +++ b/src/common/ui/screen/ModalFullScreen.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useNavigation } from '@react-navigation/core'; -import { View } from 'react-native'; +import { StatusBar, View } from 'react-native'; import { Screen, ScreenHeader } from '~ui'; import ThemedStyles from '~/styles/ThemedStyles'; @@ -13,6 +13,7 @@ type PropsType = { extra?: React.ReactNode; onBack?: () => void; back?: boolean; + leftComponent?: React.ReactNode; }; /** @@ -25,6 +26,7 @@ export const ModalFullScreen = ({ scroll, extra, back, + leftComponent, onBack, }: PropsType) => { const theme = ThemedStyles.style; @@ -34,6 +36,7 @@ export const ModalFullScreen = ({ ]); return ( + {children} diff --git a/src/common/ui/screen/ScreenHeader.tsx b/src/common/ui/screen/ScreenHeader.tsx index cb4ddcdec..1e3d0d650 100644 --- a/src/common/ui/screen/ScreenHeader.tsx +++ b/src/common/ui/screen/ScreenHeader.tsx @@ -11,6 +11,7 @@ export type ScreenHeaderType = { title: string; extra?: ReactNode; back?: boolean; + leftComponent?: ReactNode; backIcon?: IconMapNameType; border?: boolean; titleType?: TypographyPropsType['type']; @@ -22,6 +23,7 @@ export const ScreenHeader = ({ title, extra, back, + leftComponent, backIcon = 'chevron-left', onBack, border, @@ -41,6 +43,7 @@ export const ScreenHeader = ({ )} + {leftComponent ? leftComponent : null} {back && ( + { + NavigationService.navigate('SupermindCompose', { + data: store.getSupermindRequest(), + onSave: (payload: SupermindRequest) => store.setSupermindRequest(payload), + onClear: () => store.clearSupermindRequest(), + }); + }, [store]); + const onScrollHandler = useCallback( (e: NativeSyntheticEvent) => setScrollOffsetDebounced(e.nativeEvent.contentOffset.y), @@ -339,6 +348,7 @@ export default observer(function ComposeScreen(props) { onHashtag={handleHashtagPress} onMoney={handleMoneyPress} onOptions={handleOptionsPress} + onSupermind={handleSupermindPress} /> )} diff --git a/src/compose/PosterOptions/PosterStackNavigator.tsx b/src/compose/PosterOptions/PosterStackNavigator.tsx index 3b8bfbdbc..b03df17dd 100644 --- a/src/compose/PosterOptions/PosterStackNavigator.tsx +++ b/src/compose/PosterOptions/PosterStackNavigator.tsx @@ -13,6 +13,7 @@ import PosterOptions from './PosterOptions'; import ScheduleSelector from './ScheduleSelector'; import TagSelector from './TagSelector'; import MembershipMonetizeScreen from './monetize/MembershipMonetizeScreen'; +import ComposeSupermindScreen from '../SupermindComposeScreen'; // import CustomMonetizeScreen from '../compose/PosterOptions/monetize/CustomMonetizeScreen'; export type PosterStackParamList = { @@ -27,6 +28,7 @@ export type PosterStackParamList = { PlusMonetize: {}; MembershipMonetize: {}; CustomMonetize: {}; + ComposeSupermind: {}; }; const Stack = createStackNavigator(); @@ -48,6 +50,10 @@ export default function PosterStackNavigator() { + + showNotification(error, 'danger', undefined); + +type PasswordConfirmation = RouteProp; + +enum ReplyType { + text = 0, + image = 1, + video = 2, +} + +enum PaymentType { + cash = 0, + token = 1, +} + +export interface SupermindRequest { + receiver_username: string; + receiver_guid: string; + payment_options: { + payment_type: PaymentType; + payment_method_id: string; + amount: number; + }; + reply_type: ReplyType; + twitter_required: boolean; + terms_agreed: boolean; +} + +interface SupermindComposeScreen { + route?: PasswordConfirmation; +} + +/** + * Compose Screen + * @param {Object} props + */ +export default function SupermindComposeScreen(props: SupermindComposeScreen) { + const theme = ThemedStyles.style; + const data: SupermindRequest | undefined = props.route?.params?.data; + const [username, setUsername] = useState(data?.receiver_username || ''); + const [channelGuid, setChannelGuid] = useState( + data?.receiver_guid, + ); + const [replyType, setReplyType] = useState( + data?.reply_type ?? ReplyType.text, + ); + // const [requireTwitter, setRequireTwitter] = useState( + // data?.twitter_required ?? false, + // ); + const [termsAgreed, setTermsAgreed] = useState( + data?.terms_agreed || false, + ); + const [paymentMethod, setPaymentMethod] = useState( + data?.payment_options.payment_type || PaymentType.cash, + ); + const [cardId, setCardId] = useState( + data?.payment_options?.payment_method_id, + ); + const [offer, setOffer] = useState( + data?.payment_options?.amount ? String(data?.payment_options?.amount) : '', + ); + const [errors, setErrors] = useState({}); + + const validate = useCallback(() => { + const err: any = {}; + if (!channelGuid) { + err.username = 'Please select a target channel'; + } + if (paymentMethod === PaymentType.cash && !cardId) { + err.card = 'Card is required'; + } + if (!offer || !Number(offer) || Number.isNaN(Number(offer))) { + err.offer = 'Offer is not valid'; + } + if (!termsAgreed) { + err.termsAgreed = 'You have to agree to the Terms'; + } + const hasErrors = Object.keys(err).length > 0; + + if (hasErrors) { + showError(err[Object.keys(err)[0]]); + setErrors(err); + } + return !hasErrors; + }, [cardId, channelGuid, offer, paymentMethod, termsAgreed]); + + const onBack = useCallback(() => { + props.route?.params?.onClear(); + NavigationService.goBack(); + }, [props.route]); + + const onSave = useCallback(() => { + if (!validate()) { + return; + } + + const supermindRequest = { + receiver_username: username, + receiver_guid: channelGuid!, + payment_options: { + amount: Number(offer), + payment_method_id: cardId!, + payment_type: paymentMethod, + }, + reply_type: replyType, + twitter_required: false, + terms_agreed: termsAgreed, + }; + + // if object wasn't dirty, just go back without saving + if (_.isEqual(supermindRequest, props.route?.params?.data)) { + NavigationService.goBack(); + return; + } + + NavigationService.goBack(); + props.route?.params?.onSave(supermindRequest); + }, [ + validate, + username, + channelGuid, + offer, + cardId, + paymentMethod, + replyType, + termsAgreed, + props.route, + ]); + + return ( + + {i18nService.t('searchBar.clear')} + + } + extra={ + + }> + + + { + NavigationService.push('ChannelSelectScreen', { + onSelect: channel => { + setUsername(channel.username); + setChannelGuid(channel.guid); + }, + }); + setErrors(err => ({ + ...err, + username: '', + })); + }} + value={`@${username}`} + error={errors.username} + // noBottomBorder + /> + { + setOffer(value); + setErrors(err => ({ + ...err, + offer: '', + })); + }} + value={offer} + error={errors.offer} + inputType="number" + autoCorrect={false} + returnKeyType="next" + onFocus={() => + setErrors(err => ({ + ...err, + offer: '', + })) + } + keyboardType="numeric" + /> + {paymentMethod === PaymentType.cash && ( + { + setCardId(card.id); + setErrors(err => ({ + ...err, + card: '', + })); + }} + error={errors.card} + /> + )} + v.label} + keyExtractor={v => v.value} + /> + {/* setRequireTwitter(val => !val), + title: 'Require the reply to be posted to @ottman on Twitter', + icon: ( + + ), + }} + /> */} + setTermsAgreed(val => !val), + title: 'I agree to the Terms', + icon: ( + + ), + }} + /> + + + ); +} + +const styles = ThemedStyles.create({ + termsContainer: [ + 'bgPrimaryBackground', + { borderTopWidth: 0, borderBottomWidth: 0 }, + ], +}); diff --git a/src/compose/createComposeStore.js b/src/compose/createComposeStore.js index 65f7d3644..3fe9ede5b 100644 --- a/src/compose/createComposeStore.js +++ b/src/compose/createComposeStore.js @@ -16,6 +16,7 @@ import { Image, Platform } from 'react-native'; import { hashRegex } from '~/common/components/Tags'; import getNetworkError from '~/common/helpers/getNetworkError'; import { showNotification } from 'AppMessages'; +import { SupermindRequest } from './SupermindComposeScreen'; /** * Display an error message to the user. @@ -62,6 +63,7 @@ export default function (props) { group: null, postToPermaweb: false, initialized: false, + supermindRequest: undefined, onScreenFocused() { const params = props.route.params; if (this.initialized || !params) { @@ -537,19 +539,38 @@ export default function (props) { newPost.tags = this.tags; } + if (this.supermindRequest) { + newPost.supermind_request = this.supermindRequest; + } + this.setPosting(true); - const guidParam = this.isEdit ? `/${this.entity.guid}` : ''; + let response; + + if (this.isEdit) { + response = await api.post( + `api/v3/newsfeed/activity/${this.entity.guid}`, + newPost, + ); + } else { + response = await api.put('api/v3/newsfeed/activity', newPost); - const response = await api.post(`api/v2/newsfeed${guidParam}`, newPost); - if (response && response.activity) { - if (this.isEdit) { - this.entity.update(response.activity); - this.entity.setEdited('1'); - return this.entity; + if (response.supermind) { + showNotification(i18n.t('supermind.requestSubmitted'), 'success'); } - return ActivityModel.create(response.activity); } + + if (!response) { + return null; + } + + if (this.isEdit) { + this.entity.update(response); + this.entity.setEdited('1'); + return this.entity; + } + + return ActivityModel.create(response); } catch (e) { const message = getNetworkError(e); if (message) { @@ -614,5 +635,14 @@ export default function (props) { }), ); }, + getSupermindRequest() { + return this.supermindRequest; + }, + setSupermindRequest(payload: SupermindRequest) { + this.supermindRequest = payload; + }, + clearSupermindRequest() { + this.supermindRequest = undefined; + }, }; } diff --git a/src/config/Config.ts b/src/config/Config.ts index 783a03f9d..6981fbf50 100644 --- a/src/config/Config.ts +++ b/src/config/Config.ts @@ -59,10 +59,10 @@ export const IMAGE_MAX_SIZE = 2048; export const ANDROID_CHAT_APP = 'com.minds.chat'; export const MINDS_URI = 'https://www.minds.com/'; -export const MINDS_API_URI = - DEV_MODE.isActive && CUSTOM_API_URL - ? CUSTOM_API_URL - : 'https://www.minds.com/'; +export const MINDS_API_URI = 'https://feat-superminds-2370.minds.io/'; +// DEV_MODE.isActive && CUSTOM_API_URL +// ? CUSTOM_API_URL +// : 'https://www.minds.com/'; export const CONECTIVITY_CHECK_URI = 'https://www.minds.com/'; export const CONECTIVITY_CHECK_INTERVAL = 10000; diff --git a/src/navigation/NavigationStack.tsx b/src/navigation/NavigationStack.tsx index 7a09761a8..78ce4c268 100644 --- a/src/navigation/NavigationStack.tsx +++ b/src/navigation/NavigationStack.tsx @@ -317,6 +317,12 @@ const RootStack = observer(function () { getComponent={() => require('~/compose/ComposeScreen').default} options={TransitionPresets.ModalPresentationIOS} /> + + require('~/compose/SupermindComposeScreen').default + } + /> {/* Modal screens here */} require('~/tos/TosScreen').default} /> + + require('../common/components/AutoComplete/ChannelSelectScreen') + .default + } + /> ) ) : ( diff --git a/src/navigation/NavigationTypes.ts b/src/navigation/NavigationTypes.ts index 78cb487f2..8289b9e8c 100644 --- a/src/navigation/NavigationTypes.ts +++ b/src/navigation/NavigationTypes.ts @@ -11,6 +11,7 @@ import type BlogModel from '../blogs/BlogModel'; import { TwoFactorStore } from '../auth/twoFactorAuth/createTwoFactorStore'; import { TwoFactorType } from '../common/services/api.service'; import type GroupModel from '~/groups/GroupModel'; +import { SupermindRequest } from '../compose/SupermindComposeScreen'; type AnyType = any; @@ -117,6 +118,11 @@ type TwoFactorConfirmationParams = { export type RootStackParamList = { Compose: {}; + SupermindCompose: { + data: SupermindRequest; + onSave: (payload: SupermindRequest) => void; + onClear: () => void; + }; TosScreen: {}; Capture: { portrait?: boolean; @@ -182,6 +188,10 @@ export type RootStackParamList = { onCancel?: Function; }; DevTools: {}; + ChannelSelectScreen: { + username: string; + onSelect: (channel: UserModel) => void; + }; }; export type AuthStackParamList = { diff --git a/src/wire/methods/v2/StripeCardSelector.tsx b/src/wire/methods/v2/StripeCardSelector.tsx index ae9a9982f..b071d75d0 100644 --- a/src/wire/methods/v2/StripeCardSelector.tsx +++ b/src/wire/methods/v2/StripeCardSelector.tsx @@ -1,18 +1,18 @@ import * as React from 'react'; -import { View } from 'react-native'; - -import type { StripeCard } from '../../WireTypes'; -import MenuItem from '../../../common/components/menus/MenuItem'; -import ThemedStyles from '../../../styles/ThemedStyles'; -import Selector from '../../../common/components/SelectorV2'; -import stripe, { initStripe } from '../../../common/services/stripe.service'; +import { InteractionManager } from 'react-native'; +import { showNotification } from '../../../../AppMessages'; +import InputSelector from '../../../common/components/InputSelectorV2'; import api, { ApiResponse } from '../../../common/services/api.service'; import i18n from '../../../common/services/i18n.service'; -import { showNotification } from '../../../../AppMessages'; -import MText from '../../../common/components/MText'; +import stripe, { initStripe } from '../../../common/services/stripe.service'; +import ThemedStyles from '../../../styles/ThemedStyles'; +import type { StripeCard } from '../../WireTypes'; type PropsType = { - onCardSelected: Function; + selectedCardId?: string; + onCardSelected: (card: StripeCard) => void; + info?: string; + error?: string; }; type StateType = { @@ -33,12 +33,21 @@ interface IntentResponse extends ApiResponse { }; } -const selectValueExtractor = (item: StripeCard) => - item.card_brand.toUpperCase() + - ' ending in ' + - item.card_last4 + - ' - Exp: ' + - item.card_expires; +const selectValueExtractor = (item: any) => { + if (!item) return; + + if (item.name) { + return item.name; + } + + return ( + item.card_brand.toUpperCase() + + ' ending in ' + + item.card_last4 + + ' - Exp: ' + + item.card_expires + ); +}; const selectIdExtractor = item => item.id; /** @@ -76,47 +85,6 @@ export default class StripeCardSelector extends React.PureComponent< this.removeCard(index); }; - /** - * Render - */ - render(): React.ReactNode { - const theme = ThemedStyles.style; - - const current = this.state.cards[this.state.current]; - - const currentItem = current - ? { - title: selectValueExtractor(current), - icon: { name: 'chevron-down', type: 'material-community' }, - onPress: () => this.selectorRef.current?.show(current.id), - } - : null; - - return ( - - - SELECT CARD - Add Card - - {current && ( - <> - {!!currentItem && } - - - )} - - ); - } - /** * Load cards */ @@ -127,11 +95,28 @@ export default class StripeCardSelector extends React.PureComponent< ); if (result && result.paymentmethods) { + let defaultSelectedCard; if (this.props.onCardSelected && result.paymentmethods.length > 0) { - this.props.onCardSelected(result.paymentmethods[0]); + defaultSelectedCard = result.paymentmethods[0]; + + if ( + this.props.selectedCardId && + result.paymentmethods.find(p => p.id === this.props.selectedCardId) + ) { + defaultSelectedCard = result.paymentmethods.find( + p => p.id === this.props.selectedCardId, + )!; + } + this.props.onCardSelected(defaultSelectedCard); } + + const cards = result.paymentmethods.reverse(); + return this.setState({ - cards: result.paymentmethods.reverse(), + current: defaultSelectedCard + ? cards.findIndex(p => p.id === defaultSelectedCard.id) + : this.state.current, + cards, loaded: true, }); } @@ -146,9 +131,14 @@ export default class StripeCardSelector extends React.PureComponent< * Select card * @param card */ - selectCard = async (card: StripeCard) => { - const index = this.state.cards.findIndex(c => c === card); + selectCard = async (card: string) => { + const index = this.state.cards.findIndex( + c => selectIdExtractor(c) === card, + ); + if (index < 0) return; + + this.props.onCardSelected?.(this.state.cards[index]); this.setState({ current: index, }); @@ -259,4 +249,36 @@ export default class StripeCardSelector extends React.PureComponent< this.intentKey = ''; this.loadCards(); } + + /** + * Render + */ + render(): React.ReactNode { + const theme = ThemedStyles.style; + const current = this.state.cards[this.state.current]; + + return ( + + InteractionManager.runAfterInteractions(() => this.addNewCard()), + }, + ]} + error={this.props.error} + valueExtractor={selectValueExtractor} + keyExtractor={selectIdExtractor} + textStyle={theme.fontXL} + backdropOpacity={0.9} + /> + ); + } } From 41a2a38b6e837bd4b569c447675951b9d74b02e5 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Wed, 7 Sep 2022 15:10:13 +0200 Subject: [PATCH 18/89] limit reply type in composer --- src/common/ui/icons/map.ts | 4 ---- src/compose/CameraScreen.tsx | 24 ++++++++++----------- src/compose/ComposeBottomBar.tsx | 29 +++++++++++++++++++++----- src/compose/ComposeScreen.tsx | 4 ++++ src/compose/SupermindComposeScreen.tsx | 2 +- src/compose/TopBar.tsx | 1 + 6 files changed, 42 insertions(+), 22 deletions(-) diff --git a/src/common/ui/icons/map.ts b/src/common/ui/icons/map.ts index 72738b8be..ae828f9d8 100644 --- a/src/common/ui/icons/map.ts +++ b/src/common/ui/icons/map.ts @@ -269,10 +269,6 @@ const ICON_MAP = { font: 'MaterialCommunityIcons', name: 'arrow-up', }, - supermind: { - font: 'MaterialCommunityIcons', - name: 'lightbulb-on', - }, } as const; export type IconNameType = keyof typeof ICON_MAP; diff --git a/src/compose/CameraScreen.tsx b/src/compose/CameraScreen.tsx index 784df1133..38db3c1a0 100644 --- a/src/compose/CameraScreen.tsx +++ b/src/compose/CameraScreen.tsx @@ -45,9 +45,11 @@ const showError = message => { */ export default observer(function (props) { // #region states & variables - const { portrait: portraitMode, onMediaConfirmed } = + const { portrait: portraitMode, mode: allowedMode, onMediaConfirmed } = props.route?.params ?? {}; - const [mode, setMode] = useState<'photo' | 'video'>('photo'); + const [mode, setMode] = useState<'photo' | 'video'>( + allowedMode === 'video' ? 'video' : 'photo', + ); const [mediaToConfirm, setMediaToConfirm] = useState(null); /** @@ -275,16 +277,14 @@ export default observer(function (props) { extracting={extractEnabled} /> ); - } else { - if (portrait) { - bottomBar = ( - - ); - } + } else if (portrait && !allowedMode) { + bottomBar = ( + + ); } // #endregion diff --git a/src/compose/ComposeBottomBar.tsx b/src/compose/ComposeBottomBar.tsx index e5c3e4c10..7502365db 100644 --- a/src/compose/ComposeBottomBar.tsx +++ b/src/compose/ComposeBottomBar.tsx @@ -5,6 +5,7 @@ import React, { useCallback, useMemo } from 'react'; import { View, Keyboard } from 'react-native'; import { IconButton } from '~ui/icons'; import ThemedStyles from '../styles/ThemedStyles'; +import { ReplyType, SupermindRequest } from './SupermindComposeScreen'; function ComposeBottomBar(props) { const theme = ThemedStyles.style; @@ -21,20 +22,38 @@ function ComposeBottomBar(props) { ], [theme.colorIcon, theme.colorWhite, theme.padding3x], ); + const allowedMode = useMemo(() => { + let mode; + const supermindRequest: SupermindRequest = props.store.getSupermindRequest(); + if (supermindRequest) { + switch (supermindRequest.reply_type) { + case ReplyType.image: + mode = 'photo'; + break; + case ReplyType.video: + mode = 'video'; + break; + case ReplyType.text: + break; + } + } + + return mode; + }, [props.store]); const onCameraPress = useCallback(() => { Keyboard.dismiss(); navigation.navigate('Capture', { + mode: allowedMode, onMediaConfirmed: media => { props.store.onMedia(media); props.store.attachment.attachMedia(media, props.store.extra); return true; }, }); - }, [navigation, props.store]); - const onGalleryPress = useCallback( - () => props.store.selectFromGallery('any'), - [props.store], - ); + }, [navigation, props.store, allowedMode]); + const onGalleryPress = useCallback(() => { + props.store.selectFromGallery(allowedMode); + }, [allowedMode, props.store]); return ( diff --git a/src/compose/ComposeScreen.tsx b/src/compose/ComposeScreen.tsx index e34aacf8f..e676dfc76 100644 --- a/src/compose/ComposeScreen.tsx +++ b/src/compose/ComposeScreen.tsx @@ -40,6 +40,7 @@ import useDebouncedCallback from '~/common/hooks/useDebouncedCallback'; import AutoComplete from '~/common/components/AutoComplete/AutoComplete'; import onImageInput from '~/common/helpers/onImageInput'; import { SupermindRequest } from './SupermindComposeScreen'; +import SupermindLabel from '../common/components/supermind/SupermindLabel'; const { width } = Dimensions.get('window'); @@ -249,6 +250,9 @@ export default observer(function ComposeScreen(props) { : null + } onPressRight={onPost} onPressBack={onPressBack} store={store} diff --git a/src/compose/SupermindComposeScreen.tsx b/src/compose/SupermindComposeScreen.tsx index 9d3485311..99d619902 100644 --- a/src/compose/SupermindComposeScreen.tsx +++ b/src/compose/SupermindComposeScreen.tsx @@ -20,7 +20,7 @@ const showError = (error: string) => type PasswordConfirmation = RouteProp; -enum ReplyType { +export enum ReplyType { text = 0, image = 1, video = 2, diff --git a/src/compose/TopBar.tsx b/src/compose/TopBar.tsx index bb7ca6e66..e1272e366 100644 --- a/src/compose/TopBar.tsx +++ b/src/compose/TopBar.tsx @@ -27,6 +27,7 @@ export default observer(function (props) { onPress={props.onPressBack} testID="topbarBack" /> + {props.leftComponent ? props.leftComponent : null} {props.leftText && ( {props.leftText} )} From a01cebdbf1680ba519fbacaafafdf89803fcd456 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Wed, 7 Sep 2022 16:08:53 +0200 Subject: [PATCH 19/89] (feat) initiate supermind from a post --- src/compose/ComposeBottomBar.tsx | 4 ++-- src/compose/ComposeScreen.tsx | 14 ++++------- src/compose/SupermindComposeScreen.tsx | 4 ++-- src/compose/createComposeStore.js | 32 ++++++++++++++++++-------- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/compose/ComposeBottomBar.tsx b/src/compose/ComposeBottomBar.tsx index 7502365db..f7362c115 100644 --- a/src/compose/ComposeBottomBar.tsx +++ b/src/compose/ComposeBottomBar.tsx @@ -24,7 +24,7 @@ function ComposeBottomBar(props) { ); const allowedMode = useMemo(() => { let mode; - const supermindRequest: SupermindRequest = props.store.getSupermindRequest(); + const supermindRequest: SupermindRequest = props.store.supermindRequest; if (supermindRequest) { switch (supermindRequest.reply_type) { case ReplyType.image: @@ -92,7 +92,7 @@ function ComposeBottomBar(props) { name="supermind" style={iconStyle} scale - color={props.store.getSupermindRequest() ? 'Link' : 'Icon'} + color={props.store.supermindRequest ? 'Link' : 'Icon'} onPress={props.onSupermind} /> diff --git a/src/compose/ComposeScreen.tsx b/src/compose/ComposeScreen.tsx index e676dfc76..e3179ac49 100644 --- a/src/compose/ComposeScreen.tsx +++ b/src/compose/ComposeScreen.tsx @@ -164,13 +164,9 @@ export default observer(function ComposeScreen(props) { optionsRef.current.show(); }, []); - const handleSupermindPress = useCallback(() => { - NavigationService.navigate('SupermindCompose', { - data: store.getSupermindRequest(), - onSave: (payload: SupermindRequest) => store.setSupermindRequest(payload), - onClear: () => store.clearSupermindRequest(), - }); - }, [store]); + const handleSupermindPress = useCallback(() => store.openSupermindModal(), [ + store, + ]); const onScrollHandler = useCallback( (e: NativeSyntheticEvent) => @@ -250,9 +246,7 @@ export default observer(function ComposeScreen(props) { : null - } + leftComponent={store.supermindRequest ? : null} onPressRight={onPost} onPressBack={onPressBack} store={store} diff --git a/src/compose/SupermindComposeScreen.tsx b/src/compose/SupermindComposeScreen.tsx index 99d619902..e2e51fbd9 100644 --- a/src/compose/SupermindComposeScreen.tsx +++ b/src/compose/SupermindComposeScreen.tsx @@ -69,7 +69,7 @@ export default function SupermindComposeScreen(props: SupermindComposeScreen) { data?.terms_agreed || false, ); const [paymentMethod, setPaymentMethod] = useState( - data?.payment_options.payment_type || PaymentType.cash, + data?.payment_options?.payment_type || PaymentType.cash, ); const [cardId, setCardId] = useState( data?.payment_options?.payment_method_id, @@ -187,12 +187,12 @@ export default function SupermindComposeScreen(props: SupermindComposeScreen) { }} value={`@${username}`} error={errors.username} - // noBottomBorder /> { setOffer(value); setErrors(err => ({ diff --git a/src/compose/createComposeStore.js b/src/compose/createComposeStore.js index 3fe9ede5b..e9a2d9fd9 100644 --- a/src/compose/createComposeStore.js +++ b/src/compose/createComposeStore.js @@ -17,6 +17,7 @@ import { hashRegex } from '~/common/components/Tags'; import getNetworkError from '~/common/helpers/getNetworkError'; import { showNotification } from 'AppMessages'; import { SupermindRequest } from './SupermindComposeScreen'; +import NavigationService from '../navigation/NavigationService'; /** * Display an error message to the user. @@ -103,10 +104,21 @@ export default function (props) { this.hydrateFromEntity(); } - if (props.route?.params && props.route.params.group) { + if (params.group) { this.group = props.route.params.group; } + if (params.supermind) { + this.openSupermindModal( + params.entity + ? { + receiver_username: params.entity.ownerObj.username, + receiver_guid: params.entity.ownerObj.guid, + } + : undefined, + ); + } + // clear params to avoid repetition props.navigation.setParams({ group: undefined, @@ -635,14 +647,16 @@ export default function (props) { }), ); }, - getSupermindRequest() { - return this.supermindRequest; - }, - setSupermindRequest(payload: SupermindRequest) { - this.supermindRequest = payload; - }, - clearSupermindRequest() { - this.supermindRequest = undefined; + openSupermindModal(supermindRequest: Partial) { + NavigationService.navigate('SupermindCompose', { + data: supermindRequest || this.supermindRequest, + onSave: (payload: SupermindRequest) => { + this.supermindRequest = payload; + }, + onClear: () => { + this.supermindRequest = undefined; + }, + }); }, }; } From 564e74b1bba31e1966d0d60e8a352e7b53808a39 Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Wed, 7 Sep 2022 11:52:06 -0300 Subject: [PATCH 20/89] (fix) alignment --- src/newsfeed/activity/Activity.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/newsfeed/activity/Activity.tsx b/src/newsfeed/activity/Activity.tsx index 09d9a17ba..dcf3997c7 100644 --- a/src/newsfeed/activity/Activity.tsx +++ b/src/newsfeed/activity/Activity.tsx @@ -53,6 +53,7 @@ type PropsType = { storeUserTap?: boolean; showOnlyContent?: boolean; borderless?: boolean; + hideMetrics?: boolean; }; /** @@ -293,6 +294,7 @@ export default class Activity extends Component { entity={entity} showOnlyContent={this.props.showOnlyContent} hideTabs={this.props.hideTabs} + hideMetrics={this.props.hideMetrics} /> )} From cf5f2a7ed787952f7cc338ce9db19c8944e50b86 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Wed, 7 Sep 2022 17:00:25 +0200 Subject: [PATCH 21/89] call composer from supermind action --- .../activity/actions/SupermindAction.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/newsfeed/activity/actions/SupermindAction.tsx b/src/newsfeed/activity/actions/SupermindAction.tsx index c64493613..4d59f77a3 100644 --- a/src/newsfeed/activity/actions/SupermindAction.tsx +++ b/src/newsfeed/activity/actions/SupermindAction.tsx @@ -1,3 +1,4 @@ +import { useNavigation, useRoute } from '@react-navigation/native'; import React from 'react'; import { IconButtonNext } from '~/common/ui'; import { actionsContainerStyle } from './styles'; @@ -6,6 +7,8 @@ import { actionsContainerStyle } from './styles'; * Supermind activity action */ export default function SupermindAction({ entity }) { + const navigation = useNavigation(); + const { key } = useRoute(); return ( { - if (!entity.supermind) { - console.log('Call the composer here'); - } - }} + onPress={() => + navigation.navigate('Compose', { + isRemind: true, + entity, + parentKey: key, + supermind: true, + }) + } /> ); } From df79b9a02bea5d171f8eb3cd206a24fd60d58107 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Wed, 7 Sep 2022 17:01:06 +0200 Subject: [PATCH 22/89] (fix) revert config changes --- src/config/Config.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/config/Config.ts b/src/config/Config.ts index 6981fbf50..783a03f9d 100644 --- a/src/config/Config.ts +++ b/src/config/Config.ts @@ -59,10 +59,10 @@ export const IMAGE_MAX_SIZE = 2048; export const ANDROID_CHAT_APP = 'com.minds.chat'; export const MINDS_URI = 'https://www.minds.com/'; -export const MINDS_API_URI = 'https://feat-superminds-2370.minds.io/'; -// DEV_MODE.isActive && CUSTOM_API_URL -// ? CUSTOM_API_URL -// : 'https://www.minds.com/'; +export const MINDS_API_URI = + DEV_MODE.isActive && CUSTOM_API_URL + ? CUSTOM_API_URL + : 'https://www.minds.com/'; export const CONECTIVITY_CHECK_URI = 'https://www.minds.com/'; export const CONECTIVITY_CHECK_INTERVAL = 10000; From 4c9a2de70c39196f39881fdf3f7e5f52ddd55cd9 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Thu, 8 Sep 2022 10:27:01 +0200 Subject: [PATCH 23/89] (fix) allowedMode logic --- src/common/components/Input.tsx | 17 +++++++++++++++- src/compose/ComposeBottomBar.tsx | 27 ++++---------------------- src/compose/ComposeScreen.tsx | 1 - src/compose/SupermindComposeScreen.tsx | 7 ++++++- src/compose/createComposeStore.js | 5 +++++ 5 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/common/components/Input.tsx b/src/common/components/Input.tsx index 6c203d5d3..a5abb665f 100644 --- a/src/common/components/Input.tsx +++ b/src/common/components/Input.tsx @@ -8,6 +8,7 @@ import TextInput from './TextInput'; import MText from './MText'; import ErrorBoundary from './ErrorBoundary'; import DatePickerInput from './controls/DatePickerInput'; +import { B1 } from '../ui'; export interface PropsType extends TextInputProps { TFA?: any; @@ -41,6 +42,7 @@ export interface PropsType extends TextInputProps { style?: any; info?: string; error?: string; + hint?: string; } /** @@ -182,6 +184,13 @@ export default class Input extends Component { ]}> {this.props.placeholder} + {!!this.props.hint && !this.props.error && ( + + + {this.props.hint} + + + )} {this.props.info && } {!!this.props.error && ( @@ -201,7 +210,7 @@ export default class Input extends Component { } } -const styles = StyleSheet.create({ +const styles = ThemedStyles.create({ container: { paddingTop: 5, paddingBottom: 10, @@ -235,4 +244,10 @@ const styles = StyleSheet.create({ shadowRadius: 1, elevation: 1, }, + hintStyles: [ + { lineHeight: 18, fontSize: 16 }, + 'fontL', + 'colorSecondaryText', + 'textRight', + ], }); diff --git a/src/compose/ComposeBottomBar.tsx b/src/compose/ComposeBottomBar.tsx index f7362c115..a15e126c6 100644 --- a/src/compose/ComposeBottomBar.tsx +++ b/src/compose/ComposeBottomBar.tsx @@ -5,7 +5,6 @@ import React, { useCallback, useMemo } from 'react'; import { View, Keyboard } from 'react-native'; import { IconButton } from '~ui/icons'; import ThemedStyles from '../styles/ThemedStyles'; -import { ReplyType, SupermindRequest } from './SupermindComposeScreen'; function ComposeBottomBar(props) { const theme = ThemedStyles.style; @@ -22,38 +21,20 @@ function ComposeBottomBar(props) { ], [theme.colorIcon, theme.colorWhite, theme.padding3x], ); - const allowedMode = useMemo(() => { - let mode; - const supermindRequest: SupermindRequest = props.store.supermindRequest; - if (supermindRequest) { - switch (supermindRequest.reply_type) { - case ReplyType.image: - mode = 'photo'; - break; - case ReplyType.video: - mode = 'video'; - break; - case ReplyType.text: - break; - } - } - - return mode; - }, [props.store]); const onCameraPress = useCallback(() => { Keyboard.dismiss(); navigation.navigate('Capture', { - mode: allowedMode, + mode: props.store.allowedMode, onMediaConfirmed: media => { props.store.onMedia(media); props.store.attachment.attachMedia(media, props.store.extra); return true; }, }); - }, [navigation, props.store, allowedMode]); + }, [navigation, props.store]); const onGalleryPress = useCallback(() => { - props.store.selectFromGallery(allowedMode); - }, [allowedMode, props.store]); + props.store.selectFromGallery(props.store.allowedMode); + }, [props.store]); return ( diff --git a/src/compose/ComposeScreen.tsx b/src/compose/ComposeScreen.tsx index e3179ac49..f46a3323b 100644 --- a/src/compose/ComposeScreen.tsx +++ b/src/compose/ComposeScreen.tsx @@ -39,7 +39,6 @@ import Animated, { import useDebouncedCallback from '~/common/hooks/useDebouncedCallback'; import AutoComplete from '~/common/components/AutoComplete/AutoComplete'; import onImageInput from '~/common/helpers/onImageInput'; -import { SupermindRequest } from './SupermindComposeScreen'; import SupermindLabel from '../common/components/supermind/SupermindLabel'; const { width } = Dimensions.get('window'); diff --git a/src/compose/SupermindComposeScreen.tsx b/src/compose/SupermindComposeScreen.tsx index e2e51fbd9..1e8143a19 100644 --- a/src/compose/SupermindComposeScreen.tsx +++ b/src/compose/SupermindComposeScreen.tsx @@ -75,7 +75,9 @@ export default function SupermindComposeScreen(props: SupermindComposeScreen) { data?.payment_options?.payment_method_id, ); const [offer, setOffer] = useState( - data?.payment_options?.amount ? String(data?.payment_options?.amount) : '', + data?.payment_options?.amount + ? String(data?.payment_options?.amount) + : '10', ); const [errors, setErrors] = useState({}); @@ -89,6 +91,8 @@ export default function SupermindComposeScreen(props: SupermindComposeScreen) { } if (!offer || !Number(offer) || Number.isNaN(Number(offer))) { err.offer = 'Offer is not valid'; + } else if (offer && Number(offer) < 10) { + err.offer = 'Offer must be greater than 10'; } if (!termsAgreed) { err.termsAgreed = 'You have to agree to the Terms'; @@ -200,6 +204,7 @@ export default function SupermindComposeScreen(props: SupermindComposeScreen) { offer: '', })); }} + hint="Min: 10" value={offer} error={errors.offer} inputType="number" diff --git a/src/compose/createComposeStore.js b/src/compose/createComposeStore.js index e9a2d9fd9..9993ef663 100644 --- a/src/compose/createComposeStore.js +++ b/src/compose/createComposeStore.js @@ -48,6 +48,10 @@ export default function (props) { isEdit: false, accessId: 2, mode: settingsStore.composerMode, + /** + * what compose mode is allowed? photo, video, and null for any + */ + allowedMode: null, videoPoster: null, entity: null, attachment: new AttachmentStore(), @@ -76,6 +80,7 @@ export default function (props) { this.portraitMode = params.portrait; this.isRemind = params.isRemind; this.isEdit = params.isEdit; + this.allowedMode = params.allowedMode; this.entity = params.entity || null; this.mode = params.mode From e2559ddd2c111c877c283d8fa751e656cb727a45 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Thu, 8 Sep 2022 10:28:41 +0200 Subject: [PATCH 24/89] Fix padding top on supermind compose --- src/compose/SupermindComposeScreen.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compose/SupermindComposeScreen.tsx b/src/compose/SupermindComposeScreen.tsx index 1e8143a19..9bdc9f5a5 100644 --- a/src/compose/SupermindComposeScreen.tsx +++ b/src/compose/SupermindComposeScreen.tsx @@ -209,6 +209,7 @@ export default function SupermindComposeScreen(props: SupermindComposeScreen) { error={errors.offer} inputType="number" autoCorrect={false} + containerStyle={theme.paddingTop4x} returnKeyType="next" onFocus={() => setErrors(err => ({ From 5160915c8496d0cee437bccb9045b08fb90b2bd1 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Thu, 8 Sep 2022 11:22:38 +0200 Subject: [PATCH 25/89] fix pipelines --- src/common/components/InputBase.tsx | 2 +- .../{createComposeStore.js => createComposeStore.ts} | 9 +++++---- src/newsfeed/activity/Activity.tsx | 2 -- 3 files changed, 6 insertions(+), 7 deletions(-) rename src/compose/{createComposeStore.js => createComposeStore.ts} (98%) diff --git a/src/common/components/InputBase.tsx b/src/common/components/InputBase.tsx index f36b47264..006380521 100644 --- a/src/common/components/InputBase.tsx +++ b/src/common/components/InputBase.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { StyleProp, TextStyle, View } from 'react-native'; +import { StyleProp, TextStyle } from 'react-native'; import { Tooltip } from 'react-native-elements'; import ThemedStyles from '../../styles/ThemedStyles'; import { Column, Icon, Row } from '../ui'; diff --git a/src/compose/createComposeStore.js b/src/compose/createComposeStore.ts similarity index 98% rename from src/compose/createComposeStore.js rename to src/compose/createComposeStore.ts index 9993ef663..6f88ca403 100644 --- a/src/compose/createComposeStore.js +++ b/src/compose/createComposeStore.ts @@ -1,3 +1,4 @@ +// @ts-nocheck import RNPhotoEditor from 'react-native-photo-editor'; import { measureHeights } from '@bigbee.dev/react-native-measure-text-size'; import AttachmentStore from '../common/stores/AttachmentStore'; @@ -57,7 +58,7 @@ export default function (props) { attachment: new AttachmentStore(), nsfw: [], tags: [], - wire_threshold: DEFAULT_MONETIZE, + wire_threshold: DEFAULT_MONETIZE as any, embed: new RichEmbedStore(), text: '', title: '', @@ -68,7 +69,7 @@ export default function (props) { group: null, postToPermaweb: false, initialized: false, - supermindRequest: undefined, + supermindRequest: undefined as SupermindRequest, onScreenFocused() { const params = props.route.params; if (this.initialized || !params) { @@ -233,7 +234,7 @@ export default function (props) { path: this.mediaToConfirm.uri.replace('file://', ''), stickers: ['sticker6', 'sticker9'], hiddenControls: ['save', 'share'], - onDone: result => { + onDone: _result => { Image.getSize( this.mediaToConfirm.uri, (w, h) => { @@ -652,7 +653,7 @@ export default function (props) { }), ); }, - openSupermindModal(supermindRequest: Partial) { + openSupermindModal(supermindRequest?: Partial) { NavigationService.navigate('SupermindCompose', { data: supermindRequest || this.supermindRequest, onSave: (payload: SupermindRequest) => { diff --git a/src/newsfeed/activity/Activity.tsx b/src/newsfeed/activity/Activity.tsx index dcf3997c7..09d9a17ba 100644 --- a/src/newsfeed/activity/Activity.tsx +++ b/src/newsfeed/activity/Activity.tsx @@ -53,7 +53,6 @@ type PropsType = { storeUserTap?: boolean; showOnlyContent?: boolean; borderless?: boolean; - hideMetrics?: boolean; }; /** @@ -294,7 +293,6 @@ export default class Activity extends Component { entity={entity} showOnlyContent={this.props.showOnlyContent} hideTabs={this.props.hideTabs} - hideMetrics={this.props.hideMetrics} /> )} From c8ef5ccb243169769bc038f5b049211b86b694e2 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Thu, 8 Sep 2022 12:41:53 +0200 Subject: [PATCH 26/89] (feat) don't allow monetization if supermind --- src/compose/ComposeBottomBar.tsx | 2 +- src/compose/PosterOptions/PosterOptions.tsx | 3 ++- src/compose/createComposeStore.ts | 11 ++++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/compose/ComposeBottomBar.tsx b/src/compose/ComposeBottomBar.tsx index a15e126c6..28a3c1a70 100644 --- a/src/compose/ComposeBottomBar.tsx +++ b/src/compose/ComposeBottomBar.tsx @@ -55,7 +55,7 @@ function ComposeBottomBar(props) { onPress={onCameraPress} /> )} - {!props.store.isGroup() && ( + {!props.store.isGroup() && !props.store.supermindRequest && ( Date: Thu, 8 Sep 2022 14:41:28 +0200 Subject: [PATCH 27/89] (fix) only show cash option when user has connected bank account --- src/compose/SupermindComposeScreen.tsx | 75 +++++++++++++++----------- src/compose/createComposeStore.ts | 13 ++--- 2 files changed, 51 insertions(+), 37 deletions(-) diff --git a/src/compose/SupermindComposeScreen.tsx b/src/compose/SupermindComposeScreen.tsx index 9bdc9f5a5..1c21f9ed3 100644 --- a/src/compose/SupermindComposeScreen.tsx +++ b/src/compose/SupermindComposeScreen.tsx @@ -1,7 +1,8 @@ import { RouteProp } from '@react-navigation/core'; import _ from 'lodash'; -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { showNotification } from '../../AppMessages'; +import UserModel from '../channel/UserModel'; import FitScrollView from '../common/components/FitScrollView'; import InputBase from '../common/components/InputBase'; import InputContainer from '../common/components/InputContainer'; @@ -32,8 +33,7 @@ enum PaymentType { } export interface SupermindRequest { - receiver_username: string; - receiver_guid: string; + channel: UserModel; payment_options: { payment_type: PaymentType; payment_method_id: string; @@ -55,10 +55,7 @@ interface SupermindComposeScreen { export default function SupermindComposeScreen(props: SupermindComposeScreen) { const theme = ThemedStyles.style; const data: SupermindRequest | undefined = props.route?.params?.data; - const [username, setUsername] = useState(data?.receiver_username || ''); - const [channelGuid, setChannelGuid] = useState( - data?.receiver_guid, - ); + const [channel, setChannel] = useState(data?.channel); const [replyType, setReplyType] = useState( data?.reply_type ?? ReplyType.text, ); @@ -80,10 +77,11 @@ export default function SupermindComposeScreen(props: SupermindComposeScreen) { : '10', ); const [errors, setErrors] = useState({}); + const [tabsDisabled, setTabsDisabled] = useState(false); const validate = useCallback(() => { const err: any = {}; - if (!channelGuid) { + if (!channel) { err.username = 'Please select a target channel'; } if (paymentMethod === PaymentType.cash && !cardId) { @@ -104,7 +102,7 @@ export default function SupermindComposeScreen(props: SupermindComposeScreen) { setErrors(err); } return !hasErrors; - }, [cardId, channelGuid, offer, paymentMethod, termsAgreed]); + }, [cardId, channel, offer, paymentMethod, termsAgreed]); const onBack = useCallback(() => { props.route?.params?.onClear(); @@ -117,8 +115,7 @@ export default function SupermindComposeScreen(props: SupermindComposeScreen) { } const supermindRequest = { - receiver_username: username, - receiver_guid: channelGuid!, + channel: channel!, payment_options: { amount: Number(offer), payment_method_id: cardId!, @@ -139,8 +136,7 @@ export default function SupermindComposeScreen(props: SupermindComposeScreen) { props.route?.params?.onSave(supermindRequest); }, [ validate, - username, - channelGuid, + channel, offer, cardId, paymentMethod, @@ -149,6 +145,23 @@ export default function SupermindComposeScreen(props: SupermindComposeScreen) { props.route, ]); + /** + * A user can only pay in cash where the producer has + * a bank account connected to their minds account + */ + useEffect(() => { + if (!channel) { + return; + } + + if (!channel.merchant) { + setPaymentMethod(PaymentType.token); + setTabsDisabled(true); + } else { + setTabsDisabled(false); + } + }, [channel]); + return ( }> - + {!tabsDisabled && ( + + )} + { NavigationService.push('ChannelSelectScreen', { - onSelect: channel => { - setUsername(channel.username); - setChannelGuid(channel.guid); - }, + onSelect: selectedChannel => setChannel(selectedChannel), }); setErrors(err => ({ ...err, username: '', })); }} - value={`@${username}`} + value={channel ? `@${channel.username}` : '@'} error={errors.username} /> { setOffer(value); setErrors(err => ({ diff --git a/src/compose/createComposeStore.ts b/src/compose/createComposeStore.ts index b2f9c6782..4c2c0bfcc 100644 --- a/src/compose/createComposeStore.ts +++ b/src/compose/createComposeStore.ts @@ -116,11 +116,8 @@ export default function (props) { if (params.supermind) { this.openSupermindModal( - params.entity - ? { - receiver_username: params.entity.ownerObj.username, - receiver_guid: params.entity.ownerObj.guid, - } + params.entity?.ownerObj + ? { channel: params.entity?.ownerObj } : undefined, ); } @@ -511,7 +508,11 @@ export default function (props) { }; if (this.supermindRequest) { - newPost.supermind_request = this.supermindRequest; + newPost.supermind_request = { + ...this.supermindRequest, + receiver_username: this.supermindRequest.channel.username, + receiver_guid: this.supermindRequest.channel.guid, + }; } // monetization From c95b4fde0cdb4edb77e2119707b29412862effb0 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Thu, 8 Sep 2022 15:15:15 +0200 Subject: [PATCH 28/89] Initiating a supermind from a channel --- src/channel/v2/ChannelButtons.tsx | 6 ++ src/channel/v2/ChannelTopBar.tsx | 9 ++- .../components/supermind/SupermindButton.tsx | 55 +++++++++++++++++++ src/compose/createComposeStore.ts | 8 +-- 4 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 src/common/components/supermind/SupermindButton.tsx diff --git a/src/channel/v2/ChannelButtons.tsx b/src/channel/v2/ChannelButtons.tsx index 047555a95..fef75c2ca 100644 --- a/src/channel/v2/ChannelButtons.tsx +++ b/src/channel/v2/ChannelButtons.tsx @@ -20,6 +20,7 @@ import SmallCircleButton from '../../common/components/SmallCircleButton'; import { withErrorBoundary } from '../../common/components/ErrorBoundary'; import Edit from './buttons/Edit'; import { Row } from '~ui'; +import SupermindButton from '../../common/components/supermind/SupermindButton'; type ButtonsType = | 'edit' @@ -28,6 +29,7 @@ type ButtonsType = | 'subscribe' | 'message' | 'join' + | 'supermind' | 'boost'; export type ChannelButtonsPropsType = { @@ -69,6 +71,7 @@ const check = { subscribe: (store: ChannelStoreType) => !store.channel!.isOwner() && store.channel!.can(FLAG_SUBSCRIBE), boost: (store: ChannelStoreType) => store.channel!.isOwner(), + supermind: (store: ChannelStoreType) => !store.channel!.isOwner(), }; /** @@ -123,6 +126,9 @@ const ChannelButtons = withErrorBoundary( /> )} {showSubscribe && } + {shouldShow('supermind') && ( + + )} {shouldShow('more') && ( { + NavigationService.navigate('Compose', { + supermind: true, + supermindChannelTarget: entity, + allowedMode: 'video', + }); + }, [entity]); + + return ( + + + + Supermind + + + + ); +} + +const start = { x: 0, y: 0 }; +const end = { x: 1, y: 0 }; +const locations = [0, 0.4, 1]; +const styles = ThemedStyles.create({ + outerStyle: [ + { + height: 36, + borderRadius: 100, + overflow: 'hidden', + }, + 'marginLeft2x', + ], + gradient: ['flexContainerCenter', 'paddingHorizontal2x'], +}); diff --git a/src/compose/createComposeStore.ts b/src/compose/createComposeStore.ts index 4c2c0bfcc..97a1faf21 100644 --- a/src/compose/createComposeStore.ts +++ b/src/compose/createComposeStore.ts @@ -115,11 +115,9 @@ export default function (props) { } if (params.supermind) { - this.openSupermindModal( - params.entity?.ownerObj - ? { channel: params.entity?.ownerObj } - : undefined, - ); + const channel = + params.supermindChannelTarget || params.entity?.ownerObj; + this.openSupermindModal(channel ? { channel } : undefined); } // clear params to avoid repetition From 993fe8ec983037cbc6ceb4f3c7b8f23fab87b547 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Thu, 8 Sep 2022 15:34:14 +0200 Subject: [PATCH 29/89] (refactor) rename supermindrequest to supermindRequestParam --- src/common/components/supermind/SupermindButton.tsx | 2 +- src/compose/SupermindComposeScreen.tsx | 4 ++-- src/compose/createComposeStore.ts | 10 +++++----- src/navigation/NavigationTypes.ts | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/common/components/supermind/SupermindButton.tsx b/src/common/components/supermind/SupermindButton.tsx index 7619bec01..55560c18a 100644 --- a/src/common/components/supermind/SupermindButton.tsx +++ b/src/common/components/supermind/SupermindButton.tsx @@ -15,7 +15,7 @@ export default function SupermindButton({ entity }: Props) { const handlePress = useCallback(() => { NavigationService.navigate('Compose', { supermind: true, - supermindChannelTarget: entity, + supermindTargetChannel: entity, allowedMode: 'video', }); }, [entity]); diff --git a/src/compose/SupermindComposeScreen.tsx b/src/compose/SupermindComposeScreen.tsx index 1c21f9ed3..58ecbee2e 100644 --- a/src/compose/SupermindComposeScreen.tsx +++ b/src/compose/SupermindComposeScreen.tsx @@ -32,7 +32,7 @@ enum PaymentType { token = 1, } -export interface SupermindRequest { +export interface SupermindRequestParam { channel: UserModel; payment_options: { payment_type: PaymentType; @@ -54,7 +54,7 @@ interface SupermindComposeScreen { */ export default function SupermindComposeScreen(props: SupermindComposeScreen) { const theme = ThemedStyles.style; - const data: SupermindRequest | undefined = props.route?.params?.data; + const data: SupermindRequestParam | undefined = props.route?.params?.data; const [channel, setChannel] = useState(data?.channel); const [replyType, setReplyType] = useState( data?.reply_type ?? ReplyType.text, diff --git a/src/compose/createComposeStore.ts b/src/compose/createComposeStore.ts index 97a1faf21..dff3906e7 100644 --- a/src/compose/createComposeStore.ts +++ b/src/compose/createComposeStore.ts @@ -17,7 +17,7 @@ import { Image, Platform } from 'react-native'; import { hashRegex } from '~/common/components/Tags'; import getNetworkError from '~/common/helpers/getNetworkError'; import { showNotification } from 'AppMessages'; -import { SupermindRequest } from './SupermindComposeScreen'; +import { SupermindRequestParam } from './SupermindComposeScreen'; import NavigationService from '../navigation/NavigationService'; /** @@ -69,7 +69,7 @@ export default function (props) { group: null, postToPermaweb: false, initialized: false, - supermindRequest: undefined as SupermindRequest, + supermindRequest: undefined as SupermindRequestParam, onScreenFocused() { const params = props.route.params; if (this.initialized || !params) { @@ -116,7 +116,7 @@ export default function (props) { if (params.supermind) { const channel = - params.supermindChannelTarget || params.entity?.ownerObj; + params.supermindTargetChannel || params.entity?.ownerObj; this.openSupermindModal(channel ? { channel } : undefined); } @@ -653,10 +653,10 @@ export default function (props) { }), ); }, - openSupermindModal(supermindRequest?: Partial) { + openSupermindModal(supermindRequest?: Partial) { NavigationService.navigate('SupermindCompose', { data: supermindRequest || this.supermindRequest, - onSave: (payload: SupermindRequest) => { + onSave: (payload: SupermindRequestParam) => { this.supermindRequest = payload; }, onClear: () => { diff --git a/src/navigation/NavigationTypes.ts b/src/navigation/NavigationTypes.ts index 8289b9e8c..6b05fe535 100644 --- a/src/navigation/NavigationTypes.ts +++ b/src/navigation/NavigationTypes.ts @@ -11,7 +11,7 @@ import type BlogModel from '../blogs/BlogModel'; import { TwoFactorStore } from '../auth/twoFactorAuth/createTwoFactorStore'; import { TwoFactorType } from '../common/services/api.service'; import type GroupModel from '~/groups/GroupModel'; -import { SupermindRequest } from '../compose/SupermindComposeScreen'; +import { SupermindRequestParam } from '../compose/SupermindComposeScreen'; type AnyType = any; @@ -119,8 +119,8 @@ type TwoFactorConfirmationParams = { export type RootStackParamList = { Compose: {}; SupermindCompose: { - data: SupermindRequest; - onSave: (payload: SupermindRequest) => void; + data: SupermindRequestParam; + onSave: (payload: SupermindRequestParam) => void; onClear: () => void; }; TosScreen: {}; From b919edb7da1e33ef7231b50b4b021250d2eb2d58 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Thu, 8 Sep 2022 16:00:11 +0200 Subject: [PATCH 30/89] add FF --- src/channel/v2/ChannelButtons.tsx | 5 ++++- src/compose/ComposeBottomBar.tsx | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/channel/v2/ChannelButtons.tsx b/src/channel/v2/ChannelButtons.tsx index fef75c2ca..79ff6d552 100644 --- a/src/channel/v2/ChannelButtons.tsx +++ b/src/channel/v2/ChannelButtons.tsx @@ -21,6 +21,7 @@ import { withErrorBoundary } from '../../common/components/ErrorBoundary'; import Edit from './buttons/Edit'; import { Row } from '~ui'; import SupermindButton from '../../common/components/supermind/SupermindButton'; +import { IfFeatureEnabled } from '@growthbook/growthbook-react'; type ButtonsType = | 'edit' @@ -127,7 +128,9 @@ const ChannelButtons = withErrorBoundary( )} {showSubscribe && } {shouldShow('supermind') && ( - + + + )} {shouldShow('more') && ( - + + + + Date: Thu, 8 Sep 2022 16:32:36 +0200 Subject: [PATCH 31/89] minor refactors and fixing pipelines --- __tests__/auth/__snapshots__/LoginForm.js.snap | 6 ++---- __tests__/auth/__snapshots__/LoginScreen.js.snap | 6 ++---- src/compose/ComposeScreen.tsx | 2 +- src/compose/TopBar.tsx | 2 +- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/__tests__/auth/__snapshots__/LoginForm.js.snap b/__tests__/auth/__snapshots__/LoginForm.js.snap index 0d8e25a4c..0729aca3b 100644 --- a/__tests__/auth/__snapshots__/LoginForm.js.snap +++ b/__tests__/auth/__snapshots__/LoginForm.js.snap @@ -138,8 +138,7 @@ exports[`LoginForm component should renders correctly 1`] = ` } maxLength={50} onChangeText={[Function]} - placeholder="" - placeholderTextColor="#444" + placeholderTextColor="#797B82" returnKeyType="done" style={ Array [ @@ -304,8 +303,7 @@ exports[`LoginForm component should renders correctly 1`] = ` } multiline={false} onChangeText={[Function]} - placeholder="" - placeholderTextColor="#444" + placeholderTextColor="#797B82" returnKeyType="done" secureTextEntry={true} style={ diff --git a/__tests__/auth/__snapshots__/LoginScreen.js.snap b/__tests__/auth/__snapshots__/LoginScreen.js.snap index 17ac6a012..7eeecc4ed 100644 --- a/__tests__/auth/__snapshots__/LoginScreen.js.snap +++ b/__tests__/auth/__snapshots__/LoginScreen.js.snap @@ -327,8 +327,7 @@ exports[`LoginScreen component should renders correctly 1`] = ` } maxLength={50} onChangeText={[Function]} - placeholder="" - placeholderTextColor="#444" + placeholderTextColor="#797B82" returnKeyType="done" style={ Array [ @@ -493,8 +492,7 @@ exports[`LoginScreen component should renders correctly 1`] = ` } multiline={false} onChangeText={[Function]} - placeholder="" - placeholderTextColor="#444" + placeholderTextColor="#797B82" returnKeyType="done" secureTextEntry={true} style={ diff --git a/src/compose/ComposeScreen.tsx b/src/compose/ComposeScreen.tsx index f46a3323b..d598da9c4 100644 --- a/src/compose/ComposeScreen.tsx +++ b/src/compose/ComposeScreen.tsx @@ -245,7 +245,7 @@ export default observer(function ComposeScreen(props) { : null} + leftComponent={store.supermindRequest && } onPressRight={onPost} onPressBack={onPressBack} store={store} diff --git a/src/compose/TopBar.tsx b/src/compose/TopBar.tsx index e1272e366..518046971 100644 --- a/src/compose/TopBar.tsx +++ b/src/compose/TopBar.tsx @@ -27,7 +27,7 @@ export default observer(function (props) { onPress={props.onPressBack} testID="topbarBack" /> - {props.leftComponent ? props.leftComponent : null} + {props.leftComponent} {props.leftText && ( {props.leftText} )} From 40b51c06cdc80f20ac3620768805599c6ffd1642 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Thu, 8 Sep 2022 17:01:22 +0200 Subject: [PATCH 32/89] (fix) border issue --- src/common/components/InputBase.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/components/InputBase.tsx b/src/common/components/InputBase.tsx index 006380521..e4f23aa30 100644 --- a/src/common/components/InputBase.tsx +++ b/src/common/components/InputBase.tsx @@ -39,6 +39,8 @@ export default function InputBase({ theme.paddingVertical4x, theme.paddingHorizontal4x, theme.bcolorPrimaryBorder, + theme.borderLeft0x, + theme.borderRight0x, theme.borderHair, style, ]}> From f9094d454e1d6c2cd3bdac2f84ddaafcf641b0f2 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Fri, 9 Sep 2022 12:21:56 +0200 Subject: [PATCH 33/89] (fix) sockets and dev logging --- src/common/services/log.service.ts | 3 ++- src/common/services/socket.service.ts | 23 ++++++++++---------- src/config/Config.ts | 3 ++- src/newsfeed/ActivityModel.ts | 30 +++++++++++++++++++++------ 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/common/services/log.service.ts b/src/common/services/log.service.ts index f819e13e9..7394a0d1a 100644 --- a/src/common/services/log.service.ts +++ b/src/common/services/log.service.ts @@ -16,7 +16,7 @@ class LogService { info(...args) { if (__DEV__) { - console.log(...args); + console.info(...args); } if (!settingsStore.appLog) { return; @@ -32,6 +32,7 @@ class LogService { } error(...args) { + console.error(...args); // deviceLog.error(...args); } diff --git a/src/common/services/socket.service.ts b/src/common/services/socket.service.ts index c263b4bd1..b011dd47b 100644 --- a/src/common/services/socket.service.ts +++ b/src/common/services/socket.service.ts @@ -1,5 +1,6 @@ import SocketIOClient, { Socket } from 'socket.io-client'; import { SOCKET_URI } from '../../config/Config'; +import logService from './log.service'; import sessionService from './session.service'; /** @@ -25,8 +26,7 @@ export class SocketService { setUp() { this.socket?.close(); - console.log('connecting to ', SOCKET_URI); - + logService.info('connecting to ', SOCKET_URI); this.socket = SocketIOClient(SOCKET_URI, { // reconnect: true, reconnection: true, @@ -51,26 +51,27 @@ export class SocketService { // connect this.socket?.on('connect', () => { - //console.log(`[ws]::connected to ${SOCKET_URI}`); + logService.info(`[ws]::connected to ${SOCKET_URI}`); this.registerWithAccessToken(); this.join(`${this.LIVE_ROOM_NAME}:${sessionService.guid}`); }); // disconnect this.socket?.on('disconnect', () => { - //console.log(`[ws]::disconnected from ${SOCKET_URI}`); + logService.info(`[ws]::disconnected from ${SOCKET_URI}`); this.registered = false; }); // registered this.socket?.on('registered', () => { + logService.info(`[ws]::registeration completed`); this.registered = true; this.socket?.emit('join', this.rooms); }); // error - this.socket?.on('error', _e => { - //console.log('[ws]::error', e); + this.socket?.on('error', e => { + logService.info('[ws]::error', e); }); // rooms @@ -79,25 +80,25 @@ export class SocketService { return; } - //console.log(`[ws]::rcvd rooms status`, rooms); + logService.info(`[ws]::rcvd rooms status`, rooms); this.rooms = rooms; }); // joined this.socket?.on('joined', (room, rooms) => { - //console.log(`[ws]::joined`, room, rooms); + logService.info(`[ws]::joined`, room, rooms); this.rooms = rooms; }); // left this.socket?.on('left', (room, rooms) => { - //console.log(`[ws]::left`, room, rooms); + logService.info(`[ws]::left`, room, rooms); this.rooms = rooms; }); } reconnect() { - //console.log('[ws]::reconnect'); + logService.info('[ws]::reconnect'); this.registered = false; this.socket?.disconnect(); @@ -107,7 +108,7 @@ export class SocketService { } disconnect() { - //console.log('[ws]::disconnect'); + logService.info('[ws]::disconnect'); this.registered = false; this.socket?.disconnect(); diff --git a/src/config/Config.ts b/src/config/Config.ts index 783a03f9d..cbbcea7a3 100644 --- a/src/config/Config.ts +++ b/src/config/Config.ts @@ -74,7 +74,8 @@ export const MINDS_URI_SETTINGS = { export const MINDS_MAX_VIDEO_LENGTH = 5; // in minutes -export const SOCKET_URI = 'wss://ha-socket-io-us-east-1.minds.com:3030'; +// export const SOCKET_URI = 'wss://ha-socket-io-us-east-1.minds.com:3030'; +export const SOCKET_URI = 'wss://ha-socket-alb-io-us-east-1.minds.com'; export const MINDS_CDN_URI = 'https://cdn.minds.com/'; export const MINDS_ASSETS_CDN_URI = 'https://cdn-assets.minds.com/'; diff --git a/src/newsfeed/ActivityModel.ts b/src/newsfeed/ActivityModel.ts index 91edf650f..cbae3e51e 100644 --- a/src/newsfeed/ActivityModel.ts +++ b/src/newsfeed/ActivityModel.ts @@ -574,7 +574,7 @@ export default class ActivityModel extends BaseModel { } private onMetricsUpdate(event: string) { - console.log('[ActivityModel] metrics update ============>', event); + logService.log('[ActivityModel] metrics update', event); try { const metricsEvent: MetricsChangedEvent = JSON.parse(event); @@ -588,7 +588,7 @@ export default class ActivityModel extends BaseModel { } }); } catch (e) { - console.error(e, event); + logService.error(e, event); return; } } @@ -597,18 +597,36 @@ export default class ActivityModel extends BaseModel { * listens to metrics updates */ private listenForMetrics(): void { - console.log('listenForMetrics'); - + logService.info( + 'listening for metrics', + this.guid, + 'message', + this.message.slice(0, 20), + 'from', + this.ownerObj?.username, + ); socketService.join(this.metricsRoom); - socketService.subscribe(this.metricsRoom, this.onMetricsUpdate); + socketService.subscribe(this.metricsRoom, event => + this.onMetricsUpdate(event), + ); } /** * unlistens from metrics updates */ private unlistenFromMetrics(): void { + logService.info( + 'unlistening from metrics', + this.guid, + 'message', + this.message.slice(0, 20), + 'from', + this.ownerObj?.username, + ); socketService.leave(this.metricsRoom); - socketService.unsubscribe(this.metricsRoom, this.onMetricsUpdate); + socketService.unsubscribe(this.metricsRoom, event => + this.onMetricsUpdate(event), + ); } } From 4679fa37207c1589f5701c8f1919c7b6e52a53f5 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Fri, 9 Sep 2022 12:27:51 +0200 Subject: [PATCH 34/89] (fix) metrics sockets on media --- src/newsfeed/ActivityModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/newsfeed/ActivityModel.ts b/src/newsfeed/ActivityModel.ts index cbae3e51e..dccc32d28 100644 --- a/src/newsfeed/ActivityModel.ts +++ b/src/newsfeed/ActivityModel.ts @@ -570,7 +570,7 @@ export default class ActivityModel extends BaseModel { } private get metricsRoom() { - return `entity:metrics:${this.guid}`; + return `entity:metrics:${this.entity_guid || this.guid}`; } private onMetricsUpdate(event: string) { From 45e02c279fa5cc7a4795ee121615030e1b75d4d2 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Fri, 9 Sep 2022 12:51:54 +0200 Subject: [PATCH 35/89] (feat) add 1s debounce and remove logs --- src/common/services/socket.service.ts | 3 +-- src/config/Config.ts | 1 - src/newsfeed/ActivityModel.ts | 29 ++++++++++----------------- 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/common/services/socket.service.ts b/src/common/services/socket.service.ts index b011dd47b..1ea25d43b 100644 --- a/src/common/services/socket.service.ts +++ b/src/common/services/socket.service.ts @@ -28,11 +28,10 @@ export class SocketService { logService.info('connecting to ', SOCKET_URI); this.socket = SocketIOClient(SOCKET_URI, { - // reconnect: true, reconnection: true, timeout: 30000, autoConnect: false, - transports: ['websocket'], // importat with RN + transports: ['websocket'], // important with RN }); this.rooms = []; diff --git a/src/config/Config.ts b/src/config/Config.ts index cbbcea7a3..1e9237cfe 100644 --- a/src/config/Config.ts +++ b/src/config/Config.ts @@ -74,7 +74,6 @@ export const MINDS_URI_SETTINGS = { export const MINDS_MAX_VIDEO_LENGTH = 5; // in minutes -// export const SOCKET_URI = 'wss://ha-socket-io-us-east-1.minds.com:3030'; export const SOCKET_URI = 'wss://ha-socket-alb-io-us-east-1.minds.com'; export const MINDS_CDN_URI = 'https://cdn.minds.com/'; diff --git a/src/newsfeed/ActivityModel.ts b/src/newsfeed/ActivityModel.ts index dccc32d28..d241f5312 100644 --- a/src/newsfeed/ActivityModel.ts +++ b/src/newsfeed/ActivityModel.ts @@ -1,7 +1,7 @@ import { runInAction, action, observable, decorate } from 'mobx'; import FastImage, { Source } from 'react-native-fast-image'; import { FlatList, Alert, Platform } from 'react-native'; - +import _ from 'lodash'; import BaseModel from '../common/BaseModel'; import UserModel from '../channel/UserModel'; import wireService from '../wire/WireService'; @@ -339,7 +339,7 @@ export default class ActivityModel extends BaseModel { } if (visible) { - this.listenForMetrics(); + this.listenForMetricsDebounced(); } else { this.unlistenFromMetrics(); } @@ -593,18 +593,18 @@ export default class ActivityModel extends BaseModel { } } + /** + * listens to metrics updates with 1000ms debounce time + */ + private listenForMetricsDebounced = _.debounce( + () => this.listenForMetrics(), + 1000, + ); + /** * listens to metrics updates */ private listenForMetrics(): void { - logService.info( - 'listening for metrics', - this.guid, - 'message', - this.message.slice(0, 20), - 'from', - this.ownerObj?.username, - ); socketService.join(this.metricsRoom); socketService.subscribe(this.metricsRoom, event => this.onMetricsUpdate(event), @@ -615,14 +615,7 @@ export default class ActivityModel extends BaseModel { * unlistens from metrics updates */ private unlistenFromMetrics(): void { - logService.info( - 'unlistening from metrics', - this.guid, - 'message', - this.message.slice(0, 20), - 'from', - this.ownerObj?.username, - ); + this.listenForMetricsDebounced.cancel(); socketService.leave(this.metricsRoom); socketService.unsubscribe(this.metricsRoom, event => this.onMetricsUpdate(event), From 9ded1ffe537a838bb9a45e07a44eda58da2355f1 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Fri, 9 Sep 2022 12:55:19 +0200 Subject: [PATCH 36/89] add FF --- src/newsfeed/ActivityModel.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/newsfeed/ActivityModel.ts b/src/newsfeed/ActivityModel.ts index d241f5312..37049fcc3 100644 --- a/src/newsfeed/ActivityModel.ts +++ b/src/newsfeed/ActivityModel.ts @@ -28,6 +28,7 @@ import NavigationService from '../navigation/NavigationService'; import { showNotification } from '../../AppMessages'; import mediaProxyUrl from '../common/helpers/media-proxy-url'; import socketService from '~/common/services/socket.service'; +import { hasVariation } from '../../ExperimentsProvider'; type Thumbs = Record | Record[]; @@ -338,10 +339,12 @@ export default class ActivityModel extends BaseModel { this.remind_object.is_visible = visible; } - if (visible) { - this.listenForMetricsDebounced(); - } else { - this.unlistenFromMetrics(); + if (hasVariation('mob-4424-sockets')) { + if (visible) { + this.listenForMetricsDebounced(); + } else { + this.unlistenFromMetrics(); + } } } From 1940560a2b8e99be3e30c3955c59f6b04c3e909d Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Fri, 9 Sep 2022 13:22:08 +0200 Subject: [PATCH 37/89] update snapshots --- .../activity/components/__snapshots__/Activity.js.snap | 7 +++++++ .../components/__snapshots__/ActivityScreen.js.snap | 5 +++++ .../components/__snapshots__/BottomContent.js.snap | 2 ++ __tests__/blogs/__snapshots__/BlogCard.js.snap | 1 + __tests__/blogs/__snapshots__/BlogsViewScreen.js.snap | 4 ++++ src/newsfeed/ActivityModel.ts | 5 +---- 6 files changed, 20 insertions(+), 4 deletions(-) diff --git a/__tests__/activity/components/__snapshots__/Activity.js.snap b/__tests__/activity/components/__snapshots__/Activity.js.snap index 91a6aaf0d..aa55bde51 100644 --- a/__tests__/activity/components/__snapshots__/Activity.js.snap +++ b/__tests__/activity/components/__snapshots__/Activity.js.snap @@ -40,6 +40,7 @@ exports[`Activity component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", @@ -145,6 +146,7 @@ exports[`Activity component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", @@ -256,6 +258,7 @@ exports[`Activity component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", @@ -385,6 +388,7 @@ exports[`Activity component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", @@ -508,6 +512,7 @@ exports[`Activity component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", @@ -638,6 +643,7 @@ exports[`Activity component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", @@ -752,6 +758,7 @@ exports[`Activity component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", diff --git a/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap b/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap index 287fd21a2..9b15bdaab 100644 --- a/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap +++ b/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap @@ -1263,6 +1263,7 @@ exports[`Activity screen component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", @@ -1408,6 +1409,7 @@ exports[`Activity screen component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", @@ -1553,6 +1555,7 @@ exports[`Activity screen component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", @@ -1698,6 +1701,7 @@ exports[`Activity screen component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", @@ -1858,6 +1862,7 @@ exports[`Activity screen component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", diff --git a/__tests__/activity/components/__snapshots__/BottomContent.js.snap b/__tests__/activity/components/__snapshots__/BottomContent.js.snap index 2419f5289..a223ed4a1 100644 --- a/__tests__/activity/components/__snapshots__/BottomContent.js.snap +++ b/__tests__/activity/components/__snapshots__/BottomContent.js.snap @@ -22,6 +22,7 @@ exports[`BottomContent component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", @@ -117,6 +118,7 @@ exports[`BottomContent component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", diff --git a/__tests__/blogs/__snapshots__/BlogCard.js.snap b/__tests__/blogs/__snapshots__/BlogCard.js.snap index a93265925..bdea137d0 100644 --- a/__tests__/blogs/__snapshots__/BlogCard.js.snap +++ b/__tests__/blogs/__snapshots__/BlogCard.js.snap @@ -415,6 +415,7 @@ exports[`blog card component should renders correctly 1`] = ` "last_save": "1524843907", "last_updated": "1524838665", "license": "attribution-noncommercial-cc", + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "", diff --git a/__tests__/blogs/__snapshots__/BlogsViewScreen.js.snap b/__tests__/blogs/__snapshots__/BlogsViewScreen.js.snap index d65837b00..f5d080d38 100644 --- a/__tests__/blogs/__snapshots__/BlogsViewScreen.js.snap +++ b/__tests__/blogs/__snapshots__/BlogsViewScreen.js.snap @@ -942,6 +942,7 @@ exports[`blog view screen component should renders correctly 1`] = ` "last_save": "1524843907", "last_updated": "1524838665", "license": "attribution-noncommercial-cc", + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "", @@ -1123,6 +1124,7 @@ exports[`blog view screen component should renders correctly 1`] = ` "last_save": "1524843907", "last_updated": "1524838665", "license": "attribution-noncommercial-cc", + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "", @@ -1304,6 +1306,7 @@ exports[`blog view screen component should renders correctly 1`] = ` "last_save": "1524843907", "last_updated": "1524838665", "license": "attribution-noncommercial-cc", + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "", @@ -1487,6 +1490,7 @@ exports[`blog view screen component should renders correctly 1`] = ` "last_save": "1524843907", "last_updated": "1524838665", "license": "attribution-noncommercial-cc", + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "", diff --git a/src/newsfeed/ActivityModel.ts b/src/newsfeed/ActivityModel.ts index 37049fcc3..ff7c7b902 100644 --- a/src/newsfeed/ActivityModel.ts +++ b/src/newsfeed/ActivityModel.ts @@ -599,10 +599,7 @@ export default class ActivityModel extends BaseModel { /** * listens to metrics updates with 1000ms debounce time */ - private listenForMetricsDebounced = _.debounce( - () => this.listenForMetrics(), - 1000, - ); + private listenForMetricsDebounced = _.debounce(this.listenForMetrics, 1000); /** * listens to metrics updates From 1ebedba007ef511b3be6b31fec1e777442a27831 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Fri, 9 Sep 2022 14:33:55 +0200 Subject: [PATCH 38/89] (fix) crashing issue on submit --- src/common/ui/icons/IconButton.tsx | 1 + src/compose/ComposeScreen.tsx | 8 +++++--- src/compose/TopBar.tsx | 22 ++++++++++++---------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/common/ui/icons/IconButton.tsx b/src/common/ui/icons/IconButton.tsx index f24758834..80756e98f 100644 --- a/src/common/ui/icons/IconButton.tsx +++ b/src/common/ui/icons/IconButton.tsx @@ -84,6 +84,7 @@ const IconButtonNextComponent = ({ hitSlop={sizeNumeric} style={onStyle} onPress={handlePress} + disabled={more.disabled} testID={testID}> diff --git a/src/compose/ComposeScreen.tsx b/src/compose/ComposeScreen.tsx index d598da9c4..02bdfedff 100644 --- a/src/compose/ComposeScreen.tsx +++ b/src/compose/ComposeScreen.tsx @@ -9,7 +9,7 @@ import { View, } from 'react-native'; import { observer, useLocalStore } from 'mobx-react'; -import { Icon } from '~ui/icons'; +import { IconButtonNext } from '~ui/icons'; import ThemedStyles, { useMemoStyle, useStyle } from '../styles/ThemedStyles'; import i18n from '../common/services/i18n.service'; import MetaPreview from './MetaPreview'; @@ -229,9 +229,11 @@ export default observer(function ComposeScreen(props) { const rightButton = store.isEdit ? ( i18n.t('save') ) : ( - {props.leftText} )} - {props.store.posting ? ( - - - - ) : ( - props.rightText && ( + + {props.store.posting ? ( + + + + ) : typeof props.rightText === 'string' ? ( {props.rightText} - ) - )} + ) : ( + props.rightText + )} + ); }); const styles = ThemedStyles.create({ - dotIndicatorContainerStyle: ['rowJustifyEnd', 'marginRight4x'], + dotIndicatorContainerStyle: ['rowJustifyEnd'], topBar: { width: '100%', flexDirection: 'row', @@ -70,7 +73,6 @@ const styles = ThemedStyles.create({ postButton: { textAlign: 'right', fontSize: 18, - paddingRight: 20, }, back: ['colorIcon', 'paddingLeft2x', 'paddingRight2x'], }); From 7beb2f5e84a16dd52c6c505845da5a63404a8139 Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Fri, 9 Sep 2022 17:11:09 -0300 Subject: [PATCH 39/89] (feat) supermind console --- .../__snapshots__/TopbarTabbar.js.snap | 192 ++++++++---------- locales/en.json | 19 ++ src/common/components/OffsetList.tsx | 105 ++++++---- .../components/topbar-tabbar/TopbarTabbar.tsx | 7 +- src/common/hooks/useApiFetch.ts | 79 +++---- src/common/services/api.service.ts | 1 + src/common/ui/pressables/PressableLine.tsx | 2 +- src/common/ui/screen/ScreenHeader.tsx | 27 ++- src/navigation/Drawer.tsx | 14 +- src/navigation/MoreStack.tsx | 7 + src/navigation/NavigationTypes.ts | 1 + src/newsfeed/activity/Activity.tsx | 2 + src/newsfeed/activity/BottomContent.tsx | 3 +- src/supermind/AddBankInformation.tsx | 25 +++ src/supermind/SuperMindRequest.tsx | 123 +++++++++++ src/supermind/SupermindConsoleScreen.tsx | 85 ++++++++ src/supermind/SupermindRequestModel.ts | 105 ++++++++++ src/supermind/types.ts | 21 ++ 18 files changed, 618 insertions(+), 200 deletions(-) create mode 100644 src/supermind/AddBankInformation.tsx create mode 100644 src/supermind/SuperMindRequest.tsx create mode 100644 src/supermind/SupermindConsoleScreen.tsx create mode 100644 src/supermind/SupermindRequestModel.ts create mode 100644 src/supermind/types.ts diff --git a/__tests__/common/components/topbar-tabbar/__snapshots__/TopbarTabbar.js.snap b/__tests__/common/components/topbar-tabbar/__snapshots__/TopbarTabbar.js.snap index 2f8c4b7d6..f9f2a2516 100644 --- a/__tests__/common/components/topbar-tabbar/__snapshots__/TopbarTabbar.js.snap +++ b/__tests__/common/components/topbar-tabbar/__snapshots__/TopbarTabbar.js.snap @@ -36,10 +36,10 @@ exports[`renders correctly 1`] = ` "paddingVertical": 8, }, Object { - "marginHorizontal": 8, + "marginHorizontal": 12, }, Object { - "borderBottomWidth": 4, + "borderBottomWidth": 5, }, ], undefined, @@ -49,35 +49,27 @@ exports[`renders correctly 1`] = ` ] } > - - - Tokens - - + Tokens + - - - USD - - + USD + - - - Ether - - + Ether + - - - Bitcoin - - + Bitcoin + ; export default observer( // TODO: add ref types - forwardRef(function OffsetList(props: PropsType, ref: any) { + forwardRef(function OffsetList(props: PropsType, ref: any) { // =====================| STATES & VARIABLES |=====================> - type ApiFetchType = FetchResponseType & T; const theme = ThemedStyles.style; const [offset, setOffset] = useState(''); const [page, setPage] = useState(1); + const listRef = React.useRef(null); const offsetField = props.offsetField || 'offset'; const opts = { limit: 12, @@ -75,22 +79,26 @@ export default observer( [props], ); - const { - result, - loading, - error, - fetch, - refresh, - refreshing, - } = useApiFetch(props.fetchEndpoint, { + const fetchStore = useApiFetch(props.fetchEndpoint, { params: opts, dataField: props.endpointData, updateStrategy: 'merge', map, }); + + useEffect(() => { + if (fetchStore.result) { + fetchStore.setResult(null); + props.offsetPagination ? setPage(1) : setOffset(''); + // fetchStore.fetch(); + } + }, [props.fetchEndpoint, fetchStore, props.offsetPagination]); + const data = useMemo(() => { - if (result) { - return result[props.endpointData].slice(); + if (fetchStore.result) { + return Array.isArray(fetchStore.result) + ? fetchStore.result.slice() + : fetchStore.result[props.endpointData].slice(); } if (props.placeholderCount) { @@ -100,20 +108,24 @@ export default observer( } return []; - }, [result, props.placeholderCount, props.endpointData]); + }, [fetchStore.result, props.placeholderCount, props.endpointData]); - // =====================| METHODS |=====================> + // =====================| PROVIDED METHODS |=====================> useImperativeHandle(ref, () => ({ - refreshList: () => refresh(), + refreshList: () => fetchStore.refresh(), + scrollToTop: () => + listRef.current?.scrollToOffset({ offset: 0, animated: true }), })); + // =====================| METHODS |=====================> + const _refresh = React.useCallback(() => { - setOffset(''); - refresh(); - }, [refresh]); + props.offsetPagination ? setPage(1) : setOffset(''); + fetchStore.refresh(); + }, [props.offsetPagination, fetchStore]); const onFetchMore = useCallback(() => { - if (loading) { + if (fetchStore.loading) { return; } @@ -121,19 +133,29 @@ export default observer( return setPage(oldPage => oldPage + 1); } - if (result?.['load-next']) { - setOffset(result['load-next']); + if (fetchStore.result?.['load-next']) { + setOffset(fetchStore.result['load-next']); } - }, [loading, result, props.offsetPagination, hasMore]); + }, [ + fetchStore.loading, + fetchStore.result, + props.offsetPagination, + hasMore, + ]); // =====================| RENDERS |=====================> const renderItem = useMemo(() => { - if (result && result[props.endpointData]) { + if (fetchStore.result && fetchStore.result[props.endpointData]) { return props.renderItem; } return props.renderPlaceholder || props.renderItem; - }, [result, props.endpointData, props.renderPlaceholder, props.renderItem]); + }, [ + fetchStore, + props.endpointData, + props.renderPlaceholder, + props.renderItem, + ]); /** * if it was loading and we already had some results, @@ -141,21 +163,27 @@ export default observer( **/ const loadingFooter = useMemo( () => - loading && !refreshing && result?.[props.endpointData] ? ( + fetchStore.loading && + !fetchStore.refreshing && + fetchStore.result?.[props.endpointData] ? ( - ) : undefined, + ) : ( + !props.placeholderCount && + fetchStore.loading && + !fetchStore.result && + ), [ - loading, - refreshing, - result, + fetchStore.loading, + fetchStore.refreshing, + fetchStore.result, props.endpointData, theme.paddingVertical2x, ], ); - if (error && !loading) { + if (fetchStore.error && !fetchStore.loading) { return ( fetch()}> + onPress={() => fetchStore.fetch()}> {i18n.t('error') + '\n'} {i18n.t('tryAgain')} ); } - if (!props.placeholderCount && loading && !result) { - return ; - } - const List = props.ListComponent || FlatList; return ( diff --git a/src/common/components/topbar-tabbar/TopbarTabbar.tsx b/src/common/components/topbar-tabbar/TopbarTabbar.tsx index fddd1b473..dd4035958 100644 --- a/src/common/components/topbar-tabbar/TopbarTabbar.tsx +++ b/src/common/components/topbar-tabbar/TopbarTabbar.tsx @@ -38,8 +38,8 @@ function TopbarTabbar(props: PropsType) { const theme = ThemedStyles.style; const tabStyle = [ theme.paddingVertical2x, - theme.marginHorizontal2x, - theme.borderBottom4x, + theme.marginHorizontal3x, + theme.borderBottom5x, ]; return ( @@ -67,8 +67,7 @@ function TopbarTabbar(props: PropsType) { ]}> + color={tab.id === props.current ? 'primary' : 'secondary'}> {tab.title} {!!tab.subtitle && {tab.subtitle}} diff --git a/src/common/hooks/useApiFetch.ts b/src/common/hooks/useApiFetch.ts index 9df841047..f32d88853 100644 --- a/src/common/hooks/useApiFetch.ts +++ b/src/common/hooks/useApiFetch.ts @@ -11,10 +11,12 @@ export interface PostStore extends FetchStore { post: (object?) => Promise; } -type FetchResponseType = { - status: string; - 'load-next': string | number; -}; +type FetchResponseType = + | { + status: string; + 'load-next': string | number; + } + | Array; type ApiFetchType = FetchResponseType; @@ -27,25 +29,30 @@ const mergeState = (dataField: string, map = defaultMap) => ( newData: ApiFetchType, oldData: ApiFetchType, ) => - ({ - ...newData, - [dataField]: [ - ...(oldData ? oldData[dataField] : []), - ...map(newData && newData[dataField] ? newData[dataField] : []), - ], - } as ApiFetchType); + dataField + ? ({ + ...newData, + [dataField]: [ + ...(oldData ? oldData[dataField] : []), + ...map(newData && newData[dataField] ? newData[dataField] : []), + ], + } as ApiFetchType) + : [...(oldData ? (oldData as Array) : []), ...map(newData || [])]; /** * a function that replaces the state with the new state */ -const replaceState = (dataField: string, map = defaultMap) => ( - newData: any, -) => ({ - ...newData, - [dataField]: [ - ...map(newData && newData[dataField] ? newData[dataField] : []), - ], -}); +const replaceState = (dataField: string, map = defaultMap) => (newData: any) => + dataField + ? { + ...newData, + [dataField]: [ + ...map(newData && newData[dataField] ? newData[dataField] : []), + ], + } + : newData + ? map(newData) + : []; type MethodType = 'get' | 'post' | 'put' | 'delete'; @@ -93,14 +100,10 @@ export interface FetchStore { refreshing?: boolean; } -const createStore = ({ - url, - options: hookOptions, - method = 'get', -}: { +const createStore = (storeOptions: { url: string; options?: FetchOptions; - method?: MethodType; + method: MethodType; }) => ({ retryTimer: null, retryCount: 0, @@ -120,14 +123,17 @@ const createStore = ({ }, hydrate(params: any, updateState) { try { - const data = storages.user?.getMap(getCacheKey(url, params)); + const data = storages.user?.getMap(getCacheKey(storeOptions.url, params)); if (data) this.setResult(updateState(data, this.result)); } catch (e) { console.error(e); } }, persist(params: any) { - return storages.user?.setMap(getCacheKey(url, params), this.result); + return storages.user?.setMap( + getCacheKey(storeOptions.url, params), + this.result, + ); }, setResult(v: any) { this.result = v; @@ -143,7 +149,7 @@ const createStore = ({ }, async fetch(data?: object, retry = false, opts: FetchOptions = {}) { if (!data) { - data = hookOptions?.params || {}; + data = storeOptions.options?.params || {}; } let { updateState, @@ -158,7 +164,7 @@ const createStore = ({ offsetField: 'load-next', dataField: 'entities', }, - hookOptions, + storeOptions.options, opts, ); this.clearRetryTimer(!retry); @@ -182,10 +188,10 @@ const createStore = ({ this.setLoading(true); this.setError(null); try { - const result = await apiService[method]( - url, + const result = await apiService[storeOptions.method]( + storeOptions.url, data, - method === 'get' ? this : undefined, + storeOptions.method === 'get' ? this : undefined, ); // hack to remove the offset if the result was empty @@ -201,14 +207,16 @@ const createStore = ({ } } catch (err) { this.setError(err); - if (hookOptions?.retry !== undefined && !isAbort(err)) { + if (storeOptions.options?.retry !== undefined && !isAbort(err)) { if ( - hookOptions.retry > 0 ? this.retryCount < hookOptions?.retry : true + storeOptions.options.retry > 0 + ? this.retryCount < storeOptions.options?.retry + : true ) { this.retryCount++; this.retryTimer = setTimeout(() => { this.fetch(data, true); - }, hookOptions?.retryDelay || 3000); + }, storeOptions.options?.retryDelay || 3000); } } throw err; @@ -246,6 +254,7 @@ export default function useApiFetch( ): FetchStore { const store: FetchStore = useLocalStore(createStore, { url, + method: 'get', options, }); const observableParams = useAsObservableSource(options.params || {}); diff --git a/src/common/services/api.service.ts b/src/common/services/api.service.ts index ec03b4efc..7463dd2ec 100644 --- a/src/common/services/api.service.ts +++ b/src/common/services/api.service.ts @@ -372,6 +372,7 @@ export class ApiService { if (response.status >= 500) { logService.info( '[ApiService] server error', + response.status, response.request?.url || url, ); throw new ApiError('Server error ' + response.status, response.status); diff --git a/src/common/ui/pressables/PressableLine.tsx b/src/common/ui/pressables/PressableLine.tsx index e734c1c5e..c879b9055 100644 --- a/src/common/ui/pressables/PressableLine.tsx +++ b/src/common/ui/pressables/PressableLine.tsx @@ -7,7 +7,7 @@ export const PressableLine = ({ children, ...props }) => { return ( {/*TouchableHighlight adds a style: undefined that break the layout of Row/Column */} <>{children} diff --git a/src/common/ui/screen/ScreenHeader.tsx b/src/common/ui/screen/ScreenHeader.tsx index cb4ddcdec..719c3083a 100644 --- a/src/common/ui/screen/ScreenHeader.tsx +++ b/src/common/ui/screen/ScreenHeader.tsx @@ -13,17 +13,21 @@ export type ScreenHeaderType = { back?: boolean; backIcon?: IconMapNameType; border?: boolean; + shadow?: boolean; titleType?: TypographyPropsType['type']; centerTitle?: boolean; onBack?: () => void; + onTitlePress?: () => void; }; export const ScreenHeader = ({ title, extra, + shadow, back, backIcon = 'chevron-left', onBack, + onTitlePress, border, titleType = 'H2', centerTitle, @@ -31,10 +35,10 @@ export const ScreenHeader = ({ }: ScreenHeaderType & SpacerPropType) => { const navigation = useNavigation(); return ( - + {centerTitle && ( - + {title} @@ -44,13 +48,13 @@ export const ScreenHeader = ({ {back && ( navigation.goBack())} /> )} {!centerTitle && ( - + {title} )} @@ -65,5 +69,18 @@ const styles = ThemedStyles.create({ titleCenteredContainer: ['absoluteFill', 'centered', { minHeight: 55 }], border: ['bcolorPrimaryBorder', 'borderBottom1x', { minHeight: 55 }], row: ['rowJustifyStart'], + shadow: [ + 'bgPrimaryBackground', + { + shadowColor: '#000', + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 0.25, + shadowRadius: 1, + elevation: 2, + zIndex: 1000, + }, + ], }); -222; diff --git a/src/navigation/Drawer.tsx b/src/navigation/Drawer.tsx index fda73daab..a06f36fdc 100644 --- a/src/navigation/Drawer.tsx +++ b/src/navigation/Drawer.tsx @@ -22,6 +22,7 @@ import apiService, { isNetworkError } from '~/common/services/api.service'; import openUrlService from '~/common/services/open-url.service'; import { showNotification } from 'AppMessages'; import { IS_IOS } from '~/config/Config'; +import { useFeature } from '@growthbook/growthbook-react'; /** * Retrieves the link & jwt for zendesk and navigate to it. @@ -67,7 +68,7 @@ const getOptionsSmallList = navigation => { ]; }; -const getOptionsList = navigation => { +const getOptionsList = (navigation, supermindFeatureFlag) => { const hasRewards = sessionService.getUser().rewards; let list = [ @@ -78,6 +79,14 @@ const getOptionsList = navigation => { navigation.navigate('PlusDiscoveryScreen'); }, }, + supermindFeatureFlag.on + ? { + name: 'Supermind', + onPress: () => { + navigation.navigate('SupermindConsole'); + }, + } + : null, { name: i18n.t('moreScreen.wallet'), icon: 'bank', @@ -138,6 +147,7 @@ const getOptionsList = navigation => { */ export default function Drawer(props) { const channel = sessionService.getUser(); + const supermindFeatureFlag = useFeature('mobile-supermind'); const handleChannelNav = () => { props.navigation.push('Channel', { entity: channel }); @@ -148,7 +158,7 @@ export default function Drawer(props) { const avatar = channel && channel.getAvatarSource ? channel.getAvatarSource('medium') : {}; - const optionsList = getOptionsList(props.navigation); + const optionsList = getOptionsList(props.navigation, supermindFeatureFlag); const optionsSmallList = getOptionsSmallList(props.navigation); return ( diff --git a/src/navigation/MoreStack.tsx b/src/navigation/MoreStack.tsx index 6cb42700f..2a6a4c43a 100644 --- a/src/navigation/MoreStack.tsx +++ b/src/navigation/MoreStack.tsx @@ -123,6 +123,13 @@ export default function () { getComponent={() => require('~/settings/SettingsScreen').default} options={hideHeader} /> + + require('~/supermind/SupermindConsoleScreen').default + } + options={hideHeader} + /> require('~/buy-tokens/BuyTokensScreen').default} diff --git a/src/navigation/NavigationTypes.ts b/src/navigation/NavigationTypes.ts index 78cb487f2..9af91a29b 100644 --- a/src/navigation/NavigationTypes.ts +++ b/src/navigation/NavigationTypes.ts @@ -43,6 +43,7 @@ export type DiscoveryStackParamList = { }; export type MoreStackParamList = { + SupermindConsole: {}; Drawer: {}; Channel: {}; Wallet: { diff --git a/src/newsfeed/activity/Activity.tsx b/src/newsfeed/activity/Activity.tsx index 09d9a17ba..dcf3997c7 100644 --- a/src/newsfeed/activity/Activity.tsx +++ b/src/newsfeed/activity/Activity.tsx @@ -53,6 +53,7 @@ type PropsType = { storeUserTap?: boolean; showOnlyContent?: boolean; borderless?: boolean; + hideMetrics?: boolean; }; /** @@ -293,6 +294,7 @@ export default class Activity extends Component { entity={entity} showOnlyContent={this.props.showOnlyContent} hideTabs={this.props.hideTabs} + hideMetrics={this.props.hideMetrics} /> )} diff --git a/src/newsfeed/activity/BottomContent.tsx b/src/newsfeed/activity/BottomContent.tsx index 71036439e..5b9110466 100644 --- a/src/newsfeed/activity/BottomContent.tsx +++ b/src/newsfeed/activity/BottomContent.tsx @@ -14,6 +14,7 @@ type PropsType = { showOnlyContent?: boolean; entity: ActivityModel; hideTabs?: boolean; + hideMetrics?: boolean; }; const BottomContent = (props: PropsType) => { @@ -50,7 +51,7 @@ const BottomContent = (props: PropsType) => { return ( <> - + {!props.hideMetrics && } console.log('tap')}> + + + {i18nService.t('supermind.addBank')} + + {i18nService.t('supermind.addBankDetail')} + + + ); +} + +export const borderBottomStyle = ThemedStyles.combine( + 'bcolorPrimaryBorder', + 'borderBottomHair', +); diff --git a/src/supermind/SuperMindRequest.tsx b/src/supermind/SuperMindRequest.tsx new file mode 100644 index 000000000..f40fe7acc --- /dev/null +++ b/src/supermind/SuperMindRequest.tsx @@ -0,0 +1,123 @@ +import React from 'react'; +import { useNavigation } from '@react-navigation/native'; +import { View } from 'react-native'; + +import i18n from '~/common/services/i18n.service'; +import Activity from '~/newsfeed/activity/Activity'; +import { borderBottomStyle } from './AddBankInformation'; +import SupermindRequestModel from './SupermindRequestModel'; +import { B2, Button, Column, Row, Spacer } from '~/common/ui'; +import SupermindLabel from '~/common/components/supermind/SupermindLabel'; +import { SupermindRequestStatus } from './types'; +import { observer } from 'mobx-react'; + +type Props = { + request: SupermindRequestModel; + outbound?: boolean; +}; + +export default function SuperMindRequest({ request, outbound }: Props) { + const navigation = useNavigation(); + + return ( + + + + + + {i18n.t('expires')}: {request.formattedExpiration} + + + + + + {request.receiver_entity + ? i18n.t('supermind.to', { name: request.receiver_entity.username }) + + ' · ' + : i18n.t('requirements') + ': '} + {i18n.t(`supermind.replyType.${request.reply_type}`)} + {request.twitter_required && ( + <> + {' · '} + Twitter + + )} + + {outbound ? ( + + ) : ( + + )} + + ); +} + +// Composer mode based on request_type +const composerModes = [null, 'photo', 'video']; + +/** + * Inbound buttons + */ +const InboundButtons = observer( + ({ request }: { request: SupermindRequestModel }) => { + const navigation = useNavigation(); + if (request.status !== SupermindRequestStatus.CREATED) { + return null; + } + const answer = React.useCallback(() => { + navigation.navigate('Compose', { + isRemind: true, + supermind: true, + mode: composerModes[request.reply_type], + entity: request.entity, + }); + }, [navigation, request]); + return ( + + + + + ); + }, +); + +/** + * Outbound buttons + */ +const OutboundButtons = observer( + ({ request }: { request: SupermindRequestModel }) => { + if (request.status !== SupermindRequestStatus.CREATED) { + return null; + } + return ( + + + + ); + }, +); diff --git a/src/supermind/SupermindConsoleScreen.tsx b/src/supermind/SupermindConsoleScreen.tsx new file mode 100644 index 000000000..278dac1e0 --- /dev/null +++ b/src/supermind/SupermindConsoleScreen.tsx @@ -0,0 +1,85 @@ +import React from 'react'; +import OffsetList from '~/common/components/OffsetList'; + +import TopbarTabbar, { + TabType, +} from '~/common/components/topbar-tabbar/TopbarTabbar'; +import i18n from '~/common/services/i18n.service'; + +import { IconButton, Screen, ScreenHeader } from '~/common/ui'; +import ThemedStyles from '~/styles/ThemedStyles'; +import AddBankInformation from './AddBankInformation'; +import SuperMindRequest from './SuperMindRequest'; +import SupermindRequestModel from './SupermindRequestModel'; + +type TabModeType = 'inbound' | 'outbound'; + +export default function SupermindConsoleScreen({ navigation }) { + const theme = ThemedStyles.style; + const [mode, setMode] = React.useState('inbound'); + const listRef = React.useRef(null); + + const tabs: Array> = React.useMemo( + () => [ + { + id: 'inbound', + title: i18n.t('inbound'), + }, + { + id: 'outbound', + title: i18n.t('outbound'), + }, + ], + [], + ); + + return ( + + listRef.current?.scrollToTop()} + extra={ + navigation.navigate('Settings')} + /> + } + back + shadow + /> + + setMode(mode)} + current={mode} + tabStyle={theme.paddingVertical} + /> + + + } + contentContainerStyle={ThemedStyles.style.paddingTop2x} + map={mapRequests} + fetchEndpoint={ + mode === 'inbound' + ? 'api/v3/supermind/inbox' + : 'api/v3/supermind/outbox' + } + offsetPagination + renderItem={ + mode === 'inbound' ? renderSupermindInbound : renderSupermindOutbound + } + endpointData="" + /> + + ); +} + +const renderSupermindInbound = row => ; +const renderSupermindOutbound = row => ( + +); +const mapRequests = items => SupermindRequestModel.createMany(items); diff --git a/src/supermind/SupermindRequestModel.ts b/src/supermind/SupermindRequestModel.ts new file mode 100644 index 000000000..818433201 --- /dev/null +++ b/src/supermind/SupermindRequestModel.ts @@ -0,0 +1,105 @@ +import moment from 'moment-timezone'; + +import BaseModel from '~/common/BaseModel'; +import abbrev from '~/common/helpers/abbrev'; +import apiService from '~/common/services/api.service'; +import ActivityModel from '~/newsfeed/ActivityModel'; +import { showNotification } from '~/../AppMessages'; +import { + SupermindRequestPaymentMethod, + SupermindRequestReplyType, + SupermindRequestStatus, +} from './types'; +import i18nService from '~/common/services/i18n.service'; +import { action, observable } from 'mobx'; +import UserModel from '~/channel/UserModel'; + +/** + * Supermind request model + */ +export default class SupermindRequestModel extends BaseModel { + guid: string = ''; + activity_guid: string = ''; + sender_guid: string = ''; + receiver_guid: string = ''; + @observable status: SupermindRequestStatus = 0; + payment_amount: number = 0; + payment_method: SupermindRequestPaymentMethod = 0; + payment_txid?: string; + created_timestamp!: number; + expiry_threshold: number = 7 * 86400; + updated_timestamp?: number; + twitter_required: boolean = false; + reply_type: SupermindRequestReplyType = 0; + entity!: ActivityModel; + + /** + * Receiver channel (populated only for outbound) + */ + receiver_entity: null | UserModel = null; + + /** + * Used to indicate that a request is in progress + * 0: not loading + * 1: reject + * 2: revoke + */ + @observable isLoading: 0 | 1 | 2 = 0; + + /** + * Child models + */ + childModels(): any { + return { + entity: ActivityModel, + }; + } + + /** + * Returns the formatted amount + */ + get formattedAmount() { + if (this.payment_method === SupermindRequestPaymentMethod.CASH) { + return `$${abbrev(this.payment_amount, 2)}`; + } else { + return `${abbrev(this.payment_amount, 2)} token`; + } + } + + /** + * Returns the humanized difference + */ + get formattedExpiration() { + const now = moment((this.created_timestamp + this.expiry_threshold) * 1000); + const date = moment(this.created_timestamp * 1000); + const diff = moment.duration(date.diff(now)); + return diff.humanize(); + } + + @action + async revoke() { + try { + this.isLoading = 2; + await apiService.delete(`api/v3/supermind/${this.guid}`); + showNotification(i18nService.t('supermind.revoked')); + } catch (error) { + showNotification(i18nService.t('errorMessage')); + } finally { + this.isLoading = 0; + } + } + + @action + async reject() { + try { + this.isLoading = 1; + await apiService.post(`api/v3/supermind/${this.guid}/reject`); + this.status = SupermindRequestStatus.REJECTED; + showNotification(i18nService.t('supermind.rejected')); + } catch (error) { + showNotification(i18nService.t('errorMessage')); + } finally { + this.isLoading = 0; + } + } +} diff --git a/src/supermind/types.ts b/src/supermind/types.ts new file mode 100644 index 000000000..c8dc3cb4b --- /dev/null +++ b/src/supermind/types.ts @@ -0,0 +1,21 @@ +export enum SupermindRequestStatus { + PENDING = 0, + CREATED = 1, + ACCEPTED = 2, + REVOKED = 3, + REJECTED = 4, + FAILED_PAYMENT = 5, + FAILED = 6, + EXPIRED = 7, +} + +export enum SupermindRequestReplyType { + TEXT = 0, + IMAGE = 1, + VIDEO = 2, +} + +export enum SupermindRequestPaymentMethod { + CASH = 0, + OFFCHAIN_TOKEN = 1, +} From cb602cc5dfe62cb57242ae495f16b1c100495323 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari <879922-manishoo@users.noreply.gitlab.com> Date: Mon, 12 Sep 2022 12:32:42 +0000 Subject: [PATCH 40/89] Apply 1 suggestion(s) to 1 file(s) --- src/common/components/OffsetList.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/components/OffsetList.tsx b/src/common/components/OffsetList.tsx index 3eb99fed4..a0fe2a955 100644 --- a/src/common/components/OffsetList.tsx +++ b/src/common/components/OffsetList.tsx @@ -86,6 +86,7 @@ export default observer( map, }); + // if the fetchEndpoint changed, reset pagination and results useEffect(() => { if (fetchStore.result) { fetchStore.setResult(null); From a1221d83a0cd793a856fcfa00cca9821d89b0ab3 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari <879922-manishoo@users.noreply.gitlab.com> Date: Mon, 12 Sep 2022 12:32:51 +0000 Subject: [PATCH 41/89] Apply 1 suggestion(s) to 1 file(s) --- src/common/components/OffsetList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/components/OffsetList.tsx b/src/common/components/OffsetList.tsx index a0fe2a955..d380ba516 100644 --- a/src/common/components/OffsetList.tsx +++ b/src/common/components/OffsetList.tsx @@ -146,7 +146,7 @@ export default observer( // =====================| RENDERS |=====================> const renderItem = useMemo(() => { - if (fetchStore.result && fetchStore.result[props.endpointData]) { + if (fetchStore.result?.[props.endpointData]) { return props.renderItem; } From c31812318a9218b14e0ed19255a3b02c985e029b Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Mon, 12 Sep 2022 09:36:24 -0300 Subject: [PATCH 42/89] (fix) align, and supermind request naming --- src/common/components/OffsetList.tsx | 1 - src/supermind/SuperMindRequest.tsx | 4 ++-- src/supermind/SupermindConsoleScreen.tsx | 6 +++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/common/components/OffsetList.tsx b/src/common/components/OffsetList.tsx index 3eb99fed4..1ddcb2990 100644 --- a/src/common/components/OffsetList.tsx +++ b/src/common/components/OffsetList.tsx @@ -90,7 +90,6 @@ export default observer( if (fetchStore.result) { fetchStore.setResult(null); props.offsetPagination ? setPage(1) : setOffset(''); - // fetchStore.fetch(); } }, [props.fetchEndpoint, fetchStore, props.offsetPagination]); diff --git a/src/supermind/SuperMindRequest.tsx b/src/supermind/SuperMindRequest.tsx index f40fe7acc..350fe6c11 100644 --- a/src/supermind/SuperMindRequest.tsx +++ b/src/supermind/SuperMindRequest.tsx @@ -16,13 +16,13 @@ type Props = { outbound?: boolean; }; -export default function SuperMindRequest({ request, outbound }: Props) { +export default function SupermindRequest({ request, outbound }: Props) { const navigation = useNavigation(); return ( - + {i18n.t('expires')}: {request.formattedExpiration} diff --git a/src/supermind/SupermindConsoleScreen.tsx b/src/supermind/SupermindConsoleScreen.tsx index 278dac1e0..e74657951 100644 --- a/src/supermind/SupermindConsoleScreen.tsx +++ b/src/supermind/SupermindConsoleScreen.tsx @@ -9,7 +9,7 @@ import i18n from '~/common/services/i18n.service'; import { IconButton, Screen, ScreenHeader } from '~/common/ui'; import ThemedStyles from '~/styles/ThemedStyles'; import AddBankInformation from './AddBankInformation'; -import SuperMindRequest from './SuperMindRequest'; +import SupermindRequest from './SupermindRequest'; import SupermindRequestModel from './SupermindRequestModel'; type TabModeType = 'inbound' | 'outbound'; @@ -78,8 +78,8 @@ export default function SupermindConsoleScreen({ navigation }) { ); } -const renderSupermindInbound = row => ; +const renderSupermindInbound = row => ; const renderSupermindOutbound = row => ( - + ); const mapRequests = items => SupermindRequestModel.createMany(items); From ee788802d4890e40cd5831584a86d14db61604ad Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Mon, 12 Sep 2022 09:59:40 -0300 Subject: [PATCH 43/89] (feat) navigate to bank info screen and hide the button if the bank is connected --- src/supermind/AddBankInformation.tsx | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/supermind/AddBankInformation.tsx b/src/supermind/AddBankInformation.tsx index cc485bbe8..febb93973 100644 --- a/src/supermind/AddBankInformation.tsx +++ b/src/supermind/AddBankInformation.tsx @@ -1,4 +1,8 @@ +import { useNavigation } from '@react-navigation/native'; +import { observer } from 'mobx-react'; import React from 'react'; + +import { useStores } from '~/common/hooks/use-stores'; import i18nService from '~/common/services/i18n.service'; import { B2, PressableLine, Spacer } from '~/common/ui'; import ThemedStyles from '~/styles/ThemedStyles'; @@ -6,9 +10,19 @@ import ThemedStyles from '~/styles/ThemedStyles'; /** * Add bank information banner */ -export default function AddBankInformation() { +function AddBankInformation() { + const { wallet } = useStores(); + const navigation = useNavigation(); + + if (wallet.stripeDetails.hasBank) { + return null; + } return ( - console.log('tap')}> + + navigation.navigate('BankInfoScreen', { walletStore: wallet }) + }> {i18nService.t('supermind.addBank')} @@ -19,6 +33,8 @@ export default function AddBankInformation() { ); } +export default observer(AddBankInformation); + export const borderBottomStyle = ThemedStyles.combine( 'bcolorPrimaryBorder', 'borderBottomHair', From 48f74cbe6a10a9f5040b99c92c77f698c1a85b19 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Mon, 12 Sep 2022 16:05:16 +0200 Subject: [PATCH 44/89] (feat) add tests for createComposeStore --- src/compose/createComposeStore.spec.ts | 173 +++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 src/compose/createComposeStore.spec.ts diff --git a/src/compose/createComposeStore.spec.ts b/src/compose/createComposeStore.spec.ts new file mode 100644 index 000000000..9b5d2621a --- /dev/null +++ b/src/compose/createComposeStore.spec.ts @@ -0,0 +1,173 @@ +import apiService from '../common/services/api.service'; +import NavigationService from '../navigation/NavigationService'; +import createComposeStore from './createComposeStore'; +import { SupermindRequestParam } from './SupermindComposeScreen'; +import mindsConfigService from '../common/services/minds-config.service'; + +jest.mock('../navigation/NavigationService'); +jest.mock('../common/services/minds-config.service', () => ({ + settings: { + plus: { + support_tier_urn: '', + }, + }, +})); + +const mockedNavigation = { + setParams: jest.fn(), +}; + +describe('createComposeStore', () => { + let store; + + beforeEach(() => { + store = createComposeStore({ + navigation: mockedNavigation, + route: {}, + }); + store.onScreenFocused(); + }); + + it('should have the correct default values', () => { + expect(store.allowedMode).toBeNull(); + expect(store.supermindRequest).toBeUndefined(); + }); + + it('should set allowedMode from route params', () => { + const expectedAllowedMode = 'photo'; + store = createComposeStore({ + navigation: mockedNavigation, + route: { + params: { + allowedMode: expectedAllowedMode, + }, + }, + }); + store.onScreenFocused(); + expect(store.allowedMode).toBe(expectedAllowedMode); + }); + + it('should open supermind modal', () => { + store = createComposeStore({ + navigation: mockedNavigation, + route: { + params: { + supermind: true, + }, + }, + }); + store.onScreenFocused(); + expect(NavigationService.navigate).toHaveBeenCalledWith( + 'SupermindCompose', + expect.anything(), + ); + }); + + it('should open supermind modal with an entity', () => { + const fakeOwnerObj = {}; + store = createComposeStore({ + navigation: mockedNavigation, + route: { + params: { + supermind: true, + entity: { + ownerObj: fakeOwnerObj, + }, + }, + }, + }); + store.onScreenFocused(); + expect(NavigationService.navigate).toHaveBeenCalledWith( + 'SupermindCompose', + { + data: { channel: fakeOwnerObj }, + onSave: expect.any(Function), + onClear: expect.any(Function), + }, + ); + }); + + it('should open supermind modal with a targetChannel', () => { + const fakeOwnerObj = {}; + store = createComposeStore({ + navigation: mockedNavigation, + route: { + params: { + supermind: true, + entity: { + supermindTargetChannel: fakeOwnerObj, + }, + }, + }, + }); + store.onScreenFocused(); + expect(NavigationService.navigate).toHaveBeenCalledWith( + 'SupermindCompose', + { + data: { channel: fakeOwnerObj }, + onSave: expect.any(Function), + onClear: expect.any(Function), + }, + ); + }); + + it('should submit a supermind', () => { + const fakeReq = createASupermind(); + expect(apiService.put).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ + supermind_request: { + ...fakeReq, + receiver_guid: fakeReq.channel.guid, + receiver_username: fakeReq.channel.username, + }, + }), + ); + }); + + it('should not monetize if a supermind is active', () => { + store.saveMembsershipMonetize({ urn: 'fake-urn' }); + createASupermind(); + expect(apiService.put).toHaveBeenCalledWith( + expect.any(String), + expect.not.objectContaining({ + paywall: true, + }), + ); + }); + + it('should not post to permaweb if a supermind is active', () => { + store.togglePostToPermaweb(); + createASupermind(); + expect(apiService.put).toHaveBeenCalledWith( + expect.any(String), + expect.not.objectContaining({ + post_to_permaweb: true, + }), + ); + }); + + const createASupermind = () => { + const fakeSupermindRequest = { + channel: { + guid: 'fakeGuid', + username: 'fakeUsername', + }, + payment_options: { + payment_type: 1, + amount: 10, + }, + reply_type: 0, + twitter_required: false, + terms_agreed: true, + } as SupermindRequestParam; + + store.supermindRequest = fakeSupermindRequest; + store.text = 'fake post'; + + jest.spyOn(apiService, 'put'); + apiService.put.mockReturnValueOnce({ supermind: true }); + store.submit(); + return fakeSupermindRequest; + }; +}); From b379be17633c91bc768300b2300c34df1e096348 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Mon, 12 Sep 2022 16:37:40 +0200 Subject: [PATCH 45/89] (feat) further refactor and fixes of supermind reply context --- .../components/supermind/SupermindButton.tsx | 2 +- src/compose/ComposeBottomBar.tsx | 23 +++++++++++-------- src/compose/ComposeScreen.tsx | 6 ++++- src/compose/createComposeStore.spec.ts | 6 ++--- src/compose/createComposeStore.ts | 14 ++++++++++- .../activity/actions/SupermindAction.tsx | 2 +- 6 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/common/components/supermind/SupermindButton.tsx b/src/common/components/supermind/SupermindButton.tsx index 55560c18a..6e8291062 100644 --- a/src/common/components/supermind/SupermindButton.tsx +++ b/src/common/components/supermind/SupermindButton.tsx @@ -14,7 +14,7 @@ type Props = { export default function SupermindButton({ entity }: Props) { const handlePress = useCallback(() => { NavigationService.navigate('Compose', { - supermind: true, + openSupermindModal: true, supermindTargetChannel: entity, allowedMode: 'video', }); diff --git a/src/compose/ComposeBottomBar.tsx b/src/compose/ComposeBottomBar.tsx index 9c7cc8ca0..5aae9db8f 100644 --- a/src/compose/ComposeBottomBar.tsx +++ b/src/compose/ComposeBottomBar.tsx @@ -69,15 +69,20 @@ function ComposeBottomBar(props) { scale onPress={props.onHashtag} /> - - - + { + // don't allow superminding in the context of a supermind reply + !props.store.isSupermindReply && ( + + + + ) + } } + leftComponent={ + (store.supermindRequest || store.isSupermindReply) && ( + + ) + } onPressRight={onPost} onPressBack={onPressBack} store={store} diff --git a/src/compose/createComposeStore.spec.ts b/src/compose/createComposeStore.spec.ts index 9b5d2621a..6b02087db 100644 --- a/src/compose/createComposeStore.spec.ts +++ b/src/compose/createComposeStore.spec.ts @@ -52,7 +52,7 @@ describe('createComposeStore', () => { navigation: mockedNavigation, route: { params: { - supermind: true, + openSupermindModal: true, }, }, }); @@ -69,7 +69,7 @@ describe('createComposeStore', () => { navigation: mockedNavigation, route: { params: { - supermind: true, + openSupermindModal: true, entity: { ownerObj: fakeOwnerObj, }, @@ -93,7 +93,7 @@ describe('createComposeStore', () => { navigation: mockedNavigation, route: { params: { - supermind: true, + openSupermindModal: true, entity: { supermindTargetChannel: fakeOwnerObj, }, diff --git a/src/compose/createComposeStore.ts b/src/compose/createComposeStore.ts index dff3906e7..792d6ccf5 100644 --- a/src/compose/createComposeStore.ts +++ b/src/compose/createComposeStore.ts @@ -69,7 +69,15 @@ export default function (props) { group: null, postToPermaweb: false, initialized: false, + /** + * the supermind request that is built from the SupermindComposeScreen + */ supermindRequest: undefined as SupermindRequestParam, + /** + * the supermind object which is passed from the SupermindConsole and used for supermind reply functionality. + * The existence of this object means the composer is being used to reply to a supermind + */ + supermindObject: undefined as any, onScreenFocused() { const params = props.route.params; if (this.initialized || !params) { @@ -83,6 +91,7 @@ export default function (props) { this.isEdit = params.isEdit; this.allowedMode = params.allowedMode; this.entity = params.entity || null; + this.supermindObject = params.supermindObject; this.mode = params.mode ? params.mode @@ -114,7 +123,7 @@ export default function (props) { this.group = props.route.params.group; } - if (params.supermind) { + if (params.openSupermindModal) { const channel = params.supermindTargetChannel || params.entity?.ownerObj; this.openSupermindModal(channel ? { channel } : undefined); @@ -664,5 +673,8 @@ export default function (props) { }, }); }, + get isSupermindReply() { + return Boolean(this.supermindObject); + }, }; } diff --git a/src/newsfeed/activity/actions/SupermindAction.tsx b/src/newsfeed/activity/actions/SupermindAction.tsx index 4d59f77a3..730701d8c 100644 --- a/src/newsfeed/activity/actions/SupermindAction.tsx +++ b/src/newsfeed/activity/actions/SupermindAction.tsx @@ -22,7 +22,7 @@ export default function SupermindAction({ entity }) { isRemind: true, entity, parentKey: key, - supermind: true, + openSupermindModal: true, }) } /> From 55789d58df244ade89ca9ed80d45576782b46f5d Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Mon, 12 Sep 2022 14:37:30 -0300 Subject: [PATCH 46/89] (chore) spec tests --- __mocks__/fake/ActivitiesFaker.js | 83 +- .../fake/supermind/SupermindRequestFaker.js | 19 + __tests__/activity/components/Activity.js | 6 +- .../components/__snapshots__/Activity.js.snap | 77 - .../__snapshots__/ActivityScreen.js.snap | 80 - .../__snapshots__/BottomContent.js.snap | 2 - __tests__/common/components/ExplicitImage.js | 20 +- .../__snapshots__/ExplicitImage.js.snap | 19 +- .../__snapshots__/MediaView.js.snap | 1 - src/supermind/SuperMindRequest.tsx | 6 +- src/supermind/SupermindRequest.spec.tsx | 64 + .../SupermindRequest.spec.tsx.snap | 1353 +++++++++++++++++ 12 files changed, 1503 insertions(+), 227 deletions(-) create mode 100644 __mocks__/fake/supermind/SupermindRequestFaker.js create mode 100644 src/supermind/SupermindRequest.spec.tsx create mode 100644 src/supermind/__snapshots__/SupermindRequest.spec.tsx.snap diff --git a/__mocks__/fake/ActivitiesFaker.js b/__mocks__/fake/ActivitiesFaker.js index add871fd3..ed4aac5f1 100644 --- a/__mocks__/fake/ActivitiesFaker.js +++ b/__mocks__/fake/ActivitiesFaker.js @@ -1,65 +1,43 @@ - function load(count, container) { - let activities = [...Array(count)].map((_, i) => { const code = 'activityguid' + i; return { attachment_guid: false, blurb: false, - container_guid:code, - custom_data:false, - custom_type:false, + container_guid: code, + custom_data: false, + custom_type: false, rowKey: 'something' + i, - description:"Congratulations! ", - edited:"", - guid:code, - mature:false, - time_created: "1522036284", - ownerObj:{ - guid: "824853017709780997", - type: "user", + description: 'Congratulations! ', + edited: '', + guid: code, + mature: false, + time_created: '1522036284', + ownerObj: { + guid: '824853017709780997', + type: 'user', subtype: false, - time_created: "1522036284", - // getAvatarSource: () => { - // return { - // source:'http://thisisaurl' - // } - // } + time_created: '1522036284', }, - shouldBeBlured: jest.fn(), - message:"Message", - title:'TITLE', - owner_guid:"824853017709780997", - parent_guid:"838106762591510528", - perma_url:false, - thumbnail_src:false, - type:"activity", + message: 'Message', + title: 'TITLE', + owner_guid: '824853017709780997', + parent_guid: '838106762591510528', + perma_url: false, + thumbnail_src: false, + type: 'activity', wire_totals: { - tokens: 1000000000000000000 + tokens: 1000000000000000000, }, - containerObj: container - // _list: { - // viewed: { - // viewed: new Map([["1019155171608096768",true]]), - // addViewed: () => { - // return; - // } - // } - // }, - // getThumbSource: () => { - // return { - // source:'http://thisisaurl' - // } - // } - } + containerObj: container, + }; }); return { - 'activities': activities, + activities: activities, 'load-next': 'aaaaaa', - 'load-previous': 'aaaaaa' - } - + 'load-previous': 'aaaaaa', + }; } /** @@ -67,6 +45,15 @@ function load(count, container) { * @param {integer} count */ export function activitiesServiceFaker() { - return { load: load} + return { load: load }; } +/** + * Generate a single fake entity + * @param {object} containerObj optional entity container + * @returns + */ +export function fakeOne(containerObj) { + const resp = load(1, containerObj); + return resp.activities[0]; +} diff --git a/__mocks__/fake/supermind/SupermindRequestFaker.js b/__mocks__/fake/supermind/SupermindRequestFaker.js new file mode 100644 index 000000000..334706c80 --- /dev/null +++ b/__mocks__/fake/supermind/SupermindRequestFaker.js @@ -0,0 +1,19 @@ +import { fakeOne } from '../ActivitiesFaker'; + +export default function supermindRequestFaker() { + return { + guid: '1', + activity_guid: '1', + sender_guid: '1', + receiver_guid: '1', + status: 1, + payment_amount: 1, + payment_method: 1, + payment_txid: '', + created_timestamp: 1662991439, + expiry_threshold: 7 * 86400, + twitter_required: false, + reply_type: 1, + entity: fakeOne(), + }; +} diff --git a/__tests__/activity/components/Activity.js b/__tests__/activity/components/Activity.js index 3e882f5dc..22e25d6b9 100644 --- a/__tests__/activity/components/Activity.js +++ b/__tests__/activity/components/Activity.js @@ -14,7 +14,8 @@ import BottomContent from '../../../src/newsfeed/activity/BottomContent'; import MediaView from '../../../src/common/components/MediaView'; import ActivityModel from '../../../src/newsfeed/ActivityModel'; -import MindsVideoV2 from '../../../src/media/v2/mindsVideo/MindsVideo'; + +jest.mock('../../../src/common/services/session.service'); jest.mock('../../../src/media/v2/mindsVideo/MindsVideo', () => 'MindsVideoV2'); jest.mock( @@ -46,13 +47,10 @@ describe('Activity component', () => { }); it('renders correctly', async () => { - screen.update(); expect(screen).toMatchSnapshot(); }); it('should have a Media View', async () => { - screen.update(); - expect(screen.find(MediaView)).toHaveLength(1); }); diff --git a/__tests__/activity/components/__snapshots__/Activity.js.snap b/__tests__/activity/components/__snapshots__/Activity.js.snap index 91a6aaf0d..65c68ed46 100644 --- a/__tests__/activity/components/__snapshots__/Activity.js.snap +++ b/__tests__/activity/components/__snapshots__/Activity.js.snap @@ -96,17 +96,6 @@ exports[`Activity component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction] { - "calls": Array [ - Array [], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, @@ -201,17 +190,6 @@ exports[`Activity component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction] { - "calls": Array [ - Array [], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, @@ -312,17 +290,6 @@ exports[`Activity component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction] { - "calls": Array [ - Array [], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, @@ -441,17 +408,6 @@ exports[`Activity component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction] { - "calls": Array [ - Array [], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, @@ -564,17 +520,6 @@ exports[`Activity component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction] { - "calls": Array [ - Array [], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, @@ -694,17 +639,6 @@ exports[`Activity component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction] { - "calls": Array [ - Array [], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, @@ -808,17 +742,6 @@ exports[`Activity component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction] { - "calls": Array [ - Array [], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, diff --git a/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap b/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap index 287fd21a2..f3c7d7b5b 100644 --- a/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap +++ b/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap @@ -1319,22 +1319,6 @@ exports[`Activity screen component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction] { - "calls": Array [ - Array [], - Array [], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, @@ -1464,22 +1448,6 @@ exports[`Activity screen component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction] { - "calls": Array [ - Array [], - Array [], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, @@ -1609,22 +1577,6 @@ exports[`Activity screen component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction] { - "calls": Array [ - Array [], - Array [], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, @@ -1754,22 +1706,6 @@ exports[`Activity screen component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction] { - "calls": Array [ - Array [], - Array [], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, @@ -1914,22 +1850,6 @@ exports[`Activity screen component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction] { - "calls": Array [ - Array [], - Array [], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, diff --git a/__tests__/activity/components/__snapshots__/BottomContent.js.snap b/__tests__/activity/components/__snapshots__/BottomContent.js.snap index 2419f5289..a9742e0de 100644 --- a/__tests__/activity/components/__snapshots__/BottomContent.js.snap +++ b/__tests__/activity/components/__snapshots__/BottomContent.js.snap @@ -78,7 +78,6 @@ exports[`BottomContent component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction], "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, @@ -173,7 +172,6 @@ exports[`BottomContent component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction], "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, diff --git a/__tests__/common/components/ExplicitImage.js b/__tests__/common/components/ExplicitImage.js index e9335bfe0..f4b4ba026 100644 --- a/__tests__/common/components/ExplicitImage.js +++ b/__tests__/common/components/ExplicitImage.js @@ -1,28 +1,24 @@ import 'react-native'; import React from 'react'; -import { Text, TouchableOpacity } from "react-native"; import { shallow } from 'enzyme'; import ExplicitImage from '../../../src/common/components/explicit/ExplicitImage'; import { activitiesServiceFaker } from '../../../__mocks__/fake/ActivitiesFaker'; -import renderer from 'react-test-renderer'; +import ActivityModel from '../../../src/newsfeed/ActivityModel'; -describe('Explicit image component', () => { +jest.mock('../../../src/common/services/session.service'); - let user, comments, entity, screen; - beforeEach(() => { +describe('Explicit image component', () => { + let entity, screen; + it('renders correctly', async () => { let mockResponse = activitiesServiceFaker().load(1); - entity = mockResponse.activities[0]; + entity = ActivityModel.create(mockResponse.activities[0]); + entity.getUser = jest.fn(); entity.mature = true; - screen = shallow( - - ); - }); - it('renders correctly', async () => { - screen.update(); + screen = shallow(); expect(screen).toMatchSnapshot(); }); }); diff --git a/__tests__/common/components/__snapshots__/ExplicitImage.js.snap b/__tests__/common/components/__snapshots__/ExplicitImage.js.snap index 158bd07d5..dd87793b0 100644 --- a/__tests__/common/components/__snapshots__/ExplicitImage.js.snap +++ b/__tests__/common/components/__snapshots__/ExplicitImage.js.snap @@ -1,3 +1,20 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Explicit image component renders correctly 1`] = ``; +exports[`Explicit image component renders correctly 1`] = ` + +`; diff --git a/__tests__/common/components/__snapshots__/MediaView.js.snap b/__tests__/common/components/__snapshots__/MediaView.js.snap index 90bd0b7c3..03777f46b 100644 --- a/__tests__/common/components/__snapshots__/MediaView.js.snap +++ b/__tests__/common/components/__snapshots__/MediaView.js.snap @@ -34,7 +34,6 @@ exports[`Media view component renders correctly 1`] = ` "parent_guid": "838106762591510528", "perma_url": false, "rowKey": "something0", - "shouldBeBlured": [MockFunction], "subtype": "image", "thumbnail_src": false, "time_created": "1522036284", diff --git a/src/supermind/SuperMindRequest.tsx b/src/supermind/SuperMindRequest.tsx index 350fe6c11..2e64d886e 100644 --- a/src/supermind/SuperMindRequest.tsx +++ b/src/supermind/SuperMindRequest.tsx @@ -73,14 +73,15 @@ const InboundButtons = observer( const answer = React.useCallback(() => { navigation.navigate('Compose', { isRemind: true, - supermind: true, - mode: composerModes[request.reply_type], + supermindObject: request, + allowedMode: composerModes[request.reply_type], entity: request.entity, }); }, [navigation, request]); return ( + } + /> + + + + {i18n.t('supermind.getPaid')} + + {i18n.t('supermind.setMinimum')} + + {i18n.t('supermind.fansCant')} + + + + {/* TODO: add bank info component from supermind console branch */} + + + ); +}); + +/** + * Inputs (observer) + */ +const Inputs = observer( + ({ + fetchStore, + store, + }: { + fetchStore: FetchStore; + store: LocalStore; + }) => + fetchStore.loading ? ( + + ) : fetchStore.result ? ( + <> + { + store.setTokens(parseFloat(v) || 0); + }} + keyboardType="numeric" + value={store.tokens.toString()} + noBottomBorder + autoFocus + /> + { + store.setCash(parseFloat(v)); + }} + value={store.cash.toString()} + /> + + ) : ( + fetchStore.fetch() || 0} + message={i18n.t('errorMessage')} + /> + ), +); + +/** + * Local store + */ +const createStore = ({ navigation }) => { + return { + tokens: 1, + cash: 10, + setCash(v: number) { + this.cash = v; + }, + setTokens(v: number) { + this.tokens = v; + }, + async submit() { + try { + await apiService.post('api/v3/supermind/settings', { + min_cash: this.cash, + min_offchain_tokens: this.tokens, + }); + } catch (error) { + if (error instanceof Error) { + showNotification(error.message); + } else { + showNotification(i18n.t('error')); + } + return; + } + showNotification(i18n.t('settings.saved')); + navigation.goBack(); + return [this.cash, this.tokens]; + }, + }; +}; + +type LocalStore = ReturnType; diff --git a/src/settings/screens/__snapshots__/SupermindSettingsScreen.spec.tsx.snap b/src/settings/screens/__snapshots__/SupermindSettingsScreen.spec.tsx.snap new file mode 100644 index 000000000..3e3e62fa9 --- /dev/null +++ b/src/settings/screens/__snapshots__/SupermindSettingsScreen.spec.tsx.snap @@ -0,0 +1,1446 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Supermind settings should load data and render correctly 1`] = ` + + + + + + + + + + 󰅁 + + + + + + + Supermind + + + + + + + + + + Save + + + + + + + + + + + + + Get paid to reply to your fans. + + + + + Set your minimum offer amount + + + + + Fans can’t send you Supermind offers for less than your minimum offer amount. + + + + + + + + + +`; + +exports[`Supermind settings should load data and render correctly 2`] = ` + + + + + + + + + + 󰅁 + + + + + + + Supermind + + + + + + + + + + Save + + + + + + + + + + + + + Get paid to reply to your fans. + + + + + Set your minimum offer amount + + + + + Fans can’t send you Supermind offers for less than your minimum offer amount. + + + + + + + + + + Tokens + + + + + + + + + + + + + + USD + + + + + + + + + + +`; + +exports[`Supermind settings should update the store and submit 1`] = ` + + + + + + + + + + 󰅁 + + + + + + + Supermind + + + + + + + + + + Save + + + + + + + + + + + + + Get paid to reply to your fans. + + + + + Set your minimum offer amount + + + + + Fans can’t send you Supermind offers for less than your minimum offer amount. + + + + + + + + + +`; From 38e5052948a60dee6e9f3327f80e82674abc63d9 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Tue, 20 Sep 2022 14:14:54 +0200 Subject: [PATCH 70/89] (feat) official stripe sdk --- android/app/build.gradle | 2 - android/app/src/main/AndroidManifest.xml | 48 --- android/build.gradle | 3 - android/settings.gradle | 2 - package.json | 5 +- react-native.config.js | 5 - setupTests.js | 4 + src/common/components/SelectorV2.tsx | 2 +- .../StripeCardSelector.spec.tsx | 12 + .../StripeCardSelector.tsx | 149 +++++++++ .../StripeCardSelector.spec.tsx.snap | 183 +++++++++++ .../createCardSelectorStore.spec.ts | 31 ++ .../createCardSelectorStore.ts | 150 +++++++++ src/common/services/stripe.service.ts | 8 +- src/compose/SupermindComposeScreen.tsx | 2 +- src/settings/screens/BillingScreen.tsx | 4 +- src/upgrade/UpgradeScreen.tsx | 3 +- src/wire/methods/BtcPayment.tsx | 76 ----- src/wire/methods/PaymentMethodIcon.tsx | 35 --- src/wire/methods/PaymentMethodSelector.tsx | 72 ----- src/wire/methods/StripeCardCarousel.tsx | 223 -------------- src/wire/methods/StripeCardSelector.tsx | 218 -------------- src/wire/methods/v2/StripeCardSelector.tsx | 284 ------------------ src/wire/v2/TokensForm.tsx | 6 +- src/wire/v2/UsdForm.tsx | 20 +- src/wire/v2/tiers/JoinMembership.tsx | 2 +- src/wire/v2/tiers/JoinMembershipScreen.tsx | 2 +- yarn.lock | 39 +-- 28 files changed, 566 insertions(+), 1024 deletions(-) create mode 100644 src/common/components/stripe-card-selector/StripeCardSelector.spec.tsx create mode 100644 src/common/components/stripe-card-selector/StripeCardSelector.tsx create mode 100644 src/common/components/stripe-card-selector/__snapshots__/StripeCardSelector.spec.tsx.snap create mode 100644 src/common/components/stripe-card-selector/createCardSelectorStore.spec.ts create mode 100644 src/common/components/stripe-card-selector/createCardSelectorStore.ts delete mode 100644 src/wire/methods/BtcPayment.tsx delete mode 100644 src/wire/methods/PaymentMethodIcon.tsx delete mode 100644 src/wire/methods/PaymentMethodSelector.tsx delete mode 100644 src/wire/methods/StripeCardCarousel.tsx delete mode 100644 src/wire/methods/StripeCardSelector.tsx delete mode 100644 src/wire/methods/v2/StripeCardSelector.tsx diff --git a/android/app/build.gradle b/android/app/build.gradle index fe88f9673..9bf1b9ad2 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -311,10 +311,8 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.1.0-rc01' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02' - implementation project(':tipsi-stripe') implementation project(':reactnativenotifications') - implementation 'com.google.firebase:firebase-core:16.0.7' implementation 'com.google.firebase:firebase-messaging:17.3.3' // IMPORTANT: fix the crash when the app receives a push notification diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index d124c9297..095398485 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -61,54 +61,6 @@ tools:node="merge" /> - - - - - - - - - - - - { jest.mock('react-native-silent-switch'); jest.mock('react-native-mmkv-storage'); global.__reanimatedWorkletInit = jest.fn(); + +jest.mock('@stripe/stripe-react-native', () => + require('@stripe/stripe-react-native/jest/mock.js'), +); diff --git a/src/common/components/SelectorV2.tsx b/src/common/components/SelectorV2.tsx index 51fe08133..ffcaf629a 100644 --- a/src/common/components/SelectorV2.tsx +++ b/src/common/components/SelectorV2.tsx @@ -154,8 +154,8 @@ const SelectorV2: ForwardRefRenderFunction = ( item.onPress(); } else { onItemSelect(item); + close(); } - close(); }; const textStyle = isSelected(item) diff --git a/src/common/components/stripe-card-selector/StripeCardSelector.spec.tsx b/src/common/components/stripe-card-selector/StripeCardSelector.spec.tsx new file mode 100644 index 000000000..39b4d47e3 --- /dev/null +++ b/src/common/components/stripe-card-selector/StripeCardSelector.spec.tsx @@ -0,0 +1,12 @@ +import { render, screen } from '@testing-library/react-native'; +import * as React from 'react'; +import StripeCardSelector from './StripeCardSelector'; + +jest.mock('../InputSelectorV2', () => () => null); + +describe('StripeCardSelector', () => { + test('should render correctly', () => { + render(); + expect(screen.toJSON()).toMatchSnapshot(); + }); +}); diff --git a/src/common/components/stripe-card-selector/StripeCardSelector.tsx b/src/common/components/stripe-card-selector/StripeCardSelector.tsx new file mode 100644 index 000000000..8fb036f57 --- /dev/null +++ b/src/common/components/stripe-card-selector/StripeCardSelector.tsx @@ -0,0 +1,149 @@ +import { useBottomSheetModal } from '@gorhom/bottom-sheet'; +import { CardField, useStripe } from '@stripe/stripe-react-native'; +import { observer, useLocalStore } from 'mobx-react'; +import React, { forwardRef, useRef } from 'react'; +import { InteractionManager } from 'react-native'; +import { showNotification } from '../../../../AppMessages'; +import ThemedStyles from '../../../styles/ThemedStyles'; +import { StripeCard } from '../../../wire/WireTypes'; +import i18n from '../../services/i18n.service'; +import { Row } from '../../ui'; +import { BottomSheetButton, BottomSheetModal } from '../bottom-sheet'; +import InputSelector from '../InputSelectorV2'; +import createCardSelectorStore, { + selectIdExtractor, + selectValueExtractor, +} from './createCardSelectorStore'; + +type StripeCardSelectorProps = { + selectedCardId?: string; + onCardSelected: (card: StripeCard) => void; + info?: string; + error?: string; +}; + +const StripeCardSelector = observer( + ({ onCardSelected, selectedCardId }: StripeCardSelectorProps) => { + const store = useLocalStore(createCardSelectorStore, { + onCardSelected, + selectedCardId, + }); + const bottomSheetRef = useRef(null); + const { confirmSetupIntent } = useStripe(); + + React.useEffect(() => { + store.init(); + }, [store]); + + const close = React.useCallback(() => { + bottomSheetRef.current?.dismiss(); + }, []); + const show = React.useCallback(() => { + bottomSheetRef.current?.present(); + }, []); + + const onComplete = async () => { + store.setInProgress(true); + + const { error, setupIntent } = await confirmSetupIntent(store.intentKey, { + paymentMethodType: 'Card', + }); + if (setupIntent) { + await store.saveCard(); + close(); + } else { + store.setInProgress(false); + console.log(error); + showNotification( + error?.localizedMessage || i18n.t('errorMessage'), + 'warning', + 3000, + 'top', + ); + } + }; + + return ( + <> + + InteractionManager.runAfterInteractions(() => show()), + }, + ]} + valueExtractor={selectValueExtractor} + keyExtractor={selectIdExtractor} + textStyle={ThemedStyles.style.fontXL} + backdropOpacity={0.9} + /> + + + ); + }, +); + +const NewCardBottomSheet = observer( + forwardRef(({ store, onComplete }, ref) => { + const bottomSheet = useBottomSheetModal(); + return ( + + + + + {!store.cardDetailsComplete && ( + bottomSheet.dismiss()} + /> + )} + {store.cardDetailsComplete && ( + + )} + + ); + }), +); + +const cardStyle = { + borderWidth: 1, + borderRadius: 8, + fontSize: 14, +}; + +const styles = ThemedStyles.create({ + container: ['rowJustifySpaceBetween', 'padding2x'], + cardContainer: [{ width: '90%', height: 200 }], +}); + +export default StripeCardSelector; diff --git a/src/common/components/stripe-card-selector/__snapshots__/StripeCardSelector.spec.tsx.snap b/src/common/components/stripe-card-selector/__snapshots__/StripeCardSelector.spec.tsx.snap new file mode 100644 index 000000000..ad1594d08 --- /dev/null +++ b/src/common/components/stripe-card-selector/__snapshots__/StripeCardSelector.spec.tsx.snap @@ -0,0 +1,183 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`StripeCardSelector should render correctly 1`] = ` + + + + CardField + + + + + + + + + Cancel + + + + + + + +`; diff --git a/src/common/components/stripe-card-selector/createCardSelectorStore.spec.ts b/src/common/components/stripe-card-selector/createCardSelectorStore.spec.ts new file mode 100644 index 000000000..16755e826 --- /dev/null +++ b/src/common/components/stripe-card-selector/createCardSelectorStore.spec.ts @@ -0,0 +1,31 @@ +import createCardSelectorStore from './createCardSelectorStore'; + +describe('createCardSelectorStore', () => { + let store; + + beforeEach(() => { + store = createCardSelectorStore({ + onCardSelected: jest.fn(), + selectedCardId: '', + }); + store.init(); + }); + + it('should have the correct default values', () => { + expect(store.loaded).toBe(false); + expect(store.cards.length).toBe(0); + expect(store.current).toBe(0); + expect(store.inProgress).toBe(false); + expect(store.cardDetails).toEqual({}); + expect(store.intentKey).toBe(''); + expect(store.intentId).toBe(''); + }); + + // TODO: init + // TODO: initStripe + // TODO: getSetupIntent + // TODO: loadCards + // TODO: selectCard + // TODO: saveCard + // TODO: onCardChange +}); diff --git a/src/common/components/stripe-card-selector/createCardSelectorStore.ts b/src/common/components/stripe-card-selector/createCardSelectorStore.ts new file mode 100644 index 000000000..13b6f0fa0 --- /dev/null +++ b/src/common/components/stripe-card-selector/createCardSelectorStore.ts @@ -0,0 +1,150 @@ +import { CardFieldInput } from '@stripe/stripe-react-native'; +import { showNotification } from '../../../../AppMessages'; +import { StripeCard } from '../../../wire/WireTypes'; +import api, { ApiResponse } from '../../services/api.service'; +import i18nService from '../../services/i18n.service'; +import logService from '../../services/log.service'; +import { initStripe } from '../../services/stripe.service'; + +interface StripeResponse extends ApiResponse { + paymentmethods: Array; +} + +interface IntentResponse extends ApiResponse { + intent: { + client_secret: string; + id: string; + }; +} + +export const selectValueExtractor = (item: any) => { + if (!item) { + return; + } + + if (item.name) { + return item.name; + } + + return ( + item.card_brand.toUpperCase() + + ' ending in ' + + item.card_last4 + + ' - Exp: ' + + item.card_expires + ); +}; +export const selectIdExtractor = item => item.id; + +const createCardSelectorStore = ({ onCardSelected, selectedCardId }) => ({ + loaded: false, + cards: [] as Array, + current: 0, + inProgress: false, + cardDetails: {} as CardFieldInput.Details, + intentKey: '', + intentId: '', + async init() { + await initStripe(); + await this.loadCards(); + this.getSetupIntent(); + this.setLoaded(true); + }, + setInProgress(inProgress: boolean) { + this.inProgress = inProgress; + }, + setLoaded(loaded: boolean) { + this.loaded = loaded; + }, + setCards(cards: Array) { + this.cards = cards; + }, + get currentCard() { + return this.cards[this.current]; + }, + get currentCardTitle() { + return selectValueExtractor(this.currentCard); + }, + get currentCardId() { + return this.currentCard?.id; + }, + async loadCards() { + try { + const result: StripeResponse = await api.get( + 'api/v2/payments/stripe/paymentmethods', + ); + + if (result?.paymentmethods) { + let defaultSelectedCard; + if (result.paymentmethods.length > 0) { + defaultSelectedCard = result.paymentmethods[0]?.id; + + if ( + selectedCardId && + result.paymentmethods.find(p => p.id === selectedCardId) + ) { + defaultSelectedCard = result.paymentmethods.find( + p => p.id === selectedCardId, + )!; + } + } + + const cards = result.paymentmethods.reverse(); + + if (defaultSelectedCard) { + this.selectCard(defaultSelectedCard); + } + this.setCards(cards); + this.setLoaded(true); + } + } catch (err) { + console.log(err); + } + }, + /** + * Get setup intent from server + */ + async getSetupIntent() { + try { + const { intent }: IntentResponse = await api.put( + 'api/v2/payments/stripe/intents/setup', + ); + this.intentKey = intent.client_secret; + this.intentId = intent.id; + } catch (err) { + showNotification(i18nService.t('cantReachServer'), 'danger'); + } + }, + selectCard(cardId: string) { + const index = this.cards.findIndex(c => c.id === cardId); + if (index >= 0) { + onCardSelected?.(this.cards[index]); + this.current = index; + } + }, + async saveCard(): Promise { + try { + await api.post( + 'api/v2/payments/stripe/paymentmethods/apply', + { + intent_id: this.intentId, + }, + ); + this.intentKey = ''; + this.setInProgress(false); + this.loadCards(); + } catch (err) { + logService.exception('[Stripe saveCard]', err); + this.setInProgress(false); + } + }, + onCardChange(cardDetails: CardFieldInput.Details) { + this.cardDetails = cardDetails; + }, + get cardDetailsComplete() { + return this.cardDetails.complete; + }, +}); + +export default createCardSelectorStore; +export type CardSelectorStore = ReturnType; diff --git a/src/common/services/stripe.service.ts b/src/common/services/stripe.service.ts index a009056e9..205b78b27 100644 --- a/src/common/services/stripe.service.ts +++ b/src/common/services/stripe.service.ts @@ -1,6 +1,5 @@ -//@ts-nocheck -import stripe from 'tipsi-stripe'; import mindsConfigService from './minds-config.service'; +import { initStripe as stripeInit } from '@stripe/stripe-react-native'; let intialized = false; @@ -10,10 +9,7 @@ export const initStripe = async () => { const settings = mindsConfigService.getSettings(); - await stripe.setOptions({ + await stripeInit({ publishableKey: settings.stripe_key, - //androidPayMode: 'test', // Android only }); }; - -export default stripe; diff --git a/src/compose/SupermindComposeScreen.tsx b/src/compose/SupermindComposeScreen.tsx index 2523b263d..a619e25bf 100644 --- a/src/compose/SupermindComposeScreen.tsx +++ b/src/compose/SupermindComposeScreen.tsx @@ -8,6 +8,7 @@ import InputBase from '../common/components/InputBase'; import InputContainer from '../common/components/InputContainer'; import InputSelectorV2 from '../common/components/InputSelectorV2'; import MenuItem from '../common/components/menus/MenuItem'; +import StripeCardSelector from '../common/components/stripe-card-selector/StripeCardSelector'; import TopbarTabbar from '../common/components/topbar-tabbar/TopbarTabbar'; import i18nService from '../common/services/i18n.service'; import { Button, Icon, ModalFullScreen } from '../common/ui'; @@ -15,7 +16,6 @@ import { IS_IOS } from '../config/Config'; import NavigationService from '../navigation/NavigationService'; import { RootStackParamList } from '../navigation/NavigationTypes'; import ThemedStyles from '../styles/ThemedStyles'; -import StripeCardSelector from '../wire/methods/v2/StripeCardSelector'; const showError = (error: string) => showNotification(error, 'danger', undefined); diff --git a/src/settings/screens/BillingScreen.tsx b/src/settings/screens/BillingScreen.tsx index 37e752aca..62f6ef909 100644 --- a/src/settings/screens/BillingScreen.tsx +++ b/src/settings/screens/BillingScreen.tsx @@ -1,10 +1,10 @@ //@ts-nocheck import React, { Component } from 'react'; -import { View } from 'react-native'; +import { View, Text } from 'react-native'; +import StripeCardSelector from '../../common/components/stripe-card-selector/StripeCardSelector'; import i18n from '../../common/services/i18n.service'; -import StripeCardSelector from '../../wire/methods/StripeCardSelector'; import ThemedStyles from '../../styles/ThemedStyles'; import MText from '../../common/components/MText'; diff --git a/src/upgrade/UpgradeScreen.tsx b/src/upgrade/UpgradeScreen.tsx index dda5bee26..667280ecd 100644 --- a/src/upgrade/UpgradeScreen.tsx +++ b/src/upgrade/UpgradeScreen.tsx @@ -4,8 +4,6 @@ import { StyleSheet, View, Platform } from 'react-native'; import ThemedStyles from '../styles/ThemedStyles'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import i18n from '../common/services/i18n.service'; -import StripeCardSelector from '../wire/methods/v2/StripeCardSelector'; - import { UserError } from '../common/UserError'; import FitScrollView from '../common/components/FitScrollView'; import { useStores } from '../common/hooks/use-stores'; @@ -19,6 +17,7 @@ import { } from './types'; import PlanOptions from './PlanOptions'; import { useDimensions } from '@react-native-community/hooks'; +import StripeCardSelector from '../common/components/stripe-card-selector/StripeCardSelector'; import UpgradeScreenPlaceHolder from './UpgradeScreenPlaceHolder'; import { Button, Column, H3 } from '~ui'; import { PRO_PLUS_SUBSCRIPTION_ENABLED } from '~/config/Config'; diff --git a/src/wire/methods/BtcPayment.tsx b/src/wire/methods/BtcPayment.tsx deleted file mode 100644 index f60af4260..000000000 --- a/src/wire/methods/BtcPayment.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import * as React from 'react'; -import { View, Linking } from 'react-native'; -import QRCode from 'react-native-qrcode-svg'; - -import viewportPercentage from '../../common/helpers/viewportPercentage'; -import Button from '../../common/components/Button'; -import i18nService from '../../common/services/i18n.service'; -import ThemedStyles from '../../styles/ThemedStyles'; -import MText from '../../common/components/MText'; - -type PropsType = { - address: string; - amount: number; - onCancel?: Function; -}; - -/** - * Btc Payment - */ -export default class BtcPayment extends React.PureComponent { - url = ''; - - /** - * Open bitcoin link - */ - openLink = () => { - Linking.openURL(this.url); - }; - - cancel = () => { - if (this.props.onCancel) { - this.props.onCancel(); - } - }; - - /** - * Render - */ - render(): React.ReactNode { - this.url = `bitcoin:${this.props.address}?amount=${this.props.amount}`; - const theme = ThemedStyles.style; - return ( - - - Tap to send{' '} - {this.props.amount} BTC to - - - {this.props.address} - - - - - ); - }, -); +// const OutboundButtons = observer( +// ({ request }: { request: SupermindRequestModel }) => { +// if (request.status !== SupermindRequestStatus.CREATED) { +// return null; +// } +// return ( +// +// +// +// ); +// }, +// ); diff --git a/src/supermind/__snapshots__/SupermindRequest.spec.tsx.snap b/src/supermind/__snapshots__/SupermindRequest.spec.tsx.snap index 8ee503ec9..669127f69 100644 --- a/src/supermind/__snapshots__/SupermindRequest.spec.tsx.snap +++ b/src/supermind/__snapshots__/SupermindRequest.spec.tsx.snap @@ -1227,127 +1227,5 @@ exports[`SupermindRequest should render outbound 1`] = ` - - - - - - - - Cancel Offer - - - - - `; From e58e61ceaf709256e78398fac63303cc65b2a53c Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Thu, 22 Sep 2022 12:21:36 -0300 Subject: [PATCH 80/89] (feat) add bank info to supermind settings and navigation from console --- .../__snapshots__/CommentsAction.js.snap | 1 - .../__snapshots__/ThumbDownAction.js.snap | 2 +- .../__snapshots__/ThumbUpAction.js.snap | 2 +- .../screens/SupermindSettingsScreen.spec.tsx | 1 + .../screens/SupermindSettingsScreen.tsx | 3 +- .../SupermindSettingsScreen.spec.tsx.snap | 207 +++++++++++++++++- src/supermind/AddBankInformation.tsx | 12 +- src/supermind/SupermindConsoleScreen.tsx | 2 +- 8 files changed, 220 insertions(+), 10 deletions(-) diff --git a/__tests__/activity/components/actions/__snapshots__/CommentsAction.js.snap b/__tests__/activity/components/actions/__snapshots__/CommentsAction.js.snap index ff8b0ca5c..3232361ae 100644 --- a/__tests__/activity/components/actions/__snapshots__/CommentsAction.js.snap +++ b/__tests__/activity/components/actions/__snapshots__/CommentsAction.js.snap @@ -28,7 +28,6 @@ exports[`Comment action component renders correctly 1`] = ` "parent_guid": "838106762591510528", "perma_url": false, "rowKey": "something0", - "shouldBeBlured": [MockFunction], "thumbnail_src": false, "time_created": "1522036284", "title": "TITLE", diff --git a/__tests__/activity/components/actions/__snapshots__/ThumbDownAction.js.snap b/__tests__/activity/components/actions/__snapshots__/ThumbDownAction.js.snap index 6a73e6da9..c62b8a795 100644 --- a/__tests__/activity/components/actions/__snapshots__/ThumbDownAction.js.snap +++ b/__tests__/activity/components/actions/__snapshots__/ThumbDownAction.js.snap @@ -64,6 +64,7 @@ exports[`Thumb action component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", @@ -120,7 +121,6 @@ exports[`Thumb action component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction], "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, diff --git a/__tests__/activity/components/actions/__snapshots__/ThumbUpAction.js.snap b/__tests__/activity/components/actions/__snapshots__/ThumbUpAction.js.snap index 50daac86d..c8aba6ad1 100644 --- a/__tests__/activity/components/actions/__snapshots__/ThumbUpAction.js.snap +++ b/__tests__/activity/components/actions/__snapshots__/ThumbUpAction.js.snap @@ -77,6 +77,7 @@ exports[`Thumb action component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", @@ -133,7 +134,6 @@ exports[`Thumb action component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction], "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, diff --git a/src/settings/screens/SupermindSettingsScreen.spec.tsx b/src/settings/screens/SupermindSettingsScreen.spec.tsx index 333c24dde..25261c965 100644 --- a/src/settings/screens/SupermindSettingsScreen.spec.tsx +++ b/src/settings/screens/SupermindSettingsScreen.spec.tsx @@ -8,6 +8,7 @@ import { import SupermindSettingsScreen from './SupermindSettingsScreen'; import apiService from '~/common/services/api.service'; +jest.mock('~/common/hooks/use-stores'); jest.mock('~/common/services/api.service'); const mockedApi = apiService as jest.Mocked; diff --git a/src/settings/screens/SupermindSettingsScreen.tsx b/src/settings/screens/SupermindSettingsScreen.tsx index 54e3962bb..632ffbd0d 100644 --- a/src/settings/screens/SupermindSettingsScreen.tsx +++ b/src/settings/screens/SupermindSettingsScreen.tsx @@ -17,6 +17,7 @@ import { ScreenHeader, ScreenSection, } from '~/common/ui'; +import AddBankInformation from '~/supermind/AddBankInformation'; type Settings = { min_offchain_tokens: number; @@ -63,7 +64,7 @@ export default observer(function SupermindSettingsScreen({ navigation }) { - {/* TODO: add bank info component from supermind console branch */} + ); diff --git a/src/settings/screens/__snapshots__/SupermindSettingsScreen.spec.tsx.snap b/src/settings/screens/__snapshots__/SupermindSettingsScreen.spec.tsx.snap index 3e3e62fa9..4432a8beb 100644 --- a/src/settings/screens/__snapshots__/SupermindSettingsScreen.spec.tsx.snap +++ b/src/settings/screens/__snapshots__/SupermindSettingsScreen.spec.tsx.snap @@ -14,7 +14,9 @@ exports[`Supermind settings should load data and render correctly 1`] = ` ] } > - + + + + + Add your bank information + + + Start receiving cash payouts by adding your bank details. + + + @@ -398,7 +465,9 @@ exports[`Supermind settings should load data and render correctly 2`] = ` ] } > - + + + + + Add your bank information + + + Start receiving cash payouts by adding your bank details. + + + @@ -1075,7 +1209,9 @@ exports[`Supermind settings should update the store and submit 1`] = ` ] } > - + + + + + Add your bank information + + + Start receiving cash payouts by adding your bank details. + + + diff --git a/src/supermind/AddBankInformation.tsx b/src/supermind/AddBankInformation.tsx index febb93973..8f9701393 100644 --- a/src/supermind/AddBankInformation.tsx +++ b/src/supermind/AddBankInformation.tsx @@ -7,10 +7,14 @@ import i18nService from '~/common/services/i18n.service'; import { B2, PressableLine, Spacer } from '~/common/ui'; import ThemedStyles from '~/styles/ThemedStyles'; +type PropsType = { + borderTop?: boolean; +}; + /** * Add bank information banner */ -function AddBankInformation() { +function AddBankInformation({ borderTop = false }: PropsType) { const { wallet } = useStores(); const navigation = useNavigation(); @@ -19,7 +23,7 @@ function AddBankInformation() { } return ( navigation.navigate('BankInfoScreen', { walletStore: wallet }) }> @@ -39,3 +43,7 @@ export const borderBottomStyle = ThemedStyles.combine( 'bcolorPrimaryBorder', 'borderBottomHair', ); +export const borderTopStyle = ThemedStyles.combine( + 'bcolorPrimaryBorder', + 'borderTopHair', +); diff --git a/src/supermind/SupermindConsoleScreen.tsx b/src/supermind/SupermindConsoleScreen.tsx index e74657951..3cb417c55 100644 --- a/src/supermind/SupermindConsoleScreen.tsx +++ b/src/supermind/SupermindConsoleScreen.tsx @@ -41,7 +41,7 @@ export default function SupermindConsoleScreen({ navigation }) { extra={ navigation.navigate('Settings')} + onPress={() => navigation.navigate('SupermindSettingsScreen')} /> } back From 4872228c797dcb467142fde1c24e912bfe838583 Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Thu, 22 Sep 2022 13:25:44 -0300 Subject: [PATCH 81/89] (chore) update snapshot --- .../components/actions/__snapshots__/CommentsAction.js.snap | 1 - .../components/actions/__snapshots__/ThumbDownAction.js.snap | 2 +- .../components/actions/__snapshots__/ThumbUpAction.js.snap | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/__tests__/activity/components/actions/__snapshots__/CommentsAction.js.snap b/__tests__/activity/components/actions/__snapshots__/CommentsAction.js.snap index ff8b0ca5c..3232361ae 100644 --- a/__tests__/activity/components/actions/__snapshots__/CommentsAction.js.snap +++ b/__tests__/activity/components/actions/__snapshots__/CommentsAction.js.snap @@ -28,7 +28,6 @@ exports[`Comment action component renders correctly 1`] = ` "parent_guid": "838106762591510528", "perma_url": false, "rowKey": "something0", - "shouldBeBlured": [MockFunction], "thumbnail_src": false, "time_created": "1522036284", "title": "TITLE", diff --git a/__tests__/activity/components/actions/__snapshots__/ThumbDownAction.js.snap b/__tests__/activity/components/actions/__snapshots__/ThumbDownAction.js.snap index 6a73e6da9..c62b8a795 100644 --- a/__tests__/activity/components/actions/__snapshots__/ThumbDownAction.js.snap +++ b/__tests__/activity/components/actions/__snapshots__/ThumbDownAction.js.snap @@ -64,6 +64,7 @@ exports[`Thumb action component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", @@ -120,7 +121,6 @@ exports[`Thumb action component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction], "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, diff --git a/__tests__/activity/components/actions/__snapshots__/ThumbUpAction.js.snap b/__tests__/activity/components/actions/__snapshots__/ThumbUpAction.js.snap index 50daac86d..c8aba6ad1 100644 --- a/__tests__/activity/components/actions/__snapshots__/ThumbUpAction.js.snap +++ b/__tests__/activity/components/actions/__snapshots__/ThumbUpAction.js.snap @@ -77,6 +77,7 @@ exports[`Thumb action component renders correctly 1`] = ` "impressions": 0, "isOwner": [Function], "is_visible": false, + "listenForMetricsDebounced": [Function], "mature": false, "mature_visibility": false, "message": "Message", @@ -133,7 +134,6 @@ exports[`Thumb action component renders correctly 1`] = ` "quotes": 0, "reminds": 0, "rowKey": "something0", - "shouldBeBlured": [MockFunction], "subtype": "", "thumbnail_src": false, "thumbs:down:count": undefined, From 15f478154811d7a86ac242a653575ed7b2715225 Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Thu, 22 Sep 2022 16:26:57 -0300 Subject: [PATCH 82/89] (feat) multi-image: allow multi selection on the gallery, check repeated, sync errors with the web --- .../__snapshots__/ActivityScreen.js.snap | 2 ++ locales/en.json | 1 + src/comments/v2/CommentsStore.ts | 4 ++- src/common/services/attachment.service.ts | 21 ++++++++++---- src/common/services/image-picker.service.ts | 6 +++- src/common/stores/AttachmentStore.ts | 4 +++ src/common/stores/MultiAttachmentStore.ts | 23 +++++++++++++++ src/compose/MediaPreview.tsx | 9 ++++-- src/compose/createComposeStore.ts | 29 ++++++++++++++----- 9 files changed, 83 insertions(+), 16 deletions(-) diff --git a/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap b/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap index 909984521..46ca1b37b 100644 --- a/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap +++ b/__tests__/activity/components/__snapshots__/ActivityScreen.js.snap @@ -1769,7 +1769,9 @@ exports[`Activity screen component renders correctly 1`] = ` "filename": "", "guid": "", "height": 1, + "localIdentifier": "", "onClear": undefined, + "path": "", "transcoding": false, "width": 1, }, diff --git a/locales/en.json b/locales/en.json index f32b8256d..cca76a27e 100644 --- a/locales/en.json +++ b/locales/en.json @@ -273,6 +273,7 @@ }, "capture": { "discardPost": "Discard this post?", + "max4Images": "You may not upload more than 4 images", "discardPostDescription": "If you discard you’ll lose this post", "keepEditing": "Keep Editing", "yesDiscard": "Yes, Discard", diff --git a/src/comments/v2/CommentsStore.ts b/src/comments/v2/CommentsStore.ts index 3a6b4f476..c91b70b19 100644 --- a/src/comments/v2/CommentsStore.ts +++ b/src/comments/v2/CommentsStore.ts @@ -649,7 +649,9 @@ export default class CommentsStore { // nothing selected if (!response) return; - await this.attachment.attachMedia(response); + const media = Array.isArray(response) ? response[0] : response; + + await this.attachment.attachMedia(media); } catch (err) { logService.exception('[CommentsStore] gallery', err); } diff --git a/src/common/services/attachment.service.ts b/src/common/services/attachment.service.ts index 3918e081c..62ba7798d 100644 --- a/src/common/services/attachment.service.ts +++ b/src/common/services/attachment.service.ts @@ -1,5 +1,5 @@ import api, { ApiResponse } from './api.service'; -import imagePicker, { MediaType } from './image-picker.service'; +import imagePicker, { CustomImage, MediaType } from './image-picker.service'; import Cancelable from 'promise-cancelable'; import logService from './log.service'; import imageManipulatorService from './image-manipulator.service'; @@ -207,15 +207,27 @@ class AttachmentService { * Open gallery * @param {string} mediaType photo or video (or mixed only ios) */ - async gallery(mediaType: MediaType = 'photo', crop = true) { - const response = await imagePicker.launchImageLibrary(mediaType, crop); + async gallery(mediaType: MediaType = 'photo', crop = true, maxFiles = 1) { + const response = await imagePicker.launchImageLibrary( + mediaType, + crop, + maxFiles, + ); if (!response) { return null; } - const media = Array.isArray(response) ? response[0] : response; + if (Array.isArray(response)) { + return response.length > maxFiles + ? response.slice(0, maxFiles).map(this.fixMedia) + : response.map(this.fixMedia); + } else { + return this.fixMedia(response); + } + } + fixMedia(media: CustomImage) { if (!media.type) { if (!media.width) { media.type = 'video/mp4'; @@ -223,7 +235,6 @@ class AttachmentService { media.type = 'image/gif'; } } - return media; } } diff --git a/src/common/services/image-picker.service.ts b/src/common/services/image-picker.service.ts index b0c5f4081..002393747 100644 --- a/src/common/services/image-picker.service.ts +++ b/src/common/services/image-picker.service.ts @@ -81,11 +81,12 @@ class ImagePickerService { async launchImageLibrary( type: MediaType = 'photo', crop = true, + maxFiles = 1, ): Promise { // check permissions await this.checkGalleryPermissions(); - const opt = this.buildOptions(type, crop); + const opt = this.buildOptions(type, crop, false, maxFiles); return this.returnCustom(ImagePicker.openPicker(opt)); } @@ -201,8 +202,11 @@ class ImagePickerService { type: MediaType, crop: boolean = true, cropperCircleOverlay: boolean = false, + maxFiles: number = 1, ): Options { return { + multiple: maxFiles > 1, + maxFiles, mediaType: type, cropping: crop && type !== 'video', showCropGuidelines: false, diff --git a/src/common/stores/AttachmentStore.ts b/src/common/stores/AttachmentStore.ts index 3efb42da2..ceca54517 100644 --- a/src/common/stores/AttachmentStore.ts +++ b/src/common/stores/AttachmentStore.ts @@ -32,8 +32,10 @@ export default class AttachmentStore { @observable type = ''; @observable license = 'all-rights-reserved'; + localIdentifier?: string = ''; guid = ''; filename? = ''; + path? = ''; transcoding = false; width: number = 1; height: number = 1; @@ -111,6 +113,8 @@ export default class AttachmentStore { this.filename = media.filename; this.width = media.width; this.height = media.height; + this.localIdentifier = media.localIdentifier; + this.path = media.path; try { const resizedMedia = await attachmentService.processMedia(media); diff --git a/src/common/stores/MultiAttachmentStore.ts b/src/common/stores/MultiAttachmentStore.ts index af8515fdc..dcb4cde14 100644 --- a/src/common/stores/MultiAttachmentStore.ts +++ b/src/common/stores/MultiAttachmentStore.ts @@ -1,5 +1,6 @@ import { showNotification } from 'AppMessages'; import { action, computed, observable } from 'mobx'; +import { IS_IOS } from '~/config/Config'; import i18n from '../services/i18n.service'; import AttachmentStore, { Media } from './AttachmentStore'; @@ -70,6 +71,9 @@ export default class MultiAttachmentStore { @action attachMedia(media: Media, extra: any) { if (this.attachments.length === this.max) { + if (media.type.startsWith('image')) { + showNotification(i18n.t('capture.max4Images')); + } return false; } @@ -83,11 +87,30 @@ export default class MultiAttachmentStore { return false; } + // check if already exist + if (this.mediaExists(media)) { + return false; + } + const store = this.addAttachment(); store.attachMedia(media, extra); return store; } + /** + * Checks if a media is already attached + * @param media {Media} + */ + mediaExists(media: Media): boolean { + if (IS_IOS) { + return this.attachments.some( + a => a.localIdentifier === media.localIdentifier, + ); + } else { + return this.attachments.some(a => a.path === media.path); + } + } + /** * Removes the media (cancel or deleting from server) * and removes the store from the attachments array diff --git a/src/compose/MediaPreview.tsx b/src/compose/MediaPreview.tsx index 3f2f15baa..b08cf6490 100644 --- a/src/compose/MediaPreview.tsx +++ b/src/compose/MediaPreview.tsx @@ -26,7 +26,13 @@ export default observer(function MediaPreview({ store }: PropsType) { switch (store.attachments.length) { case 1: return ( - + { + this.attachments.attachMedia(m, this.extra); + }); + this.mode = 'text'; + } else { + if (this.portraitMode && media.height < media.width) { + showError(i18n.t('capture.mediaPortraitError')); + return; + } + this.attachments.attachMedia(media, this.extra); + this.mode = 'text'; } - this.mediaToConfirm = media; - this.acceptMedia(); }, /** * Accept media From e9bb1bbc91be3d39150166c40dffa3e44afe356b Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Fri, 23 Sep 2022 10:41:02 -0300 Subject: [PATCH 83/89] (fix) comments input refresh the screen on group & blogs --- src/navigation/NavigationStack.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/navigation/NavigationStack.tsx b/src/navigation/NavigationStack.tsx index 7a09761a8..eabeed46e 100644 --- a/src/navigation/NavigationStack.tsx +++ b/src/navigation/NavigationStack.tsx @@ -140,16 +140,12 @@ const AppStack = observer(() => { /> - withModalProvider(require('~/groups/GroupViewScreen').withModal) - } + getComponent={() => require('~/groups/GroupViewScreen').withModal} options={hideHeader} /> - withModalProvider(require('~/blogs/BlogsViewScreen').withModal) - } + getComponent={() => require('~/blogs/BlogsViewScreen').withModal} options={hideHeader} /> Date: Fri, 23 Sep 2022 17:40:11 -0300 Subject: [PATCH 84/89] (fix) codepush cli install --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1f0e4ee54..bcdec4fa8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -464,6 +464,7 @@ release:codepush-ios: stage: release tags: [minds-ci] before_script: + - npm install -g patch-package - npm install -g appcenter-cli - yarn install script: @@ -481,6 +482,7 @@ release:codepush-android: stage: release tags: [minds-ci] before_script: + - npm install -g patch-package - npm install -g appcenter-cli - yarn install script: From fcc3c603d9bd9b41cd548e0a0a70e3742d5ee272 Mon Sep 17 00:00:00 2001 From: Mani Shooshtari Date: Tue, 27 Sep 2022 13:42:17 +0200 Subject: [PATCH 85/89] (fix) update snapshots --- src/media/__snapshots__/ImageGalleryScreen.spec.tsx.snap | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/media/__snapshots__/ImageGalleryScreen.spec.tsx.snap b/src/media/__snapshots__/ImageGalleryScreen.spec.tsx.snap index 4fd4cf57c..a06ccc310 100644 --- a/src/media/__snapshots__/ImageGalleryScreen.spec.tsx.snap +++ b/src/media/__snapshots__/ImageGalleryScreen.spec.tsx.snap @@ -28,7 +28,9 @@ exports[`ImageGalleryScreen should render correctly 1`] = ` ] } > - + Date: Tue, 27 Sep 2022 10:35:31 -0300 Subject: [PATCH 86/89] (feat) simplify button state for async actions and add state to supermind settings screen --- src/auth/login/createLoginStore.ts | 7 +-- src/channel/v2/ChannelButtons.tsx | 3 +- src/common/ui/buttons/Button.tsx | 14 +++-- .../screens/SupermindSettingsScreen.tsx | 1 + .../SupermindSettingsScreen.spec.tsx.snap | 60 ++++++++++++++++++- 5 files changed, 70 insertions(+), 15 deletions(-) diff --git a/src/auth/login/createLoginStore.ts b/src/auth/login/createLoginStore.ts index 4ed89da21..81586cd62 100644 --- a/src/auth/login/createLoginStore.ts +++ b/src/auth/login/createLoginStore.ts @@ -38,23 +38,20 @@ const createLoginStore = ({ props }) => ({ showNotification(msg, 'warning', 3000); this.inProgress = false; }, - onLoginPress(releaseButton: any) { + onLoginPress() { if (!this.username || !this.password) { this.showErrors = true; - releaseButton(); return; } this.initLogin(); // is two factor auth - AuthService.login(this.username, this.password) + return AuthService.login(this.username, this.password) .then(() => { props.onLogin && props.onLogin(); - releaseButton(); }) .catch(err => { const errJson = err.response ? err.response.data : err; - releaseButton(); LayoutAnimation.configureNext(LayoutAnimation.Presets.spring); if ( errJson.error === 'invalid_grant' || diff --git a/src/channel/v2/ChannelButtons.tsx b/src/channel/v2/ChannelButtons.tsx index 79ff6d552..caeb99368 100644 --- a/src/channel/v2/ChannelButtons.tsx +++ b/src/channel/v2/ChannelButtons.tsx @@ -1,5 +1,4 @@ import React, { PropsWithChildren, useCallback, useRef } from 'react'; -import type { GestureResponderEvent } from 'react-native'; import { Platform } from 'react-native'; import { useNavigation } from '@react-navigation/native'; import { observer } from 'mobx-react'; @@ -35,7 +34,7 @@ type ButtonsType = export type ChannelButtonsPropsType = { store: ChannelStoreType; - onEditPress: (ev: GestureResponderEvent) => void; + onEditPress: () => void; onSearchChannelPressed: () => void; notShow?: Array; containerStyle?: any; diff --git a/src/common/ui/buttons/Button.tsx b/src/common/ui/buttons/Button.tsx index b9c61bfd0..2ef6eaea3 100644 --- a/src/common/ui/buttons/Button.tsx +++ b/src/common/ui/buttons/Button.tsx @@ -36,7 +36,7 @@ export type ButtonPropsType = { shouldAnimateChanges?: boolean; loading?: boolean; children?: React.ReactNode; - onPress?: (release: any) => void; + onPress?: () => void; testID?: string; accessibilityLabel?: string; icon?: React.ReactNode; @@ -163,7 +163,7 @@ export const ButtonComponent = ({ hideActive(); }; - const handlePress = () => { + const handlePress = async () => { if (shouldBreak(2, disabled, stateRef.current)) { return; } @@ -176,9 +176,13 @@ export const ButtonComponent = ({ if (spinner) { showSpinner(); } - onPress(() => { - hideSpinner(); - }); + + try { + await onPress(); + } catch (error) { + console.log(error); + } + hideSpinner(); frameThrower(4, () => { stateRef.current.pressing = false; }); diff --git a/src/settings/screens/SupermindSettingsScreen.tsx b/src/settings/screens/SupermindSettingsScreen.tsx index 632ffbd0d..0ab9ec258 100644 --- a/src/settings/screens/SupermindSettingsScreen.tsx +++ b/src/settings/screens/SupermindSettingsScreen.tsx @@ -48,6 +48,7 @@ export default observer(function SupermindSettingsScreen({ navigation }) { mode="flat" type="action" testID="save" + spinner onPress={() => localStore.submit()}> {i18n.t('save')} diff --git a/src/settings/screens/__snapshots__/SupermindSettingsScreen.spec.tsx.snap b/src/settings/screens/__snapshots__/SupermindSettingsScreen.spec.tsx.snap index 4432a8beb..b8cf097f2 100644 --- a/src/settings/screens/__snapshots__/SupermindSettingsScreen.spec.tsx.snap +++ b/src/settings/screens/__snapshots__/SupermindSettingsScreen.spec.tsx.snap @@ -213,7 +213,25 @@ exports[`Supermind settings should load data and render correctly 1`] = ` "top": 0, } } - /> + > + + + + + > + + + + + > + + + + Date: Wed, 28 Sep 2022 17:31:42 -0300 Subject: [PATCH 87/89] (fix) supermind settings input NaN and move it to settings->payments --- locales/en.json | 2 +- src/navigation/MoreStack.tsx | 38 ++++++++++--------- src/settings/SettingsScreen.tsx | 15 ++------ .../screens/SupermindSettingsScreen.tsx | 4 +- 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/locales/en.json b/locales/en.json index cce6acda4..013918f8e 100644 --- a/locales/en.json +++ b/locales/en.json @@ -739,7 +739,7 @@ "account": "Account", "network": "Network", "security": "Security", - "billing": "Billing", + "billing": "Payments", "invalidPassword": "Insecure password", "passwordsNotMatch": "Passwords should match", "passwordFormatMinCharacters": "8 or more characters", diff --git a/src/navigation/MoreStack.tsx b/src/navigation/MoreStack.tsx index 7ea4efb04..767d825b2 100644 --- a/src/navigation/MoreStack.tsx +++ b/src/navigation/MoreStack.tsx @@ -24,10 +24,6 @@ export default function () { title: i18n.t('settings.accountOptions.1'), onPress: () => navigation.push('SettingsEmail'), }, - { - title: 'Supermind', - onPress: () => navigation.push('SupermindSettingsScreen'), - }, { title: i18n.t('settings.accountOptions.2'), onPress: () => navigation.push('LanguageScreen'), @@ -78,19 +74,27 @@ export default function () { }, ]; - let BillingScreenOptions; - if (!IS_IOS) { - BillingScreenOptions = navigation => [ - { - title: i18n.t('settings.billingOptions.1'), - onPress: () => navigation.push('PaymentMethods'), - }, - { - title: i18n.t('settings.billingOptions.2'), - onPress: () => navigation.push('RecurringPayments'), - }, - ]; - } + const BillingScreenOptions = !IS_IOS + ? navigation => [ + { + title: i18n.t('settings.billingOptions.1'), + onPress: () => navigation.push('PaymentMethods'), + }, + { + title: i18n.t('settings.billingOptions.2'), + onPress: () => navigation.push('RecurringPayments'), + }, + { + title: 'Supermind', + onPress: () => navigation.push('SupermindSettingsScreen'), + }, + ] + : navigation => [ + { + title: 'Supermind', + onPress: () => navigation.push('SupermindSettingsScreen'), + }, + ]; return ( diff --git a/src/settings/SettingsScreen.tsx b/src/settings/SettingsScreen.tsx index 6cdbf6ac2..124468571 100644 --- a/src/settings/SettingsScreen.tsx +++ b/src/settings/SettingsScreen.tsx @@ -13,11 +13,7 @@ import { ScreenHeader, Screen } from '~/common/ui/screen'; import { showNotification } from 'AppMessages'; import { observer } from 'mobx-react'; import { HiddenTap } from './screens/DevToolsScreen'; -import { - DEV_MODE, - IS_IOS, - PRO_PLUS_SUBSCRIPTION_ENABLED, -} from '~/config/Config'; +import { DEV_MODE, PRO_PLUS_SUBSCRIPTION_ENABLED } from '~/config/Config'; interface HelpResponse extends ApiResponse { url: string; @@ -84,15 +80,12 @@ const SettingsScreen = observer(({ navigation }) => { screen: 'Security', params: {}, }, - ]; - - if (!IS_IOS) { - firstSection.push({ + { title: i18n.t('settings.billing'), screen: 'Billing', params: {}, - }); - } + }, + ]; if (!user.plus && PRO_PLUS_SUBSCRIPTION_ENABLED) { firstSection.push({ diff --git a/src/settings/screens/SupermindSettingsScreen.tsx b/src/settings/screens/SupermindSettingsScreen.tsx index 0ab9ec258..19a670488 100644 --- a/src/settings/screens/SupermindSettingsScreen.tsx +++ b/src/settings/screens/SupermindSettingsScreen.tsx @@ -102,14 +102,14 @@ const Inputs = observer( placeholder={i18n.t('usd')} keyboardType="numeric" onChangeText={v => { - store.setCash(parseFloat(v)); + store.setCash(parseFloat(v) || 0); }} value={store.cash.toString()} /> ) : ( fetchStore.fetch() || 0} + tryAgain={() => fetchStore.fetch()} message={i18n.t('errorMessage')} /> ), From 5ae7905c3753d403ae69f968eeb7325fb4fee6b2 Mon Sep 17 00:00:00 2001 From: Martin Santangelo Date: Thu, 29 Sep 2022 14:20:18 -0300 Subject: [PATCH 88/89] (feat) add extra validations --- locales/en.json | 2 + .../screens/SupermindSettingsScreen.tsx | 58 +++++++++++++++---- .../SupermindSettingsScreen.spec.tsx.snap | 2 + 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/locales/en.json b/locales/en.json index 013918f8e..d69398bb4 100644 --- a/locales/en.json +++ b/locales/en.json @@ -70,6 +70,8 @@ "decline": "Decline", "revoked": "revoked", "rejected": "rejected", + "minimum": "Must be a minimum of {{value}}", + "maxTwoDecimals": "Must have less than 2 decimals", "replyType": { "0": "Text Reply", "1": "Image Reply", diff --git a/src/settings/screens/SupermindSettingsScreen.tsx b/src/settings/screens/SupermindSettingsScreen.tsx index 19a670488..579bc9539 100644 --- a/src/settings/screens/SupermindSettingsScreen.tsx +++ b/src/settings/screens/SupermindSettingsScreen.tsx @@ -8,6 +8,7 @@ import InputContainer from '~/common/components/InputContainer'; import useApiFetch, { FetchStore } from '~/common/hooks/useApiFetch'; import apiService from '~/common/services/api.service'; import i18n from '~/common/services/i18n.service'; +import mindsConfigService from '~/common/services/minds-config.service'; import { B1, @@ -33,8 +34,12 @@ export default observer(function SupermindSettingsScreen({ navigation }) { */ React.useEffect(() => { if (fetchStore.result) { - localStore.setCash(fetchStore.result.min_cash); - localStore.setTokens(fetchStore.result.min_offchain_tokens); + localStore.setCash(fetchStore.result.min_cash.toString()); + localStore.setTokens(fetchStore.result.min_offchain_tokens.toString()); + const config = mindsConfigService.getSettings(); + if (config.min_thresholds) { + localStore.setThresholds(config.min_thresholds); + } } }, [fetchStore.result, localStore]); @@ -49,6 +54,7 @@ export default observer(function SupermindSettingsScreen({ navigation }) { type="action" testID="save" spinner + disabled={Boolean(localStore.cashError || localStore.tokenError)} onPress={() => localStore.submit()}> {i18n.t('save')} @@ -90,9 +96,10 @@ const Inputs = observer( testID="tokensInput" placeholder={i18n.t('tokens')} onChangeText={v => { - store.setTokens(parseFloat(v) || 0); + store.setTokens(v); }} keyboardType="numeric" + error={store.tokenError} value={store.tokens.toString()} noBottomBorder autoFocus @@ -101,8 +108,9 @@ const Inputs = observer( testID="cashInput" placeholder={i18n.t('usd')} keyboardType="numeric" + error={store.cashError} onChangeText={v => { - store.setCash(parseFloat(v) || 0); + store.setCash(v); }} value={store.cash.toString()} /> @@ -115,24 +123,54 @@ const Inputs = observer( ), ); +type Thresholds = { min_cash: number; min_offchain_tokens: number }; + /** * Local store */ const createStore = ({ navigation }) => { return { - tokens: 1, - cash: 10, - setCash(v: number) { + tokens: '1', + cash: '10', + cashError: '', + tokenError: '', + min_thresholds: { min_cash: 10, min_offchain_tokens: 1 }, + setThresholds(thresholds: Thresholds) { + this.min_thresholds = thresholds; + }, + setCash(v: string) { this.cash = v; + this.cashError = ''; + this.cashError = this.validateInput(v, this.min_thresholds.min_cash); + }, + validateInput(value: string, minimum: number) { + const numericValue = parseFloat(value) || 0; + if (value === '') { + return i18n.t('auth.fieldRequired'); + } + if (numericValue < minimum) { + return i18n.t('supermind.minimum', { + value: minimum, + }); + } + if (value.includes('.') && value.split('.')[1].length > 2) { + return i18n.t('supermind.maxTwoDecimals'); + } + return ''; }, - setTokens(v: number) { + setTokens(v: string) { this.tokens = v; + this.tokenError = ''; + this.tokenError = this.validateInput( + v, + this.min_thresholds.min_offchain_tokens, + ); }, async submit() { try { await apiService.post('api/v3/supermind/settings', { - min_cash: this.cash, - min_offchain_tokens: this.tokens, + min_cash: parseFloat(this.cash), + min_offchain_tokens: parseFloat(this.tokens), }); } catch (error) { if (error instanceof Error) { diff --git a/src/settings/screens/__snapshots__/SupermindSettingsScreen.spec.tsx.snap b/src/settings/screens/__snapshots__/SupermindSettingsScreen.spec.tsx.snap index b8cf097f2..b7d8aba45 100644 --- a/src/settings/screens/__snapshots__/SupermindSettingsScreen.spec.tsx.snap +++ b/src/settings/screens/__snapshots__/SupermindSettingsScreen.spec.tsx.snap @@ -948,6 +948,7 @@ exports[`Supermind settings should load data and render correctly 2`] = ` Date: Thu, 29 Sep 2022 16:34:25 -0300 Subject: [PATCH 89/89] (chore) settings behind FF --- src/navigation/MoreStack.tsx | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/navigation/MoreStack.tsx b/src/navigation/MoreStack.tsx index 767d825b2..5945f0295 100644 --- a/src/navigation/MoreStack.tsx +++ b/src/navigation/MoreStack.tsx @@ -9,6 +9,7 @@ import ThemedStyles from '~/styles/ThemedStyles'; import Drawer from './Drawer'; import i18n from '~/common/services/i18n.service'; import { IS_IOS } from '~/config/Config'; +import { useFeature } from '@growthbook/growthbook-react'; const MoreStack = createNativeStackNavigator(); const hideHeader: NativeStackNavigationOptions = { headerShown: false }; @@ -19,6 +20,7 @@ const WalletOptions = () => ({ }); export default function () { + const supermindFeatureFlag = useFeature('mobile-supermind'); const AccountScreenOptions = navigation => [ { title: i18n.t('settings.accountOptions.1'), @@ -84,16 +86,20 @@ export default function () { title: i18n.t('settings.billingOptions.2'), onPress: () => navigation.push('RecurringPayments'), }, - { - title: 'Supermind', - onPress: () => navigation.push('SupermindSettingsScreen'), - }, + supermindFeatureFlag.on + ? { + title: 'Supermind', + onPress: () => navigation.push('SupermindSettingsScreen'), + } + : null, ] : navigation => [ - { - title: 'Supermind', - onPress: () => navigation.push('SupermindSettingsScreen'), - }, + supermindFeatureFlag.on + ? { + title: 'Supermind', + onPress: () => navigation.push('SupermindSettingsScreen'), + } + : null, ]; return (