├── .watchmanconfig ├── .gitattributes ├── .babelrc ├── android ├── app │ ├── src │ │ └── main │ │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ └── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── assets │ │ │ └── fonts │ │ │ │ ├── Entypo.ttf │ │ │ │ ├── Zocial.ttf │ │ │ │ ├── Ionicons.ttf │ │ │ │ ├── Octicons.ttf │ │ │ │ ├── EvilIcons.ttf │ │ │ │ ├── FontAwesome.ttf │ │ │ │ ├── Foundation.ttf │ │ │ │ ├── MaterialIcons.ttf │ │ │ │ └── SimpleLineIcons.ttf │ │ │ ├── java │ │ │ └── com │ │ │ │ └── mastodon │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── AndroidManifest.xml │ ├── BUCK │ ├── proguard-rules.pro │ └── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── keystores │ ├── debug.keystore.properties │ └── BUCK ├── settings.gradle ├── build.gradle ├── gradle.properties ├── gradlew.bat └── gradlew ├── .buckconfig ├── index.ios.js ├── index.android.js ├── README.md ├── src ├── reducers │ ├── index.js │ ├── tokens.js │ └── credentials.js ├── features │ ├── notifications │ │ └── index.js │ ├── login │ │ ├── reducer.js │ │ ├── index.js │ │ └── actions.js │ └── home │ │ └── index.js ├── actions │ └── credentials.js ├── root.js └── containers │ └── app.js ├── __tests__ ├── index.ios.js └── index.android.js ├── ios ├── Mastodon │ ├── AppDelegate.h │ ├── main.m │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── AppDelegate.m │ ├── Info.plist │ └── Base.lproj │ │ └── LaunchScreen.xib ├── MastodonTests │ ├── Info.plist │ └── MastodonTests.m └── Mastodon.xcodeproj │ ├── xcshareddata │ └── xcschemes │ │ └── Mastodon.xcscheme │ └── project.pbxproj ├── .gitignore ├── package.json └── .flowconfig /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"] 3 | } -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Mastodon 3 | 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mastodon/mastodon-native/HEAD/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Entypo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mastodon/mastodon-native/HEAD/android/app/src/main/assets/fonts/Entypo.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Zocial.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mastodon/mastodon-native/HEAD/android/app/src/main/assets/fonts/Zocial.ttf -------------------------------------------------------------------------------- /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mastodon/mastodon-native/HEAD/android/app/src/main/assets/fonts/Ionicons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Octicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mastodon/mastodon-native/HEAD/android/app/src/main/assets/fonts/Octicons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/EvilIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mastodon/mastodon-native/HEAD/android/app/src/main/assets/fonts/EvilIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mastodon/mastodon-native/HEAD/android/app/src/main/assets/fonts/FontAwesome.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Foundation.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mastodon/mastodon-native/HEAD/android/app/src/main/assets/fonts/Foundation.ttf -------------------------------------------------------------------------------- /index.ios.js: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native' 2 | import Root from './src/root' 3 | 4 | AppRegistry.registerComponent('Mastodon', () => Root); 5 | -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/MaterialIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mastodon/mastodon-native/HEAD/android/app/src/main/assets/fonts/MaterialIcons.ttf -------------------------------------------------------------------------------- /index.android.js: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native' 2 | import Root from './src/root' 3 | 4 | AppRegistry.registerComponent('Mastodon', () => Root); 5 | -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/SimpleLineIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mastodon/mastodon-native/HEAD/android/app/src/main/assets/fonts/SimpleLineIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mastodon/mastodon-native/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mastodon/mastodon-native/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mastodon/mastodon-native/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mastodon/mastodon-native/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = 'debug', 3 | store = 'debug.keystore', 4 | properties = 'debug.keystore.properties', 5 | visibility = [ 6 | 'PUBLIC', 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mastodon Native 2 | 3 | The beginnings of what will hopefully become a mobile app (Android and iOS) for Mastodon, built using [React Native](https://facebook.github.io/react-native/) 4 | 5 | Contributions welcome. 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'Mastodon' 2 | include ':react-native-vector-icons' 3 | project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android') 4 | 5 | include ':app' 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip 6 | -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux-immutable' 2 | 3 | import credentials from './credentials' 4 | import tokens from './tokens' 5 | import login from '../features/login/reducer' 6 | 7 | export default combineReducers({ 8 | credentials, 9 | tokens, 10 | login, 11 | }) 12 | -------------------------------------------------------------------------------- /__tests__/index.ios.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import Index from '../index.ios.js'; 4 | 5 | // Note: test renderer must be required after react-native. 6 | import renderer from 'react-test-renderer'; 7 | 8 | it('renders correctly', () => { 9 | const tree = renderer.create( 10 | 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /__tests__/index.android.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import Index from '../index.android.js'; 4 | 5 | // Note: test renderer must be required after react-native. 6 | import renderer from 'react-test-renderer'; 7 | 8 | it('renders correctly', () => { 9 | const tree = renderer.create( 10 | 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /src/features/notifications/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, Text } from 'react-native' 3 | 4 | const Notifications = React.createClass({ 5 | render () { 6 | return ( 7 | 8 | This is notifications 9 | 10 | ) 11 | } 12 | }) 13 | 14 | export default Notifications 15 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/mastodon/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.mastodon; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. 9 | * This is used to schedule rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "Mastodon"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/reducers/tokens.js: -------------------------------------------------------------------------------- 1 | import Immutable from 'immutable' 2 | 3 | import { CREDENTIALS_TOKEN_SET } from '../actions/credentials' 4 | 5 | const initialState = Immutable.List() 6 | 7 | export default function (state = initialState, action) { 8 | switch(action.type) { 9 | case CREDENTIALS_TOKEN_SET: 10 | return state.push(Immutable.Map({ 11 | domain: action.domain, 12 | token: action.token 13 | })) 14 | default: 15 | return state 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/actions/credentials.js: -------------------------------------------------------------------------------- 1 | export const CREDENTIALS_SET = 'CREDENTIALS_SET' 2 | export const CREDENTIALS_TOKEN_SET = 'CREDENTIALS_TOKEN_SET' 3 | 4 | export function setCredentials (domain, clientId, clientSecret) { 5 | return { 6 | type: CREDENTIALS_SET, 7 | domain, 8 | clientId, 9 | clientSecret 10 | } 11 | } 12 | 13 | export function setCredentialsToken(domain, token) { 14 | return { 15 | type: CREDENTIALS_TOKEN_SET, 16 | domain, 17 | token 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/reducers/credentials.js: -------------------------------------------------------------------------------- 1 | import Immutable from 'immutable' 2 | 3 | import { CREDENTIALS_SET } from '../actions/credentials' 4 | 5 | const initialState = Immutable.Map() 6 | 7 | export default function (state = initialState, action) { 8 | switch(action.type) { 9 | case CREDENTIALS_SET: 10 | return state.set(action.domain, Immutable.Map({ 11 | clientId: action.clientId, 12 | clientSecret: action.clientSecret 13 | })) 14 | default: 15 | return state 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/features/login/reducer.js: -------------------------------------------------------------------------------- 1 | import Immutable from 'immutable' 2 | import { LOGIN_CHANGE_ID, LOGIN_START } from './actions' 3 | 4 | const initialState = Immutable.Map({ 5 | id: '', 6 | domain: null, 7 | }) 8 | 9 | export default function (state = initialState, action) { 10 | switch(action.type) { 11 | case LOGIN_CHANGE_ID: 12 | return state.set('id', action.text) 13 | case LOGIN_START: 14 | return state.set('domain', action.domain) 15 | default: 16 | return state 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ios/Mastodon/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ios/Mastodon/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "AppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | android/app/libs 42 | *.keystore 43 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.3.1' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | mavenLocal() 18 | jcenter() 19 | maven { 20 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 21 | url "$rootDir/../node_modules/react-native/android" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ios/Mastodon/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /ios/MastodonTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Mastodon", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "test": "jest" 8 | }, 9 | "dependencies": { 10 | "axios": "^0.15.3", 11 | "immutable": "^3.8.1", 12 | "react": "15.4.1", 13 | "react-native": "0.39.2", 14 | "react-native-router-flux": "^3.37.0", 15 | "react-native-vector-icons": "^3.0.0", 16 | "react-redux": "^5.0.1", 17 | "redux": "^3.6.0", 18 | "redux-immutable": "^3.0.9", 19 | "redux-storage": "^4.1.2", 20 | "redux-storage-engine-reactnativeasyncstorage": "^1.0.5", 21 | "redux-storage-merger-immutablejs": "^1.0.5", 22 | "redux-thunk": "^2.1.0" 23 | }, 24 | "devDependencies": { 25 | "babel-jest": "18.0.0", 26 | "babel-preset-react-native": "1.9.1", 27 | "jest": "18.1.0", 28 | "react-test-renderer": "15.4.1" 29 | }, 30 | "jest": { 31 | "preset": "react-native" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /src/root.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { createStore, applyMiddleware } from 'redux' 3 | import { AsyncStorage } from 'react-native' 4 | import * as storage from 'redux-storage' 5 | import createEngine from 'redux-storage-engine-reactnativeasyncstorage' 6 | import merger from 'redux-storage-merger-immutablejs' 7 | import thunk from 'redux-thunk' 8 | import { Provider } from 'react-redux' 9 | import appReducer from './reducers' 10 | import App from './containers/app' 11 | 12 | const engine = createEngine('mastodon') 13 | const store = createStore(storage.reducer(appReducer, merger), applyMiddleware(thunk, storage.createMiddleware(engine))) 14 | 15 | const load = storage.createLoader(engine); 16 | load(store).then(newState => console.log('Loaded state:', newState)); 17 | 18 | export default class Root extends Component { 19 | render () { 20 | return ( 21 | 22 | 23 | 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/features/home/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, Text, ActivityIndicator } from 'react-native' 3 | import { connect } from 'react-redux' 4 | import axios from 'axios' 5 | 6 | const mapStateToProps = state => ({ 7 | accessor: state.get('tokens').last() 8 | }) 9 | 10 | const Home = React.createClass({ 11 | getInitialState () { 12 | return { 13 | account: null 14 | } 15 | }, 16 | 17 | componentDidMount () { 18 | axios.get(`https://${this.props.accessor.get('domain')}/api/v1/accounts/verify_credentials`, { 19 | headers: { 20 | Authorization: `Bearer ${this.props.accessor.get('token')}` 21 | } 22 | }).then(response => { 23 | this.setState({ account: response.data }) 24 | }).catch(error => { 25 | console.error(error) 26 | }) 27 | }, 28 | 29 | render () { 30 | const { account } = this.state 31 | 32 | if (account === null) { 33 | return 34 | } 35 | 36 | return ( 37 | 38 | Hello {this.state.account.acct} 39 | 40 | ) 41 | } 42 | }) 43 | 44 | export default connect(mapStateToProps)(Home) 45 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/mastodon/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.mastodon; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import com.facebook.react.ReactApplication; 7 | import com.oblador.vectoricons.VectorIconsPackage; 8 | import com.facebook.react.ReactInstanceManager; 9 | import com.facebook.react.ReactNativeHost; 10 | import com.facebook.react.ReactPackage; 11 | import com.facebook.react.shell.MainReactPackage; 12 | import com.facebook.soloader.SoLoader; 13 | 14 | import java.util.Arrays; 15 | import java.util.List; 16 | 17 | public class MainApplication extends Application implements ReactApplication { 18 | 19 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 20 | @Override 21 | protected boolean getUseDeveloperSupport() { 22 | return BuildConfig.DEBUG; 23 | } 24 | 25 | @Override 26 | protected List getPackages() { 27 | return Arrays.asList( 28 | new MainReactPackage(), 29 | new VectorIconsPackage() 30 | ); 31 | } 32 | }; 33 | 34 | @Override 35 | public ReactNativeHost getReactNativeHost() { 36 | return mReactNativeHost; 37 | } 38 | 39 | @Override 40 | public void onCreate() { 41 | super.onCreate(); 42 | SoLoader.init(this, /* native exopackage */ false); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | .*/Libraries/react-native/ReactNative.js 16 | 17 | [include] 18 | 19 | [libs] 20 | node_modules/react-native/Libraries/react-native/react-native-interface.js 21 | node_modules/react-native/flow 22 | flow/ 23 | 24 | [options] 25 | module.system=haste 26 | 27 | experimental.strict_type_args=true 28 | 29 | munge_underscores=true 30 | 31 | module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' 32 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 33 | 34 | suppress_type=$FlowIssue 35 | suppress_type=$FlowFixMe 36 | suppress_type=$FixMe 37 | 38 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-5]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 39 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-5]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 40 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 41 | 42 | unsafe.enable_getters_and_setters=true 43 | 44 | [version] 45 | ^0.35.0 46 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 19 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /android/app/BUCK: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # To learn about Buck see [Docs](https://buckbuild.com/). 4 | # To run your application with Buck: 5 | # - install Buck 6 | # - `npm start` - to start the packager 7 | # - `cd android` 8 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 9 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 10 | # - `buck install -r android/app` - compile, install and run application 11 | # 12 | 13 | lib_deps = [] 14 | for jarfile in glob(['libs/*.jar']): 15 | name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile) 16 | lib_deps.append(':' + name) 17 | prebuilt_jar( 18 | name = name, 19 | binary_jar = jarfile, 20 | ) 21 | 22 | for aarfile in glob(['libs/*.aar']): 23 | name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile) 24 | lib_deps.append(':' + name) 25 | android_prebuilt_aar( 26 | name = name, 27 | aar = aarfile, 28 | ) 29 | 30 | android_library( 31 | name = 'all-libs', 32 | exported_deps = lib_deps 33 | ) 34 | 35 | android_library( 36 | name = 'app-code', 37 | srcs = glob([ 38 | 'src/main/java/**/*.java', 39 | ]), 40 | deps = [ 41 | ':all-libs', 42 | ':build_config', 43 | ':res', 44 | ], 45 | ) 46 | 47 | android_build_config( 48 | name = 'build_config', 49 | package = 'com.mastodon', 50 | ) 51 | 52 | android_resource( 53 | name = 'res', 54 | res = 'src/main/res', 55 | package = 'com.mastodon', 56 | ) 57 | 58 | android_binary( 59 | name = 'app', 60 | package_type = 'debug', 61 | manifest = 'src/main/AndroidManifest.xml', 62 | keystore = '//android/keystores:debug', 63 | deps = [ 64 | ':app-code', 65 | ], 66 | ) 67 | -------------------------------------------------------------------------------- /ios/Mastodon/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #import "RCTBundleURLProvider.h" 13 | #import "RCTRootView.h" 14 | #import "RCTLinkingManager.h" 15 | 16 | 17 | @implementation AppDelegate 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 20 | { 21 | NSURL *jsCodeLocation; 22 | 23 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; 24 | 25 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 26 | moduleName:@"Mastodon" 27 | initialProperties:nil 28 | launchOptions:launchOptions]; 29 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 30 | 31 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 32 | UIViewController *rootViewController = [UIViewController new]; 33 | rootViewController.view = rootView; 34 | self.window.rootViewController = rootViewController; 35 | [self.window makeKeyAndVisible]; 36 | return YES; 37 | } 38 | 39 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url 40 | sourceApplication:(NSString *)sourceApplication annotation:(id)annotation 41 | { 42 | return [RCTLinkingManager application:application openURL:url sourceApplication:sourceApplication annotation:annotation]; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /src/features/login/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, TextInput, Button, Linking } from 'react-native' 3 | import { connect } from 'react-redux' 4 | import { changeId, submit, getToken } from './actions' 5 | import { Actions } from 'react-native-router-flux' 6 | 7 | const mapStateToProps = state => ({ 8 | id: state.getIn(['login', 'id']), 9 | loggedIn: state.get('tokens').size > 0 10 | }) 11 | 12 | const mapDispatchToProps = dispatch => ({ 13 | onChangeId (text) { 14 | dispatch(changeId(text)) 15 | }, 16 | 17 | onSubmit () { 18 | dispatch(submit()) 19 | }, 20 | 21 | onCallback (url) { 22 | dispatch(getToken(url)) 23 | } 24 | }) 25 | 26 | const Login = React.createClass({ 27 | propTypes: { 28 | id: React.PropTypes.string, 29 | loggedIn: React.PropTypes.bool, 30 | onChangeId: React.PropTypes.func, 31 | onSubmit: React.PropTypes.func, 32 | onCallback: React.PropTypes.func 33 | }, 34 | 35 | componentDidMount () { 36 | Linking.addEventListener('url', this._handleOpenURL) 37 | }, 38 | 39 | componentWillUnmount () { 40 | Linking.removeEventListener('url', this._handleOpenURL) 41 | }, 42 | 43 | _handleOpenURL (e) { 44 | this.props.onCallback(e.url) 45 | }, 46 | 47 | componentWillReceiveProps (nextProps) { 48 | if (nextProps.loggedIn) { 49 | Actions.main() 50 | } 51 | }, 52 | 53 | render () { 54 | const { id, onChangeId, onSubmit } = this.props; 55 | 56 | return ( 57 | 58 | 67 | 68 |