├── .vscode └── settings.json ├── assets ├── disc.png ├── home.png ├── icon.png ├── user.png ├── heart.png ├── reply.png ├── search.png ├── splash.png ├── favicon.png ├── message.png ├── new-video.png ├── music-note.png ├── plus-button.png ├── adaptive-icon.png ├── message-circle.png └── floating-music-note.png ├── README.md ├── babel.config.js ├── clients ├── apollo.js └── livepeer.js ├── .gitignore ├── App.js ├── routes.js ├── app.json ├── package.json ├── screens ├── Home.js └── Login.js ├── queries.js └── components ├── BottomTabs.js └── VideoPlayer.js /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /assets/disc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/decentralized-tiktok/HEAD/assets/disc.png -------------------------------------------------------------------------------- /assets/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/decentralized-tiktok/HEAD/assets/home.png -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/decentralized-tiktok/HEAD/assets/icon.png -------------------------------------------------------------------------------- /assets/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/decentralized-tiktok/HEAD/assets/user.png -------------------------------------------------------------------------------- /assets/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/decentralized-tiktok/HEAD/assets/heart.png -------------------------------------------------------------------------------- /assets/reply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/decentralized-tiktok/HEAD/assets/reply.png -------------------------------------------------------------------------------- /assets/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/decentralized-tiktok/HEAD/assets/search.png -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/decentralized-tiktok/HEAD/assets/splash.png -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/decentralized-tiktok/HEAD/assets/favicon.png -------------------------------------------------------------------------------- /assets/message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/decentralized-tiktok/HEAD/assets/message.png -------------------------------------------------------------------------------- /assets/new-video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/decentralized-tiktok/HEAD/assets/new-video.png -------------------------------------------------------------------------------- /assets/music-note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/decentralized-tiktok/HEAD/assets/music-note.png -------------------------------------------------------------------------------- /assets/plus-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/decentralized-tiktok/HEAD/assets/plus-button.png -------------------------------------------------------------------------------- /assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/decentralized-tiktok/HEAD/assets/adaptive-icon.png -------------------------------------------------------------------------------- /assets/message-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/decentralized-tiktok/HEAD/assets/message-circle.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # decentralized-tiktok 2 | A decentralized Tiktok built on top of Livepeer, Lens Protocol, and Bundlr Network 3 | 4 | -------------------------------------------------------------------------------- /assets/floating-music-note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/decentralized-tiktok/HEAD/assets/floating-music-note.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /clients/apollo.js: -------------------------------------------------------------------------------- 1 | import { ApolloClient, InMemoryCache } from "@apollo/client"; 2 | 3 | const APClient = new ApolloClient({ 4 | uri: "https://api.lens.dev", 5 | cache: new InMemoryCache(), 6 | }); 7 | export default APClient; 8 | -------------------------------------------------------------------------------- /.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 | 13 | # macOS 14 | .DS_Store 15 | 16 | # Temporary files created by Metro to check the health of the file watcher 17 | .metro-health-check* 18 | -------------------------------------------------------------------------------- /clients/livepeer.js: -------------------------------------------------------------------------------- 1 | import { createReactClient } from "@livepeer/react-native"; 2 | import { studioProvider } from "livepeer/providers/studio"; 3 | 4 | const LPClient = createReactClient({ 5 | provider: studioProvider({ apiKey: "2cedff44-a68e-4149-9345-e2b25b1cdbd2" }), 6 | }); 7 | 8 | export default LPClient; -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Routes from './routes' 3 | import WalletConnectProvider from "react-native-walletconnect"; 4 | import { LivepeerConfig } from '@livepeer/react-native'; 5 | import LPClient from './clients/livepeer'; 6 | import { ApolloProvider } from '@apollo/client'; 7 | import APClient from './clients/apollo'; 8 | 9 | export default function App() { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ) 19 | } -------------------------------------------------------------------------------- /routes.js: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from "@react-navigation/native"; 2 | import { createStackNavigator } from "@react-navigation/stack"; 3 | import BottomTabs from "./components/BottomTabs"; 4 | import Login from "./screens/Login"; 5 | 6 | const Stack = createStackNavigator(); 7 | 8 | function Routes() { 9 | return ( 10 | 11 | 16 | 17 | 18 | 19 | 20 | ); 21 | } 22 | export default Routes; 23 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "web3-tiktok", 4 | "slug": "web3-tiktok", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "userInterfaceStyle": "light", 9 | "splash": { 10 | "image": "./assets/splash.png", 11 | "resizeMode": "contain", 12 | "backgroundColor": "#ffffff" 13 | }, 14 | "assetBundlePatterns": [ 15 | "**/*" 16 | ], 17 | "ios": { 18 | "supportsTablet": true 19 | }, 20 | "android": { 21 | "adaptiveIcon": { 22 | "foregroundImage": "./assets/adaptive-icon.png", 23 | "backgroundColor": "#ffffff" 24 | } 25 | }, 26 | "web": { 27 | "favicon": "./assets/favicon.png" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web3-tiktok", 3 | "version": "1.0.0", 4 | "main": "node_modules/expo/AppEntry.js", 5 | "scripts": { 6 | "start": "expo start", 7 | "android": "expo start --android", 8 | "ios": "expo start --ios", 9 | "web": "expo start --web" 10 | }, 11 | "dependencies": { 12 | "@apollo/client": "^3.7.9", 13 | "@livepeer/react-native": "^1.2.5", 14 | "@react-native-async-storage/async-storage": "^1.17.11", 15 | "@react-navigation/bottom-tabs": "^6.5.7", 16 | "@react-navigation/material-bottom-tabs": "^6.2.15", 17 | "@react-navigation/native": "^6.1.6", 18 | "@react-navigation/stack": "^6.3.16", 19 | "expo": "~48.0.4", 20 | "expo-av": "^13.2.1", 21 | "expo-media-library": "^15.2.2", 22 | "expo-status-bar": "~1.4.4", 23 | "graphql": "^16.6.0", 24 | "livepeer": "^2.2.3", 25 | "react": "18.2.0", 26 | "react-native": "0.71.3", 27 | "react-native-gesture-handler": "^2.9.0", 28 | "react-native-safe-area-context": "^4.5.0", 29 | "react-native-screens": "^3.20.0", 30 | "react-native-svg": "^13.8.0", 31 | "react-native-walletconnect": "^0.0.1-alpha.2", 32 | "react-native-webview": "^11.26.1" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "^7.20.0" 36 | }, 37 | "private": true 38 | } 39 | -------------------------------------------------------------------------------- /screens/Home.js: -------------------------------------------------------------------------------- 1 | import { FlatList, StyleSheet, View, Text, Dimensions } from "react-native"; 2 | import React, { useState } from "react"; 3 | import { EXPLORE_POSTS } from "../queries"; 4 | import { useQuery } from "@apollo/client"; 5 | import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs"; 6 | import VideoPlayer from "../components/VideoPlayer"; 7 | 8 | export default function Home() { 9 | const [activeVideoIndex, setActiveVideoIndex] = useState(0); 10 | 11 | const bottomTabHeight = useBottomTabBarHeight(); 12 | const { height: WINDOW_HEIGHT } = Dimensions.get("window"); 13 | const { data } = useQuery(EXPLORE_POSTS, { 14 | variables: { 15 | request: { 16 | limit: 5, 17 | sources: ["lenstube-bytes"], 18 | publicationTypes: ["POST"], 19 | sortCriteria: "CURATED_PROFILES", 20 | }, 21 | }, 22 | }); 23 | 24 | const pageInfo = data?.explorePublications?.pageInfo; 25 | const videos = data?.explorePublications?.items; 26 | 27 | return ( 28 | 29 | ( 33 | 34 | )} 35 | onScroll={(e) => { 36 | const index = Math.round( 37 | e.nativeEvent.contentOffset.y / (WINDOW_HEIGHT - bottomTabHeight) 38 | ); 39 | setActiveVideoIndex(index); 40 | }} 41 | /> 42 | 43 | ); 44 | } 45 | 46 | const styles = StyleSheet.create({ 47 | container: { 48 | flex: 1, 49 | justifyContent: "center", 50 | backgroundColor: "#fff", 51 | alignItems: "center", 52 | }, 53 | }); 54 | -------------------------------------------------------------------------------- /queries.js: -------------------------------------------------------------------------------- 1 | import { gql } from "@apollo/client"; 2 | export const EXPLORE_POSTS = gql` 3 | query ($request: ExplorePublicationRequest!) { 4 | explorePublications(request: $request) { 5 | items { 6 | __typename 7 | ... on Post { 8 | ...PostFields 9 | } 10 | } 11 | pageInfo { 12 | prev 13 | next 14 | totalCount 15 | } 16 | } 17 | } 18 | fragment MediaFields on Media { 19 | url 20 | width 21 | height 22 | mimeType 23 | } 24 | fragment ProfileFields on Profile { 25 | id 26 | name 27 | picture { 28 | ... on NftImage { 29 | contractAddress 30 | tokenId 31 | uri 32 | verified 33 | } 34 | ... on MediaSet { 35 | original { 36 | ...MediaFields 37 | } 38 | small { 39 | ...MediaFields 40 | } 41 | medium { 42 | ...MediaFields 43 | } 44 | } 45 | } 46 | } 47 | fragment PublicationStatsFields on PublicationStats { 48 | totalAmountOfMirrors 49 | totalUpvotes 50 | totalAmountOfCollects 51 | totalAmountOfComments 52 | } 53 | fragment MetadataOutputFields on MetadataOutput { 54 | name 55 | description 56 | content 57 | media { 58 | original { 59 | ...MediaFields 60 | } 61 | small { 62 | ...MediaFields 63 | } 64 | medium { 65 | ...MediaFields 66 | } 67 | } 68 | } 69 | 70 | fragment PostFields on Post { 71 | id 72 | profile { 73 | ...ProfileFields 74 | } 75 | stats { 76 | ...PublicationStatsFields 77 | } 78 | metadata { 79 | ...MetadataOutputFields 80 | } 81 | createdAt 82 | } 83 | `; 84 | -------------------------------------------------------------------------------- /screens/Login.js: -------------------------------------------------------------------------------- 1 | import { Button, Pressable, StyleSheet, Text, TextBase, View } from 'react-native' 2 | import React from 'react' 3 | import { StatusBar } from 'expo-status-bar'; 4 | import { useWalletConnect } from "react-native-walletconnect"; 5 | 6 | export default function Login() { 7 | const { 8 | createSession, 9 | killSession, 10 | session, 11 | signTransaction, 12 | } = useWalletConnect(); 13 | 14 | const hasWallet = !!session.length; 15 | 16 | 17 | return ( 18 | 19 | {!hasWallet && ( 20 |