├── packages ├── server │ ├── .gitignore │ ├── .env.example │ ├── src │ │ ├── modules │ │ │ ├── store │ │ │ │ ├── mutations │ │ │ │ │ ├── index.ts │ │ │ │ │ └── StoreCreate.ts │ │ │ │ ├── StoreLoader.ts │ │ │ │ ├── StoreModel.ts │ │ │ │ └── StoreType.ts │ │ │ ├── product │ │ │ │ ├── mutations │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ProductCreate.ts │ │ │ │ ├── ProductLoader.ts │ │ │ │ ├── ProductModel.ts │ │ │ │ └── ProductType.ts │ │ │ ├── userStore │ │ │ │ ├── mutations │ │ │ │ │ ├── index.ts │ │ │ │ │ └── UserStoreCreate.ts │ │ │ │ ├── UserStoreLoader.ts │ │ │ │ ├── UserStoreModel.ts │ │ │ │ └── UserStoreType.ts │ │ │ ├── userPoints │ │ │ │ ├── mutations │ │ │ │ │ ├── index.ts │ │ │ │ │ └── UserPointsCreateOrUpdate.ts │ │ │ │ ├── UserPointsLoader.ts │ │ │ │ ├── UserPointsModel.ts │ │ │ │ └── UserPointsType.ts │ │ │ ├── user │ │ │ │ ├── mutations │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── UserLoginWithEmailMutation.ts │ │ │ │ │ └── UserRegisterWithEmailMutation.ts │ │ │ │ ├── UserLoader.ts │ │ │ │ ├── UserModel.ts │ │ │ │ └── UserType.ts │ │ │ └── node │ │ │ │ └── typeRegister.ts │ │ ├── shared │ │ │ ├── logger.config.ts │ │ │ ├── server.config.ts │ │ │ └── index.ts │ │ ├── schema │ │ │ ├── schema.ts │ │ │ ├── MutationType.ts │ │ │ └── QueryType.ts │ │ ├── graphql │ │ │ ├── errorField.ts │ │ │ ├── successField.ts │ │ │ ├── index.ts │ │ │ ├── withFilter.ts │ │ │ ├── withConnectionCursor.ts │ │ │ ├── loaderRegister.ts │ │ │ ├── createLoader.ts │ │ │ └── connectionDefinitions.ts │ │ ├── config.ts │ │ ├── database │ │ │ └── database.ts │ │ ├── auth.ts │ │ ├── index.ts │ │ └── app.ts │ ├── tsconfig.json │ ├── scripts │ │ ├── updateSchema.ts │ │ └── updateSchema.js │ ├── package.json │ └── schema │ │ └── schema.graphql ├── app │ ├── .env.example │ ├── src │ │ ├── types │ │ │ └── env.d.ts │ │ ├── core │ │ │ └── auth │ │ │ │ ├── useAuth.tsx │ │ │ │ ├── security.ts │ │ │ │ ├── userType.ts │ │ │ │ └── AuthContext.tsx │ │ ├── components │ │ │ ├── qrCode │ │ │ │ ├── QrCodeMeQuery.tsx │ │ │ │ ├── ScanQrCodeStoreQuery.tsx │ │ │ │ ├── ScanQrCodeUserPointsCreateOrUpdateMutation.tsx │ │ │ │ ├── QrCode.tsx │ │ │ │ └── ScanQrCode.tsx │ │ │ ├── settings │ │ │ │ ├── SettingsMeQuery.tsx │ │ │ │ └── Settings.tsx │ │ │ ├── Home │ │ │ │ ├── CreateStoreMutation.tsx │ │ │ │ ├── NewProductMutation.tsx │ │ │ │ ├── HomeStoreListQuery.tsx │ │ │ │ ├── Store.tsx │ │ │ │ ├── Home.tsx │ │ │ │ ├── CreateStore.tsx │ │ │ │ └── NewProduct.tsx │ │ │ ├── auth │ │ │ │ ├── SignInUserLoginWithEmailMutation.tsx │ │ │ │ ├── SignUpUserRegisterWithEmailMutation.tsx │ │ │ │ ├── SignIn.tsx │ │ │ │ └── SignUp.tsx │ │ │ ├── ui │ │ │ │ ├── Alert.tsx │ │ │ │ ├── TextInput.tsx │ │ │ │ ├── Button.tsx │ │ │ │ ├── RadioButton.tsx │ │ │ │ └── Product.tsx │ │ │ └── storeDetails │ │ │ │ ├── StoreDetailsQuery.tsx │ │ │ │ ├── Header.tsx │ │ │ │ └── StoreDetails.tsx │ │ ├── relay │ │ │ ├── Environment.ts │ │ │ └── fetchQuery.ts │ │ ├── Providers.tsx │ │ └── routes.tsx │ ├── assets │ │ ├── icon.png │ │ ├── favicon.png │ │ ├── splash.png │ │ └── adaptive-icon.png │ ├── relay.config.js │ ├── metro.config.js │ ├── tsconfig.json │ ├── .expo-shared │ │ └── assets.json │ ├── .gitignore │ ├── babel.config.js │ ├── App.tsx │ ├── app.json │ └── package.json ├── .DS_Store └── website │ ├── amora_emoji.png │ ├── package.json │ └── index.html ├── .gitignore ├── README.md ├── .prettierrc.json ├── package.json └── .eslintrc.json /packages/server/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | lib/ 3 | dist/ -------------------------------------------------------------------------------- /packages/app/.env.example: -------------------------------------------------------------------------------- 1 | API_URL=http://localhost:9001 -------------------------------------------------------------------------------- /packages/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akinncar/amora/HEAD/packages/.DS_Store -------------------------------------------------------------------------------- /packages/app/src/types/env.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@env' { 2 | export const API_URL: string; 3 | } -------------------------------------------------------------------------------- /packages/app/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akinncar/amora/HEAD/packages/app/assets/icon.png -------------------------------------------------------------------------------- /packages/app/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akinncar/amora/HEAD/packages/app/assets/favicon.png -------------------------------------------------------------------------------- /packages/app/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akinncar/amora/HEAD/packages/app/assets/splash.png -------------------------------------------------------------------------------- /packages/website/amora_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akinncar/amora/HEAD/packages/website/amora_emoji.png -------------------------------------------------------------------------------- /packages/app/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akinncar/amora/HEAD/packages/app/assets/adaptive-icon.png -------------------------------------------------------------------------------- /packages/server/.env.example: -------------------------------------------------------------------------------- 1 | PORT=9001 2 | NODE_ENV=development 3 | 4 | MONGO_URI=mongodb://localhost/rbaf 5 | JWT_SECRET=jwt_secret -------------------------------------------------------------------------------- /packages/server/src/modules/store/mutations/index.ts: -------------------------------------------------------------------------------- 1 | import StoreCreate from './StoreCreate'; 2 | 3 | export default { 4 | StoreCreate, 5 | }; -------------------------------------------------------------------------------- /packages/server/src/modules/product/mutations/index.ts: -------------------------------------------------------------------------------- 1 | import ProductCreate from './ProductCreate'; 2 | 3 | export default { 4 | ProductCreate, 5 | }; -------------------------------------------------------------------------------- /packages/app/relay.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | src: './src', 3 | schema: '../server/schema/schema.graphql', 4 | language: 'typescript' 5 | }; 6 | -------------------------------------------------------------------------------- /packages/app/metro.config.js: -------------------------------------------------------------------------------- 1 | const { createMetroConfiguration } = require('expo-yarn-workspaces'); 2 | 3 | module.exports = createMetroConfiguration(__dirname); -------------------------------------------------------------------------------- /packages/server/src/modules/userStore/mutations/index.ts: -------------------------------------------------------------------------------- 1 | import UserStoreCreate from './UserStoreCreate'; 2 | 3 | export default { 4 | UserStoreCreate, 5 | }; -------------------------------------------------------------------------------- /packages/app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "typeRoots": ["./src/types"], 4 | "compilerOptions": { 5 | "strict": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/server/src/modules/userPoints/mutations/index.ts: -------------------------------------------------------------------------------- 1 | import UserPointsCreateOrUpdate from './UserPointsCreateOrUpdate'; 2 | 3 | export default { 4 | UserPointsCreateOrUpdate, 5 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Monorepo 2 | node_modules/ 3 | apps/*/credentials.json 4 | packages/*/build 5 | yarn-error.log 6 | .DS_Store 7 | 8 | # Expo 9 | .expo 10 | __generated__ 11 | web-build -------------------------------------------------------------------------------- /packages/app/src/core/auth/useAuth.tsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | 3 | import { AuthContext } from './AuthContext'; 4 | 5 | export const useAuth = () => useContext(AuthContext); 6 | -------------------------------------------------------------------------------- /packages/app/.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 4 | } 5 | -------------------------------------------------------------------------------- /packages/app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | .env 13 | 14 | # macOS 15 | .DS_Store 16 | -------------------------------------------------------------------------------- /packages/server/src/shared/logger.config.ts: -------------------------------------------------------------------------------- 1 | const LOG_ENV = { 2 | 'production': { level: 'info' }, 3 | 'preproduction': { level: 'info' }, 4 | 'development': { level: 'all' } 5 | }; 6 | 7 | export default LOG_ENV; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :construction: 🫐 Amora 2 | Amora is a mobile app to help establishments to retain customers made with React Native. 3 | 4 | 5 | # Deploy web app 6 | 7 | https://docs.expo.dev/distribution/publishing-websites/#vercel -------------------------------------------------------------------------------- /packages/app/src/components/qrCode/QrCodeMeQuery.tsx: -------------------------------------------------------------------------------- 1 | import { graphql } from 'react-relay'; 2 | 3 | export const QrCodeMeQuery = graphql` 4 | query QrCodeMeQuery { 5 | me { 6 | _id 7 | } 8 | } 9 | `; 10 | -------------------------------------------------------------------------------- /packages/app/src/components/settings/SettingsMeQuery.tsx: -------------------------------------------------------------------------------- 1 | import { graphql } from 'react-relay'; 2 | 3 | export const SettingsMeQuery = graphql` 4 | query SettingsMeQuery { 5 | me { 6 | _id 7 | name 8 | email 9 | } 10 | } 11 | `; 12 | -------------------------------------------------------------------------------- /packages/server/src/modules/user/mutations/index.ts: -------------------------------------------------------------------------------- 1 | import UserLoginWithEmail from './UserLoginWithEmailMutation'; 2 | import UserRegisterWithEmail from './UserRegisterWithEmailMutation'; 3 | 4 | export default { 5 | UserLoginWithEmail, 6 | UserRegisterWithEmail, 7 | }; -------------------------------------------------------------------------------- /packages/server/src/schema/schema.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLSchema } from 'graphql' 2 | 3 | import QueryType from './QueryType' 4 | import MutationType from './MutationType' 5 | 6 | export const schema = new GraphQLSchema({ 7 | query: QueryType, 8 | mutation: MutationType 9 | }) -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "semi": true, 5 | "singleQuote": true, 6 | "jsxSingleQuote": false, 7 | "bracketSpacing": true, 8 | "jsxBracketSameLine": false, 9 | "endOfLine": "auto", 10 | "arrowParens": "avoid" 11 | } -------------------------------------------------------------------------------- /packages/website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@amora/website", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "deploy": "gh-pages -d ./" 8 | }, 9 | "dependencies": { 10 | "gh-pages": "^3.2.3" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/server/src/shared/server.config.ts: -------------------------------------------------------------------------------- 1 | 2 | const SERVER_ENV = { 3 | production: { 4 | SERVER_PORT: process.env.PORT || 9001, 5 | }, 6 | preproduction: { 7 | SERVER_PORT: 9001, 8 | }, 9 | development: { 10 | SERVER_PORT: 9001, 11 | }, 12 | }; 13 | 14 | export default SERVER_ENV; -------------------------------------------------------------------------------- /packages/server/src/graphql/errorField.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLString } from 'graphql'; 2 | 3 | export const errorField = { 4 | error: { 5 | type: GraphQLString, 6 | // TODO check if this resolver is actually needed. graphqljs defaults to something along these lines 7 | resolve: ({ error }: { readonly error: string }) => error, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/server/src/shared/index.ts: -------------------------------------------------------------------------------- 1 | import SERVER_ENV from './server.config'; 2 | import LOG_ENV from './logger.config'; 3 | 4 | const environment = process.env.NODE_ENV || 'development'; 5 | 6 | const serverConf = SERVER_ENV[environment]; 7 | const logConf = LOG_ENV[environment]; 8 | 9 | export { 10 | environment, 11 | serverConf, 12 | logConf 13 | }; -------------------------------------------------------------------------------- /packages/server/src/graphql/successField.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLString } from 'graphql'; 2 | 3 | export const successField = { 4 | success: { 5 | type: GraphQLString, 6 | // TODO check if this resolver is actually needed. graphqljs defaults to something along these lines 7 | resolve: ({ success }: { readonly success: string }) => success, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/app/src/components/qrCode/ScanQrCodeStoreQuery.tsx: -------------------------------------------------------------------------------- 1 | import { graphql } from 'react-relay'; 2 | 3 | export const ScanQrCodeStoreQuery = graphql` 4 | query ScanQrCodeStoreQuery { 5 | userStoreByUserId { 6 | edges { 7 | node { 8 | _id 9 | storeId 10 | userId 11 | } 12 | } 13 | } 14 | } 15 | `; 16 | -------------------------------------------------------------------------------- /packages/app/src/components/Home/CreateStoreMutation.tsx: -------------------------------------------------------------------------------- 1 | import { graphql } from 'react-relay'; 2 | 3 | export const CreateStoreMutation = graphql` 4 | mutation CreateStoreMutation($input: StoreCreateInput!) { 5 | StoreCreate(input: $input) { 6 | store { 7 | name 8 | description 9 | pictureUrl 10 | } 11 | error 12 | } 13 | } 14 | `; 15 | -------------------------------------------------------------------------------- /packages/server/src/graphql/index.ts: -------------------------------------------------------------------------------- 1 | export { errorField } from './errorField'; 2 | export { successField } from './successField'; 3 | export { createLoader } from './createLoader'; 4 | export { withConnectionCursor } from './withConnectionCursor'; 5 | export { registerLoader, getDataloaders } from './loaderRegister'; 6 | export { connectionArgs, connectionDefinitions } from './connectionDefinitions'; 7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/app/src/components/Home/NewProductMutation.tsx: -------------------------------------------------------------------------------- 1 | import { graphql } from 'react-relay'; 2 | 3 | export const NewProductMutation = graphql` 4 | mutation NewProductMutation($input: ProductCreateInput!) { 5 | ProductCreate(input: $input) { 6 | product { 7 | name 8 | description 9 | pictureUrl 10 | points 11 | storeId 12 | } 13 | error 14 | } 15 | } 16 | `; 17 | -------------------------------------------------------------------------------- /packages/server/src/graphql/withFilter.ts: -------------------------------------------------------------------------------- 1 | import { ConnectionArguments } from 'graphql-relay'; 2 | 3 | export type ArgsWithFilter = ConnectionArguments & { 4 | filters: { [key: string]: string }; 5 | [key: string]: any; 6 | }; 7 | 8 | export const withFilter = (args: ArgsWithFilter | { [key: string]: any }, filters: object) => ({ 9 | ...args, 10 | filters: { 11 | ...(args.filters || {}), 12 | ...filters, 13 | }, 14 | }); -------------------------------------------------------------------------------- /packages/app/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | plugins: [ 6 | 'relay', 7 | ["module:react-native-dotenv", { 8 | "moduleName": "@env", 9 | "path": ".env", 10 | "blacklist": null, 11 | "whitelist": null, 12 | "safe": false, 13 | "allowUndefined": true 14 | }], 15 | ], 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/app/src/core/auth/security.ts: -------------------------------------------------------------------------------- 1 | import AsyncStorage from '@react-native-async-storage/async-storage'; 2 | 3 | const JWT_TOKEN_KEY = '@user/TOKEN'; 4 | 5 | export const getAuthToken = () => AsyncStorage.getItem(JWT_TOKEN_KEY); 6 | 7 | export const updateAuthToken = (token?: any) => { 8 | if (!token) { 9 | return AsyncStorage.removeItem(JWT_TOKEN_KEY); 10 | } else { 11 | return AsyncStorage.setItem(JWT_TOKEN_KEY, token); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /packages/server/src/config.ts: -------------------------------------------------------------------------------- 1 | import dotenvSafe from 'dotenv-safe'; 2 | import path from 'path'; 3 | 4 | const cwd = process.cwd(); 5 | 6 | const root = path.join.bind(cwd); 7 | 8 | dotenvSafe.config({ 9 | path: root('.env'), 10 | sample: root('.env.example'), 11 | }); 12 | 13 | export const config = { 14 | PORT: process.env.PORT || 9001, 15 | NODE_ENV: process.env.NODE_ENV, 16 | MONGO_URI: process.env.MONGO_URI, 17 | JWT_SECRET: process.env.JWT_SECRET, 18 | }; 19 | -------------------------------------------------------------------------------- /packages/app/src/core/auth/userType.ts: -------------------------------------------------------------------------------- 1 | import AsyncStorage from '@react-native-async-storage/async-storage'; 2 | 3 | const USER_TYPE_KEY = '@user/TYPE'; 4 | 5 | export const getUserType = () => AsyncStorage.getItem(USER_TYPE_KEY); 6 | 7 | export const updateUserType = (userType?: 'customer' | 'provider') => { 8 | if (!userType) { 9 | return AsyncStorage.removeItem(USER_TYPE_KEY); 10 | } else { 11 | return AsyncStorage.setItem(USER_TYPE_KEY, userType); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /packages/app/src/components/auth/SignInUserLoginWithEmailMutation.tsx: -------------------------------------------------------------------------------- 1 | import { graphql } from 'react-relay'; 2 | 3 | export const SignInUserLoginWithEmailMutation = graphql` 4 | mutation SignInUserLoginWithEmailMutation($input: UserLoginWithEmailInput!) { 5 | UserLoginWithEmail(input: $input) { 6 | token 7 | error 8 | success 9 | me { 10 | _id 11 | name 12 | username 13 | email 14 | type 15 | } 16 | } 17 | } 18 | `; 19 | -------------------------------------------------------------------------------- /packages/app/src/components/auth/SignUpUserRegisterWithEmailMutation.tsx: -------------------------------------------------------------------------------- 1 | import { graphql } from 'react-relay'; 2 | 3 | export const SignUpUserRegisterWithEmailMutation = graphql` 4 | mutation SignUpUserRegisterWithEmailMutation( 5 | $input: UserRegisterWithEmailInput! 6 | ) { 7 | UserRegisterWithEmail(input: $input) { 8 | token 9 | error 10 | success 11 | me { 12 | id 13 | name 14 | username 15 | email 16 | type 17 | } 18 | } 19 | } 20 | `; 21 | -------------------------------------------------------------------------------- /packages/server/src/modules/user/UserLoader.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import { createLoader } from '../../graphql/createLoader'; 3 | 4 | import { registerLoader } from '../../graphql/loaderRegister'; 5 | 6 | import UserModel from './UserModel'; 7 | 8 | const { Wrapper: User, getLoader, clearCache, load, loadAll } = createLoader({ 9 | model: UserModel, 10 | loaderName: 'UserLoader', 11 | }); 12 | 13 | export { getLoader, clearCache, load, loadAll }; 14 | export default User; 15 | 16 | registerLoader('UserLoader', getLoader); 17 | -------------------------------------------------------------------------------- /packages/app/src/components/qrCode/ScanQrCodeUserPointsCreateOrUpdateMutation.tsx: -------------------------------------------------------------------------------- 1 | import { graphql } from 'react-relay'; 2 | 3 | export const ScanQrCodeUserPointsCreateOrUpdateMutation = graphql` 4 | mutation ScanQrCodeUserPointsCreateOrUpdateMutation( 5 | $input: UserPointsCreateOrUpdateInput! 6 | ) { 7 | UserPointsCreateOrUpdate(input: $input) { 8 | userPoints { 9 | _id 10 | points 11 | storeId 12 | userId 13 | } 14 | success 15 | error 16 | } 17 | } 18 | `; 19 | -------------------------------------------------------------------------------- /packages/app/src/relay/Environment.ts: -------------------------------------------------------------------------------- 1 | import { Environment, Network, QueryResponseCache, RecordSource, Store } from 'relay-runtime'; 2 | 3 | import { fetchQuery } from './fetchQuery' 4 | 5 | const __DEV__ = process.env.NODE_ENV === 'development'; 6 | 7 | const oneMinute = 60 * 1000; 8 | const network = Network.create(fetchQuery); 9 | 10 | const environment = new Environment({ 11 | network, 12 | store: new Store(new RecordSource(), { 13 | gcReleaseBufferSize: 10, 14 | }), 15 | }); 16 | 17 | export default environment; 18 | -------------------------------------------------------------------------------- /packages/server/src/modules/store/StoreLoader.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import { createLoader } from '../../graphql/createLoader'; 3 | 4 | import { registerLoader } from '../../graphql/loaderRegister'; 5 | 6 | import StoreModel from './StoreModel'; 7 | 8 | const { Wrapper: Store, getLoader, clearCache, load, loadAll } = createLoader({ 9 | model: StoreModel, 10 | loaderName: 'StoreLoader', 11 | }); 12 | 13 | export { getLoader, clearCache, load, loadAll }; 14 | export default Store; 15 | 16 | registerLoader('StoreLoader', getLoader); 17 | -------------------------------------------------------------------------------- /packages/server/src/modules/product/ProductLoader.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import { createLoader } from '../../graphql/createLoader'; 3 | 4 | import { registerLoader } from '../../graphql/loaderRegister'; 5 | 6 | import ProductModel from './ProductModel'; 7 | 8 | const { Wrapper: Product, getLoader, clearCache, load, loadAll } = createLoader({ 9 | model: ProductModel, 10 | loaderName: 'ProductLoader', 11 | }); 12 | 13 | export { getLoader, clearCache, load, loadAll }; 14 | export default Product; 15 | 16 | registerLoader('ProductLoader', getLoader); 17 | -------------------------------------------------------------------------------- /packages/server/src/modules/userStore/UserStoreLoader.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import { createLoader } from '../../graphql/createLoader'; 3 | 4 | import { registerLoader } from '../../graphql/loaderRegister'; 5 | 6 | import UserStoreModel from './UserStoreModel'; 7 | 8 | const { Wrapper: UserStore, getLoader, clearCache, load, loadAll } = createLoader({ 9 | model: UserStoreModel, 10 | loaderName: 'UserStoreLoader', 11 | }); 12 | 13 | export { getLoader, clearCache, load, loadAll }; 14 | export default UserStore; 15 | 16 | registerLoader('UserStoreLoader', getLoader); 17 | -------------------------------------------------------------------------------- /packages/server/src/modules/userPoints/UserPointsLoader.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import { createLoader } from '../../graphql/createLoader'; 3 | 4 | import { registerLoader } from '../../graphql/loaderRegister'; 5 | 6 | import UserPointsModel from './UserPointsModel'; 7 | 8 | const { Wrapper: UserPoints, getLoader, clearCache, load, loadAll } = createLoader({ 9 | model: UserPointsModel, 10 | loaderName: 'UserPointsLoader', 11 | }); 12 | 13 | export { getLoader, clearCache, load, loadAll }; 14 | export default UserPoints; 15 | 16 | registerLoader('UserPointsLoader', getLoader); 17 | -------------------------------------------------------------------------------- /packages/app/src/Providers.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { RelayEnvironmentProvider } from 'react-relay'; 3 | 4 | import Environment from './relay/Environment'; 5 | import { AuthProvider } from './core/auth/AuthContext'; 6 | 7 | type Props = { 8 | readonly children: React.ReactNode; 9 | readonly environment: typeof Environment; 10 | }; 11 | const Providers = ({ children, environment = Environment }: Props) => { 12 | return ( 13 | 14 | {children} 15 | 16 | ); 17 | }; 18 | 19 | export default Providers; 20 | -------------------------------------------------------------------------------- /packages/server/src/database/database.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import mongoose from 'mongoose'; 3 | 4 | // import { config } from "../config"; 5 | 6 | const initDB = () => { 7 | mongoose.connect(process.env.MONGO_URI, { 8 | useNewUrlParser: true, 9 | useUnifiedTopology: true, 10 | }); 11 | 12 | mongoose.connection 13 | .on('error', (error) => console.log(error)) 14 | .once('open', () => { 15 | // const info = mongoose.connections[0]; 16 | // console.log(`Connected to ${info.host}:${info.port}/${info.name}`) 17 | console.log('Connected to Database'); 18 | }); 19 | }; 20 | 21 | export default initDB; 22 | -------------------------------------------------------------------------------- /packages/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "outDir": "./dist", 7 | "rootDir": "./src", 8 | "lib": [ /* Specify library files to be included in the compilation. */ 9 | "esnext", 10 | "dom", 11 | "dom.iterable" 12 | ], 13 | "strict": true, 14 | "resolveJsonModule": true, 15 | "esModuleInterop": true, 16 | "skipLibCheck": true, 17 | "checkJs": false, 18 | "noImplicitAny": false 19 | }, 20 | "include": ["src/**/*"], 21 | "exclude": ["node_modules"] 22 | } -------------------------------------------------------------------------------- /packages/app/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from 'react'; 2 | import { ActivityIndicator, StatusBar } from 'react-native'; 3 | 4 | import AsyncStorage from '@react-native-async-storage/async-storage'; 5 | 6 | import Providers from './src/Providers'; 7 | import Environment from './src/relay/Environment'; 8 | import { Routes } from './src/routes'; 9 | 10 | // AsyncStorage.clear(); 11 | 12 | export default function App() { 13 | return ( 14 | 15 | }> 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /packages/app/src/components/ui/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { Alert, Platform } from 'react-native'; 2 | 3 | const alertPolyfill = (title, description, options, extra) => { 4 | const result = window.confirm( 5 | [title, description].filter(Boolean).join('\n') 6 | ); 7 | 8 | if (result) { 9 | const confirmOption = options.find(({ style }) => style !== 'cancel'); 10 | confirmOption && confirmOption.onPress(); 11 | } else { 12 | const cancelOption = options.find(({ style }) => style === 'cancel'); 13 | cancelOption && cancelOption.onPress(); 14 | } 15 | }; 16 | 17 | const alert = Platform.OS === 'web' ? alertPolyfill : Alert.alert; 18 | 19 | export default { alert }; 20 | -------------------------------------------------------------------------------- /packages/server/src/schema/MutationType.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLObjectType } from 'graphql'; 2 | 3 | import UserMutations from '../modules/user/mutations'; 4 | import StoreMutations from '../modules/store/mutations'; 5 | import ProductMutations from '../modules/product/mutations'; 6 | import UserPointsMutations from '../modules/userPoints/mutations'; 7 | import UserStoreMutations from '../modules/userStore/mutations'; 8 | 9 | export default new GraphQLObjectType({ 10 | name: 'Mutation', 11 | description: 'Root of ... mutations', 12 | fields: () => ({ 13 | ...UserMutations, 14 | ...StoreMutations, 15 | ...ProductMutations, 16 | ...UserPointsMutations, 17 | ...UserStoreMutations, 18 | }), 19 | }); -------------------------------------------------------------------------------- /packages/server/scripts/updateSchema.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import { promisify } from 'util'; 4 | 5 | import { printSchema } from 'graphql/utilities'; 6 | 7 | import { schema as schemaGraphql } from '../src/schema/schema'; 8 | 9 | const writeFileAsync = promisify(fs.writeFile); 10 | 11 | const cwd = process.cwd(); 12 | 13 | (async () => { 14 | const configs = [ 15 | { 16 | schema: schemaGraphql, 17 | path: path.join(cwd, './schema/schema.graphql'), 18 | }, 19 | ]; 20 | 21 | await Promise.all([ 22 | ...configs.map(async config => { 23 | await writeFileAsync(config.path, printSchema(config.schema)); 24 | }), 25 | ]); 26 | 27 | process.exit(0); 28 | })(); 29 | -------------------------------------------------------------------------------- /packages/server/src/auth.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import jwt from 'jsonwebtoken'; 3 | 4 | // import { User } from './model'; 5 | import { config } from './config'; 6 | import User, { IUser } from './modules/user/UserModel'; 7 | 8 | export const getUser = async (token: string | null | undefined) => { 9 | if (!token) return { user: null }; 10 | 11 | try { 12 | const decodedToken = jwt.verify(token.substring(4), config.JWT_SECRET); 13 | 14 | const user = await User.findOne({ _id: (decodedToken as { readonly id: string }).id }); 15 | 16 | return { 17 | user, 18 | }; 19 | } catch (err) { 20 | return { user: null }; 21 | } 22 | }; 23 | 24 | export const generateToken = (user: IUser) => { 25 | return `JWT ${jwt.sign({ id: user._id }, config.JWT_SECRET)}`; 26 | }; -------------------------------------------------------------------------------- /packages/app/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "amora", 4 | "slug": "amora", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": [ 17 | "**/*" 18 | ], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "android": { 23 | "adaptiveIcon": { 24 | "foregroundImage": "./assets/adaptive-icon.png", 25 | "backgroundColor": "#FFFFFF" 26 | } 27 | }, 28 | "web": { 29 | "favicon": "./assets/favicon.png" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/app/src/components/ui/TextInput.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | TextInput as RNTextInput, 4 | TextInputProps as RNTextInputProps, 5 | StyleProp, 6 | Text, 7 | View, 8 | ViewStyle, 9 | } from 'react-native'; 10 | 11 | type TextInputProps = { 12 | readonly label?: string; 13 | readonly style?: StyleProp; 14 | } & RNTextInputProps; 15 | 16 | export function TextInput({ label, style, ...props }: TextInputProps) { 17 | return ( 18 | 19 | {label && {label}} 20 | 28 | 29 | 30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /packages/app/src/components/Home/HomeStoreListQuery.tsx: -------------------------------------------------------------------------------- 1 | import { graphql } from 'react-relay'; 2 | 3 | export const HomeStoreListQuery = graphql` 4 | query HomeStoreListQuery { 5 | stores { 6 | edges { 7 | node { 8 | _id 9 | name 10 | description 11 | pictureUrl 12 | } 13 | } 14 | } 15 | userStoreByUserId { 16 | edges { 17 | node { 18 | _id 19 | storeId 20 | userId 21 | store { 22 | _id 23 | name 24 | products { 25 | edges { 26 | node { 27 | _id 28 | name 29 | pictureUrl 30 | points 31 | } 32 | } 33 | } 34 | } 35 | } 36 | } 37 | } 38 | } 39 | `; 40 | -------------------------------------------------------------------------------- /packages/app/src/components/storeDetails/StoreDetailsQuery.tsx: -------------------------------------------------------------------------------- 1 | import { graphql } from 'react-relay'; 2 | 3 | export const StoreDetailsQuery = graphql` 4 | query StoreDetailsQuery($storeId: ID!) { 5 | me { 6 | _id 7 | email 8 | } 9 | store: storeByStoreId(id: $storeId) { 10 | _id 11 | name 12 | description 13 | pictureUrl 14 | } 15 | products: productsByStoreId(storeId: $storeId) { 16 | edges { 17 | node { 18 | _id 19 | name 20 | description 21 | pictureUrl 22 | points 23 | storeId 24 | } 25 | } 26 | } 27 | userPoints: userPointsByStoreIdAndUserId(storeId: $storeId) { 28 | edges { 29 | node { 30 | _id 31 | points 32 | storeId 33 | userId 34 | } 35 | } 36 | } 37 | } 38 | `; 39 | -------------------------------------------------------------------------------- /packages/server/src/graphql/withConnectionCursor.ts: -------------------------------------------------------------------------------- 1 | import { connectionFromMongoCursor } from '@entria/graphql-mongoose-loader'; 2 | import { Model } from 'mongoose'; 3 | 4 | import { DataLoaderKey } from './createLoader'; 5 | 6 | export type LoaderFn = (ctx: Context, id: DataLoaderKey) => any; 7 | 8 | export const withConnectionCursor = 9 | ( 10 | model: Model, 11 | loader: LoaderFn, 12 | condFn: (...p: readonly any[]) => { readonly conditions?: object; readonly sort?: object }, 13 | ) => 14 | (...params: readonly any[]) => { 15 | const { conditions = {}, sort = {} } = condFn(...params); 16 | 17 | const [context, args] = params; 18 | 19 | const cursor = model.find(conditions).sort(sort); 20 | 21 | return connectionFromMongoCursor({ 22 | cursor, 23 | context, 24 | args, 25 | loader: loader as any, 26 | }); 27 | }; 28 | -------------------------------------------------------------------------------- /packages/server/src/modules/userStore/UserStoreModel.ts: -------------------------------------------------------------------------------- 1 | import mongoose, { Document, Model } from 'mongoose'; 2 | 3 | const { ObjectId } = mongoose.Schema.Types; 4 | const Schema = mongoose.Schema; 5 | 6 | const UserStoreSchema = new Schema( 7 | { 8 | storeId: { 9 | type: ObjectId, 10 | ref: 'Store', 11 | required: false, 12 | }, 13 | userId: { 14 | type: ObjectId, 15 | ref: 'User', 16 | required: false, 17 | }, 18 | }, 19 | { 20 | collection: 'UserStore', 21 | timestamps: { 22 | createdAt: 'createdAt', 23 | updatedAt: 'updatedAt', 24 | }, 25 | } 26 | ); 27 | 28 | export interface IUserStore extends Document { 29 | readonly storeId: string; 30 | readonly userId: string; 31 | readonly createdAt: Date; 32 | readonly updatedAt: Date; 33 | } 34 | 35 | const UserStoreModel: Model = mongoose.model('UserStore', UserStoreSchema); 36 | 37 | export default UserStoreModel; 38 | -------------------------------------------------------------------------------- /packages/app/src/components/qrCode/QrCode.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Text, View } from 'react-native'; 3 | 4 | import { useLazyLoadQuery } from 'react-relay'; 5 | import QRCode from 'react-native-qrcode-svg'; 6 | import { useRoute } from '@react-navigation/native'; 7 | 8 | import { QrCodeMeQuery } from './QrCodeMeQuery'; 9 | import type { QrCodeMeQuery as QrCodeMeQueryType } from './__generated__/QrCodeMeQuery.graphql'; 10 | 11 | export function QrCode() { 12 | const { params = { points: 0 } } = useRoute(); 13 | const { points } = params; 14 | 15 | const data = useLazyLoadQuery( 16 | QrCodeMeQuery, 17 | {}, 18 | undefined 19 | ); 20 | 21 | const code = { 22 | userId: data.me?._id, 23 | points: points || 0, 24 | }; 25 | 26 | return ( 27 | 28 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/server/src/graphql/loaderRegister.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | export interface DataLoaders { 3 | // UserLoader: ReturnType< 4 | // typeof import("../server/modules/user/UserLoader").getLoader 5 | // >; 6 | // TeamLoader: ReturnType< 7 | // typeof import("../server/modules/team/TeamLoader").getLoader 8 | // >; 9 | } 10 | 11 | const loaders: { readonly 12 | [Name in keyof DataLoaders]: () => DataLoaders[Name]; 13 | } = {} as any; 14 | 15 | const registerLoader = ( 16 | key: Name, 17 | getLoader: () => DataLoaders[Name] 18 | ) => { 19 | loaders[key] = getLoader as any; 20 | }; 21 | 22 | const getDataloaders = (): DataLoaders => 23 | (Object.keys(loaders) as readonly (keyof DataLoaders)[]).reduce( 24 | (prev, loaderKey) => ({ 25 | ...prev, 26 | [loaderKey]: loaders[loaderKey](), 27 | }), 28 | {} 29 | ) as any; 30 | 31 | export { registerLoader, getDataloaders }; -------------------------------------------------------------------------------- /packages/website/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Amora APP 11 | 12 | 13 |
14 | Amora Logo 15 |

Amora App

16 |

Coming Soon...

17 |
18 | 19 | -------------------------------------------------------------------------------- /packages/server/scripts/updateSchema.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const fs_1 = __importDefault(require("fs")); 7 | const path_1 = __importDefault(require("path")); 8 | const util_1 = require("util"); 9 | const utilities_1 = require("graphql/utilities"); 10 | const schema_1 = require("../src/schema/schema"); 11 | const writeFileAsync = (0, util_1.promisify)(fs_1.default.writeFile); 12 | const cwd = process.cwd(); 13 | (async () => { 14 | const configs = [ 15 | { 16 | schema: schema_1.schema, 17 | path: path_1.default.join(cwd, './schema/schema.graphql'), 18 | }, 19 | ]; 20 | await Promise.all([ 21 | ...configs.map(async (config) => { 22 | await writeFileAsync(config.path, (0, utilities_1.printSchema)(config.schema)); 23 | }), 24 | ]); 25 | process.exit(0); 26 | })(); 27 | -------------------------------------------------------------------------------- /packages/server/src/modules/store/StoreModel.ts: -------------------------------------------------------------------------------- 1 | import mongoose, { Document, Model, Types } from 'mongoose'; 2 | import bcrypt from 'bcrypt'; 3 | 4 | const { ObjectId } = mongoose.Schema.Types; 5 | const Schema = mongoose.Schema; 6 | 7 | const StoreSchema = new Schema( 8 | { 9 | name: { 10 | type: String, 11 | required: 'name is required', 12 | }, 13 | description: { 14 | type: String, 15 | required: false, 16 | }, 17 | pictureUrl: { 18 | type: String, 19 | required: false, 20 | }, 21 | }, 22 | { 23 | collection: 'Store', 24 | timestamps: { 25 | createdAt: 'createdAt', 26 | updatedAt: 'updatedAt', 27 | }, 28 | } 29 | ); 30 | 31 | export interface IStore extends Document { 32 | readonly name: string; 33 | readonly description: string; 34 | readonly pictureUrl: string; 35 | readonly createdAt: Date; 36 | readonly updatedAt: Date; 37 | } 38 | 39 | const StoreModel: Model = mongoose.model('Store', StoreSchema); 40 | 41 | export default StoreModel; 42 | -------------------------------------------------------------------------------- /packages/app/src/relay/fetchQuery.ts: -------------------------------------------------------------------------------- 1 | import Constants from 'expo-constants'; 2 | const { manifest } = Constants; 3 | 4 | import { API_URL } from '@env' 5 | 6 | import { getAuthToken } from '../core/auth/security' 7 | 8 | // Define a function that fetches the results of an operation (query/mutation/etc) 9 | // and returns its results as a Promise: 10 | export async function fetchQuery( 11 | operation: any, 12 | variables: any, 13 | cacheConfig: any, 14 | uploadables: any, 15 | ) { 16 | const authorization = await getAuthToken(); 17 | 18 | const headers = { 19 | 'content-type': 'application/json', 20 | authorization, 21 | }; 22 | 23 | // const uri = manifest?.debuggerHost ? `http://${manifest.debuggerHost.split(':').shift().concat(':9001')}` : API_URL; 24 | const uri = API_URL; 25 | 26 | const response = await fetch(`${uri}/graphql`, { 27 | method: 'POST', 28 | headers, 29 | body: JSON.stringify({ 30 | query: operation.text, // GraphQL text from input 31 | variables, 32 | }), 33 | }) 34 | 35 | return response.json() 36 | } 37 | -------------------------------------------------------------------------------- /packages/server/src/modules/userPoints/UserPointsModel.ts: -------------------------------------------------------------------------------- 1 | import mongoose, { Document, Model } from 'mongoose'; 2 | 3 | const { ObjectId } = mongoose.Schema.Types; 4 | const Schema = mongoose.Schema; 5 | 6 | const UserPointsSchema = new Schema( 7 | { 8 | points: { 9 | type: Number, 10 | required: false, 11 | }, 12 | storeId: { 13 | type: ObjectId, 14 | ref: 'Store', 15 | required: false, 16 | }, 17 | userId: { 18 | type: ObjectId, 19 | ref: 'User', 20 | required: false, 21 | }, 22 | }, 23 | { 24 | collection: 'UserPoints', 25 | timestamps: { 26 | createdAt: 'createdAt', 27 | updatedAt: 'updatedAt', 28 | }, 29 | } 30 | ); 31 | 32 | export interface IUserPoints extends Document { 33 | readonly points: number; 34 | readonly storeId: string; 35 | readonly userId: string; 36 | readonly createdAt: Date; 37 | readonly updatedAt: Date; 38 | } 39 | 40 | const UserPointsModel: Model = mongoose.model('UserPoints', UserPointsSchema); 41 | 42 | export default UserPointsModel; 43 | -------------------------------------------------------------------------------- /packages/app/src/components/ui/Button.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | ActivityIndicator, 4 | GestureResponderEvent, 5 | StyleProp, 6 | Text, 7 | TouchableOpacity, 8 | ViewStyle, 9 | } from 'react-native'; 10 | 11 | type ButtonProps = { 12 | readonly title: string; 13 | readonly onPress?: (event: GestureResponderEvent) => void; 14 | readonly style?: StyleProp; 15 | readonly isLoading?: boolean; 16 | readonly disabled?: boolean; 17 | }; 18 | 19 | export function Button({ 20 | title, 21 | onPress, 22 | style, 23 | isLoading = false, 24 | disabled = false, 25 | }: ButtonProps) { 26 | return ( 27 | 40 | {isLoading ? ( 41 | 42 | ) : ( 43 | {title} 44 | )} 45 | 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /packages/server/src/modules/product/ProductModel.ts: -------------------------------------------------------------------------------- 1 | import mongoose, { Document, Model } from 'mongoose'; 2 | 3 | const { ObjectId } = mongoose.Schema.Types; 4 | const Schema = mongoose.Schema; 5 | 6 | const ProductSchema = new Schema( 7 | { 8 | name: { 9 | type: String, 10 | required: 'name is required', 11 | }, 12 | description: { 13 | type: String, 14 | required: false, 15 | }, 16 | pictureUrl: { 17 | type: String, 18 | required: false, 19 | }, 20 | points: { 21 | type: Number, 22 | required: false, 23 | }, 24 | storeId: { 25 | type: ObjectId, 26 | ref: 'Store', 27 | required: false, 28 | }, 29 | }, 30 | { 31 | collection: 'Product', 32 | timestamps: { 33 | createdAt: 'createdAt', 34 | updatedAt: 'updatedAt', 35 | }, 36 | } 37 | ); 38 | 39 | export interface IProduct extends Document { 40 | readonly name: string; 41 | readonly description: string; 42 | readonly pictureUrl: string; 43 | readonly points: number; 44 | readonly storeId: string; 45 | readonly createdAt: Date; 46 | readonly updatedAt: Date; 47 | } 48 | 49 | const ProductModel: Model = mongoose.model('Product', ProductSchema); 50 | 51 | export default ProductModel; 52 | -------------------------------------------------------------------------------- /packages/server/src/modules/userStore/mutations/UserStoreCreate.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLID, GraphQLInt, GraphQLNonNull, GraphQLString } from 'graphql'; 2 | import { mutationWithClientMutationId } from 'graphql-relay'; 3 | 4 | import { errorField, successField } from '../../../graphql'; 5 | 6 | import * as UserStoreLoader from '../UserStoreLoader'; 7 | import UserStore from '../UserStoreModel'; 8 | import UserStoreType from '../UserStoreType'; 9 | 10 | export default mutationWithClientMutationId({ 11 | name: 'UserStoreCreate', 12 | inputFields: { 13 | storeId: { 14 | type: new GraphQLNonNull(GraphQLID), 15 | }, 16 | userId: { 17 | type: new GraphQLNonNull(GraphQLID), 18 | }, 19 | }, 20 | mutateAndGetPayload: async ({ id, storeId, userId }) => { 21 | const userStore = await new UserStore({ 22 | id, 23 | storeId, 24 | userId 25 | }).save(); 26 | 27 | return { 28 | id: userStore.id, 29 | success: 'User points register success', 30 | }; 31 | }, 32 | outputFields: { 33 | userStore: { 34 | type: UserStoreType, 35 | resolve: async ({ id }, _, context) => { 36 | return await UserStoreLoader.load(context, id); 37 | }, 38 | }, 39 | ...errorField, 40 | ...successField, 41 | }, 42 | }); 43 | -------------------------------------------------------------------------------- /packages/app/src/components/Home/Store.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Image, Text, View } from 'react-native'; 3 | import { useNavigation } from '@react-navigation/native'; 4 | 5 | import { Button } from '../ui/Button'; 6 | 7 | export function Store({ store }) { 8 | const { _id, pictureUrl, name, description } = store; 9 | 10 | const { navigate } = useNavigation(); 11 | 12 | return ( 13 | 25 | 26 | 30 | 31 | {name} 32 | {description} 33 | 34 | 35 |