├── .nvmrc
├── apps
├── .gitkeep
├── next-app
│ ├── public
│ │ └── .gitkeep
│ ├── index.d.ts
│ ├── next-env.d.ts
│ ├── pages
│ │ ├── index.tsx
│ │ ├── _app.tsx
│ │ └── api
│ │ │ └── trpc
│ │ │ └── [trpc].ts
│ ├── specs
│ │ └── index.spec.tsx
│ ├── jest.config.ts
│ ├── next.config.js
│ ├── tsconfig.spec.json
│ ├── tsconfig.json
│ ├── .eslintrc.json
│ └── project.json
├── mobile
│ ├── test-setup.ts
│ ├── assets
│ │ ├── icon.png
│ │ ├── splash.png
│ │ ├── favicon.png
│ │ ├── adaptive-icon.png
│ │ └── star.svg
│ ├── src
│ │ └── app
│ │ │ ├── icons
│ │ │ ├── logo.png
│ │ │ ├── chevron-right.svg
│ │ │ ├── terminal.svg
│ │ │ ├── heart.svg
│ │ │ ├── pointer.svg
│ │ │ ├── blog.svg
│ │ │ ├── book.svg
│ │ │ ├── nx-cloud.svg
│ │ │ ├── youtube.svg
│ │ │ ├── vscode.svg
│ │ │ ├── courses.svg
│ │ │ ├── checkmark.svg
│ │ │ └── github.svg
│ │ │ ├── App.spec.tsx
│ │ │ ├── ReactotronConfig.ts
│ │ │ └── App.tsx
│ ├── babel.config.js
│ ├── tsconfig.app.json
│ ├── jest.config.ts
│ ├── index.js
│ ├── .expo-shared
│ │ ├── assets.json
│ │ └── README.md
│ ├── tsconfig.spec.json
│ ├── .eslintrc.json
│ ├── webpack.config.js
│ ├── tsconfig.json
│ ├── jest.config.js
│ ├── eas.json
│ ├── .gitignore
│ ├── metro.config.js
│ ├── app.config.js
│ ├── package.json
│ └── project.json
├── next-app-e2e
│ ├── src
│ │ ├── support
│ │ │ ├── app.po.ts
│ │ │ ├── index.ts
│ │ │ └── commands.ts
│ │ ├── fixtures
│ │ │ └── example.json
│ │ └── integration
│ │ │ └── app.spec.ts
│ ├── .eslintrc.json
│ ├── tsconfig.json
│ ├── cypress.json
│ └── project.json
└── mobile-e2e
│ ├── test-setup.ts
│ ├── .babelrc
│ ├── tsconfig.json
│ ├── tsconfig.e2e.json
│ ├── src
│ └── app.spec.ts
│ ├── .eslintrc.json
│ ├── jest.config.json
│ ├── environment.js
│ ├── project.json
│ └── .detoxrc.json
├── libs
├── .gitkeep
├── database
│ └── edgedb-client
│ │ ├── .gitignore
│ │ ├── package.json
│ │ ├── src
│ │ ├── lib
│ │ │ ├── client.ts
│ │ │ └── edgedb-client.ts
│ │ └── index.ts
│ │ ├── .babelrc
│ │ ├── tsconfig.spec.json
│ │ ├── tsconfig.lib.json
│ │ ├── README.md
│ │ ├── .eslintrc.json
│ │ ├── jest.config.ts
│ │ ├── tsconfig.json
│ │ └── project.json
├── frontend
│ ├── trpc-client
│ │ ├── src
│ │ │ ├── index.ts
│ │ │ └── lib
│ │ │ │ ├── trpc-client.spec.ts
│ │ │ │ └── trpc-client.ts
│ │ ├── .babelrc
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.lib.json
│ │ ├── README.md
│ │ ├── .eslintrc.json
│ │ ├── jest.config.ts
│ │ ├── tsconfig.spec.json
│ │ └── project.json
│ └── mobile-app
│ │ ├── .babelrc
│ │ ├── test-setup.ts
│ │ ├── custom.d.ts
│ │ ├── src
│ │ ├── index.ts
│ │ └── lib
│ │ │ ├── features
│ │ │ ├── movie
│ │ │ │ └── detail-screen
│ │ │ │ │ ├── stringifyMovieId.tsx
│ │ │ │ │ ├── parseMovieId.tsx
│ │ │ │ │ ├── DetailScreen.spec.tsx
│ │ │ │ │ └── DetailScreen.tsx
│ │ │ └── home
│ │ │ │ ├── Home.spec.tsx
│ │ │ │ ├── ButtonLink.tsx
│ │ │ │ ├── Home.tsx
│ │ │ │ └── MoviePoster.tsx
│ │ │ ├── navigation
│ │ │ ├── Navigation.spec.tsx
│ │ │ ├── MovieList.tsx
│ │ │ ├── Navigation.tsx
│ │ │ ├── MyWatchlist.tsx
│ │ │ ├── RootNavigator.tsx
│ │ │ ├── tmdb_logo.svg
│ │ │ ├── TabStack.tsx
│ │ │ └── AuthStack.tsx
│ │ │ ├── native-base
│ │ │ ├── NativeBase.spec.tsx
│ │ │ └── NativeBaseProvider.tsx
│ │ │ └── providers
│ │ │ ├── Provider.tsx
│ │ │ ├── AuthenticationProvider.tsx
│ │ │ ├── NavigationProvider.tsx
│ │ │ └── TRPCProvider.tsx
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── tsconfig.lib.json
│ │ ├── jest.config.ts
│ │ ├── tsconfig.spec.json
│ │ ├── .eslintrc.json
│ │ ├── tsconfig.json
│ │ ├── jest.config.js
│ │ └── project.json
└── api
│ └── trpc-server-edgedb
│ ├── package.json
│ ├── .babelrc
│ ├── src
│ ├── index.ts
│ └── lib
│ │ ├── router.ts
│ │ ├── MoviesSchema.ts
│ │ ├── context.ts
│ │ ├── trpc-helper.ts
│ │ ├── user-router.ts
│ │ └── tmdb-router.ts
│ ├── tsconfig.spec.json
│ ├── tsconfig.lib.json
│ ├── README.md
│ ├── .eslintrc.json
│ ├── jest.config.ts
│ ├── tsconfig.json
│ └── project.json
├── tools
├── generators
│ └── .gitkeep
└── tsconfig.tools.json
├── edgedb.toml
├── .env.example
├── babel.config.json
├── .prettierrc
├── ss-signin.png
├── ss-signout.png
├── ss-splash.png
├── edgedb-globals.png
├── ss-mywatchlist.png
├── ss-nowplaying.png
├── .prettierignore
├── jest.preset.js
├── jest.config.ts
├── .vscode
└── extensions.json
├── .editorconfig
├── dbschema
├── migrations
│ ├── 00003.edgeql
│ ├── 00005.edgeql
│ ├── 00002.edgeql
│ ├── 00004.edgeql
│ ├── 00007.edgeql
│ ├── 00001.edgeql
│ └── 00006.edgeql
└── default.esdl
├── workspace.json
├── .github
└── workflows
│ └── develop.yml
├── .eslintrc.json
├── .gitignore
├── nx.json
├── tsconfig.base.json
├── README.md
└── package.json
/.nvmrc:
--------------------------------------------------------------------------------
1 | 16
--------------------------------------------------------------------------------
/apps/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libs/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tools/generators/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/next-app/public/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/edgedb.toml:
--------------------------------------------------------------------------------
1 | [edgedb]
2 | server-version = "2.0"
3 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | TMDB_BEARER_TOKEN='your-tmdb-bearer-token'
--------------------------------------------------------------------------------
/babel.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "babelrcRoots": ["*"]
3 | }
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "semi": false
4 | }
5 |
--------------------------------------------------------------------------------
/libs/database/edgedb-client/.gitignore:
--------------------------------------------------------------------------------
1 | codegen
2 | src/lib/codegen
3 |
--------------------------------------------------------------------------------
/libs/frontend/trpc-client/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/trpc-client';
2 |
--------------------------------------------------------------------------------
/apps/mobile/test-setup.ts:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-native/extend-expect';
2 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"]
3 | }
4 |
--------------------------------------------------------------------------------
/apps/next-app-e2e/src/support/app.po.ts:
--------------------------------------------------------------------------------
1 | export const getGreeting = () => cy.get('h1');
2 |
--------------------------------------------------------------------------------
/ss-signin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwarger/tmdb-watchlist-edgedb/HEAD/ss-signin.png
--------------------------------------------------------------------------------
/ss-signout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwarger/tmdb-watchlist-edgedb/HEAD/ss-signout.png
--------------------------------------------------------------------------------
/ss-splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwarger/tmdb-watchlist-edgedb/HEAD/ss-splash.png
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/test-setup.ts:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-native/extend-expect';
2 |
--------------------------------------------------------------------------------
/edgedb-globals.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwarger/tmdb-watchlist-edgedb/HEAD/edgedb-globals.png
--------------------------------------------------------------------------------
/ss-mywatchlist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwarger/tmdb-watchlist-edgedb/HEAD/ss-mywatchlist.png
--------------------------------------------------------------------------------
/ss-nowplaying.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwarger/tmdb-watchlist-edgedb/HEAD/ss-nowplaying.png
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Add files here to ignore them from prettier formatting
2 |
3 | /dist
4 | /coverage
5 |
--------------------------------------------------------------------------------
/libs/frontend/trpc-client/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]]
3 | }
4 |
--------------------------------------------------------------------------------
/jest.preset.js:
--------------------------------------------------------------------------------
1 | const nxPreset = require('@nrwl/jest/preset').default;
2 |
3 | module.exports = { ...nxPreset };
4 |
--------------------------------------------------------------------------------
/apps/mobile/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwarger/tmdb-watchlist-edgedb/HEAD/apps/mobile/assets/icon.png
--------------------------------------------------------------------------------
/apps/mobile/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwarger/tmdb-watchlist-edgedb/HEAD/apps/mobile/assets/splash.png
--------------------------------------------------------------------------------
/libs/frontend/trpc-client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@conference-demos/trpc-client",
3 | "version": "0.0.1"
4 | }
5 |
--------------------------------------------------------------------------------
/apps/mobile/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwarger/tmdb-watchlist-edgedb/HEAD/apps/mobile/assets/favicon.png
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/custom.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.svg' {
2 | const content: any
3 | export default content
4 | }
5 |
--------------------------------------------------------------------------------
/apps/mobile/src/app/icons/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwarger/tmdb-watchlist-edgedb/HEAD/apps/mobile/src/app/icons/logo.png
--------------------------------------------------------------------------------
/jest.config.ts:
--------------------------------------------------------------------------------
1 | import { getJestProjects } from '@nrwl/jest';
2 |
3 | export default {
4 | projects: getJestProjects(),
5 | };
6 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/navigation/RootNavigator'
2 | export * from './lib/providers/Provider'
3 |
--------------------------------------------------------------------------------
/apps/mobile-e2e/test-setup.ts:
--------------------------------------------------------------------------------
1 | import { device } from 'detox';
2 |
3 | beforeAll(async () => {
4 | await device.launchApp();
5 | });
6 |
--------------------------------------------------------------------------------
/apps/mobile/assets/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwarger/tmdb-watchlist-edgedb/HEAD/apps/mobile/assets/adaptive-icon.png
--------------------------------------------------------------------------------
/apps/next-app-e2e/src/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io"
4 | }
5 |
--------------------------------------------------------------------------------
/libs/database/edgedb-client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@conference-demos/edgedb-client",
3 | "version": "0.0.1",
4 | "type": "commonjs"
5 | }
6 |
--------------------------------------------------------------------------------
/libs/api/trpc-server-edgedb/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@conference-demos/api/trpc-server-edgedb",
3 | "version": "0.0.1",
4 | "type": "commonjs"
5 | }
6 |
--------------------------------------------------------------------------------
/libs/database/edgedb-client/src/lib/client.ts:
--------------------------------------------------------------------------------
1 | import * as edgedb from 'edgedb'
2 |
3 | export const client = edgedb.createClient({
4 | logging: true,
5 | })
6 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@conference-demos/frontend-mobile-app",
3 | "version": "0.0.1",
4 | "main": "src/index.ts"
5 | }
6 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/features/movie/detail-screen/stringifyMovieId.tsx:
--------------------------------------------------------------------------------
1 | export function stringifyMovieId(value: number) {
2 | return value.toString()
3 | }
4 |
--------------------------------------------------------------------------------
/libs/api/trpc-server-edgedb/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@nrwl/web/babel",
5 | {
6 | "useBuiltIns": "usage"
7 | }
8 | ]
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/libs/database/edgedb-client/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@nrwl/web/babel",
5 | {
6 | "useBuiltIns": "usage"
7 | }
8 | ]
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/mobile-e2e/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@nrwl/react/babel",
5 | {
6 | "runtime": "automatic"
7 | }
8 | ]
9 | ],
10 | "plugins": []
11 | }
12 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "nrwl.angular-console",
4 | "esbenp.prettier-vscode",
5 | "firsttris.vscode-jest-runner",
6 | "dbaeumer.vscode-eslint"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/apps/mobile/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function (api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | plugins: ['react-native-reanimated/plugin'],
6 | };
7 | };
8 |
--------------------------------------------------------------------------------
/apps/next-app/index.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | declare module '*.svg' {
3 | const content: any;
4 | export const ReactComponent: any;
5 | export default content;
6 | }
7 |
--------------------------------------------------------------------------------
/apps/mobile-e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.e2e.json"
8 | }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/libs/database/edgedb-client/src/index.ts:
--------------------------------------------------------------------------------
1 | import defaultExport from './lib/codegen';
2 | export * from './lib/client'
3 | export * from './lib/edgedb-client'
4 | export * from './lib/codegen'
5 | export const e = defaultExport
6 |
--------------------------------------------------------------------------------
/apps/mobile/src/app/icons/chevron-right.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/next-app/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/libs/frontend/trpc-client/src/lib/trpc-client.spec.ts:
--------------------------------------------------------------------------------
1 | import { trpcClient } from './trpc-client';
2 |
3 | describe('trpcClient', () => {
4 | it('should work', () => {
5 | expect(trpcClient()).toEqual('trpc-client');
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/README.md:
--------------------------------------------------------------------------------
1 | # frontend-mobile-app
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `nx test frontend-mobile-app` to execute the unit tests via [Jest](https://jestjs.io).
8 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/features/movie/detail-screen/parseMovieId.tsx:
--------------------------------------------------------------------------------
1 | export function parseMovieId(id: string | string[] | undefined) {
2 | if (!id) return 0
3 |
4 | if (Array.isArray(id)) return 0
5 |
6 | return parseInt(id, 10)
7 | }
8 |
--------------------------------------------------------------------------------
/libs/api/trpc-server-edgedb/src/index.ts:
--------------------------------------------------------------------------------
1 | export { appRouter } from './lib/router'
2 | export type { AppRouter } from './lib/router'
3 | export { createContext } from './lib/context'
4 | export * from './lib/MoviesSchema'
5 | export * from './lib/trpc-helper'
6 |
--------------------------------------------------------------------------------
/apps/next-app-e2e/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:cypress/recommended", "../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/next-app/pages/index.tsx:
--------------------------------------------------------------------------------
1 | export function Index() {
2 | /*
3 | * Replace the elements below with your own.
4 | *
5 | * Note: The corresponding styles are in the ./index.css file.
6 | */
7 | return
hello trpc
8 | }
9 |
10 | export default Index;
11 |
--------------------------------------------------------------------------------
/apps/mobile/src/app/icons/terminal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libs/frontend/trpc-client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.lib.json"
8 | },
9 | {
10 | "path": "./tsconfig.spec.json"
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/apps/mobile-e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "sourceMap": false,
5 | "outDir": "../../dist/out-tsc",
6 | "allowJs": true,
7 | "types": ["node", "jest", "detox"]
8 | },
9 | "include": ["src/**/*.ts", "src/**/*.js"]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/next-app-e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "sourceMap": false,
5 | "outDir": "../../dist/out-tsc",
6 | "allowJs": true,
7 | "types": ["cypress", "node"]
8 | },
9 | "include": ["src/**/*.ts", "src/**/*.js"]
10 | }
11 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/dbschema/migrations/00003.edgeql:
--------------------------------------------------------------------------------
1 | CREATE MIGRATION m1mmqv3hkzvt7bbdb3zguv2t3t4fffzcktvjvfj5cmtdghwgprkbia
2 | ONTO m12zsfbmdex4f7rbpkzdgwd3pwrygt3ivofmd52jw4hk55x5oxf3nq
3 | {
4 | ALTER TYPE default::User {
5 | ALTER LINK watchList {
6 | RESET ON TARGET DELETE;
7 | };
8 | };
9 | };
10 |
--------------------------------------------------------------------------------
/dbschema/migrations/00005.edgeql:
--------------------------------------------------------------------------------
1 | CREATE MIGRATION m1dkmumathom2dvjzz33v6kzlozzo4ti47wi2ruvbbui6l3qlhr2da
2 | ONTO m17t62wnp52hxp5mfnpso2wkx74y7vjm3giykplvibywjyhe6oimbq
3 | {
4 | ALTER TYPE default::User {
5 | ALTER LINK watchList {
6 | ON TARGET DELETE ALLOW;
7 | };
8 | };
9 | };
10 |
--------------------------------------------------------------------------------
/libs/api/trpc-server-edgedb/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/libs/database/edgedb-client/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/apps/mobile/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "types": ["node"],
6 | },
7 |
8 | "exclude": ["**/*.spec.ts", "**/*.spec.tsx", "test-setup.ts"],
9 | "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]
10 | }
11 |
--------------------------------------------------------------------------------
/dbschema/migrations/00002.edgeql:
--------------------------------------------------------------------------------
1 | CREATE MIGRATION m12zsfbmdex4f7rbpkzdgwd3pwrygt3ivofmd52jw4hk55x5oxf3nq
2 | ONTO m1lhwnequj3gz3a5k5qy224mmmguvahqbxymup2f2fruhb7s4h3g6a
3 | {
4 | ALTER TYPE default::User {
5 | ALTER LINK watchList {
6 | ON TARGET DELETE DELETE SOURCE;
7 | };
8 | };
9 | };
10 |
--------------------------------------------------------------------------------
/dbschema/migrations/00004.edgeql:
--------------------------------------------------------------------------------
1 | CREATE MIGRATION m17t62wnp52hxp5mfnpso2wkx74y7vjm3giykplvibywjyhe6oimbq
2 | ONTO m1mmqv3hkzvt7bbdb3zguv2t3t4fffzcktvjvfj5cmtdghwgprkbia
3 | {
4 | ALTER TYPE default::User {
5 | ALTER LINK watchList {
6 | ON TARGET DELETE DELETE SOURCE;
7 | };
8 | };
9 | };
10 |
--------------------------------------------------------------------------------
/libs/api/trpc-server-edgedb/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "declaration": true,
6 | "types": []
7 | },
8 | "include": ["**/*.ts"],
9 | "exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/libs/database/edgedb-client/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "declaration": true,
6 | "types": []
7 | },
8 | "include": ["**/*.ts"],
9 | "exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/mobile/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'mobile',
4 | preset: '../../jest.preset.js',
5 | transform: {
6 | '^.+\\.[tj]sx?$': 'babel-jest',
7 | },
8 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
9 | coverageDirectory: '../../coverage/apps/mobile',
10 | };
11 |
--------------------------------------------------------------------------------
/apps/mobile/src/app/App.spec.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { render } from '@testing-library/react-native';
3 |
4 | import App from './App';
5 |
6 | test('renders correctly', () => {
7 | const { getByTestId } = render();
8 | expect(getByTestId('heading')).toHaveTextContent('Welcome');
9 | });
10 |
--------------------------------------------------------------------------------
/libs/database/edgedb-client/README.md:
--------------------------------------------------------------------------------
1 | # edgedb-client
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Building
6 |
7 | Run `nx build edgedb-client` to build the library.
8 |
9 | ## Running unit tests
10 |
11 | Run `nx test edgedb-client` to execute the unit tests via [Jest](https://jestjs.io).
12 |
--------------------------------------------------------------------------------
/tools/tsconfig.tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "../dist/out-tsc/tools",
5 | "rootDir": ".",
6 | "module": "commonjs",
7 | "target": "es5",
8 | "types": ["node"],
9 | "importHelpers": false
10 | },
11 | "include": ["**/*.ts"]
12 | }
13 |
--------------------------------------------------------------------------------
/apps/mobile/src/app/icons/heart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dbschema/migrations/00007.edgeql:
--------------------------------------------------------------------------------
1 | CREATE MIGRATION m1iymnmz6qvqvs3kkxn6xi2gs4tdmeephzliznocat5nsjsgurcm5a
2 | ONTO m16io3iwyqlsurvqp4ymklkljrzp4cwjoobmfazp7fcfyghtdmaeqa
3 | {
4 | ALTER TYPE default::User {
5 | CREATE ACCESS POLICY own_user
6 | ALLOW ALL USING ((.uid ?= GLOBAL default::current_user));
7 | };
8 | };
9 |
--------------------------------------------------------------------------------
/apps/mobile/src/app/icons/pointer.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/mobile/src/app/icons/blog.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/mobile/index.js:
--------------------------------------------------------------------------------
1 | import { registerRootComponent } from 'expo'
2 |
3 | import App from './src/app/App'
4 |
5 | // registerRootComponent calls AppRegistry.registerComponent('main', () => App);
6 | // It also ensures that whether you load the app in Expo Go or in a native build,
7 | // the environment is set up appropriately
8 | registerRootComponent(App)
9 |
--------------------------------------------------------------------------------
/apps/next-app/specs/index.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 |
4 | import Index from '../pages/index';
5 |
6 | describe('Index', () => {
7 | it('should render successfully', () => {
8 | const { baseElement } = render();
9 | expect(baseElement).toBeTruthy();
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/libs/api/trpc-server-edgedb/README.md:
--------------------------------------------------------------------------------
1 | # api-trpc-server-edgedb
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Building
6 |
7 | Run `nx build api-trpc-server-edgedb` to build the library.
8 |
9 | ## Running unit tests
10 |
11 | Run `nx test api-trpc-server-edgedb` to execute the unit tests via [Jest](https://jestjs.io).
12 |
--------------------------------------------------------------------------------
/libs/frontend/trpc-client/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "outDir": "../../../dist/out-tsc",
6 | "declaration": true,
7 | "types": ["node"]
8 | },
9 | "exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts"],
10 | "include": ["**/*.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "types": ["node"]
6 | },
7 | "exclude": ["**/*.spec.ts", "**/*.spec.tsx", "test-setup.ts", "jest.config.ts"],
8 | "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx", "**/*.d.ts"],
9 | }
10 |
--------------------------------------------------------------------------------
/apps/mobile-e2e/src/app.spec.ts:
--------------------------------------------------------------------------------
1 | import { device, element, by, expect } from 'detox';
2 |
3 | describe('Mobile', () => {
4 | beforeEach(async () => {
5 | await device.reloadReactNative();
6 | });
7 |
8 | it('should display welcome message', async () => {
9 | await expect(element(by.id('heading'))).toHaveText('Welcome Mobile 👋');
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'frontend-mobile-app',
4 | preset: '../../../jest.preset.js',
5 | transform: {
6 | '^.+\\.[tj]sx?$': 'babel-jest',
7 | },
8 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
9 | coverageDirectory: '../../../coverage/libs/frontend/mobile-app',
10 | };
11 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/features/home/Home.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react-native';
3 |
4 | import Home from './Home';
5 |
6 | describe('Home', () => {
7 | it('should render successfully', () => {
8 | const { container } = render();
9 | expect(container).toBeTruthy();
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/libs/frontend/trpc-client/README.md:
--------------------------------------------------------------------------------
1 | # frontend-trpc-client
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `nx test frontend-trpc-client` to execute the unit tests via [Jest](https://jestjs.io).
8 |
9 | ## Running lint
10 |
11 | Run `nx lint frontend-trpc-client` to execute the lint via [ESLint](https://eslint.org/).
12 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/navigation/Navigation.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react-native';
3 |
4 | import Navigation from './Navigation';
5 |
6 | describe('Navigation', () => {
7 | it('should render successfully', () => {
8 | const { container } = render();
9 | expect(container).toBeTruthy();
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/native-base/NativeBase.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react-native';
3 |
4 | import NativeBase from './NativeBaseProvider'
5 |
6 | describe('NativeBase', () => {
7 | it('should render successfully', () => {
8 | const { container } = render();
9 | expect(container).toBeTruthy();
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/apps/mobile/assets/star.svg:
--------------------------------------------------------------------------------
1 |
2 |
12 |
--------------------------------------------------------------------------------
/libs/api/trpc-server-edgedb/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | },
9 | {
10 | "files": ["*.ts", "*.tsx"],
11 | "rules": {}
12 | },
13 | {
14 | "files": ["*.js", "*.jsx"],
15 | "rules": {}
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/libs/database/edgedb-client/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | },
9 | {
10 | "files": ["*.ts", "*.tsx"],
11 | "rules": {}
12 | },
13 | {
14 | "files": ["*.js", "*.jsx"],
15 | "rules": {}
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/libs/frontend/trpc-client/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | },
9 | {
10 | "files": ["*.ts", "*.tsx"],
11 | "rules": {}
12 | },
13 | {
14 | "files": ["*.js", "*.jsx"],
15 | "rules": {}
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/apps/mobile/.expo-shared/assets.json:
--------------------------------------------------------------------------------
1 | {
2 | "f38fa18336efcf5d823eabc8ae6be42137a0ba80dc02a7638b3acfce6f57ceaa": true,
3 | "5f4c0a732b6325bf4071d9124d2ae67e037cb24fcc9c482ef82bea742109a3b8": true,
4 | "a33587a8984d9699cb920876e55ebfea4a60f017312da8687c7e3e8768f97eb1": true,
5 | "2a0b82777a71f08eaecaf8c8a5b62c282fb1c4e054c2f9092d15b09c93d97a23": true,
6 | "f174b754e326fc2ddf18aed31ada557c4379d02e245e10d537e9241d1e4b721f": true
7 | }
8 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/features/movie/detail-screen/DetailScreen.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from '@testing-library/react-native'
3 |
4 | import { MovieDetailScreen } from './DetailScreen'
5 |
6 | describe('DetailScreen', () => {
7 | it('should render successfully', () => {
8 | const { container } = render()
9 | expect(container).toBeTruthy()
10 | })
11 | })
12 |
--------------------------------------------------------------------------------
/apps/mobile-e2e/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | },
9 | {
10 | "files": ["*.ts", "*.tsx"],
11 | "rules": {}
12 | },
13 | {
14 | "files": ["*.js", "*.jsx"],
15 | "rules": {}
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/apps/next-app/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import { AppProps } from 'next/app';
2 | import Head from 'next/head'
3 |
4 | function CustomApp({ Component, pageProps }: AppProps) {
5 | return (
6 | <>
7 |
8 | Welcome to next-app!
9 |
10 |
11 |
12 |
13 | >
14 | );
15 | }
16 |
17 | export default CustomApp;
18 |
--------------------------------------------------------------------------------
/apps/mobile/src/app/icons/book.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/next-app/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'next-app',
4 | preset: '../../jest.preset.js',
5 | transform: {
6 | '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
7 | '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/next/babel'] }],
8 | },
9 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
10 | coverageDirectory: '../../coverage/apps/next-app',
11 | };
12 |
--------------------------------------------------------------------------------
/apps/mobile/src/app/icons/nx-cloud.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/mobile-e2e/jest.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "preset": "../../jest.preset",
3 | "testEnvironment": "./environment",
4 | "testRunner": "jest-circus/runner",
5 | "testTimeout": 120000,
6 | "reporters": ["detox/runners/jest/streamlineReporter"],
7 | "setupFilesAfterEnv": ["/test-setup.ts"],
8 | "transform": {
9 | "^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "@nrwl/react/plugins/jest",
10 | "^.+\\.[tj]sx?$": "babel-jest"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/apps/next-app/next.config.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/no-var-requires
2 | const withNx = require('@nrwl/next/plugins/with-nx');
3 |
4 | /**
5 | * @type {import('@nrwl/next/plugins/with-nx').WithNxOptions}
6 | **/
7 | const nextConfig = {
8 | nx: {
9 | // Set this to true if you would like to to use SVGR
10 | // See: https://github.com/gregberge/svgr
11 | svgr: false,
12 | },
13 | };
14 |
15 | module.exports = withNx(nextConfig);
16 |
--------------------------------------------------------------------------------
/libs/database/edgedb-client/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'edgedb-client',
4 | preset: '../../../jest.preset.js',
5 | globals: {
6 | 'ts-jest': {
7 | tsconfig: '/tsconfig.spec.json',
8 | },
9 | },
10 | transform: {
11 | '^.+\\.[tj]s$': 'ts-jest',
12 | },
13 | moduleFileExtensions: ['ts', 'js', 'html'],
14 | coverageDirectory: '../../../coverage/libs/database/edgedb-client',
15 | };
16 |
--------------------------------------------------------------------------------
/apps/next-app-e2e/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileServerFolder": ".",
3 | "fixturesFolder": "./src/fixtures",
4 | "integrationFolder": "./src/integration",
5 | "modifyObstructiveCode": false,
6 | "supportFile": "./src/support/index.ts",
7 | "pluginsFile": false,
8 | "video": true,
9 | "videosFolder": "../../dist/cypress/apps/next-app-e2e/videos",
10 | "screenshotsFolder": "../../dist/cypress/apps/next-app-e2e/screenshots",
11 | "chromeWebSecurity": false
12 | }
13 |
--------------------------------------------------------------------------------
/libs/api/trpc-server-edgedb/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'api-trpc-server-edgedb',
4 | preset: '../../../jest.preset.js',
5 | globals: {
6 | 'ts-jest': {
7 | tsconfig: '/tsconfig.spec.json',
8 | },
9 | },
10 | transform: {
11 | '^.+\\.[tj]s$': 'ts-jest',
12 | },
13 | moduleFileExtensions: ['ts', 'js', 'html'],
14 | coverageDirectory: '../../../coverage/libs/api/trpc-server-edgedb',
15 | }
16 |
--------------------------------------------------------------------------------
/libs/frontend/trpc-client/src/lib/trpc-client.ts:
--------------------------------------------------------------------------------
1 | import type { AppRouter } from '@conference-demos/api/trpc-server-edgedb'
2 | import { createTRPCClient } from '@trpc/client'
3 | import { createReactQueryHooks } from '@trpc/react'
4 | // import superjson from 'superjson';
5 |
6 | export const client = createTRPCClient({
7 | url: process.env.API_ENDPOINT,
8 | // transformer: superjson,
9 | })
10 |
11 | export const trpc = createReactQueryHooks()
12 |
--------------------------------------------------------------------------------
/apps/mobile/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "jest.config.ts",
10 | "**/*.test.ts",
11 | "**/*.spec.ts",
12 | "**/*.test.tsx",
13 | "**/*.spec.tsx",
14 | "**/*.test.js",
15 | "**/*.spec.js",
16 | "**/*.test.jsx",
17 | "**/*.spec.jsx",
18 | "**/*.d.ts"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/apps/next-app-e2e/src/integration/app.spec.ts:
--------------------------------------------------------------------------------
1 | import { getGreeting } from '../support/app.po';
2 |
3 | describe('next-app', () => {
4 | beforeEach(() => cy.visit('/'));
5 |
6 | it('should display welcome message', () => {
7 | // Custom command example, see `../support/commands.ts` file
8 | cy.login('my-email@something.com', 'myPassword');
9 |
10 | // Function helper example, see `../support/app.po.ts` file
11 | getGreeting().contains('Welcome next-app');
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/apps/mobile/src/app/icons/youtube.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dbschema/migrations/00001.edgeql:
--------------------------------------------------------------------------------
1 | CREATE MIGRATION m1lhwnequj3gz3a5k5qy224mmmguvahqbxymup2f2fruhb7s4h3g6a
2 | ONTO initial
3 | {
4 | CREATE TYPE default::WatchListItem {
5 | CREATE REQUIRED PROPERTY movieId -> std::str;
6 | };
7 | CREATE TYPE default::User {
8 | CREATE MULTI LINK watchList -> default::WatchListItem;
9 | CREATE PROPERTY email -> std::str;
10 | CREATE PROPERTY uid -> std::str {
11 | CREATE CONSTRAINT std::exclusive;
12 | };
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/libs/api/trpc-server-edgedb/src/lib/router.ts:
--------------------------------------------------------------------------------
1 | // src/server/router/index.ts
2 | import { createRouter } from './context';
3 | // import superjson from 'superjson';
4 |
5 | import { tmdbRouter } from './tmdb-router'
6 | import { userDataRouter } from './user-router'
7 |
8 | export const appRouter = createRouter()
9 | // .transformer(superjson)
10 | .merge('tmdb.', tmdbRouter)
11 | .merge('user.', userDataRouter)
12 |
13 | // export type definition of API
14 | export type AppRouter = typeof appRouter;
15 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "jest.config.ts",
10 | "**/*.test.ts",
11 | "**/*.spec.ts",
12 | "**/*.test.tsx",
13 | "**/*.spec.tsx",
14 | "**/*.test.js",
15 | "**/*.spec.js",
16 | "**/*.test.jsx",
17 | "**/*.spec.jsx",
18 | "**/*.d.ts"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/libs/frontend/trpc-client/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'frontend-trpc-client',
4 | preset: '../../../jest.preset.js',
5 | globals: {
6 | 'ts-jest': {
7 | tsconfig: '/tsconfig.spec.json',
8 | },
9 | },
10 | testEnvironment: 'node',
11 | transform: {
12 | '^.+\\.[tj]sx?$': 'ts-jest',
13 | },
14 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
15 | coverageDirectory: '../../../coverage/libs/frontend/trpc-client',
16 | };
17 |
--------------------------------------------------------------------------------
/libs/frontend/trpc-client/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "jest.config.ts",
10 | "**/*.test.ts",
11 | "**/*.spec.ts",
12 | "**/*.test.tsx",
13 | "**/*.spec.tsx",
14 | "**/*.test.js",
15 | "**/*.spec.js",
16 | "**/*.test.jsx",
17 | "**/*.spec.jsx",
18 | "**/*.d.ts"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/apps/next-app/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"],
7 | "jsx": "react"
8 | },
9 | "include": [
10 | "jest.config.ts",
11 | "**/*.test.ts",
12 | "**/*.spec.ts",
13 | "**/*.test.tsx",
14 | "**/*.spec.tsx",
15 | "**/*.test.js",
16 | "**/*.spec.js",
17 | "**/*.test.jsx",
18 | "**/*.spec.jsx",
19 | "**/*.d.ts"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/apps/mobile/src/app/icons/vscode.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dbschema/migrations/00006.edgeql:
--------------------------------------------------------------------------------
1 | CREATE MIGRATION m16io3iwyqlsurvqp4ymklkljrzp4cwjoobmfazp7fcfyghtdmaeqa
2 | ONTO m1dkmumathom2dvjzz33v6kzlozzo4ti47wi2ruvbbui6l3qlhr2da
3 | {
4 | CREATE GLOBAL default::current_user -> std::str;
5 | ALTER TYPE default::WatchListItem {
6 | CREATE LINK user -> default::User;
7 | CREATE ACCESS POLICY own_watchlist
8 | ALLOW ALL USING ((.user.uid ?= GLOBAL default::current_user));
9 | };
10 | ALTER TYPE default::User {
11 | DROP LINK watchList;
12 | };
13 | };
14 |
--------------------------------------------------------------------------------
/workspace.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/nx/schemas/workspace-schema.json",
3 | "version": 2,
4 | "projects": {
5 | "api-trpc-server-edgedb": "libs/api/trpc-server-edgedb",
6 | "edgedb-client": "libs/database/edgedb-client",
7 | "frontend-mobile-app": "libs/frontend/mobile-app",
8 | "frontend-trpc-client": "libs/frontend/trpc-client",
9 | "mobile": "apps/mobile",
10 | "mobile-e2e": "apps/mobile-e2e",
11 | "next-app": "apps/next-app",
12 | "next-app-e2e": "apps/next-app-e2e"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/apps/mobile/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*", ".expo", "node_modules", "web-build"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {
8 | "@typescript-eslint/ban-ts-comment": "off"
9 | }
10 | },
11 | {
12 | "files": ["*.ts", "*.tsx"],
13 | "rules": {}
14 | },
15 | {
16 | "files": ["*.js", "*.jsx"],
17 | "rules": {}
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/features/home/ButtonLink.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Button } from 'native-base';
3 | import { useLink, UseLinkProps } from 'solito/link';
4 |
5 | export function ButtonLink({ href, as, ...props }: ButtonLinkProps) {
6 | const linkProps = useLink({
7 | href,
8 | as,
9 | });
10 |
11 |
12 | return (
13 |
16 | )
17 | }
18 | type ButtonLinkProps = UseLinkProps & { children: React.ReactNode };
19 |
--------------------------------------------------------------------------------
/dbschema/default.esdl:
--------------------------------------------------------------------------------
1 |
2 | module default {
3 | global current_user -> str;
4 |
5 | type User {
6 | property uid -> str {
7 | constraint exclusive;
8 | };
9 | property email -> str;
10 | access policy own_user
11 | allow all
12 | using (.uid ?= global current_user)
13 | }
14 |
15 | type WatchListItem {
16 | required property movieId -> str;
17 | link user -> User;
18 | access policy own_watchlist
19 | allow all
20 | using (.user.uid ?= global current_user)
21 | }
22 |
23 | };
--------------------------------------------------------------------------------
/apps/mobile/webpack.config.js:
--------------------------------------------------------------------------------
1 | const createExpoWebpackConfigAsync = require('@expo/webpack-config');
2 | const { withNxWebpack } = require('@nrwl/expo');
3 |
4 | module.exports = async function (env, argv) {
5 | let config = await createExpoWebpackConfigAsync(env, argv);
6 | config = await withNxWebpack(config);
7 |
8 | // You can override the config here, for example:
9 | // config.resolve.alias = {
10 | // ...config.resolve.alias,
11 | // react: path.resolve('../../node_modules/react'),
12 | // };
13 |
14 | return config;
15 | };
16 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:@nrwl/nx/react", "../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*", ".expo", "node_modules", "web-build"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {
8 | "@typescript-eslint/ban-ts-comment": "off"
9 | }
10 | },
11 | {
12 | "files": ["*.ts", "*.tsx"],
13 | "rules": {}
14 | },
15 | {
16 | "files": ["*.js", "*.jsx"],
17 | "rules": {}
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/apps/mobile/src/app/ReactotronConfig.ts:
--------------------------------------------------------------------------------
1 | import Reactotron, { networking } from 'reactotron-react-native'
2 | import AsyncStorage from '@react-native-async-storage/async-storage'
3 |
4 | Reactotron.setAsyncStorageHandler?.(AsyncStorage) // AsyncStorage would either come from `react-native` or `@react-native-async-storage/async-storage` depending on where you get it from
5 | .configure() // controls connection & communication settings
6 | .useReactNative() // add all built-in react native plugins
7 | .use(networking()) // <--- here we go!
8 | .connect() // let's connect!
9 |
--------------------------------------------------------------------------------
/apps/mobile/src/app/icons/courses.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libs/api/trpc-server-edgedb/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "forceConsistentCasingInFileNames": true,
6 | "strict": true,
7 | "noImplicitOverride": true,
8 | "noPropertyAccessFromIndexSignature": true,
9 | "noImplicitReturns": true,
10 | "noFallthroughCasesInSwitch": true
11 | },
12 | "files": [],
13 | "include": [],
14 | "references": [
15 | {
16 | "path": "./tsconfig.lib.json"
17 | },
18 | {
19 | "path": "./tsconfig.spec.json"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/libs/database/edgedb-client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "forceConsistentCasingInFileNames": true,
6 | "strict": true,
7 | "noImplicitOverride": true,
8 | "noPropertyAccessFromIndexSignature": true,
9 | "noImplicitReturns": true,
10 | "noFallthroughCasesInSwitch": true
11 | },
12 | "files": [],
13 | "include": [],
14 | "references": [
15 | {
16 | "path": "./tsconfig.lib.json"
17 | },
18 | {
19 | "path": "./tsconfig.spec.json"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/apps/mobile/.expo-shared/README.md:
--------------------------------------------------------------------------------
1 | > Why do I have a folder named ".expo-shared" in my project?
2 |
3 | The ".expo-shared" folder is created when running commands that produce state that is intended to be shared with all developers on the project. For example, "npx expo-optimize".
4 |
5 | > What does the "assets.json" file contain?
6 |
7 | The "assets.json" file describes the assets that have been optimized through "expo-optimize" and do not need to be processed again.
8 |
9 | > Should I commit the ".expo-shared" folder?
10 |
11 | Yes, you should share the ".expo-shared" folder with your collaborators.
12 |
--------------------------------------------------------------------------------
/apps/next-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "jsx": "preserve",
5 | "allowJs": true,
6 | "esModuleInterop": true,
7 | "allowSyntheticDefaultImports": true,
8 | "strict": false,
9 | "forceConsistentCasingInFileNames": true,
10 | "noEmit": true,
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "incremental": true,
14 | "types": ["jest", "node"]
15 | },
16 | "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "next-env.d.ts"],
17 | "exclude": ["node_modules", "jest.config.ts"]
18 | }
19 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "jsx": "react-native",
5 | "allowJs": true,
6 | "esModuleInterop": true,
7 | "allowSyntheticDefaultImports": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "strict": true,
10 | "noImplicitReturns": true,
11 | "noFallthroughCasesInSwitch": true
12 | },
13 | "files": [],
14 | "include": [],
15 | "references": [
16 | {
17 | "path": "./tsconfig.lib.json"
18 | },
19 | {
20 | "path": "./tsconfig.spec.json"
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/apps/mobile/src/app/icons/checkmark.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/mobile/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "allowSyntheticDefaultImports": true,
5 | "jsx": "react-native",
6 | "lib": ["dom", "esnext"],
7 | "moduleResolution": "node",
8 | "noEmit": true,
9 | "skipLibCheck": true,
10 | "resolveJsonModule": true,
11 | "strict": true
12 | },
13 | "files": ["../../node_modules/@nrwl/expo/typings/svg.d.ts"],
14 | "include": [],
15 | "references": [
16 | {
17 | "path": "./tsconfig.app.json"
18 | },
19 | {
20 | "path": "./tsconfig.spec.json"
21 | }
22 | ],
23 | "exclude": ["node_modules"]
24 | }
25 |
--------------------------------------------------------------------------------
/apps/next-app/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "plugin:@nrwl/nx/react-typescript",
4 | "../../.eslintrc.json",
5 | "next",
6 | "next/core-web-vitals"
7 | ],
8 | "ignorePatterns": ["!**/*"],
9 | "overrides": [
10 | {
11 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
12 | "rules": {
13 | "@next/next/no-html-link-for-pages": ["error", "apps/next-app/pages"]
14 | }
15 | },
16 | {
17 | "files": ["*.ts", "*.tsx"],
18 | "rules": {}
19 | },
20 | {
21 | "files": ["*.js", "*.jsx"],
22 | "rules": {}
23 | }
24 | ],
25 | "env": {
26 | "jest": true
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/apps/next-app-e2e/src/support/index.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands';
18 |
--------------------------------------------------------------------------------
/apps/mobile-e2e/environment.js:
--------------------------------------------------------------------------------
1 | const {
2 | DetoxCircusEnvironment,
3 | SpecReporter,
4 | WorkerAssignReporter,
5 | } = require('detox/runners/jest-circus');
6 |
7 | class CustomDetoxEnvironment extends DetoxCircusEnvironment {
8 | constructor(config, context) {
9 | super(config, context);
10 |
11 | // Can be safely removed, if you are content with the default value (=300000ms)
12 | this.initTimeout = 300000;
13 |
14 | // This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level.
15 | // This is strictly optional.
16 | this.registerListeners({
17 | SpecReporter,
18 | WorkerAssignReporter,
19 | });
20 | }
21 | }
22 |
23 | module.exports = CustomDetoxEnvironment;
24 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/providers/Provider.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import NativeBaseProvider from '../native-base/NativeBaseProvider'
3 | import { NavigationProvider } from './NavigationProvider'
4 | import { AuthenticationProvider } from './AuthenticationProvider'
5 | import { TRPCProvider } from './TRPCProvider'
6 |
7 | export interface ProviderProps {
8 | children: React.ReactNode
9 | }
10 |
11 | export function Provider({ children }: ProviderProps) {
12 | return (
13 |
14 |
15 |
16 | {children}
17 |
18 |
19 |
20 | )
21 | }
22 |
23 | export default Provider
24 |
--------------------------------------------------------------------------------
/.github/workflows/develop.yml:
--------------------------------------------------------------------------------
1 | name: develop
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 |
9 | jobs:
10 | main:
11 | name: Nx Cloud - Main Job
12 | uses: nrwl/ci/.github/workflows/nx-cloud-main.yml@v0.4
13 | with:
14 | parallel-commands: |
15 | yarn nx-cloud record -- yarn nx workspace-lint
16 | yarn nx-cloud record -- yarn nx format:check
17 | parallel-commands-on-agents: |
18 | yarn nx affected --target=lint --parallel=3
19 | yarn nx affected --target=test --parallel=3 --ci --code-coverage
20 | yarn nx affected --target=build --parallel=3
21 |
22 | agents:
23 | name: Nx Cloud - Agents
24 | uses: nrwl/ci/.github/workflows/nx-cloud-agents.yml@v0.4
25 | with:
26 | number-of-agents: 3
27 |
--------------------------------------------------------------------------------
/apps/mobile/src/app/icons/github.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libs/api/trpc-server-edgedb/src/lib/MoviesSchema.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod';
2 |
3 | export const MovieSchema = z.object({
4 | id: z.number(),
5 | title: z.string(),
6 | poster_path: z.string(),
7 | posterImage: z.string(),
8 | backdrop_path: z.string().nullable(),
9 | backdropImage: z.string(),
10 | release_date: z.string(),
11 | overview: z.string(),
12 | vote_average: z.number(),
13 | vote_count: z.number(),
14 | popularity: z.number(),
15 | original_language: z.string(),
16 | original_title: z.string(),
17 | genre_ids: z.array(z.number()),
18 | video: z.boolean(),
19 | adult: z.boolean(),
20 | })
21 | export const MoviesSchema = z.object({
22 | movies: z.array(MovieSchema),
23 | })
24 |
25 | export type MoviesType = z.infer;
26 | export type MovieType = z.infer
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/features/home/Home.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Center, Spinner, Text } from 'native-base'
3 | import { trpc } from '@conference-demos/trpc-client'
4 | import { MovieList } from '../../navigation/MovieList'
5 |
6 | export function NowPlayingScreen() {
7 | const { data, error, isLoading } = trpc.useQuery(['tmdb.nowPlaying'])
8 |
9 | if (error) {
10 | console.log('error', error)
11 |
12 | return Error: {error.message}
13 | }
14 |
15 | if (isLoading)
16 | return (
17 |
18 |
19 |
20 | )
21 | if (!data)
22 | return (
23 |
24 |
25 |
26 | )
27 |
28 |
29 | const movieData = data.movies
30 |
31 | return
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/apps/mobile/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | displayName: 'mobile',
3 | resolver: '@nrwl/jest/plugins/resolver',
4 | preset: 'jest-expo',
5 | transformIgnorePatterns: [
6 | 'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)',
7 | ],
8 | moduleFileExtensions: ['ts', 'js', 'html', 'tsx', 'jsx'],
9 | setupFilesAfterEnv: ['/test-setup.ts'],
10 | moduleNameMapper: {
11 | '.svg': '@nrwl/expo/plugins/jest/svg-mock',
12 | },
13 | transform: {
14 | '\\.(js|ts|tsx)$': require.resolve('react-native/jest/preprocessor.js'),
15 | '^.+\\.(bmp|gif|jpg|jpeg|mp4|png|psd|svg|webp|ttf)$': require.resolve(
16 | 'react-native/jest/assetFileTransformer.js'
17 | ),
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/apps/next-app-e2e/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
3 | "sourceRoot": "apps/next-app-e2e/src",
4 | "projectType": "application",
5 | "targets": {
6 | "e2e": {
7 | "executor": "@nrwl/cypress:cypress",
8 | "options": {
9 | "cypressConfig": "apps/next-app-e2e/cypress.json",
10 | "devServerTarget": "next-app:serve:development"
11 | },
12 | "configurations": {
13 | "production": {
14 | "devServerTarget": "next-app:serve:production"
15 | }
16 | }
17 | },
18 | "lint": {
19 | "executor": "@nrwl/linter:eslint",
20 | "outputs": ["{options.outputFile}"],
21 | "options": {
22 | "lintFilePatterns": ["apps/next-app-e2e/**/*.{js,ts}"]
23 | }
24 | }
25 | },
26 | "tags": [],
27 | "implicitDependencies": ["next-app"]
28 | }
29 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | displayName: 'app',
3 | resolver: '@nrwl/jest/plugins/resolver',
4 | preset: 'jest-expo',
5 | transformIgnorePatterns: [
6 | 'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)',
7 | ],
8 | moduleFileExtensions: ['ts', 'js', 'html', 'tsx', 'jsx'],
9 | setupFilesAfterEnv: ['/test-setup.ts'],
10 | moduleNameMapper: {
11 | '.svg': '@nrwl/expo/plugins/jest/svg-mock',
12 | },
13 | transform: {
14 | '\\.(js|ts|tsx)$': require.resolve('react-native/jest/preprocessor.js'),
15 | '^.+\\.(bmp|gif|jpg|jpeg|mp4|png|psd|svg|webp|ttf)$': require.resolve(
16 | 'react-native/jest/assetFileTransformer.js'
17 | ),
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/apps/next-app/pages/api/trpc/[trpc].ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file contains tRPC's HTTP response handler
3 | */
4 | import * as trpcNext from '@trpc/server/adapters/next';
5 | import {
6 | appRouter,
7 | createContext,
8 | } from '@conference-demos/api/trpc-server-edgedb'
9 |
10 | export default trpcNext.createNextApiHandler({
11 | router: appRouter,
12 | /**
13 | * @link https://trpc.io/docs/context
14 | */
15 | createContext: createContext,
16 | /**
17 | * @link https://trpc.io/docs/error-handling
18 | */
19 | onError({ error }) {
20 | if (error.code === 'INTERNAL_SERVER_ERROR') {
21 | // send to bug reporting
22 | console.error('Something went wrong', error);
23 | }
24 | },
25 | /**
26 | * Enable query batching
27 | */
28 | batching: {
29 | enabled: true,
30 | },
31 | /**
32 | * @link https://trpc.io/docs/caching#api-response-caching
33 | */
34 | // responseMeta() {
35 | // // ...
36 | // },
37 | });
38 |
--------------------------------------------------------------------------------
/apps/mobile/eas.json:
--------------------------------------------------------------------------------
1 | {
2 | "build": {
3 | "production": {
4 | "releaseChannel": "production",
5 | "android": {
6 | "buildType": "app-bundle"
7 | },
8 | "env": {
9 | "APP_ENV": "production"
10 | }
11 | },
12 | "development": {
13 | "android": {
14 | "developmentClient": true,
15 | "distribution": "internal"
16 | }
17 | },
18 | "preview": {
19 | "distribution": "internal",
20 | "releaseChannel": "staging",
21 | "ios": {
22 | "simulator": true
23 | },
24 | "android": {
25 | "buildType": "apk"
26 | },
27 | "env": {
28 | "APP_ENV": "staging"
29 | }
30 | },
31 | "test": {
32 | "android": {
33 | "buildType": "apk"
34 | },
35 | "ios": {
36 | "simulator": true
37 | },
38 | "env": {
39 | "APP_ENV": "staging"
40 | }
41 | }
42 | },
43 | "cli": {
44 | "version": ">= 0.38.1"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/apps/mobile/.gitignore:
--------------------------------------------------------------------------------
1 | ios
2 | android
3 | # @generated expo-cli sync-e7dcf75f4e856f7b6f3239b3f3a7dd614ee755a8
4 | # The following patterns were generated by expo-cli
5 |
6 | # OSX
7 | #
8 | .DS_Store
9 |
10 | # Xcode
11 | #
12 | build/
13 | *.pbxuser
14 | !default.pbxuser
15 | *.mode1v3
16 | !default.mode1v3
17 | *.mode2v3
18 | !default.mode2v3
19 | *.perspectivev3
20 | !default.perspectivev3
21 | xcuserdata
22 | *.xccheckout
23 | *.moved-aside
24 | DerivedData
25 | *.hmap
26 | *.ipa
27 | *.xcuserstate
28 | project.xcworkspace
29 |
30 | # Android/IntelliJ
31 | #
32 | build/
33 | .idea
34 | .gradle
35 | local.properties
36 | *.iml
37 | *.hprof
38 |
39 | # node.js
40 | #
41 | node_modules/
42 | npm-debug.log
43 | yarn-error.log
44 |
45 | # BUCK
46 | buck-out/
47 | \.buckd/
48 | *.keystore
49 | !debug.keystore
50 |
51 | # Bundle artifacts
52 | *.jsbundle
53 |
54 | # CocoaPods
55 | /ios/Pods/
56 |
57 | # Expo
58 | .expo/
59 | web-build/
60 | dist/
61 |
62 | # @end expo-cli
63 |
64 | google-services.json
65 | GoogleService-Info.plist
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/navigation/MovieList.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Box, FlatList } from 'native-base'
3 | import { MoviePoster } from '../features/home/MoviePoster'
4 | import { MoviesType } from '@conference-demos/api/trpc-server-edgedb'
5 | import { Link } from 'solito/link'
6 |
7 | export type Movie = MoviesType['movies'][number]
8 |
9 | export function MovieList({ movieData }: { movieData: Movie[] }) {
10 | return (
11 |
17 | {
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | )
30 | }}
31 | keyExtractor={(item) => item.id.toString()}
32 | />
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "ignorePatterns": ["**/*"],
4 | "plugins": ["@nrwl/nx"],
5 | "overrides": [
6 | {
7 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
8 | "rules": {
9 | "@nrwl/nx/enforce-module-boundaries": [
10 | "error",
11 | {
12 | "enforceBuildableLibDependency": true,
13 | "allow": [],
14 | "depConstraints": [
15 | {
16 | "sourceTag": "*",
17 | "onlyDependOnLibsWithTags": ["*"]
18 | }
19 | ]
20 | }
21 | ]
22 | }
23 | },
24 | {
25 | "files": ["*.ts", "*.tsx"],
26 | "extends": ["plugin:@nrwl/nx/typescript"],
27 | "rules": {}
28 | },
29 | {
30 | "files": ["*.js", "*.jsx"],
31 | "extends": ["plugin:@nrwl/nx/javascript"],
32 | "rules": {}
33 | },
34 | {
35 | "files": ["swagger.ts"],
36 | "extends": ["plugin:@nrwl/nx/typescript"],
37 | "rules": {
38 | "@typescript-eslint/no-empty-interface": "off"
39 | }
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/apps/mobile/metro.config.js:
--------------------------------------------------------------------------------
1 | const { withNxMetro } = require('@nrwl/expo');
2 | const { getDefaultConfig } = require('@expo/metro-config');
3 |
4 | const defaultConfig = getDefaultConfig(__dirname);
5 |
6 | module.exports = (async () => {
7 | defaultConfig.transformer.babelTransformerPath = require.resolve(
8 | 'react-native-svg-transformer'
9 | );
10 | defaultConfig.resolver.assetExts = defaultConfig.resolver.assetExts.filter(
11 | (ext) => ext !== 'svg'
12 | );
13 |
14 | defaultConfig.resolver.sourceExts.push('svg');
15 | defaultConfig.resolver.sourceExts.push('cjs');
16 |
17 | return withNxMetro(defaultConfig, {
18 | // Change this to true to see debugging info.
19 | // Useful if you have issues resolving modules
20 | debug: false,
21 | // all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx'
22 | extensions: ['cjs'],
23 | // the project root to start the metro server
24 | projectRoot: __dirname,
25 | // Specify any additional (to projectRoot) watch folders, this is used to know which files to watch
26 | watchFolders: [],
27 | });
28 | })();
29 |
--------------------------------------------------------------------------------
/libs/api/trpc-server-edgedb/src/lib/context.ts:
--------------------------------------------------------------------------------
1 | import * as trpc from '@trpc/server'
2 | import * as trpcNext from '@trpc/server/adapters/next'
3 | import { client as defaultClient } from '@conference-demos/edgedb-client'
4 |
5 | export const createContext = async ({
6 | req,
7 | res,
8 | }: trpcNext.CreateNextContextOptions) => {
9 | const TMDB_TOKEN = 'Bearer ' + process.env['TMDB_BEARER_TOKEN']
10 |
11 | const response = { req, res, client: defaultClient, TMDB_TOKEN, uid: '' }
12 |
13 | const bearerToken = req.headers.authorization || ''
14 | const bearerTokenParts = bearerToken.split('Bearer ')
15 | const bearerTokenValue = bearerTokenParts[1]
16 |
17 | if (bearerTokenValue) {
18 | response.uid = bearerTokenValue
19 |
20 | // create scoped client for edgedb auth
21 | const scopedClient = defaultClient.withGlobals({
22 | current_user: response.uid,
23 | })
24 |
25 | response.client = scopedClient
26 | return response
27 | }
28 |
29 | return response
30 | }
31 |
32 | type Context = trpc.inferAsyncReturnType
33 |
34 | export const createRouter = () => trpc.router()
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | yarn-error.log
34 | testem.log
35 | /typings
36 |
37 | # System Files
38 | .DS_Store
39 | Thumbs.db
40 |
41 | # Expo
42 | node_modules/
43 | .expo/
44 | dist/
45 | npm-debug.*
46 | *.jks
47 | *.p8
48 | *.p12
49 | *.key
50 | *.mobileprovision
51 | *.orig.*
52 | web-build/
53 |
54 |
55 | apps/mobile-e2e/artifacts
56 | .env
57 |
58 | serviceAccountKey.json
59 | apps/mobile/pc-api-8267020174778927726-748-81eb349346aa.json
60 | tmdb-watchlist-8db00-firebase-adminsdk-h6fmf-7cf0af9244.json
61 | apps/mobile/google-services.prod.json
62 | apps/mobile/GoogleService-Info.prod.plist
63 |
--------------------------------------------------------------------------------
/libs/api/trpc-server-edgedb/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../../node_modules/nx/schemas/project-schema.json",
3 | "sourceRoot": "libs/api/trpc-server-edgedb/src",
4 | "projectType": "library",
5 | "targets": {
6 | "build": {
7 | "executor": "@nrwl/js:tsc",
8 | "outputs": ["{options.outputPath}"],
9 | "options": {
10 | "outputPath": "dist/libs/api/trpc-server-edgedb",
11 | "main": "libs/api/trpc-server-edgedb/src/index.ts",
12 | "tsConfig": "libs/api/trpc-server-edgedb/tsconfig.lib.json",
13 | "assets": ["libs/api/trpc-server-edgedb/*.md"]
14 | }
15 | },
16 | "lint": {
17 | "executor": "@nrwl/linter:eslint",
18 | "outputs": ["{options.outputFile}"],
19 | "options": {
20 | "lintFilePatterns": ["libs/api/trpc-server-edgedb/**/*.ts"]
21 | }
22 | },
23 | "test": {
24 | "executor": "@nrwl/jest:jest",
25 | "outputs": ["coverage/libs/api/trpc-server-edgedb"],
26 | "options": {
27 | "jestConfig": "libs/api/trpc-server-edgedb/jest.config.ts",
28 | "passWithNoTests": true
29 | }
30 | }
31 | },
32 | "tags": []
33 | }
34 |
--------------------------------------------------------------------------------
/libs/database/edgedb-client/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../../node_modules/nx/schemas/project-schema.json",
3 | "sourceRoot": "libs/database/edgedb-client/src",
4 | "projectType": "library",
5 | "targets": {
6 | "build": {
7 | "executor": "@nrwl/js:tsc",
8 | "outputs": ["{options.outputPath}"],
9 | "options": {
10 | "outputPath": "dist/libs/database/edgedb-client",
11 | "main": "libs/database/edgedb-client/src/index.ts",
12 | "tsConfig": "libs/database/edgedb-client/tsconfig.lib.json",
13 | "assets": ["libs/database/edgedb-client/*.md"]
14 | }
15 | },
16 | "lint": {
17 | "executor": "@nrwl/linter:eslint",
18 | "outputs": ["{options.outputFile}"],
19 | "options": {
20 | "lintFilePatterns": ["libs/database/edgedb-client/**/*.ts"]
21 | }
22 | },
23 | "test": {
24 | "executor": "@nrwl/jest:jest",
25 | "outputs": ["coverage/libs/database/edgedb-client"],
26 | "options": {
27 | "jestConfig": "libs/database/edgedb-client/jest.config.ts",
28 | "passWithNoTests": true
29 | }
30 | }
31 | },
32 | "tags": []
33 | }
34 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/navigation/Navigation.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | createNativeStackNavigator,
3 | NativeStackScreenProps,
4 | } from '@react-navigation/native-stack'
5 | import { MovieDetailScreen } from '../features/movie/detail-screen/DetailScreen'
6 | import React from 'react'
7 | import { NavigatorScreenParams } from '@react-navigation/native'
8 | import { HomeStackTabs, TabNavigation } from './TabStack'
9 |
10 | export type WatchlistStackParams = {
11 | HomeStack: NavigatorScreenParams
12 | Details: {
13 | id: string
14 | }
15 | }
16 |
17 | export type WatchlistMovieScreenNavigationProps = NativeStackScreenProps<
18 | WatchlistStackParams,
19 | 'Details'
20 | >
21 |
22 | const Stack = createNativeStackNavigator()
23 |
24 | export function WatchlistStack() {
25 | return (
26 |
27 |
34 |
35 |
36 | )
37 | }
38 |
39 |
40 |
--------------------------------------------------------------------------------
/apps/mobile/src/app/App.tsx:
--------------------------------------------------------------------------------
1 | import { LogBox } from 'react-native'
2 | import React from 'react'
3 | import { Provider, RootNavigator } from '@conference-demos/frontend-mobile-app'
4 | import * as SplashScreen from 'expo-splash-screen'
5 | if (__DEV__) {
6 | // debugging with Reactotron
7 | import('./ReactotronConfig').then(() => console.log('Reactotron Configured'))
8 | }
9 |
10 | export default function App() {
11 | React.useEffect(() => {
12 | async function prepare() {
13 | try {
14 | // Keep the splash screen visible while we fetch resources
15 | await SplashScreen.preventAutoHideAsync()
16 |
17 | // Artificially delay for two seconds to simulate a slow loading
18 | // experience. Please remove this if you copy and paste the code!
19 | await new Promise((resolve) => setTimeout(resolve, 2000))
20 | } catch (e) {
21 | console.warn(e)
22 | }
23 | }
24 |
25 | prepare()
26 | }, [])
27 |
28 | return (
29 |
30 |
31 |
32 | )
33 | }
34 |
35 | LogBox.ignoreLogs([
36 | "Can't perform a React state update on an unmounted component",
37 | ])
38 |
--------------------------------------------------------------------------------
/nx.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/nx/schemas/nx-schema.json",
3 | "npmScope": "conference-demos",
4 | "affected": {
5 | "defaultBase": "main"
6 | },
7 | "implicitDependencies": {
8 | "package.json": {
9 | "dependencies": "*",
10 | "devDependencies": "*"
11 | },
12 | ".eslintrc.json": "*"
13 | },
14 | "tasksRunnerOptions": {
15 | "default": {
16 | "runner": "@nrwl/nx-cloud",
17 | "options": {
18 | "cacheableOperations": ["build", "lint", "test", "e2e"]
19 | }
20 | }
21 | },
22 | "targetDefaults": {
23 | "build": {
24 | "dependsOn": ["^build"]
25 | }
26 | },
27 | "defaultProject": "mobile",
28 | "generators": {
29 | "@nrwl/react": {
30 | "application": {
31 | "style": "none",
32 | "linter": "eslint",
33 | "babel": true
34 | },
35 | "component": {
36 | "style": "none"
37 | },
38 | "library": {
39 | "style": "none",
40 | "linter": "eslint"
41 | }
42 | },
43 | "@nrwl/next": {
44 | "application": {
45 | "style": "css",
46 | "linter": "eslint"
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/libs/frontend/trpc-client/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../../node_modules/nx/schemas/project-schema.json",
3 | "sourceRoot": "libs/frontend/trpc-client/src",
4 | "projectType": "library",
5 | "targets": {
6 | "build": {
7 | "executor": "@nrwl/js:tsc",
8 | "outputs": ["{options.outputPath}"],
9 | "options": {
10 | "outputPath": "dist/libs/frontend/trpc-client",
11 | "tsConfig": "libs/frontend/trpc-client/tsconfig.lib.json",
12 | "packageJson": "libs/frontend/trpc-client/package.json",
13 | "main": "libs/frontend/trpc-client/src/index.ts",
14 | "assets": ["libs/frontend/trpc-client/*.md"]
15 | }
16 | },
17 | "lint": {
18 | "executor": "@nrwl/linter:eslint",
19 | "outputs": ["{options.outputFile}"],
20 | "options": {
21 | "lintFilePatterns": ["libs/frontend/trpc-client/**/*.ts"]
22 | }
23 | },
24 | "test": {
25 | "executor": "@nrwl/jest:jest",
26 | "outputs": ["coverage/libs/frontend/trpc-client"],
27 | "options": {
28 | "jestConfig": "libs/frontend/trpc-client/jest.config.ts",
29 | "passWithNoTests": true
30 | }
31 | }
32 | },
33 | "tags": []
34 | }
35 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "rootDir": ".",
5 | "sourceMap": true,
6 | "declaration": false,
7 | "moduleResolution": "node",
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 | "importHelpers": true,
11 | "target": "es2015",
12 | "module": "esnext",
13 | "lib": ["es2017", "dom"],
14 | "skipLibCheck": true,
15 | "skipDefaultLibCheck": true,
16 | "baseUrl": ".",
17 | "paths": {
18 | "@conference-demos/api/trpc-server-edgedb": [
19 | "libs/api/trpc-server-edgedb/src/index.ts"
20 | ],
21 | "@conference-demos/edgedb-client": [
22 | "libs/database/edgedb-client/src/index.ts"
23 | ],
24 | "@conference-demos/frontend-mobile-app": [
25 | "libs/frontend/mobile-app/src/index.ts"
26 | ],
27 | "@conference-demos/prisma-client": [
28 | "libs/database/prisma-client/src/index.ts"
29 | ],
30 | "@conference-demos/trpc-client": [
31 | "libs/frontend/trpc-client/src/index.ts"
32 | ],
33 | "@conference-demos/trpc-server": ["libs/api/trpc-server/src/index.ts"]
34 | }
35 | },
36 | "exclude": ["node_modules", "tmp"]
37 | }
38 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/navigation/MyWatchlist.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Center, Spinner, Text } from 'native-base'
3 | import { trpc } from '@conference-demos/trpc-client'
4 | import { MovieList } from './MovieList'
5 | import { TextLink } from 'solito/link'
6 |
7 | export function MyWatchlist() {
8 | const { data, error, isLoading } = trpc.useQuery(['user.watchlist'])
9 |
10 | if (error) {
11 | console.log('error', error)
12 |
13 | return Error: {error.message}
14 | }
15 |
16 | if (isLoading)
17 | return (
18 |
19 |
20 |
21 | )
22 | if (!data)
23 | return (
24 |
25 |
26 |
27 | )
28 |
29 | const movieData = data.movies
30 |
31 | // show nothing if no movies
32 | if (!movieData.length)
33 | return (
34 |
35 |
36 | No movies to show. Find a movie in{' '}
37 |
38 | Now Playing
39 |
40 |
41 |
42 | )
43 |
44 | return
45 | }
46 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/providers/AuthenticationProvider.tsx:
--------------------------------------------------------------------------------
1 | import React, { Dispatch, SetStateAction } from 'react'
2 |
3 | /* eslint-disable-next-line */
4 | export interface AuthenticationProviderProps {
5 | children: React.ReactNode
6 | }
7 |
8 | type UserDataType = {
9 | hasUser: boolean
10 | username: string
11 | }
12 | export const AuthenticatedUserContext = React.createContext<
13 | | {
14 | userData: UserDataType
15 | setUser: Dispatch>
16 | }
17 | | undefined
18 | >(undefined)
19 |
20 | export function AuthenticationProvider({
21 | children,
22 | }: AuthenticationProviderProps) {
23 | const [userData, setUser] = React.useState({ hasUser: false, username: '' })
24 |
25 | return (
26 |
32 | {children}
33 |
34 | )
35 | }
36 |
37 | export const useAuthenticatedUser = () => {
38 | const context = React.useContext(AuthenticatedUserContext)
39 |
40 | if (context === undefined) {
41 | throw new Error(
42 | 'useAuthenticatedUser must be used within a AuthenticationProvider'
43 | )
44 | }
45 |
46 | return context
47 | }
48 |
--------------------------------------------------------------------------------
/apps/next-app-e2e/src/support/commands.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 |
11 | // eslint-disable-next-line @typescript-eslint/no-namespace
12 | declare namespace Cypress {
13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
14 | interface Chainable {
15 | login(email: string, password: string): void;
16 | }
17 | }
18 | //
19 | // -- This is a parent command --
20 | Cypress.Commands.add('login', (email, password) => {
21 | console.log('Custom command example: Login', email, password);
22 | });
23 | //
24 | // -- This is a child command --
25 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
26 | //
27 | //
28 | // -- This is a dual command --
29 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
30 | //
31 | //
32 | // -- This will overwrite an existing command --
33 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
34 |
--------------------------------------------------------------------------------
/libs/database/edgedb-client/src/lib/edgedb-client.ts:
--------------------------------------------------------------------------------
1 | import e from './codegen'
2 | import * as edgedb from 'edgedb'
3 |
4 | export async function upsertUser(client: edgedb.Client) {
5 | const upsertQuery = e.insert(e.User, {
6 | uid: e.global.current_user,
7 | })
8 |
9 | return upsertQuery.run(client)
10 | }
11 |
12 | export async function createWatchlistItem(
13 | data: {
14 | movieId: string
15 | },
16 | client: edgedb.Client
17 | ) {
18 | const insertQuery = e.insert(e.WatchListItem, {
19 | movieId: data.movieId,
20 | user: e.select(e.User).assert_single(),
21 | })
22 |
23 | const result = await insertQuery.run(client)
24 |
25 | return result
26 | }
27 |
28 | export async function deleteWatchlistItem(
29 | data: { id: string },
30 | client: edgedb.Client
31 | ) {
32 | const deletion = e.delete(e.WatchListItem, (watchListItem) => ({
33 | filter: e.op(watchListItem.id, '=', e.uuid(data.id)),
34 | }))
35 |
36 | const result = await deletion.run(client)
37 |
38 | return result
39 | }
40 |
41 | // get watchlist
42 | export async function getWatchlist(client: edgedb.Client) {
43 | const query = e.select(e.WatchListItem, () => ({
44 | id: true,
45 | movieId: true,
46 | }))
47 |
48 | const result = await query.run(client)
49 |
50 | return result
51 | }
52 |
--------------------------------------------------------------------------------
/apps/mobile/app.config.js:
--------------------------------------------------------------------------------
1 | import 'dotenv/config'
2 |
3 | let Config = {
4 | apiUrl: process.env.API_ENDPOINT,
5 | }
6 |
7 | if (process.env.APP_ENV === 'production') {
8 | Config.apiUrl = 'https://conference-demos.vercel.app'
9 | } else if (process.env.APP_ENV === 'staging') {
10 | Config.apiUrl = 'https://conference-demos-git-develop-mwarger.vercel.app'
11 | }
12 |
13 | export default {
14 | expo: {
15 | owner: 'mwarger',
16 | name: 'Mobile',
17 | slug: 'mobile',
18 | scheme: 'com.tmdb.watchlist.edgedb',
19 | version: '1.0.0',
20 | orientation: 'portrait',
21 | icon: './assets/icon.png',
22 | splash: {
23 | image: './assets/splash.png',
24 | resizeMode: 'contain',
25 | backgroundColor: '#f44336',
26 | },
27 | updates: {
28 | fallbackToCacheTimeout: 0,
29 | },
30 | assetBundlePatterns: ['**/*'],
31 | ios: {
32 | supportsTablet: true,
33 | bundleIdentifier: 'com.tmdb.watchlist.edgedb',
34 | },
35 | android: {
36 | adaptiveIcon: {
37 | foregroundImage: './assets/adaptive-icon.png',
38 | backgroundColor: '#f44336',
39 | },
40 | package: 'com.tmdb.watchlist.edgedb',
41 | },
42 | web: {
43 | favicon: './assets/favicon.png',
44 | },
45 | extra: {
46 | ...Config,
47 | },
48 | },
49 | }
50 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/providers/NavigationProvider.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | NavigationContainer,
3 | DefaultTheme,
4 | DarkTheme,
5 | LinkingOptions,
6 | } from '@react-navigation/native'
7 | import * as Linking from 'expo-linking'
8 | import { useMemo } from 'react'
9 | import { parseMovieId } from '../features/movie/detail-screen/parseMovieId'
10 | import { stringifyMovieId } from '../features/movie/detail-screen/stringifyMovieId'
11 |
12 | import { useColorMode } from 'native-base'
13 | import { WatchlistStackParams } from '../navigation/Navigation'
14 |
15 | const linking: LinkingOptions = {
16 | prefixes: [Linking.createURL('/')],
17 | config: {
18 | screens: {
19 | HomeStack: {
20 | screens: {
21 | NowPlaying: 'nowPlaying',
22 | MyWatchlist: 'watchlist',
23 | },
24 | },
25 | Details: {
26 | parse: {
27 | id: parseMovieId,
28 | },
29 | stringify: { id: stringifyMovieId },
30 | path: 'movie/:id',
31 | },
32 | },
33 | },
34 | }
35 |
36 | export function NavigationProvider({
37 | children,
38 | }: {
39 | children: React.ReactNode
40 | }) {
41 | const { colorMode } = useColorMode()
42 | return (
43 | linking, [])}
46 | >
47 | {children}
48 |
49 | )
50 | }
51 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../../node_modules/nx/schemas/project-schema.json",
3 | "sourceRoot": "libs/frontend/mobile-app/src",
4 | "projectType": "library",
5 | "tags": [],
6 | "targets": {
7 | "build": {
8 | "executor": "@nrwl/web:rollup",
9 | "outputs": ["{options.outputPath}"],
10 | "options": {
11 | "outputPath": "dist/libs/frontend/mobile-app",
12 | "tsConfig": "libs/frontend/mobile-app/tsconfig.lib.json",
13 | "project": "libs/frontend/mobile-app/package.json",
14 | "entryFile": "libs/frontend/mobile-app/src/index.ts",
15 | "external": ["react/jsx-runtime"],
16 | "rollupConfig": "@nrwl/react/plugins/bundle-rollup",
17 | "assets": [
18 | {
19 | "glob": "libs/frontend/mobile-app/README.md",
20 | "input": ".",
21 | "output": "."
22 | }
23 | ]
24 | }
25 | },
26 | "lint": {
27 | "executor": "@nrwl/linter:eslint",
28 | "outputs": ["{options.outputFile}"],
29 | "options": {
30 | "lintFilePatterns": ["libs/frontend/mobile-app/**/*.{ts,tsx,js,jsx}"]
31 | }
32 | },
33 | "test": {
34 | "executor": "@nrwl/jest:jest",
35 | "outputs": ["coverage/libs/frontend/mobile-app"],
36 | "options": {
37 | "jestConfig": "libs/frontend/mobile-app/jest.config.ts",
38 | "passWithNoTests": true
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # tmdb-watchlist-edgedb
2 |
3 | This app uses TMDB to retrieve a list of Now Playing movies. You can add/remove movies to track which ones you've watched. There's a "fake" auth flow as well to show navigation as well as tRPC middleware.
4 |
5 | Simple app that showcases the following technology.
6 |
7 | Expo + tRPC + EdgeDB + NextJS + Nx + zod + react-hook-form + solito
8 |
9 | ## Setup
10 |
11 | Add your database and TMDB bearer token (instructions [here](https://www.themoviedb.org/documentation/api?language=en-US)) to the `.env` file.
12 |
13 | This also showcases access control for the user's watchlist items!
14 |
15 | Install EdgeDB for your system - https://www.edgedb.com/install
16 |
17 | Run `edgedb project init` in the root of the project to setup the existing schema in a new instance.
18 |
19 | Run `yarn edgedb:codegen` to generate the TypeScript client library.
20 |
21 | To check out your data, run, run `edgedb ui` and check it out!
22 |
23 | To be able to see your data, make sure to add the UID whose data you'd like to see to the globals section at the top of the page.
24 |
25 |
26 |
27 | ## Running the App
28 |
29 | ### Run the API
30 |
31 | `nx serve next-app`
32 |
33 | ### Run the App
34 |
35 | `nx run-ios mobile`
36 |
37 | ### Screenshots
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/providers/TRPCProvider.tsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useState } from 'react'
2 | import { QueryClient, QueryClientProvider } from 'react-query'
3 | import { trpc } from '@conference-demos/trpc-client'
4 | import { ProviderProps } from './Provider'
5 | import reactotron from 'reactotron-react-native'
6 |
7 | import Constants from 'expo-constants'
8 | import { useAuthenticatedUser } from './AuthenticationProvider'
9 |
10 | const { manifest } = Constants
11 | let apiHost = ''
12 | const endpoint = Constants.manifest?.extra?.apiUrl ?? ''
13 | reactotron.log?.('endpoint', endpoint)
14 |
15 | if (__DEV__) {
16 | apiHost =
17 | typeof manifest?.packagerOpts === `object` && manifest.packagerOpts.dev
18 | ? manifest.debuggerHost?.split(`:`).shift()?.concat(`:4200`)
19 | : endpoint
20 |
21 | // add http if not present
22 | if (!apiHost.startsWith('http')) {
23 | apiHost = `http://${apiHost}`
24 | }
25 | } else {
26 | apiHost = endpoint
27 | }
28 |
29 | export function TRPCProvider({ children }: ProviderProps) {
30 | const { userData } = useAuthenticatedUser()
31 | const [queryClient] = useState(() => new QueryClient())
32 | const trpcClient = useMemo(() => {
33 | return trpc.createClient({
34 | url: `${apiHost}/api/trpc`,
35 |
36 | // optional
37 | headers() {
38 | const idToken = userData.username
39 |
40 | return {
41 | authorization: `Bearer ${idToken}`,
42 | }
43 | },
44 | })
45 | }, [userData.username])
46 |
47 | return (
48 |
49 | {children}
50 |
51 | )
52 | }
53 |
--------------------------------------------------------------------------------
/apps/next-app/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
3 | "sourceRoot": "apps/next-app",
4 | "projectType": "application",
5 | "targets": {
6 | "build": {
7 | "executor": "@nrwl/next:build",
8 | "outputs": ["{options.outputPath}"],
9 | "defaultConfiguration": "production",
10 | "options": {
11 | "root": "apps/next-app",
12 | "outputPath": "dist/apps/next-app"
13 | },
14 | "configurations": {
15 | "development": {},
16 | "production": {}
17 | }
18 | },
19 | "serve": {
20 | "executor": "@nrwl/next:server",
21 | "defaultConfiguration": "development",
22 | "options": {
23 | "buildTarget": "next-app:build",
24 | "dev": true
25 | },
26 | "configurations": {
27 | "development": {
28 | "buildTarget": "next-app:build:development",
29 | "dev": true
30 | },
31 | "production": {
32 | "buildTarget": "next-app:build:production",
33 | "dev": false
34 | }
35 | }
36 | },
37 | "export": {
38 | "executor": "@nrwl/next:export",
39 | "options": {
40 | "buildTarget": "next-app:build:production"
41 | }
42 | },
43 | "test": {
44 | "executor": "@nrwl/jest:jest",
45 | "outputs": ["coverage/apps/next-app"],
46 | "options": {
47 | "jestConfig": "apps/next-app/jest.config.ts",
48 | "passWithNoTests": true
49 | }
50 | },
51 | "lint": {
52 | "executor": "@nrwl/linter:eslint",
53 | "outputs": ["{options.outputFile}"],
54 | "options": {
55 | "lintFilePatterns": ["apps/next-app/**/*.{ts,tsx,js,jsx}"]
56 | }
57 | }
58 | },
59 | "tags": []
60 | }
61 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/navigation/RootNavigator.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useAuthenticatedUser } from '../providers/AuthenticationProvider'
3 | import { Center, Spinner, View } from 'native-base'
4 | import { AuthStack } from './AuthStack'
5 | import { WatchlistStack } from './Navigation'
6 | import * as SplashScreen from 'expo-splash-screen'
7 | import reactotron from 'reactotron-react-native'
8 | import { trpc } from '@conference-demos/trpc-client'
9 |
10 | export function RootNavigator() {
11 | reactotron.log?.('RootNavigator')
12 |
13 | const syncAccount = trpc.useMutation(['user.syncAccount'], {}).mutateAsync
14 | const { userData } = useAuthenticatedUser()
15 | const [isLoading, setIsLoading] = React.useState(true)
16 |
17 | React.useEffect(() => {
18 | async function syncUser() {
19 | try {
20 | await syncAccount()
21 | } catch (error) {
22 | console.log('syncUser error', error)
23 | } finally {
24 | setIsLoading(false)
25 | }
26 | }
27 |
28 | if (userData.hasUser && userData.username) {
29 | syncUser()
30 | } else {
31 | setIsLoading(false)
32 | }
33 | }, [syncAccount, userData.hasUser, userData.username])
34 |
35 | const onLayoutRootView = React.useCallback(async () => {
36 | if (!isLoading) {
37 | await SplashScreen.hideAsync()
38 | }
39 | }, [isLoading])
40 |
41 | if (isLoading) {
42 | return (
43 |
44 |
45 |
46 | )
47 | }
48 |
49 | return (
50 |
56 | {userData?.hasUser ? : }
57 |
58 | )
59 | }
60 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/features/home/MoviePoster.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Image, Text, AspectRatio, ScrollView } from 'native-base'
3 | import { Movie } from '../../navigation/MovieList'
4 |
5 | export function MoviePoster({
6 | item,
7 | showOverview,
8 | }: {
9 | item: Movie
10 | showOverview?: boolean
11 | }) {
12 | // const route = useRoute()
13 |
14 | return (
15 |
16 |
22 |
31 |
32 |
43 | {item.title}
44 |
45 | {showOverview ? (
46 |
56 | {item.overview}
57 |
58 | ) : null}
59 |
67 | {item.release_date
68 | ? `Released: ${new Date(item.release_date).toLocaleDateString()}`
69 | : 'No date scheduled.'}
70 |
71 |
72 | )
73 | }
74 |
--------------------------------------------------------------------------------
/apps/mobile-e2e/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
3 | "sourceRoot": "apps/mobile-e2e/src",
4 | "projectType": "application",
5 | "targets": {
6 | "build-ios": {
7 | "executor": "@nrwl/detox:build",
8 | "options": {
9 | "detoxConfiguration": "ios.sim.debug"
10 | },
11 | "configurations": {
12 | "production": {
13 | "detoxConfiguration": "ios.sim.release"
14 | }
15 | }
16 | },
17 | "test-ios": {
18 | "executor": "@nrwl/detox:test",
19 | "options": {
20 | "detoxConfiguration": "ios.sim.debug",
21 | "buildTarget": "mobile-e2e:build-ios"
22 | },
23 | "configurations": {
24 | "production": {
25 | "detoxConfiguration": "ios.sim.release",
26 | "buildTarget": "mobile-e2e:build-ios:prod"
27 | }
28 | }
29 | },
30 | "build-android": {
31 | "executor": "@nrwl/detox:build",
32 | "options": {
33 | "detoxConfiguration": "android.emu.debug"
34 | },
35 | "configurations": {
36 | "production": {
37 | "detoxConfiguration": "android.emu.release"
38 | }
39 | }
40 | },
41 | "test-android": {
42 | "executor": "@nrwl/detox:test",
43 | "options": {
44 | "detoxConfiguration": "android.emu.debug",
45 | "buildTarget": "mobile-e2e:build-android"
46 | },
47 | "configurations": {
48 | "production": {
49 | "detoxConfiguration": "android.emu.release",
50 | "buildTarget": "mobile-e2e:build-android:prod"
51 | }
52 | }
53 | },
54 | "lint": {
55 | "executor": "@nrwl/linter:eslint",
56 | "outputs": ["{options.outputFile}"],
57 | "options": {
58 | "lintFilePatterns": ["apps/mobile-e2e/**/*.{ts,tsx,js,jsx}"]
59 | }
60 | }
61 | },
62 | "tags": [],
63 | "implicitDependencies": ["mobile"]
64 | }
65 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/navigation/tmdb_logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/mobile-e2e/.detoxrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "testRunner": "jest",
3 | "runnerConfig": "jest.config.json",
4 | "apps": {
5 | "ios.debug": {
6 | "type": "ios.app",
7 | "build": "cd ../mobile/ios && xcodebuild -workspace Mobile.xcworkspace -scheme Mobile -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 13' -derivedDataPath ./build -quiet",
8 | "binaryPath": "../mobile/ios/build/Build/Products/Debug-iphonesimulator/Mobile.app"
9 | },
10 | "ios.release": {
11 | "type": "ios.app",
12 | "build": "cd ../mobile/ios && xcodebuild -workspace Mobile.xcworkspace -scheme Mobile -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 13' -derivedDataPath ./build -quiet",
13 | "binaryPath": "../mobile/ios/build/Build/Products/Release-iphonesimulator/Mobile.app"
14 | },
15 | "android.debug": {
16 | "type": "android.apk",
17 | "build": "cd ../mobile/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug",
18 | "binaryPath": "../mobile/android/app/build/outputs/apk/debug/app-debug.apk"
19 | },
20 | "android.release": {
21 | "type": "android.apk",
22 | "build": "cd ../mobile/android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release",
23 | "binaryPath": "../mobile/android/app/build/outputs/apk/release/app-release.apk"
24 | }
25 | },
26 | "devices": {
27 | "simulator": {
28 | "type": "ios.simulator",
29 | "device": {
30 | "type": "iPhone 13"
31 | }
32 | },
33 | "emulator": {
34 | "type": "android.emulator",
35 | "device": {
36 | "avdName": "Pixel_4a_API_30"
37 | }
38 | }
39 | },
40 | "configurations": {
41 | "ios.sim.release": {
42 | "device": "simulator",
43 | "app": "ios.release"
44 | },
45 | "ios.sim.debug": {
46 | "device": "simulator",
47 | "app": "ios.debug"
48 | },
49 | "android.emu.release": {
50 | "device": "emulator",
51 | "app": "android.release"
52 | },
53 | "android.emu.debug": {
54 | "device": "emulator",
55 | "app": "android.debug"
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/native-base/NativeBaseProvider.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { NativeBaseProvider, Box } from 'native-base';
4 |
5 | import { extendTheme } from 'native-base'
6 |
7 | // 1. Extend the theme
8 | const customTheme = extendTheme({
9 | colors: {
10 | primary: {
11 | 50: '#ffe2e7',
12 | 100: '#ffb3bb',
13 | 200: '#fc8393',
14 | 300: '#f9526d',
15 | 400: '#f6224b',
16 | 500: '#dd0939',
17 | 600: '#ad0320',
18 | 700: '#7c000e',
19 | 800: '#4d0002',
20 | 900: '#200400',
21 | },
22 | },
23 | config: {
24 | initialColorMode: 'dark',
25 | // useSystemColorMode: true,
26 | },
27 | components: {
28 | Button: {
29 | // Can simply pass default props to change default behaviour of components.
30 | // baseStyle: {
31 | // rounded: 'md',
32 | // _light: {
33 | // color: 'darkText',
34 | // backgroundColor: 'lightText',
35 | // },
36 | // _dark: {
37 | // color: 'lightText',
38 | // backgroundColor: 'darkText',
39 | // },
40 | // },
41 | defaultProps: {
42 | colorScheme: 'red',
43 | },
44 | },
45 | Heading: {
46 | baseStyle: {
47 | _light: {
48 | color: 'darkText',
49 | },
50 | _dark: {
51 | color: 'lightText',
52 | },
53 | fontWeight: 'normal',
54 | },
55 | //
56 | // baseStyle: ({ colorMode }: { colorMode: 'light' | 'dark' }) => {
57 | // return {
58 | // color: colorMode === 'dark' ? 'red.300' : 'blue.300',
59 | // fontWeight: 'normal',
60 | // }
61 | // },
62 | },
63 | },
64 | })
65 |
66 | // 2. Get the type of the CustomTheme
67 | type CustomThemeType = typeof customTheme
68 |
69 | // 3. Extend the internal NativeBase Theme
70 | declare module 'native-base' {
71 | type ICustomTheme = CustomThemeType
72 | }
73 |
74 | export interface NativeBaseProps {
75 | children: React.ReactNode
76 | }
77 |
78 | export function NativeBase(props: NativeBaseProps) {
79 | return (
80 |
81 |
82 | {props.children}
83 |
84 |
85 | )
86 | }
87 |
88 | export default NativeBase;
89 |
--------------------------------------------------------------------------------
/apps/mobile/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mobile",
3 | "version": "0.0.1",
4 | "private": true,
5 | "dependencies": {
6 | "@expo/metro-config": "*",
7 | "@nrwl/expo": "*",
8 | "@react-navigation/native": "*",
9 | "@testing-library/jest-native": "*",
10 | "@testing-library/react-native": "*",
11 | "@types/react": "*",
12 | "expo": "*",
13 | "expo-dev-client": "*",
14 | "expo-linking": "*",
15 | "expo-splash-screen": "*",
16 | "expo-status-bar": "*",
17 | "expo-structured-headers": "*",
18 | "expo-updates": "*",
19 | "native-base": "*",
20 | "react": "17.0.2",
21 | "react-dom": "17.0.2",
22 | "react-native": "0.68.2",
23 | "react-native-gesture-handler": "*",
24 | "react-native-reanimated": "*",
25 | "react-native-safe-area-context": "*",
26 | "react-native-screens": "*",
27 | "react-native-svg": "*",
28 | "react-native-web": "*",
29 | "solito": "*",
30 | "typescript": "*",
31 | "@react-navigation/native-stack": "*",
32 | "react-query": "*",
33 | "@trpc/server": "*",
34 | "zod": "*",
35 | "node-fetch": "*",
36 | "@trpc/client": "*",
37 | "@trpc/react": "*",
38 | "expo-web-browser": "*",
39 | "@types/react-dom": "*",
40 | "@react-navigation/bottom-tabs": "*",
41 | "edgedb": "*",
42 | "reactotron-react-native": "*",
43 | "@react-native-async-storage/async-storage": "*",
44 | "react-hook-form": "*",
45 | "@hookform/resolvers": "*"
46 | },
47 | "scripts": {
48 | "eas-build-pre-install": "cd ../../ && cp yarn.lock ./apps/mobile/ && echo $GOOGLE_SERVICES_BASE64 && echo $GOOGLE_SERVICES_BASE64 | base64 --decode > ./apps/mobile/google-services.json && cat ./apps/mobile/google-services.json && echo $GOOGLE_SERVICE_INFO_PLIST && echo $GOOGLE_SERVICE_INFO_PLIST | base64 --decode > ./apps/mobile/GoogleService-Info.plist && cat ./apps/mobile/GoogleService-Info.plist ",
49 | "postinstall": "rm -r node_modules && cd ../../ && yarn install --ignore-engines --frozen-lockfile && npx nx sync-deps mobile && npx nx ensure-symlink mobile && cd apps/mobile && expo prebuild --no-install",
50 | "start": "expo start --dev-client",
51 | "android": "expo run:android",
52 | "ios": "expo run:ios"
53 | },
54 | "devDependencies": {
55 | "@babel/core": "^7.12.9",
56 | "@types/react": "*",
57 | "@types/react-dom": "*",
58 | "typescript": "*"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/navigation/TabStack.tsx:
--------------------------------------------------------------------------------
1 | import { NowPlayingScreen } from '../features/home/Home'
2 | import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
3 | import React from 'react'
4 | import { MaterialCommunityIcons, MaterialIcons } from '@expo/vector-icons'
5 | import { MyWatchlist } from './MyWatchlist'
6 | import { Button, Center, Text, Box, Stack } from 'native-base'
7 | import { useAuthenticatedUser } from '../providers/AuthenticationProvider'
8 | import TMDBLogo from './tmdb_logo.svg'
9 |
10 | export type HomeStackTabs = {
11 | MyWatchlist: undefined
12 | NowPlaying: undefined
13 | Settings: undefined
14 | }
15 | const Tabs = createBottomTabNavigator()
16 |
17 | export function TabNavigation() {
18 | return (
19 |
24 | (
31 |
36 | ),
37 | }}
38 | />
39 | (
46 |
47 | ),
48 | }}
49 | />
50 | (
57 |
58 | ),
59 | }}
60 | />
61 |
62 | )
63 | }
64 |
65 | function Settings() {
66 | const { setUser } = useAuthenticatedUser()
67 | return (
68 |
69 |
70 |
71 | API provided by:
72 |
73 |
74 |
81 |
82 |
83 | )
84 | }
85 |
--------------------------------------------------------------------------------
/libs/api/trpc-server-edgedb/src/lib/trpc-helper.ts:
--------------------------------------------------------------------------------
1 | // trpc-helper.ts
2 | import type {
3 | inferProcedureOutput,
4 | inferProcedureInput,
5 | inferSubscriptionOutput,
6 | } from '@trpc/server'
7 | import { AppRouter } from './router'
8 |
9 | /**
10 | * Enum containing all api query paths
11 | */
12 | export type TQuery = keyof AppRouter['_def']['queries']
13 |
14 | /**
15 | * Enum containing all api mutation paths
16 | */
17 | export type TMutation = keyof AppRouter['_def']['mutations']
18 |
19 | /**
20 | * Enum containing all api subscription paths
21 | */
22 | export type TSubscription = keyof AppRouter['_def']['subscriptions']
23 |
24 | /**
25 | * This is a helper method to infer the output of a query resolver
26 | * @example type HelloOutput = InferQueryOutput<'hello'>
27 | */
28 | export type InferQueryOutput = inferProcedureOutput<
29 | AppRouter['_def']['queries'][TRouteKey]
30 | >
31 |
32 | /**
33 | * This is a helper method to infer the input of a query resolver
34 | * @example type HelloInput = InferQueryInput<'hello'>
35 | */
36 | export type InferQueryInput = inferProcedureInput<
37 | AppRouter['_def']['queries'][TRouteKey]
38 | >
39 |
40 | /**
41 | * This is a helper method to infer the output of a mutation resolver
42 | * @example type HelloOutput = InferMutationOutput<'hello'>
43 | */
44 | export type InferMutationOutput =
45 | inferProcedureOutput
46 |
47 | /**
48 | * This is a helper method to infer the input of a mutation resolver
49 | * @example type HelloInput = InferMutationInput<'hello'>
50 | */
51 | export type InferMutationInput =
52 | inferProcedureInput
53 |
54 | /**
55 | * This is a helper method to infer the output of a subscription resolver
56 | * @example type HelloOutput = InferSubscriptionOutput<'hello'>
57 | */
58 | export type InferSubscriptionOutput =
59 | inferProcedureOutput
60 |
61 | /**
62 | * This is a helper method to infer the asynchronous output of a subscription resolver
63 | * @example type HelloAsyncOutput = InferAsyncSubscriptionOutput<'hello'>
64 | */
65 | export type InferAsyncSubscriptionOutput =
66 | inferSubscriptionOutput
67 |
68 | /**
69 | * This is a helper method to infer the input of a subscription resolver
70 | * @example type HelloInput = InferSubscriptionInput<'hello'>
71 | */
72 | export type InferSubscriptionInput =
73 | inferProcedureInput
74 |
--------------------------------------------------------------------------------
/apps/mobile/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
3 | "sourceRoot": "apps/mobile/src",
4 | "projectType": "application",
5 | "targets": {
6 | "build": {
7 | "executor": "nx:run-commands",
8 | "options": {
9 | "command": "echo \"Info: The 'build' target is not implemented for mobile.\""
10 | }
11 | },
12 | "start": {
13 | "executor": "@nrwl/expo:start",
14 | "options": {
15 | "port": 8081
16 | }
17 | },
18 | "web": {
19 | "executor": "@nrwl/expo:start",
20 | "options": {
21 | "port": 8081,
22 | "webpack": true
23 | }
24 | },
25 | "serve": {
26 | "executor": "@nrwl/workspace:run-commands",
27 | "options": {
28 | "command": "nx start mobile"
29 | }
30 | },
31 | "run-ios": {
32 | "executor": "@nrwl/expo:run",
33 | "options": {
34 | "platform": "ios"
35 | }
36 | },
37 | "run-android": {
38 | "executor": "@nrwl/expo:run",
39 | "options": {
40 | "platform": "android"
41 | }
42 | },
43 | "build-ios": {
44 | "executor": "@nrwl/expo:build-ios",
45 | "options": {}
46 | },
47 | "build-android": {
48 | "executor": "@nrwl/expo:build-android",
49 | "options": {}
50 | },
51 | "build-web": {
52 | "executor": "@nrwl/expo:build-web",
53 | "options": {}
54 | },
55 | "build-status": {
56 | "executor": "@nrwl/expo:build-web",
57 | "options": {}
58 | },
59 | "sync-deps": {
60 | "executor": "@nrwl/expo:sync-deps",
61 | "options": {}
62 | },
63 | "ensure-symlink": {
64 | "executor": "@nrwl/expo:ensure-symlink",
65 | "options": {}
66 | },
67 | "publish": {
68 | "executor": "@nrwl/expo:publish",
69 | "options": {}
70 | },
71 | "publish-set": {
72 | "executor": "@nrwl/expo:publish-set",
73 | "options": {}
74 | },
75 | "rollback": {
76 | "executor": "@nrwl/expo:rollback",
77 | "options": {}
78 | },
79 | "eject": {
80 | "executor": "@nrwl/expo:eject",
81 | "options": {}
82 | },
83 | "lint": {
84 | "executor": "@nrwl/linter:eslint",
85 | "outputs": ["{options.outputFile}"],
86 | "options": {
87 | "lintFilePatterns": ["apps/mobile/**/*.{ts,tsx,js,jsx}"]
88 | }
89 | },
90 | "test": {
91 | "executor": "@nrwl/jest:jest",
92 | "outputs": ["coverage/apps/mobile"],
93 | "options": {
94 | "jestConfig": "apps/mobile/jest.config.ts",
95 | "passWithNoTests": true
96 | }
97 | }
98 | },
99 | "tags": []
100 | }
101 |
--------------------------------------------------------------------------------
/libs/api/trpc-server-edgedb/src/lib/user-router.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod'
2 | import { createRouter } from './context'
3 | import { TRPCError } from '@trpc/server'
4 | import {
5 | upsertUser,
6 | createWatchlistItem,
7 | deleteWatchlistItem,
8 | getWatchlist,
9 | } from '@conference-demos/edgedb-client'
10 |
11 | export const userDataRouter = createRouter()
12 | .middleware(async ({ ctx, next }) => {
13 | if (!ctx.uid) {
14 | throw new TRPCError({ code: 'UNAUTHORIZED' })
15 | }
16 | return next()
17 | })
18 | .mutation('syncAccount', {
19 | output: z.void(),
20 | input: z.void(),
21 | async resolve({ ctx }) {
22 | await upsertUser(ctx.client)
23 | },
24 | })
25 | .mutation('addToWatchlist', {
26 | output: z.void(),
27 | input: z.object({
28 | id: z.string(),
29 | }),
30 | async resolve({ ctx, input }) {
31 | createWatchlistItem(
32 | {
33 | movieId: input.id,
34 | },
35 | ctx.client
36 | )
37 | },
38 | })
39 | .mutation('removeFromWatchlist', {
40 | output: z.void(),
41 | input: z.object({
42 | id: z.string(),
43 | }),
44 | async resolve({ input, ctx }) {
45 | await deleteWatchlistItem({ id: input.id }, ctx.client)
46 | },
47 | })
48 | .query('watchlist', {
49 | input: z.void(),
50 | async resolve({ ctx }) {
51 | const watchlist = await getWatchlist(ctx.client)
52 |
53 | const watchList = watchlist ?? []
54 |
55 | // make movie promises for each movie in watchlist
56 | const moviePromises =
57 | watchList.map(({ movieId }) =>
58 | fetch(`https://api.themoviedb.org/3/movie/${movieId}`, {
59 | headers: {
60 | Authorization: ctx.TMDB_TOKEN,
61 | },
62 | })
63 | ) ?? []
64 |
65 | // wait for all movie promises to resolve
66 | const moviesResults = await Promise.all(moviePromises)
67 |
68 | // get movies from results
69 | const moviesJsonPromises = moviesResults.map((response) =>
70 | response.json()
71 | )
72 |
73 | // wait for all movies to resolve
74 | const moviesJson = await Promise.all(moviesJsonPromises)
75 |
76 | const movies = moviesJson ?? []
77 |
78 | return {
79 | movies: movies.map((movie) => {
80 | const watchListId =
81 | watchList.find(
82 | (watchlistItem) => watchlistItem.movieId === movie.id.toString()
83 | )?.id ?? ''
84 |
85 | return {
86 | ...movie,
87 | posterImage: `https://image.tmdb.org/t/p/w500/${movie.poster_path}`,
88 | backdropImage: `https://image.tmdb.org/t/p/w500/${movie.backdrop_path}`,
89 | watchListId,
90 | }
91 | }),
92 | }
93 | },
94 | })
95 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/navigation/AuthStack.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { createNativeStackNavigator } from '@react-navigation/native-stack'
3 | import { Button, Center, FormControl, Icon, Input, Stack } from 'native-base'
4 | import reactotron from 'reactotron-react-native'
5 | import { useAuthenticatedUser } from '../providers/AuthenticationProvider'
6 | import { MaterialIcons } from '@expo/vector-icons'
7 | import { useForm, Controller } from 'react-hook-form'
8 | import * as z from 'zod'
9 | import { zodResolver } from '@hookform/resolvers/zod'
10 |
11 | const Schema = z.object({
12 | email: z.string().email(),
13 | })
14 |
15 | // infer type from Schema
16 | type SchemaType = z.infer
17 |
18 | const NavStack = createNativeStackNavigator()
19 |
20 | function LoginScreen() {
21 | const { setUser } = useAuthenticatedUser()
22 | const [loading, setLoading] = React.useState(false)
23 |
24 | const {
25 | control,
26 | handleSubmit,
27 | formState: { errors },
28 | } = useForm({
29 | defaultValues: {
30 | email: '',
31 | },
32 | resolver: zodResolver(Schema),
33 | })
34 |
35 | const signIn = React.useCallback(
36 | async (data: SchemaType) => {
37 | try {
38 | setLoading(true)
39 |
40 | setUser({ hasUser: true, username: data.email })
41 | } catch (error) {
42 | reactotron.log?.('signInAnonymously error', error)
43 | console.error(error)
44 | } finally {
45 | setLoading(false)
46 | }
47 | },
48 | [setUser]
49 | )
50 |
51 | return (
52 |
53 |
54 |
55 | {
58 | return (
59 | }
63 | size={5}
64 | ml="2"
65 | color="muted.400"
66 | />
67 | }
68 | w="75%"
69 | maxW="300px"
70 | size={'md'}
71 | onChangeText={(value) => onChange(value.toLowerCase())}
72 | onBlur={onBlur}
73 | value={value}
74 | placeholder="Email"
75 | />
76 | )
77 | }}
78 | name="email"
79 | />
80 |
81 | {errors.email?.message}
82 |
83 |
84 |
85 |
92 |
93 |
94 | )
95 | }
96 |
97 | export function AuthStack() {
98 | return (
99 |
104 |
105 |
106 | )
107 | }
108 |
--------------------------------------------------------------------------------
/libs/frontend/mobile-app/src/lib/features/movie/detail-screen/DetailScreen.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { createParam } from 'solito'
3 |
4 | import { Text, Box, VStack, Button, Spinner } from 'native-base'
5 | import { trpc } from '@conference-demos/trpc-client'
6 | import { InferQueryOutput } from '@conference-demos/api/trpc-server-edgedb'
7 | import { parseMovieId } from './parseMovieId'
8 | import { stringifyMovieId } from './stringifyMovieId'
9 | import { MoviePoster } from '../../home/MoviePoster'
10 | import { WatchlistMovieScreenNavigationProps } from '../../../navigation/Navigation'
11 |
12 | const { useParam } = createParam<{ id: number }>()
13 |
14 | type MovieById = InferQueryOutput<'tmdb.byId'>
15 |
16 | export function MovieDetailScreen({
17 | navigation,
18 | }: WatchlistMovieScreenNavigationProps) {
19 | const [id] = useParam('id', {
20 | initial: 0,
21 | parse: parseMovieId,
22 | stringify: stringifyMovieId,
23 | })
24 |
25 | const { mutate: addToWatchlist, isLoading: addToWatchlistLoading } =
26 | trpc.useMutation(['user.addToWatchlist'])
27 | const { mutate: removeFromWatchlist, isLoading: removeFromWatchlistLoading } =
28 | trpc.useMutation(['user.removeFromWatchlist'])
29 |
30 | const { data, isLoading: isWatchlistLoading } = trpc.useQuery([
31 | 'user.watchlist',
32 | ])
33 | const watchlist = data?.movies ?? []
34 | const utils = trpc.useContext()
35 | const movieResult = trpc.useQuery(['tmdb.byId', { id }], {
36 | enabled: id > 0,
37 | })
38 |
39 | const movie = movieResult.data
40 |
41 | let watchedMovieId = ''
42 | if (watchlist) {
43 | watchedMovieId = watchlist.find((m) => m.id === id)?.watchListId
44 | console.log('watchedMovieId', watchedMovieId)
45 |
46 | }
47 |
48 | React.useEffect(() => {
49 | navigation.setOptions({ title: movie?.title ?? 'Movie' })
50 | }, [movie?.title, navigation])
51 |
52 | if (!movieResult) return null
53 |
54 | function handleWatched(movie: MovieById) {
55 | if (!movie) {
56 | return
57 | }
58 |
59 | addToWatchlist(
60 | { id: movie.id.toString() },
61 | {
62 | onSuccess() {
63 | utils.invalidateQueries(['user.watchlist'])
64 | },
65 | }
66 | )
67 | }
68 |
69 | function removeWatched(watchlistId: string) {
70 | removeFromWatchlist(
71 | { id: watchlistId },
72 | {
73 | onSuccess() {
74 | utils.invalidateQueries(['user.watchlist'])
75 | },
76 | }
77 | )
78 | }
79 |
80 | if (movieResult.error) {
81 | return Error: {movieResult.error.message}
82 | }
83 |
84 | return movie ? (
85 |
91 |
99 |
100 | {isWatchlistLoading ? (
101 |
102 | ) : watchedMovieId ? (
103 |
109 | ) : (
110 |
116 | )}
117 |
118 |
119 | ) : null
120 | }
121 |
--------------------------------------------------------------------------------
/libs/api/trpc-server-edgedb/src/lib/tmdb-router.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod'
2 | import { createRouter } from './context'
3 | import { MoviesSchema, MovieType } from './MoviesSchema'
4 |
5 | export const tmdbRouter = createRouter()
6 | .query('nowPlaying', {
7 | output: MoviesSchema,
8 | input: z.void(),
9 | async resolve({ ctx }) {
10 | const url = 'movie/now_playing'
11 | const response = await fetch(
12 | `https://api.themoviedb.org/3/${url}?page=1`,
13 | {
14 | headers: {
15 | Authorization: ctx.TMDB_TOKEN,
16 | },
17 | }
18 | )
19 | const responseJson = await response.json()
20 |
21 | const movies = responseJson.results ?? []
22 |
23 | return {
24 | movies: movies.map((movie: MovieType) => ({
25 | ...movie,
26 | posterImage: `https://image.tmdb.org/t/p/w500/${movie.poster_path}`,
27 | backdropImage: `https://image.tmdb.org/t/p/w500/${movie.backdrop_path}`,
28 | })),
29 | }
30 | },
31 | })
32 | .query('search', {
33 | output: MoviesSchema,
34 | input: z.object({
35 | query: z.string(),
36 | }),
37 | async resolve({ ctx, input }) {
38 | const url = `search/movie?query=${input.query}`
39 | const response = await fetch(`https://api.themoviedb.org/3/${url}`, {
40 | headers: {
41 | Authorization: ctx.TMDB_TOKEN,
42 | },
43 | })
44 |
45 | const responseJson = await response.json()
46 |
47 | const movies = responseJson.results ?? []
48 |
49 | return {
50 | movies: movies.map((movie: MovieType) => ({
51 | ...movie,
52 | posterImage: `https://image.tmdb.org/t/p/w500/${movie.poster_path}`,
53 | backdropImage: `https://image.tmdb.org/t/p/w500/${movie.backdrop_path}`,
54 | })),
55 | }
56 | },
57 | })
58 | .query('byId', {
59 | // output: MovieSchema.nullable(),
60 | input: z.object({
61 | id: z.number(),
62 | }),
63 | async resolve({ ctx, input }) {
64 | const url = `movie/${input.id}`
65 | const response = await fetch(`https://api.themoviedb.org/3/${url}`, {
66 | headers: {
67 | Authorization: ctx.TMDB_TOKEN,
68 | },
69 | })
70 |
71 | const responseJson = await response.json()
72 |
73 | const movie: MovieType = responseJson
74 |
75 | return {
76 | ...movie,
77 | posterImage: `https://image.tmdb.org/t/p/w500/${movie.poster_path}`,
78 | backdropImage: `https://image.tmdb.org/t/p/w500/${movie.backdrop_path}`,
79 | }
80 | },
81 | })
82 | .query('creditsForMovieId', {
83 | // output: MovieSchema.nullable(),
84 | input: z.object({
85 | id: z.number(),
86 | }),
87 | async resolve({ ctx, input }) {
88 | const url = `movie/${input.id}/credits`
89 | const response = await fetch(`https://api.themoviedb.org/3/${url}`, {
90 | headers: {
91 | Authorization: ctx.TMDB_TOKEN,
92 | },
93 | })
94 |
95 | const responseJson = await response.json()
96 |
97 | const movie: MovieType = responseJson
98 |
99 | return movie
100 | },
101 | })
102 | .query('creditById', {
103 | // output: MovieSchema.nullable(),
104 | input: z.object({
105 | id: z.string(),
106 | }),
107 | async resolve({ ctx, input }) {
108 | const url = `credit/${input.id}`
109 | const response = await fetch(`https://api.themoviedb.org/3/${url}`, {
110 | headers: {
111 | Authorization: ctx.TMDB_TOKEN,
112 | },
113 | })
114 | const responseJson = await response.json()
115 |
116 | const movie: MovieType = responseJson
117 |
118 | return movie
119 | },
120 | })
121 | .query('personById', {
122 | // output: MovieSchema.nullable(),
123 | input: z.object({
124 | id: z.number(),
125 | }),
126 | async resolve({ ctx, input }) {
127 | const url = `person/${input.id}`
128 | const response = await fetch(`https://api.themoviedb.org/3/${url}`, {
129 | headers: {
130 | Authorization: ctx.TMDB_TOKEN,
131 | },
132 | })
133 | const responseJson = await response.json()
134 |
135 | const movie: MovieType = responseJson
136 |
137 | return movie
138 | },
139 | })
140 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tmdb-watchlist-edgedb",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "scripts": {
6 | "start": "nx serve",
7 | "build": "nx build",
8 | "test": "nx test",
9 | "vercel-build": "prisma generate && prisma migrate dev && nx build next-app",
10 | "edgedb": "edgedb",
11 | "edgedb:create-migration": "edgedb migration create",
12 | "edgedb:migrate": "edgedb migrate",
13 | "edgedb:codegen": "cd libs/database/edgedb-client && npx edgeql-js --output-dir src/lib/codegen --force-overwrite",
14 | "mobile:build:android:dev": "DEBUG=* cd apps/mobile && eas build --profile preview --platform android",
15 | "mobile:build:ios:dev": "DEBUG=* cd apps/mobile && rm -rf ios && eas build --profile preview --platform ios",
16 | "mobile:clean": "cd apps/mobile && expo prebuild --clean"
17 | },
18 | "private": true,
19 | "dependencies": {
20 | "@expo/metro-config": "0.3.17",
21 | "@hookform/resolvers": "^2.9.6",
22 | "@react-native-async-storage/async-storage": "~1.17.3",
23 | "@react-navigation/bottom-tabs": "^6.3.1",
24 | "@react-navigation/native": "^6.0.10",
25 | "@react-navigation/native-stack": "^6.6.2",
26 | "@trpc/client": "^9.25.3",
27 | "@trpc/next": "^9.25.3",
28 | "@trpc/react": "^9.25.3",
29 | "@trpc/server": "^9.25.3",
30 | "core-js": "^3.6.5",
31 | "cors": "^2.8.5",
32 | "edgedb": "^0.21.1",
33 | "expo": "45.0.5",
34 | "expo-dev-client": "~1.0.0",
35 | "expo-linking": "^3.1.0",
36 | "expo-splash-screen": "~0.15.1",
37 | "expo-status-bar": "~1.3.0",
38 | "expo-structured-headers": "~2.2.1",
39 | "expo-updates": "~0.13.3",
40 | "expo-web-browser": "^10.2.1",
41 | "express": "4.17.2",
42 | "graphql": "^16.5.0",
43 | "native-base": "^3.4.7",
44 | "next": "^12.1.6",
45 | "node-fetch": "2",
46 | "react": "17.0.2",
47 | "react-dom": "17.0.2",
48 | "react-hook-form": "^7.33.1",
49 | "react-native": "0.68.2",
50 | "react-native-gesture-handler": "~2.2.1",
51 | "react-native-reanimated": "~2.8.0",
52 | "react-native-safe-area-context": "4.2.4",
53 | "react-native-screens": "~3.11.1",
54 | "react-native-svg": "12.3.0",
55 | "react-native-svg-transformer": "1.0.0",
56 | "react-native-web": "0.17.7",
57 | "react-query": "^3.39.1",
58 | "regenerator-runtime": "0.13.7",
59 | "solito": "^0.0.26",
60 | "superjson": "^1.9.1",
61 | "tslib": "^2.3.0",
62 | "zod": "^3.17.3"
63 | },
64 | "devDependencies": {
65 | "@nrwl/cli": "14.3.6",
66 | "@nrwl/cypress": "14.3.6",
67 | "@nrwl/detox": "14.3.6",
68 | "@nrwl/eslint-plugin-nx": "14.3.6",
69 | "@nrwl/expo": "^14.3.1",
70 | "@nrwl/express": "^14.3.6",
71 | "@nrwl/jest": "14.3.6",
72 | "@nrwl/js": "14.3.6",
73 | "@nrwl/linter": "14.3.6",
74 | "@nrwl/next": "^14.3.6",
75 | "@nrwl/node": "14.3.6",
76 | "@nrwl/nx-cloud": "latest",
77 | "@nrwl/react": "14.3.6",
78 | "@nrwl/web": "14.3.6",
79 | "@nrwl/workspace": "14.3.6",
80 | "@svgr/webpack": "^5.5.0",
81 | "@testing-library/jest-dom": "5.16.4",
82 | "@testing-library/jest-native": "4.0.5",
83 | "@testing-library/react": "13.3.0",
84 | "@testing-library/react-native": "9.1.0",
85 | "@types/express": "4.17.13",
86 | "@types/jest": "27.4.1",
87 | "@types/node": "16.11.7",
88 | "@types/node-fetch": "^2.6.2",
89 | "@types/react": "18.0.13",
90 | "@types/react-dom": "18.0.5",
91 | "@types/react-native": "0.67.8",
92 | "@typescript-eslint/eslint-plugin": "~5.24.0",
93 | "@typescript-eslint/parser": "~5.24.0",
94 | "babel-jest": "27.5.1",
95 | "babel-preset-expo": "~9.1.0",
96 | "cypress": "^9.1.0",
97 | "detox": "19.7.1",
98 | "eslint": "~8.15.0",
99 | "eslint-config-next": "12.1.6",
100 | "eslint-config-prettier": "8.1.0",
101 | "eslint-plugin-cypress": "^2.10.3",
102 | "eslint-plugin-import": "2.26.0",
103 | "eslint-plugin-jsx-a11y": "6.5.1",
104 | "eslint-plugin-react": "7.30.0",
105 | "eslint-plugin-react-hooks": "4.6.0",
106 | "expo-cli": "5.4.9",
107 | "jest": "27.5.1",
108 | "jest-circus": "27.5.1",
109 | "jest-expo": "45.0.1",
110 | "metro-babel-register": "0.71.0",
111 | "metro-resolver": "0.71.0",
112 | "nx": "14.3.6",
113 | "prettier": "^2.6.2",
114 | "react-test-renderer": "18.2.0",
115 | "reactotron-react-native": "^5.0.2",
116 | "ts-jest": "27.1.4",
117 | "ts-node": "^10.8.2",
118 | "typescript": "~4.7.2"
119 | }
120 | }
121 |
--------------------------------------------------------------------------------