├── .npmrc ├── hooks ├── useColorScheme.ts ├── useColorScheme.web.ts └── useThemeColor.ts ├── app.db ├── app.db-journal ├── app ├── new-notion.tsx ├── +not-found.tsx ├── doc-actions-sheet.tsx ├── (tabs) │ ├── index.tsx │ ├── _layout.tsx │ ├── explore.tsx │ └── new-notion.tsx ├── +html.tsx └── _layout.tsx ├── assets ├── images │ ├── icon.png │ ├── favicon.png │ ├── splash.png │ ├── react-logo.png │ ├── adaptive-icon.png │ ├── react-logo@2x.png │ ├── react-logo@3x.png │ └── partial-react-logo.png └── fonts │ └── SpaceMono-Regular.ttf ├── babel.config.js ├── migrations ├── migration_lock.toml ├── 20240703032200_one │ └── migration.sql ├── 20240703215900_add_order_to_notionfile │ └── migration.sql └── 20240704014730_add_on_delete_cascade_for_notion_files │ └── migration.sql ├── queries.sql ├── tsconfig.json ├── components ├── __tests__ │ ├── ThemedText-test.tsx │ └── __snapshots__ │ │ └── ThemedText-test.tsx.snap ├── navigation │ └── TabBarIcon.tsx ├── ThemedView.tsx ├── ExternalLink.tsx ├── HelloWave.tsx ├── Collapsible.tsx ├── ThemedText.tsx ├── ResentFiles.tsx ├── NotionButton.tsx ├── ParallaxScrollView.tsx ├── ResentFileCard.tsx ├── DraggableNotionList.tsx └── DraggableNotionListItem.tsx ├── eas.json ├── .gitignore ├── constants ├── Gradients.ts ├── Colors.ts └── MarkdownStyle.ts ├── myDbModule.ts ├── schema.prisma ├── app.json ├── package.json ├── scripts └── reset-project.js └── README.md /.npmrc: -------------------------------------------------------------------------------- 1 | node-linker=hoisted 2 | -------------------------------------------------------------------------------- /hooks/useColorScheme.ts: -------------------------------------------------------------------------------- 1 | export { useColorScheme } from 'react-native'; 2 | -------------------------------------------------------------------------------- /app.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/betomoedano/React-Native-Notion-Clone/HEAD/app.db -------------------------------------------------------------------------------- /app.db-journal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/betomoedano/React-Native-Notion-Clone/HEAD/app.db-journal -------------------------------------------------------------------------------- /app/new-notion.tsx: -------------------------------------------------------------------------------- 1 | import NewNotionScreen from "./(tabs)/new-notion"; 2 | 3 | export default NewNotionScreen; 4 | -------------------------------------------------------------------------------- /assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/betomoedano/React-Native-Notion-Clone/HEAD/assets/images/icon.png -------------------------------------------------------------------------------- /assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/betomoedano/React-Native-Notion-Clone/HEAD/assets/images/favicon.png -------------------------------------------------------------------------------- /assets/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/betomoedano/React-Native-Notion-Clone/HEAD/assets/images/splash.png -------------------------------------------------------------------------------- /assets/images/react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/betomoedano/React-Native-Notion-Clone/HEAD/assets/images/react-logo.png -------------------------------------------------------------------------------- /assets/images/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/betomoedano/React-Native-Notion-Clone/HEAD/assets/images/adaptive-icon.png -------------------------------------------------------------------------------- /assets/images/react-logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/betomoedano/React-Native-Notion-Clone/HEAD/assets/images/react-logo@2x.png -------------------------------------------------------------------------------- /assets/images/react-logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/betomoedano/React-Native-Notion-Clone/HEAD/assets/images/react-logo@3x.png -------------------------------------------------------------------------------- /assets/fonts/SpaceMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/betomoedano/React-Native-Notion-Clone/HEAD/assets/fonts/SpaceMono-Regular.ttf -------------------------------------------------------------------------------- /assets/images/partial-react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/betomoedano/React-Native-Notion-Clone/HEAD/assets/images/partial-react-logo.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "sqlite" -------------------------------------------------------------------------------- /queries.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO User (name) VALUES ('John Doe'); 2 | INSERT INTO NotionFile (coverPhoto, icon, title, description, content, type, authorId) 3 | VALUES ('https://i.ytimg.com/vi/XoumiwHbKDg/maxresdefault.jpg', '🔥', 'Snapchat Clone', 'Sample Description', 'Sample Content', 'text', 1); 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "strict": true, 5 | "paths": { 6 | "@/*": [ 7 | "./*" 8 | ] 9 | } 10 | }, 11 | "include": [ 12 | "**/*.ts", 13 | "**/*.tsx", 14 | ".expo/types/**/*.ts", 15 | "expo-env.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /components/__tests__/ThemedText-test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | 4 | import { ThemedText } from '../ThemedText'; 5 | 6 | it(`renders correctly`, () => { 7 | const tree = renderer.create(Snapshot test!).toJSON(); 8 | 9 | expect(tree).toMatchSnapshot(); 10 | }); 11 | -------------------------------------------------------------------------------- /eas.json: -------------------------------------------------------------------------------- 1 | { 2 | "cli": { 3 | "version": ">= 10.2.4" 4 | }, 5 | "build": { 6 | "development": { 7 | "developmentClient": true, 8 | "distribution": "internal" 9 | }, 10 | "preview": { 11 | "distribution": "internal" 12 | }, 13 | "production": {} 14 | }, 15 | "submit": { 16 | "production": {} 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | ios/ 13 | android/ 14 | 15 | # macOS 16 | .DS_Store 17 | 18 | # @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb 19 | # The following patterns were generated by expo-cli 20 | 21 | expo-env.d.ts 22 | # @end expo-cli -------------------------------------------------------------------------------- /components/__tests__/__snapshots__/ThemedText-test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`renders correctly 1`] = ` 4 | 22 | Snapshot test! 23 | 24 | `; 25 | -------------------------------------------------------------------------------- /components/navigation/TabBarIcon.tsx: -------------------------------------------------------------------------------- 1 | // You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/ 2 | 3 | import Ionicons from '@expo/vector-icons/Ionicons'; 4 | import { type IconProps } from '@expo/vector-icons/build/createIconSet'; 5 | import { type ComponentProps } from 'react'; 6 | 7 | export function TabBarIcon({ style, ...rest }: IconProps['name']>) { 8 | return ; 9 | } 10 | -------------------------------------------------------------------------------- /hooks/useColorScheme.web.ts: -------------------------------------------------------------------------------- 1 | // NOTE: The default React Native styling doesn't support server rendering. 2 | // Server rendered styles should not change between the first render of the HTML 3 | // and the first render on the client. Typically, web developers will use CSS media queries 4 | // to render different styles on the client and server, these aren't directly supported in React Native 5 | // but can be achieved using a styling library like Nativewind. 6 | export function useColorScheme() { 7 | return 'light'; 8 | } 9 | -------------------------------------------------------------------------------- /components/ThemedView.tsx: -------------------------------------------------------------------------------- 1 | import { View, type ViewProps } from 'react-native'; 2 | 3 | import { useThemeColor } from '@/hooks/useThemeColor'; 4 | 5 | export type ThemedViewProps = ViewProps & { 6 | lightColor?: string; 7 | darkColor?: string; 8 | }; 9 | 10 | export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) { 11 | const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background'); 12 | 13 | return ; 14 | } 15 | -------------------------------------------------------------------------------- /hooks/useThemeColor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Learn more about light and dark modes: 3 | * https://docs.expo.dev/guides/color-schemes/ 4 | */ 5 | 6 | import { useColorScheme } from 'react-native'; 7 | 8 | import { Colors } from '@/constants/Colors'; 9 | 10 | export function useThemeColor( 11 | props: { light?: string; dark?: string }, 12 | colorName: keyof typeof Colors.light & keyof typeof Colors.dark 13 | ) { 14 | const theme = useColorScheme() ?? 'light'; 15 | const colorFromProps = props[theme]; 16 | 17 | if (colorFromProps) { 18 | return colorFromProps; 19 | } else { 20 | return Colors[theme][colorName]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /constants/Gradients.ts: -------------------------------------------------------------------------------- 1 | import { LinearGradientProps } from "expo-linear-gradient"; 2 | 3 | const gradients = [ 4 | { name: "Oceanic", colors: ["#0093E9", "#80D0C7"] }, 5 | { name: "Cotton Candy", colors: ["#D9AFD9", "#97D9E1"] }, 6 | { name: "Sunset", colors: ["#FAD961", "#F76B1C"] }, 7 | { name: "Beachside", colors: ["#00CDAC", "#02AAB0"] }, 8 | { name: "Peachy", colors: ["#FFA751", "#FFE259"] }, 9 | { name: "Pumpkin", colors: ["#D4145A", "#FBB03B"] }, 10 | { name: "Arendelle", colors: ["#9796f0", "#fbc7d4"] }, 11 | { name: "High Tide", colors: ["#0082C8", "#667DB6"] }, 12 | ]; 13 | 14 | export function getRandomGradient(): LinearGradientProps["colors"] { 15 | const randomIndex = Math.floor(Math.random() * gradients.length); 16 | return gradients[randomIndex].colors; 17 | } 18 | -------------------------------------------------------------------------------- /components/ExternalLink.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'expo-router'; 2 | import { openBrowserAsync } from 'expo-web-browser'; 3 | import { type ComponentProps } from 'react'; 4 | import { Platform } from 'react-native'; 5 | 6 | type Props = Omit, 'href'> & { href: string }; 7 | 8 | export function ExternalLink({ href, ...rest }: Props) { 9 | return ( 10 | { 15 | if (Platform.OS !== 'web') { 16 | // Prevent the default behavior of linking to the default browser on native. 17 | event.preventDefault(); 18 | // Open the link in an in-app browser. 19 | await openBrowserAsync(href); 20 | } 21 | }} 22 | /> 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /app/+not-found.tsx: -------------------------------------------------------------------------------- 1 | import { Link, Stack } from 'expo-router'; 2 | import { StyleSheet } from 'react-native'; 3 | 4 | import { ThemedText } from '@/components/ThemedText'; 5 | import { ThemedView } from '@/components/ThemedView'; 6 | 7 | export default function NotFoundScreen() { 8 | return ( 9 | <> 10 | 11 | 12 | This screen doesn't exist. 13 | 14 | Go to home screen! 15 | 16 | 17 | 18 | ); 19 | } 20 | 21 | const styles = StyleSheet.create({ 22 | container: { 23 | flex: 1, 24 | alignItems: 'center', 25 | justifyContent: 'center', 26 | padding: 20, 27 | }, 28 | link: { 29 | marginTop: 15, 30 | paddingVertical: 15, 31 | }, 32 | }); 33 | -------------------------------------------------------------------------------- /constants/Colors.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Below are the colors that are used in the app. The colors are defined in the light and dark mode. 3 | * There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc. 4 | */ 5 | 6 | const tintColorLight = "#000"; 7 | const tintColorDark = "#fff"; 8 | 9 | export const Colors = { 10 | light: { 11 | text: "#11181C", 12 | background: "#fff", 13 | backgroundSecondary: "#00000005", 14 | tint: tintColorLight, 15 | icon: "#687076", 16 | tabIconDefault: "#687076", 17 | tabIconSelected: tintColorLight, 18 | }, 19 | dark: { 20 | text: "#ECEDEE", 21 | background: "#151718", 22 | backgroundSecondary: "#ffffff05", 23 | tint: tintColorDark, 24 | icon: "#9BA1A6", 25 | tabIconDefault: "#9BA1A6", 26 | tabIconSelected: tintColorDark, 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /myDbModule.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client/react-native"; 2 | import { reactiveHooksExtension } from "@prisma/react-native"; 3 | 4 | export const baseClient = new PrismaClient({ 5 | log: [ 6 | // { emit: "stdout", level: "query" }, 7 | { emit: "stdout", level: "info" }, 8 | { emit: "stdout", level: "warn" }, 9 | { emit: "stdout", level: "error" }, 10 | ], 11 | }); 12 | 13 | export const extendedClient = baseClient.$extends(reactiveHooksExtension()); 14 | 15 | export async function initializeDb() { 16 | try { 17 | await baseClient.$applyPendingMigrations(); 18 | console.log("db initialized!"); 19 | } catch (e) { 20 | console.error(`failed to apply migrations: ${e}`); 21 | throw new Error( 22 | "Applying migrations failed, your app is now in an inconsistent state. We cannot guarantee safety, it is now your responsibility to reset the database or tell the user to re-install the app" 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/doc-actions-sheet.tsx: -------------------------------------------------------------------------------- 1 | import { Link, Stack } from "expo-router"; 2 | import { StyleSheet } from "react-native"; 3 | 4 | import { ThemedText } from "@/components/ThemedText"; 5 | import { ThemedView } from "@/components/ThemedView"; 6 | 7 | export default function DocActionsSheetScreen() { 8 | return ( 9 | <> 10 | 11 | 12 | This screen doesn't exist. 13 | 14 | Go to home screen! 15 | 16 | 17 | 18 | ); 19 | } 20 | 21 | const styles = StyleSheet.create({ 22 | container: { 23 | flex: 1, 24 | alignItems: "center", 25 | justifyContent: "center", 26 | padding: 20, 27 | }, 28 | link: { 29 | marginTop: 15, 30 | paddingVertical: 15, 31 | }, 32 | }); 33 | -------------------------------------------------------------------------------- /app/(tabs)/index.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, SafeAreaView, Button } from "react-native"; 2 | 3 | import { ThemedView } from "@/components/ThemedView"; 4 | import { extendedClient } from "@/myDbModule"; 5 | import ResentFiles from "@/components/ResentFiles"; 6 | import DraggableNotionList from "@/components/DraggableNotionList"; 7 | 8 | export default function HomeScreen() { 9 | const user = extendedClient.user.useFindFirst({ where: { id: 1 } }); 10 | 11 | const createUser = () => { 12 | const newUser = { name: "Beto", email: "beto@expo.dev" }; 13 | extendedClient.user.create({ data: newUser }); 14 | }; 15 | return ( 16 | 17 | 18 | {/*