├── .babelrc
├── .editorconfig
├── .gitignore
├── .watchmanconfig
├── App.js
├── actions
├── bull.js
├── currencies.js
├── currency.js
├── graphs.js
├── language.js
├── navigation.js
├── news.js
├── portfolio.js
├── search.js
└── theme.js
├── api
├── currencies.js
├── graphs.js
├── images.js
├── news.js
└── push-notifications.js
├── app.json
├── assets
├── fonts
│ └── SpaceMono-Regular.ttf
└── icons
│ ├── application.png
│ ├── notification-icon.png
│ └── watermark.png
├── components
├── adverts
│ └── button.js
├── bull
│ ├── 404.js
│ ├── header.js
│ ├── overview.js
│ └── refresh.js
├── converter
│ └── header.js
├── currencies
│ ├── header.js
│ ├── item.js
│ └── load-all.js
├── errors
│ └── ajax.js
├── graphs
│ ├── axis-y.js
│ ├── bar.js
│ ├── header.js
│ └── tree.js
├── navigations
│ └── tabbar-bottom.js
├── news
│ ├── header.js
│ └── refresh.js
├── portfolio
│ ├── header.js
│ ├── item.js
│ └── modal-add.js
├── search
│ ├── icon.js
│ └── input.js
└── utilities
│ ├── back.js
│ ├── button.js
│ ├── code.js
│ ├── header-action.js
│ ├── headings.js
│ ├── integer.js
│ ├── loader.js
│ ├── notification.js
│ └── sections.js
├── configuration
├── adverts.js
├── application.js
├── database.js
├── environment.js
└── store.js
├── constants
├── bull.js
├── currencies.js
├── currency.js
├── graphs.js
├── language.js
├── navigation.js
├── news.js
├── portfolio.js
├── search.js
└── theme.js
├── middleware
├── analytics.js
├── currency.js
├── language.js
├── portfolio.js
└── theme.js
├── mock
├── currencies.js
└── graphs.js
├── navigations
└── router.js
├── package-lock.json
├── package.json
├── properties
├── currencies.js
├── device.js
├── exchanges.js
├── languages.js
├── languages
│ ├── chinese.js
│ ├── english.js
│ ├── french.js
│ ├── german.js
│ ├── malay.js
│ ├── spanish.js
│ ├── turkish.js
│ └── vietnamese.js
├── themes.js
└── themes
│ ├── amazon.js
│ ├── default.js
│ ├── facebook.js
│ ├── google.js
│ ├── heineken.js
│ ├── killbill.js
│ ├── lagoon.js
│ ├── lego.js
│ ├── marley.js
│ ├── matrix.js
│ ├── mcdonalds.js
│ ├── midnight.js
│ ├── twitter.js
│ └── windows.js
├── readme.md
├── reducers
├── bull.js
├── currencies.js
├── currency.js
├── graphs.js
├── index.js
├── language.js
├── navigation.js
├── news.js
├── portfolio.js
├── search.js
└── theme.js
├── schematics
├── currency.js
├── graphs.js
└── news.js
├── screens
├── bull.js
├── converter.js
├── currencies.js
├── currency.js
├── detail.js
├── donate.js
├── exchanges.js
├── language.js
├── languages.js
├── main.js
├── news.js
├── portfolio.js
├── settings.js
├── theme.js
└── themes.js
├── styles
├── adverts.js
├── bull.js
├── button.js
├── code.js
├── converter.js
├── currencies.js
├── detail.js
├── errors.js
├── graphs.js
├── header.js
├── headings.js
├── help.js
├── integer.js
├── layout.js
├── list-control.js
├── list.js
├── loader.js
├── main.js
├── modal.js
├── news.js
├── notification.js
├── portfolio.js
├── push-notifications.js
├── scene.js
├── search.js
├── section.js
├── seperators.js
├── stripe.js
└── tabbar.js
└── utilities
├── __tests__
└── cache.js
├── analytics.js
├── array.js
├── cache.js
├── colors.js
├── numbers.js
├── routes.js
└── string.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets" : [
3 | "babel-preset-expo"
4 | ] ,
5 | "env" : {
6 | "development" : {
7 | "plugins" : [
8 | "transform-react-jsx-source"
9 | ]
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 |
2 | # EditorConfig is awesome: http://EditorConfig.org
3 | root = true
4 | [*]
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = tab
8 | indent_size = 4
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/**/*
3 |
--------------------------------------------------------------------------------
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Provider } from 'react-redux';
4 | import { AppLoading ,
5 | Constants ,
6 | FacebookAds } from 'expo';
7 | import Sentry from 'sentry-expo';
8 | import { Ionicons } from '@expo/vector-icons';
9 | import Main from './screens/main';
10 | import currency from './actions/currency';
11 | import language from './actions/language';
12 | import theme from './actions/theme';
13 | import portfolio from './actions/portfolio';
14 | import application from './configuration/application';
15 | import database from './configuration/database';
16 | import configuration from './configuration/store';
17 | import analytics from './utilities/analytics';
18 | import cache from './utilities/cache';
19 |
20 | Sentry.config ( application.sentry ).install ();
21 |
22 | const store = configuration ();
23 |
24 | export default class Application extends React.Component {
25 |
26 | state = {
27 | cache : false
28 | };
29 |
30 | async setCache () {
31 |
32 | try {
33 |
34 | await cache.assets ({
35 | images : [] ,
36 | fonts : [
37 | Ionicons.font ,
38 | {
39 | 'space-mono' : require ( './assets/fonts/SpaceMono-Regular.ttf' )
40 | }
41 | ]
42 | });
43 | }
44 |
45 | catch ( error ) {
46 | console.log ( error.message );
47 | }
48 |
49 | finally {
50 |
51 | this.setState ({
52 | cache : true
53 | });
54 | }
55 | }
56 |
57 | componentWillMount () {
58 |
59 | this.setCache ();
60 |
61 | // Setup the local databases
62 | database.portfolio.setup ();
63 | database.settings.setup ();
64 |
65 | // Setup the analytics
66 | analytics.setup ();
67 |
68 | // Get any data from the local databases
69 | store.dispatch ( theme.get ());
70 | store.dispatch ( language.get ());
71 | store.dispatch ( portfolio.get ());
72 |
73 | // Getting a users preferred currency is the catalyst to kicking off the correct calls to the currencies.
74 | // Once we know this we know what currency conversion to use
75 | store.dispatch ( currency.get ());
76 |
77 | // Send test ads to the device if in development mode
78 | if ( __DEV__ ) {
79 |
80 | FacebookAds.AdSettings.addTestDevice ( FacebookAds.AdSettings.currentDeviceHash );
81 | }
82 | }
83 |
84 | render () {
85 |
86 | if ( this.state.cache ) {
87 |
88 | return (
89 |
90 |
91 |
92 |
93 | );
94 | }
95 |
96 | else {
97 |
98 | return ;
99 | }
100 | }
101 | };
102 |
--------------------------------------------------------------------------------
/actions/bull.js:
--------------------------------------------------------------------------------
1 |
2 | import constants from '../constants/bull';
3 |
4 | export default {
5 |
6 | error ( data ) {
7 |
8 | return {
9 | error : data ,
10 | type : constants.error
11 | };
12 | } ,
13 |
14 | get () {
15 |
16 | return {
17 | loading : true ,
18 | type : constants.get
19 | };
20 | } ,
21 |
22 | set ( currencies ) {
23 |
24 | return {
25 | currencies : currencies ,
26 | loading : false ,
27 | type : constants.set
28 | };
29 | }
30 |
31 | };
32 |
--------------------------------------------------------------------------------
/actions/currencies.js:
--------------------------------------------------------------------------------
1 |
2 | import api from '../api/currencies';
3 | import constants from '../constants/currencies';
4 | import bull from '../actions/bull';
5 | import schematic from '../schematics/currency';
6 | import environment from '../configuration/environment';
7 |
8 | const currencies = {
9 |
10 | error ( data ) {
11 |
12 | return {
13 | error : data ,
14 | type : constants.error
15 | };
16 | } ,
17 |
18 | get () {
19 |
20 | return {
21 | type : constants.get
22 | };
23 | } ,
24 |
25 | order ( order ) {
26 |
27 | return {
28 | order : order ,
29 | type : constants.order
30 | };
31 | } ,
32 |
33 | set ( data ) {
34 |
35 | return {
36 | items : data ,
37 | type : constants.set
38 | };
39 | }
40 |
41 | } ,
42 |
43 | callbacks = {
44 |
45 | // Dispatch an error
46 | error ( data , dispatch ) {
47 |
48 | dispatch ( bull.error ( data ));
49 | dispatch ( currencies.error ( data ));
50 | } ,
51 |
52 | // Dispatch that we are getting data
53 | get ( dispatch ) {
54 |
55 | dispatch ( currencies.get ());
56 | dispatch ( bull.get ());
57 | } ,
58 |
59 | // Rewrite the API response to our data schema
60 | normalise ( data , dispatch , currency ) {
61 |
62 | const normalised = schematic.get ( data , currency );
63 |
64 | dispatch ( currencies.set ( normalised ));
65 | dispatch ( bull.set ( normalised ));
66 | } ,
67 |
68 | // Return correct response depending on environment
69 | response ( response ) {
70 |
71 | return environment.data.mock ? response : response.json ();
72 | }
73 | };
74 |
75 | export default {
76 |
77 | order : currencies.order ,
78 |
79 | get ( currency ) {
80 |
81 | return ( dispatch ) => {
82 |
83 | callbacks.get ( dispatch );
84 |
85 | // Get the currencies
86 | return api.get ( currency )
87 | .then ( callbacks.response )
88 | .then (( data ) => callbacks.normalise ( data , dispatch , currency ))
89 | .catch (( data ) => callbacks.error ( data , dispatch ));
90 | }
91 | } ,
92 |
93 | stream ( currency ) {
94 |
95 | return ( dispatch ) => {
96 |
97 | callbacks.get ( dispatch );
98 |
99 | // Get the currencies
100 | return api.stream ( currency )
101 | .then ( callbacks.response )
102 | .then (( data ) => callbacks.normalise ( data , dispatch , currency ))
103 | .catch (( data ) => callbacks.error ( data , dispatch ));
104 | }
105 | }
106 | };
107 |
--------------------------------------------------------------------------------
/actions/currency.js:
--------------------------------------------------------------------------------
1 |
2 | import constants from '../constants/currency';
3 |
4 | export default {
5 |
6 | get () {
7 |
8 | return {
9 | type : constants.get
10 | };
11 | } ,
12 |
13 | save ( id ) {
14 |
15 | return {
16 | id : id ,
17 | type : constants.save
18 | };
19 | } ,
20 |
21 | set ( id ) {
22 |
23 | return {
24 | id : id ,
25 | type : constants.set
26 | };
27 | }
28 |
29 | };
30 |
--------------------------------------------------------------------------------
/actions/graphs.js:
--------------------------------------------------------------------------------
1 |
2 | import api from '../api/graphs';
3 | import constants from '../constants/graphs';
4 | import environment from '../configuration/environment';
5 | import schematic from '../schematics/graphs';
6 |
7 | const graphs = {
8 |
9 | error ( data ) {
10 |
11 | return {
12 | error : data ,
13 | type : constants.error
14 | };
15 | } ,
16 |
17 | get () {
18 |
19 | return {
20 | type : constants.get
21 | };
22 | } ,
23 |
24 | set ( data ) {
25 |
26 | return {
27 | market : data.market ,
28 | prices : data.prices ,
29 | type : constants.set
30 | };
31 | }
32 |
33 | };
34 |
35 | export default {
36 |
37 | get ( id ) {
38 |
39 | return ( dispatch ) => {
40 |
41 | dispatch ( graphs.get ());
42 |
43 | // Get the currencies
44 | return api.get ( id )
45 |
46 | // Transform the reponse
47 | .then (( response ) => {
48 |
49 | return environment.data.mock ? response : response.json ();
50 |
51 | })
52 |
53 | // Dispatch the data
54 | .then (( data ) => {
55 |
56 | const normalised = schematic.get ( data );
57 |
58 | dispatch ( graphs.set ( normalised ));
59 |
60 | })
61 |
62 | // Or dispatch an error
63 | .catch ( function ( data ) {
64 |
65 | dispatch ( graphs.error ( data ));
66 |
67 | });
68 | }
69 | } ,
70 |
71 | reset () {
72 |
73 | return {
74 | type : constants.reset
75 | };
76 | }
77 | };
78 |
--------------------------------------------------------------------------------
/actions/language.js:
--------------------------------------------------------------------------------
1 |
2 | import constants from '../constants/language';
3 |
4 | export default {
5 |
6 | get () {
7 |
8 | return {
9 | type : constants.get
10 | };
11 | } ,
12 |
13 | save ( id ) {
14 |
15 | return {
16 | id : id ,
17 | type : constants.save
18 | };
19 | } ,
20 |
21 | set ( id ) {
22 |
23 | return {
24 | id : id ,
25 | type : constants.set
26 | };
27 | }
28 |
29 | };
30 |
--------------------------------------------------------------------------------
/actions/navigation.js:
--------------------------------------------------------------------------------
1 |
2 | import constants from '../constants/navigation';
3 |
4 | export default {
5 |
6 | navigate ( previous , current ) {
7 |
8 | return {
9 | previous : previous ,
10 | current : current ,
11 | type : constants.navigate
12 | };
13 | }
14 |
15 | };
16 |
--------------------------------------------------------------------------------
/actions/news.js:
--------------------------------------------------------------------------------
1 |
2 | import api from '../api/news';
3 | import constants from '../constants/news';
4 | import schematic from '../schematics/news';
5 |
6 | const news = {
7 |
8 | error ( data ) {
9 |
10 | return {
11 | error : data ,
12 | type : constants.error
13 | };
14 | } ,
15 |
16 | get () {
17 |
18 | return {
19 | type : constants.get
20 | };
21 | } ,
22 |
23 | set ( data ) {
24 |
25 | return {
26 | items : data ,
27 | type : constants.set
28 | };
29 | }
30 | };
31 |
32 | export default {
33 |
34 | get () {
35 |
36 | return ( dispatch ) => {
37 |
38 | dispatch ( news.get ());
39 |
40 | // Get the currencies
41 | return api.get ()
42 |
43 | // Transform the reponse
44 | .then (( response ) => response.text ())
45 |
46 | // Dispatch the data
47 | .then (( data ) => {
48 |
49 | const normalised = schematic.get ( data );
50 |
51 | dispatch ( news.set ( normalised ));
52 |
53 | })
54 |
55 | // Or dispatch an error
56 | .catch (( data ) => dispatch ( news.error ( data )));
57 | }
58 | }
59 | };
--------------------------------------------------------------------------------
/actions/portfolio.js:
--------------------------------------------------------------------------------
1 |
2 | import constants from '../constants/portfolio';
3 |
4 | export default {
5 |
6 | delete ( id ) {
7 |
8 | return {
9 | id : id ,
10 | type : constants.delete
11 | };
12 | } ,
13 |
14 | reset ( id ) {
15 |
16 | return {
17 | id : id ,
18 | type : constants.reset
19 | };
20 | } ,
21 |
22 | get () {
23 |
24 | return {
25 | type : constants.get
26 | };
27 | } ,
28 |
29 | save ( id , amount , name ) {
30 |
31 | return {
32 | amount : amount ,
33 | id : id ,
34 | name : name ,
35 | type : constants.save
36 | };
37 | } ,
38 |
39 | set ( id , amount , name ) {
40 |
41 | return {
42 | amount : amount ,
43 | id : id ,
44 | name : name ,
45 | type : constants.set
46 | };
47 | } ,
48 |
49 | setup ( items ) {
50 |
51 | return {
52 | items : items ,
53 | type : constants.setup
54 | };
55 | }
56 | };
57 |
--------------------------------------------------------------------------------
/actions/search.js:
--------------------------------------------------------------------------------
1 |
2 | import constants from '../constants/search';
3 |
4 | export default {
5 |
6 | on ( boolean ) {
7 |
8 | return {
9 | value : boolean ,
10 | type : constants.on
11 | };
12 | } ,
13 |
14 | set ( value ) {
15 |
16 | return {
17 | value : value ,
18 | type : constants.set
19 | };
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/actions/theme.js:
--------------------------------------------------------------------------------
1 |
2 | import constants from '../constants/theme';
3 |
4 | export default {
5 |
6 | get () {
7 |
8 | return {
9 | type : constants.get
10 | };
11 | } ,
12 |
13 | save ( id ) {
14 |
15 | return {
16 | id : id ,
17 | type : constants.save
18 | };
19 | } ,
20 |
21 | set ( id ) {
22 |
23 | return {
24 | id : id ,
25 | type : constants.set
26 | };
27 | }
28 |
29 | };
30 |
--------------------------------------------------------------------------------
/api/currencies.js:
--------------------------------------------------------------------------------
1 |
2 | import environment from '../configuration/environment';
3 | import currencies from '../mock/currencies';
4 |
5 | const limit = 100 ,
6 | api = {
7 | domain : 'https://api.coinmarketcap.com' ,
8 | path :'/v1/ticker/' ,
9 | params : {
10 | limit : 'limit=' ,
11 | currency : 'convert='
12 | } ,
13 | headers : {
14 | Accept : 'application/json' ,
15 | headers : {
16 | 'Content-Type' : 'application/json'
17 | }
18 | }
19 | };
20 |
21 | export default {
22 |
23 | get : async function ( currency ) {
24 |
25 | const url = api.domain + api.path + '?' + api.params.limit + limit + '&' + api.params.currency + currency;
26 |
27 | return environment.data.mock ? currencies : fetch ( url , {
28 | ...api.headers ,
29 | method : 'GET'
30 | });
31 | } ,
32 |
33 | limit : limit ,
34 |
35 | stream : async function ( currency ) {
36 |
37 | const url = api.domain + api.path + '?' + api.params.limit + '0&' + api.params.currency + currency;
38 |
39 | return environment.data.mock ? currencies : fetch ( url , {
40 | ...api.headers ,
41 | method : 'GET'
42 | });
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/api/graphs.js:
--------------------------------------------------------------------------------
1 |
2 | import environment from '../configuration/environment';
3 | import graphs from '../mock/graphs';
4 |
5 | const api = {
6 | domain : 'https://graphs2.coinmarketcap.com' ,
7 | path :'/currencies/' ,
8 | headers : {
9 | Accept : 'application/json' ,
10 | headers : {
11 | 'Content-Type' : 'application/json'
12 | }
13 | }
14 | };
15 |
16 | export default {
17 |
18 | get : async function ( id ) {
19 |
20 | return ( environment.data.mock ? graphs : fetch ( api.domain + api.path + id , {
21 | ...api.headers ,
22 | method : 'GET'
23 | }));
24 | }
25 |
26 | };
27 |
--------------------------------------------------------------------------------
/api/images.js:
--------------------------------------------------------------------------------
1 |
2 | const api = {
3 |
4 | domain : 'https://files.coinmarketcap.com' ,
5 |
6 | icons : {
7 |
8 | small : '/static/img/coins/16x16/' ,
9 | medium : '/static/img/coins/32x32/' ,
10 | large : '/static/img/coins/64x64/'
11 |
12 | }
13 | };
14 |
15 | export default {
16 |
17 | currencies : {
18 |
19 | small ( id ) {
20 |
21 | return api.domain + api.icons.small + id + '.png';
22 | } ,
23 |
24 | medium ( id ) {
25 |
26 | return api.domain + api.icons.medium + id + '.png';
27 | } ,
28 |
29 | large ( id ) {
30 |
31 | return api.domain + api.icons.large + id + '.png';
32 | }
33 | }
34 | };
--------------------------------------------------------------------------------
/api/news.js:
--------------------------------------------------------------------------------
1 |
2 | const api = {
3 | domain : 'https://feeds.feedburner.com' ,
4 | path :'/CoinDesk' ,
5 | params : {
6 | format : '?format=xml'
7 | } ,
8 | headers : {
9 | Accept : 'text/xml' ,
10 | headers : {
11 | 'Content-Type' : 'text/xml'
12 | }
13 | }
14 | };
15 |
16 | export default {
17 |
18 | get : async function () {
19 |
20 | const url = api.domain + api.path + api.params.format;
21 |
22 | return fetch ( url , {
23 | ...api.headers ,
24 | method : 'GET'
25 | });
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/api/push-notifications.js:
--------------------------------------------------------------------------------
1 |
2 | import { Permissions ,
3 | Notifications } from 'expo';
4 |
5 | // Example server, implemented in Rails: https://git.io/vKHKv
6 | const api = {
7 | doman : 'https://exponent-push-server.herokuapp.com' ,
8 | path :'/tokens' ,
9 | headers : {
10 | Accept : 'application/json' ,
11 | headers : {
12 | 'Content-Type' : 'application/json'
13 | }
14 | }
15 | }
16 |
17 | export default {
18 |
19 | setup : async function () {
20 |
21 | // Android remote notification permissions are granted during the app
22 | // install, so this will only ask on iOS
23 | let { status } = await Permissions.askAsync ( Permissions.REMOTE_NOTIFICATIONS ) ,
24 | token;
25 |
26 | // Stop here if the user did not grant permissions
27 | if ( status !== 'granted ') {
28 | return;
29 | }
30 |
31 | // Get the token that uniquely identifies this device
32 | token = await Notifications.getExponentPushTokenAsync ();
33 |
34 | // POST the token to our backend so we can use it to send pushes from there
35 | return fetch ( api.domain + api.path , {
36 | ...headers ,
37 | body : JSON.stringify ({
38 | token : {
39 | value : token
40 | }
41 | }) ,
42 | method : 'POST'
43 | });
44 | }
45 |
46 | };
47 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo" : {
3 | "name" : "Cryptobullography",
4 | "description" : "The latest Bull on crytpo coins and blockchain tokens.",
5 | "slug" : "bullet",
6 | "privacy" : "public",
7 | "sdkVersion" : "20.0.0",
8 | "version" : "6.1.0",
9 | "orientation" : "portrait",
10 | "primaryColor" : "#000",
11 | "icon" : "./assets/icons/application.png",
12 | "notification" : {
13 | "icon" : "./assets/icons/notification-icon.png",
14 | "color" : "#dcdcdc"
15 | },
16 | "loading" : {
17 | "backgroundColor" : "#333",
18 | "backgroundImage" : "",
19 | "icon" : "./assets/icons/watermark.png",
20 | "hideExponentText" : true
21 | },
22 | "packagerOpts" : {
23 | "assetExts" : ["ttf"]
24 | },
25 | "android": {
26 | "package": "com.webstew.bullet" ,
27 | "versionCode": 5
28 | } ,
29 | "ios" : {
30 | "bundleIdentifier": "com.webstew.bullet" ,
31 | "supportsTablet" : true
32 | } ,
33 | "hooks": {
34 | "postPublish": [
35 | {
36 | "file": "sentry-expo/upload-sourcemaps",
37 | "config": {
38 | "organization": "stuart-pretorius",
39 | "project": "cryptobullography",
40 | "authToken": "ed48dbbf8e374dcdaa813a6850dbf06e5f56835566114b728c4215ecbe7aad03"
41 | }
42 | }
43 | ]
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/assets/fonts/SpaceMono-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WebStew/bullet/f51c42e8e6420ab093e92c73f09742abc6b90a74/assets/fonts/SpaceMono-Regular.ttf
--------------------------------------------------------------------------------
/assets/icons/application.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WebStew/bullet/f51c42e8e6420ab093e92c73f09742abc6b90a74/assets/icons/application.png
--------------------------------------------------------------------------------
/assets/icons/notification-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WebStew/bullet/f51c42e8e6420ab093e92c73f09742abc6b90a74/assets/icons/notification-icon.png
--------------------------------------------------------------------------------
/assets/icons/watermark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WebStew/bullet/f51c42e8e6420ab093e92c73f09742abc6b90a74/assets/icons/watermark.png
--------------------------------------------------------------------------------
/components/adverts/button.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Image ,
4 | Platform ,
5 | Text ,
6 | View } from 'react-native';
7 | import { FacebookAds } from 'expo';
8 | import style from '../../styles/adverts';
9 | import layout from '../../styles/layout';
10 |
11 | const manager = ( id ) => new FacebookAds.NativeAdsManager ( id , 1 ) ,
12 | Component = FacebookAds.withNativeAd ( class Ad extends React.Component {
13 |
14 | render () {
15 |
16 | const advert = this.props.nativeAd ,
17 | language = this.props.language ,
18 | theme = this.props.theme ,
19 | appearance = style ( theme ).button ;
20 |
21 | return (
22 |
23 |
24 | { language.actions.ad }
25 |
26 |
27 |
33 |
34 |
35 |
39 | { advert.title }
40 |
41 |
42 |
43 |
46 | { advert.description }
47 |
48 |
49 |
50 |
54 | { advert.callToActionText }
55 |
56 |
57 |
58 |
59 |
60 | );
61 | }
62 | });
63 |
64 | export default class ButtonAd extends React.Component {
65 |
66 | render () {
67 |
68 | return (
69 |
74 | );
75 | }
76 | };
77 |
78 |
--------------------------------------------------------------------------------
/components/bull/404.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Text ,
4 | View } from 'react-native';
5 | import { Ionicons } from '@expo/vector-icons';
6 | import layout from '../../styles/layout';
7 | import style from '../../styles/errors';
8 | import bull from '../../styles/bull';
9 |
10 | export default class NotFound extends React.Component {
11 |
12 | render () {
13 |
14 | const theme = this.props.theme ,
15 | language = this.props.language ,
16 | appearance = style ( theme ) ,
17 | arrange = layout ( theme ) ;
18 |
19 | // Only render if the bull rating is zero
20 | if (
21 | this.props.bull.loading ||
22 | this.props.bull.error ||
23 | this.props.bull.rating !== 0
24 | ) {
25 | return null;
26 | }
27 |
28 | return (
29 |
34 |
35 |
40 |
41 | { language.screens.bull [ '404' ]}
42 |
43 |
44 |
45 | );
46 | }
47 | };
48 |
--------------------------------------------------------------------------------
/components/bull/header.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { Text } from 'react-native';
5 | import style from '../../styles/header';
6 |
7 |
8 | export default connect (
9 |
10 | state => ({
11 | currencies : state.currencies ,
12 | language : state.language ,
13 | theme : state.theme
14 | })
15 |
16 | ) ( class Header extends React.Component {
17 |
18 | render () {
19 |
20 | const language = this.props.language ,
21 | theme = this.props.theme ,
22 | title = this.props.currencies.loading ? language.actions.calculating : language.screens.bull.title ,
23 | appearance = style ( theme ) ;
24 |
25 | return (
26 |
27 | { title }
28 |
29 | );
30 | }
31 | });
--------------------------------------------------------------------------------
/components/bull/refresh.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { TouchableOpacity } from 'react-native';
5 | import { Ionicons } from '@expo/vector-icons';
6 | import actions from '../../actions/currencies';
7 | import style from '../../styles/header';
8 | import api from '../../api/currencies';
9 | import analytics from '../../utilities/analytics';
10 |
11 | export default connect (
12 |
13 | state => ({
14 | bull : state.bull ,
15 | currency : state.currency ,
16 | language : state.language ,
17 | theme : state.theme
18 | })
19 |
20 | ) ( class Refresh extends React.Component {
21 |
22 | constructor ( props ) {
23 | super ( props );
24 |
25 | this.refresh = this.refresh.bind ( this );
26 | }
27 |
28 | refresh () {
29 |
30 | const action = this.props.bull.competitors > api.limit ? 'stream' : 'get';
31 |
32 | analytics.event (
33 | 'bull' ,
34 | 'refresh' ,
35 | action ,
36 | 'user'
37 | );
38 | this.props.dispatch ( actions [ action ] ( this.props.currency.id ));
39 | }
40 |
41 | render () {
42 |
43 | const theme = this.props.theme ,
44 | appearance = style ( theme ) ;
45 |
46 | if ( this.props.bull.loading ) {
47 |
48 | return null;
49 | }
50 |
51 | return (
52 |
56 |
61 |
62 | );
63 | }
64 | });
--------------------------------------------------------------------------------
/components/converter/header.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { Text } from 'react-native';
5 | import style from '../../styles/header';
6 |
7 |
8 | export default connect (
9 |
10 | state => ({
11 | currencies : state.currencies ,
12 | language : state.language ,
13 | theme : state.theme
14 | })
15 |
16 | ) ( class Header extends React.Component {
17 |
18 | render () {
19 |
20 | const language = this.props.language ,
21 | theme = this.props.theme ,
22 | title = this.props.currencies.loading ? language.actions.loading : language.screens.converter.title ,
23 | appearance = style ( theme ) ;
24 |
25 | return (
26 |
27 | { title }
28 |
29 | );
30 | }
31 | });
--------------------------------------------------------------------------------
/components/currencies/header.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { Text } from 'react-native';
5 | import style from '../../styles/header';
6 |
7 | export default connect (
8 |
9 | state => ({
10 | currencies : state.currencies ,
11 | language : state.language ,
12 | theme : state.theme
13 | })
14 |
15 | ) ( class Header extends React.Component {
16 |
17 | render () {
18 |
19 | const language = this.props.language ,
20 | title = this.props.currencies.loading ? language.actions.loading : language.screens.currencies.title.replace ( '{{length}}' , this.props.currencies.items.length ) ,
21 | theme = this.props.theme ,
22 | appearance = style ( theme ) ;
23 |
24 | return (
25 |
29 | { title }
30 |
31 | );
32 | }
33 | });
--------------------------------------------------------------------------------
/components/currencies/item.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Image ,
4 | Text ,
5 | TouchableOpacity ,
6 | View } from 'react-native';
7 | import Integer from '../utilities/integer';
8 | import list from '../../styles/list';
9 | import style from '../../styles/currencies';
10 | import images from '../../api/images';
11 | import numbers from '../../utilities/numbers';
12 |
13 | export default class Item extends React.PureComponent {
14 |
15 | constructor ( props ) {
16 | super ( props );
17 |
18 | this.detail = this.detail.bind ( this );
19 | }
20 |
21 | detail () {
22 |
23 | this.props.navigation.navigate (
24 | 'detail' ,
25 | {
26 | item : this.props.item
27 | }
28 | );
29 | }
30 |
31 | render () {
32 |
33 | const currency = this.props.currency ,
34 | item = this.props.item ,
35 | language = this.props.language ,
36 | theme = this.props.theme ,
37 | items = list ( theme ) ,
38 | appearance = style ( theme ) ;
39 |
40 | return (
41 |
42 |
43 |
50 |
54 |
60 |
61 |
69 | { item.name }
70 |
71 |
72 |
80 | { item.rating ? numbers.format ( item.rating ) : language.errors [ 500 ]}
81 |
82 |
94 |
102 | { item.prices.fiat ? currency.symbol + numbers.format ( item.prices.fiat.toFixed ( 2 )) : language.errors [ 500 ]}
103 |
104 |
105 |
106 | );
107 | }
108 | };
--------------------------------------------------------------------------------
/components/currencies/load-all.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { Text ,
5 | TouchableOpacity } from 'react-native';
6 | import actions from '../../actions/currencies';
7 | import style from '../../styles/header';
8 | import api from '../../api/currencies';
9 | import analytics from '../../utilities/analytics';
10 |
11 | export default connect (
12 |
13 | state => ({
14 | currency : state.currency ,
15 | currencies : state.currencies ,
16 | language : state.language ,
17 | theme : state.theme
18 | })
19 |
20 | ) ( class All extends React.Component {
21 |
22 | constructor ( props ) {
23 | super ( props );
24 |
25 | this.refresh = this.refresh.bind ( this );
26 | }
27 |
28 | refresh () {
29 |
30 | const action = this.props.currencies.items.length > api.limit ? 'get' : 'stream';
31 |
32 | analytics.event (
33 | 'currencies' ,
34 | 'load' ,
35 | action ,
36 | 'user'
37 | );
38 | this.props.dispatch ( actions [ action ] ( this.props.currency.id ));
39 | }
40 |
41 | render () {
42 |
43 | const theme = this.props.theme ,
44 | language = this.props.language ,
45 | appearance = style ( theme ) ;
46 | let action;
47 |
48 | if ( this.props.currencies.loading ) {
49 | return null;
50 | }
51 |
52 | action = language.actions.load + ' ';
53 | action += this.props.currencies.items.length > api.limit ? api.limit : language.actions.all;
54 |
55 | return (
56 |
60 |
61 | { action }
62 |
63 |
64 | );
65 | }
66 | });
--------------------------------------------------------------------------------
/components/errors/ajax.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Text ,
4 | TouchableOpacity ,
5 | View } from 'react-native';
6 | import { Ionicons } from '@expo/vector-icons';
7 | import style from '../../styles/errors';
8 | import layout from '../../styles/layout';
9 |
10 | export default class Error extends React.Component {
11 |
12 | render () {
13 |
14 | const language = this.props.language ,
15 | text = this.props.text || language.errors.default ,
16 | theme = this.props.theme ,
17 | arrange = layout ( theme ) ,
18 | appearance = style ( theme ) ;
19 |
20 | // If there is no error return
21 | if ( ! this.props.error ) {
22 | return null;
23 | }
24 |
25 | return (
26 |
30 |
34 |
39 |
40 | { text }
41 |
42 |
43 |
44 | );
45 | }
46 | };
--------------------------------------------------------------------------------
/components/graphs/axis-y.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Text ,
4 | View } from 'react-native';
5 | import style from '../../styles/graphs';
6 |
7 | export default class AxisY extends React.Component {
8 |
9 | render () {
10 |
11 | const theme = this.props.theme ,
12 | appearance = style ( theme ) ;
13 |
14 | let cells = this.props.data.map (( item , index ) => {
15 |
16 | return (
17 |
21 |
22 | { item }
23 |
24 |
25 | );
26 | });
27 |
28 | return (
29 |
30 | { cells }
31 |
32 | );
33 | }
34 | }
--------------------------------------------------------------------------------
/components/graphs/bar.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Animated ,
4 | View } from 'react-native';
5 | import colour from '../../utilities/colors';
6 |
7 | export default class Bar extends React.PureComponent {
8 |
9 | constructor ( props ) {
10 | super ( props );
11 |
12 | this.state = {
13 | height : new Animated.Value ( 0 )
14 | };
15 | }
16 |
17 | componentDidMount () {
18 |
19 | const value = this.props.value;
20 |
21 | Animated.timing (
22 | this.state.height ,
23 | {
24 | toValue : value ,
25 | duration : 1000
26 | }
27 | ).start ();
28 | }
29 |
30 | render () {
31 |
32 | const style = this.props.style ,
33 | color = this.props.color ,
34 | padding = this.props.padding ,
35 | height = this.state.height ;
36 |
37 | return (
38 |
45 |
55 |
56 | );
57 | }
58 | }
--------------------------------------------------------------------------------
/components/graphs/header.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Text ,
4 | View } from 'react-native';
5 |
6 | export default class Header extends React.PureComponent {
7 |
8 | render () {
9 |
10 | const style = this.props.style;
11 |
12 | return (
13 |
14 |
15 | { this.props.value }
16 |
17 |
18 | );
19 | }
20 | }
--------------------------------------------------------------------------------
/components/news/header.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { Text } from 'react-native';
5 | import style from '../../styles/header';
6 |
7 | export default connect (
8 |
9 | state => ({
10 | news : state.news ,
11 | language : state.language ,
12 | theme : state.theme
13 | })
14 |
15 | ) ( class Header extends React.Component {
16 |
17 | render () {
18 |
19 | const language = this.props.language ,
20 | title = this.props.news.loading ? language.actions.loading : language.screens.news.title ,
21 | theme = this.props.theme ,
22 | appearance = style ( theme ) ;
23 |
24 | return (
25 |
29 | { title }
30 |
31 | );
32 | }
33 | });
--------------------------------------------------------------------------------
/components/news/refresh.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { TouchableOpacity } from 'react-native';
5 | import { Ionicons } from '@expo/vector-icons';
6 | import actions from '../../actions/news';
7 | import style from '../../styles/header';
8 | import analytics from '../../utilities/analytics';
9 |
10 | export default connect (
11 |
12 | state => ({
13 | language : state.language ,
14 | news : state.news ,
15 | theme : state.theme
16 | })
17 |
18 | ) ( class Refresh extends React.Component {
19 |
20 | constructor ( props ) {
21 | super ( props );
22 |
23 | this.refresh = this.refresh.bind ( this );
24 | }
25 |
26 | refresh () {
27 |
28 | analytics.event (
29 | 'news' ,
30 | 'refresh' ,
31 | 'get' ,
32 | 'user'
33 | );
34 | this.props.dispatch ( actions.get ());
35 | }
36 |
37 | render () {
38 |
39 | const theme = this.props.theme ,
40 | appearance = style ( theme ) ;
41 |
42 | if ( this.props.news.loading ) {
43 |
44 | return null;
45 | }
46 |
47 | return (
48 |
52 |
57 |
58 | );
59 | }
60 | });
--------------------------------------------------------------------------------
/components/portfolio/header.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { Text } from 'react-native';
5 | import style from '../../styles/header';
6 |
7 |
8 | export default connect (
9 |
10 | state => ({
11 | currencies : state.currencies ,
12 | language : state.language ,
13 | theme : state.theme
14 | })
15 |
16 | ) ( class Header extends React.Component {
17 |
18 | render () {
19 |
20 | const language = this.props.language ,
21 | theme = this.props.theme ,
22 | title = this.props.currencies.loading ? language.actions.loading : language.screens.portfolio.title ,
23 | appearance = style ( theme ) ;
24 |
25 | return (
26 |
27 | { title }
28 |
29 | );
30 | }
31 | });
--------------------------------------------------------------------------------
/components/portfolio/item.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Image ,
4 | Text ,
5 | TouchableOpacity ,
6 | View } from 'react-native';
7 | import Integer from '../utilities/integer';
8 | import list from '../../styles/list';
9 | import style from '../../styles/portfolio';
10 | import images from '../../api/images';
11 | import numbers from '../../utilities/numbers';
12 |
13 | export default class Item extends React.PureComponent {
14 |
15 | constructor ( props ) {
16 |
17 | super ( props );
18 |
19 | this.detail = this.detail.bind ( this );
20 | }
21 |
22 | detail () {
23 |
24 | this.props.navigation.navigate (
25 | 'detail' ,
26 | {
27 | item : this.props.data
28 | }
29 | );
30 | }
31 |
32 | render () {
33 |
34 | const currency = this.props.currency ,
35 | item = this.props.item ,
36 | language = this.props.language ,
37 | theme = this.props.theme ,
38 | items = list ( theme ) ,
39 | appearance = style ( theme ) ;
40 |
41 | return (
42 |
43 |
50 |
54 |
60 |
68 | { item.name }
69 |
70 |
71 |
79 | { numbers.format ( item.amount )}
80 |
81 |
88 | { item.price ? currency.symbol + numbers.format ( item.price.toFixed ( 2 )) : language.errors [ 500 ]}
89 |
90 |
98 | { item.total ? currency.symbol + numbers.format ( item.total.toFixed ( 2 )) : '0' }
99 |
100 |
101 |
102 | );
103 | }
104 | };
--------------------------------------------------------------------------------
/components/portfolio/modal-add.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Modal ,
4 | Text ,
5 | TextInput ,
6 | View } from 'react-native';
7 | import actions from '../../actions/portfolio';
8 | import Button from '../utilities/button';
9 | import Heading from '../utilities/headings';
10 | import analytics from '../../utilities/analytics';
11 | import style from '../../styles/modal';
12 |
13 | export default class Dialogue extends React.Component {
14 |
15 | constructor ( props ) {
16 | super ( props );
17 |
18 | this.amount = this.amount.bind ( this );
19 | this.blur = this.blur.bind ( this );
20 | this.focus = this.focus.bind ( this );
21 | this.reset = this.reset.bind ( this );
22 | this.set = this.set.bind ( this );
23 | this.state = {
24 | amount : '0'
25 | };
26 | }
27 |
28 | amount ( value ) {
29 |
30 | this.setState ({
31 | amount : value
32 | });
33 | }
34 |
35 | blur () {
36 |
37 | if ( ! this.state.amount ) {
38 |
39 | this.setState ({
40 | amount : '0'
41 | });
42 | }
43 | }
44 |
45 | focus () {
46 |
47 | if ( this.state.amount === '0' ) {
48 |
49 | this.setState ({
50 | amount : ''
51 | });
52 | }
53 | }
54 |
55 | remove ( portfolioed ) {
56 |
57 | const theme = this.props.theme ,
58 | language = this.props.language ;
59 |
60 | if ( ! portfolioed ) {
61 |
62 | return null;
63 | }
64 |
65 | return (
66 |
67 |
73 | )
74 | }
75 |
76 | reset () {
77 |
78 | const item = this.props.item;
79 |
80 | analytics.event (
81 | 'portfolio' ,
82 | 'remove' ,
83 | item.name
84 | );
85 | this.props.reset ();
86 | this.props.dispatch (
87 | actions.delete ( item.id )
88 | );
89 | }
90 |
91 | set () {
92 |
93 | const item = this.props.item;
94 |
95 | analytics.event (
96 | 'portfolio' ,
97 | 'add' ,
98 | item.name
99 | );
100 | this.props.reset ();
101 | this.props.dispatch (
102 | actions.save (
103 | item.id ,
104 | this.state.amount ,
105 | item.name
106 | )
107 | );
108 | }
109 |
110 | componentWillMount () {
111 |
112 | const item = this.props.item ,
113 | portfolioed = this.props.portfolio.items.find (( value , index ) => value.id === item.id );
114 |
115 | if ( portfolioed ) {
116 |
117 | this.setState ({
118 | amount : portfolioed.amount
119 | });
120 | }
121 | }
122 |
123 | render () {
124 |
125 | const language = this.props.language ,
126 | theme = this.props.theme ,
127 | item = this.props.item ,
128 | portfolioed = this.props.portfolio.items.find (( value , index ) => value.id === item.id ) ,
129 | action = portfolioed ? language.actions.update : language.actions.add ,
130 | appearance = style ( theme ) ;
131 |
132 | return (
133 |
138 |
139 |
140 |
145 |
146 | { language.screens.portfolio.description.replace ( '{{placeholder}}' , item.name )}
147 |
148 |
149 |
150 |
151 | { language.screens.portfolio.headers.amount }
152 |
153 |
154 |
163 |
164 |
169 | { this.remove ( portfolioed )}
170 |
176 |
177 |
178 |
179 | );
180 | }
181 | };
--------------------------------------------------------------------------------
/components/search/icon.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { TouchableOpacity } from 'react-native';
5 | import { Ionicons } from '@expo/vector-icons';
6 | import analytics from '../../utilities/analytics';
7 | import actions from '../../actions/search';
8 | import style from '../../styles/search';
9 |
10 | export default connect (
11 |
12 | state => ({
13 | search : state.search ,
14 | theme : state.theme
15 | })
16 |
17 | ) ( class SearchIcon extends React.Component {
18 |
19 | constructor ( props ) {
20 | super ( props );
21 |
22 | this.on = this.on.bind ( this );
23 | }
24 |
25 | on () {
26 |
27 | const state = this.props.search.on ? 'off' : 'on';
28 |
29 | analytics.event ( 'search' , 'toggle' , state );
30 | this.props.dispatch ( actions.on ( ! this.props.search.on ));
31 | }
32 |
33 | render () {
34 |
35 | const theme = this.props.theme ,
36 | appearance = style ( theme ) ;
37 |
38 | return (
39 |
40 |
44 |
45 |
50 |
51 |
52 | );
53 | }
54 | });
55 |
--------------------------------------------------------------------------------
/components/search/input.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { TextInput ,
5 | TouchableOpacity ,
6 | View } from 'react-native';
7 | import { Ionicons } from '@expo/vector-icons';
8 | import analytics from '../../utilities/analytics';
9 | import actions from '../../actions/search';
10 | import style from '../../styles/search';
11 |
12 | export default connect (
13 |
14 | state => ({
15 | language : state.language ,
16 | search : state.search ,
17 | theme : state.theme
18 | })
19 |
20 | ) ( class SearchInput extends React.Component {
21 |
22 | constructor ( props ) {
23 | super ( props );
24 |
25 | this.set = this.set.bind ( this );
26 | this.off = this.off.bind ( this );
27 | }
28 |
29 | set ( term ) {
30 |
31 | analytics.event ( 'search' , 'term' , term );
32 | this.props.dispatch ( actions.set ( term ));
33 | }
34 |
35 | off () {
36 |
37 | analytics.event ( 'search' , 'toggle' , 'off' );
38 | this.props.dispatch ( actions.on ( false ));
39 | }
40 |
41 | render () {
42 |
43 | const language = this.props.language ,
44 | theme = this.props.theme ,
45 | appearance = style ( theme ) ;
46 |
47 | if ( ! this.props.search.on ) {
48 |
49 | return null;
50 | }
51 |
52 | return (
53 |
54 |
55 |
56 | this.set ( null )}
59 | >
60 |
65 |
66 |
67 |
78 |
79 |
83 |
88 |
89 |
90 |
91 |
92 | );
93 | }
94 | });
95 |
--------------------------------------------------------------------------------
/components/utilities/back.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { TouchableOpacity ,
5 | Text } from 'react-native';
6 | import { Ionicons } from '@expo/vector-icons';
7 | import style from '../../styles/header';
8 |
9 | export default connect (
10 |
11 | state => ({
12 | theme : state.theme
13 | })
14 |
15 | ) ( class Back extends React.Component {
16 |
17 | render () {
18 |
19 | const theme = this.props.theme ,
20 | appearance = style ( theme ) ;
21 |
22 | return (
23 |
27 |
32 |
33 | { this.props.value }
34 |
35 |
36 | );
37 |
38 | }
39 | });
40 |
--------------------------------------------------------------------------------
/components/utilities/button.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Text ,
4 | TouchableOpacity ,
5 | View } from 'react-native';
6 | import style from '../../styles/button';
7 |
8 | export default class Button extends React.Component {
9 |
10 | render () {
11 |
12 | const theme = this.props.theme ,
13 | type = this.props.type ? this.props.type : 'primary' ,
14 | appearance = style ( theme ) ;
15 |
16 | return (
17 |
20 |
21 |
22 | { this.props.value.toUpperCase ()}
23 |
24 |
25 |
26 | );
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/components/utilities/code.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Text ,
4 | View } from 'react-native';
5 | import style from '../../styles/code';
6 |
7 | export default class Code extends React.Component {
8 |
9 | render () {
10 |
11 | const theme = this.props.theme ,
12 | appearance = style ( theme ) ;
13 |
14 | return (
15 |
16 |
17 | { this.props.value }
18 |
19 |
20 | );
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/components/utilities/header-action.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { TouchableOpacity ,
5 | Text } from 'react-native';
6 | import { Ionicons } from '@expo/vector-icons';
7 | import style from '../../styles/header';
8 |
9 | export default connect (
10 |
11 | state => ({
12 | theme : state.theme
13 | })
14 |
15 | ) ( class Action extends React.Component {
16 |
17 | render () {
18 |
19 | const theme = this.props.theme ,
20 | appearance = style ( theme ) ;
21 |
22 | return (
23 |
27 |
32 |
33 | { this.props.value }
34 |
35 |
36 | );
37 | }
38 | });
39 |
--------------------------------------------------------------------------------
/components/utilities/headings.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Text ,
4 | View } from 'react-native';
5 | import styles from '../../styles/headings';
6 |
7 | export default class Headings extends React.Component {
8 |
9 | render () {
10 |
11 | const type = this.props.type || 1 ,
12 | theme = this.props.theme ,
13 | style = styles ( theme ) [ type ] ;
14 |
15 | return (
16 |
17 | { this.props.title }
18 |
19 | );
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/components/utilities/integer.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Text } from 'react-native';
4 | import styles from '../../styles/integer';
5 | import strings from '../../utilities/string';
6 | import numbers from '../../utilities/numbers';
7 |
8 | export default class Integer extends React.Component {
9 |
10 | render () {
11 |
12 | // Styles are available are highligt ( two options for gt lt 0 ) and default
13 | const value = this.props.value ,
14 | number = value ? ! isNaN ( value ) : false ,
15 | theme = this.props.theme ,
16 | appearance = styles ( theme ) ,
17 | style = this.props.type === 'highlight' && number ? value > 0 ? appearance.positive : appearance.negative : {} ,
18 | prefix = this.props.prefix && value ? this.props.prefix : '' ,
19 | suffix = this.props.suffix && value ? this.props.suffix : '' ;
20 |
21 | let text = number ? prefix + numbers.format ( this.props.value ) + suffix : this.props.value ;
22 |
23 | text = text || this.props.language.errors [ '500' ];
24 |
25 | return (
26 |
34 | { text }
35 |
36 | );
37 | }
38 | };
39 |
--------------------------------------------------------------------------------
/components/utilities/loader.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { ActivityIndicator } from 'react-native';
4 | import style from '../../styles/loader';
5 |
6 | export default class Loader extends React.Component {
7 |
8 | render () {
9 |
10 | const theme = this.props.theme ,
11 | appearance = style ( theme ) ;
12 |
13 | // If we're not in loading state there is no need to render
14 | if ( ! this.props.loading ) {
15 | return null;
16 | }
17 |
18 | return (
19 |
25 | );
26 | }
27 | };
--------------------------------------------------------------------------------
/components/utilities/notification.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { View ,
4 | Text } from 'react-native';
5 | import styles from '../../styles/notification';
6 |
7 | export default class Notification extends React.Component {
8 |
9 | render () {
10 |
11 | const type = this.props.type ,
12 | theme = this.props.theme ,
13 | appearance = styles ( theme ) ,
14 | style = appearance [ type ] || appearance.notification ;
15 |
16 | return (
17 |
21 | { this.props.message }
22 |
23 | );
24 | }
25 |
26 | };
--------------------------------------------------------------------------------
/components/utilities/sections.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Text ,
4 | View } from 'react-native';
5 | import Heading from '../utilities/headings';
6 | import Integer from '../utilities/integer';
7 | import style from '../../styles/section';
8 | import layout from '../../styles/layout';
9 |
10 | export default class Sections extends React.Component {
11 |
12 | sections () {
13 |
14 | const language = this.props.language ,
15 | theme = this.props.theme ,
16 | appearance = style ( theme ) ,
17 | arrange = layout ( theme ) ;
18 |
19 | return this.props.sections.map (( section , index ) => {
20 |
21 | let stripe = index % 2 === 0 ? appearance.body : { ...appearance.body , ...appearance.stripe } ,
22 | data = section.data.map (( item , index ) => {
23 |
24 | return (
25 |
29 |
35 | { item.property }
36 |
37 |
49 |
50 | );
51 | });
52 |
53 | return (
54 |
57 |
62 | { data }
63 |
64 | );
65 | });
66 | }
67 |
68 | render () {
69 |
70 | const theme = this.props.theme ,
71 | appearance = style ( theme ) ;
72 |
73 | return (
74 |
75 | { this.sections ()}
76 |
77 | );
78 | }
79 | };
80 |
--------------------------------------------------------------------------------
/configuration/adverts.js:
--------------------------------------------------------------------------------
1 |
2 | import { Platform } from 'react-native';
3 |
4 | export default {
5 |
6 | bull : {
7 | button : [
8 | Platform.OS === 'ios' ? '1426488217459169_1426513627456628' : '1426488217459169_1426529717455019' ,
9 | Platform.OS === 'ios' ? '1426488217459169_1427178637390127' : '1426488217459169_1427179214056736'
10 | ]
11 | }
12 | };
--------------------------------------------------------------------------------
/configuration/application.js:
--------------------------------------------------------------------------------
1 |
2 | import device from '../properties/device';
3 |
4 | const store = device.os.ios ? 'app' : 'play';
5 |
6 | export default {
7 |
8 | email : 'cryptobullography@gmail.com' ,
9 | name : 'Cryptobullography' ,
10 | version : 'v4.1.0' ,
11 |
12 | sentry : 'https://5f21c527a51e43dfb28b70feb6018448@sentry.io/221697' ,
13 |
14 | cryptocoinminer () {
15 |
16 | const stores = {
17 | app : 'https://itunes.apple.com/gb/app/crypto-coin-miner/id1289304469?mt=8' ,
18 | play : 'https://play.google.com/store/apps/details?id=com.webstew.cryptocoinminer&hl=en'
19 | };
20 |
21 | return stores [ store ];
22 | } ,
23 |
24 | store () {
25 |
26 | const stores = {
27 | app : 'https://itunes.apple.com/gb/app/crypto-coin-bull/id1257246245?mt=8' ,
28 | play : 'https://play.google.com/store/apps/details?id=com.webstew.bullet&hl=en'
29 | };
30 |
31 | return stores [ store ];
32 | }
33 |
34 | };
--------------------------------------------------------------------------------
/configuration/database.js:
--------------------------------------------------------------------------------
1 |
2 | import Expo from 'expo';
3 |
4 | const configuration = {
5 | name : 'cryptobullography' ,
6 | version : 1
7 | } ,
8 |
9 | connection = Expo.SQLite.openDatabase ( configuration ) ,
10 |
11 | error = ( data ) => {
12 | console.log ( 'DATABASE ERROR - ' , data );
13 | };
14 |
15 | // Return a connection to our database
16 | export default {
17 |
18 | connection : connection ,
19 |
20 | portfolio : {
21 |
22 | /**
23 | * Setup function to create the table to keep our saved settings in
24 | */
25 | setup : () => {
26 |
27 | return connection.transaction (( transaction ) => {
28 |
29 | transaction.executeSql (
30 | 'CREATE TABLE IF NOT EXISTS portfolio ( ' +
31 | 'id TEXT NOT NULL PRIMARY KEY , ' +
32 | 'amount TEXT NOT NULL ,' +
33 | 'name TEXT NOT NULL ' +
34 | ');'
35 | );
36 | });
37 | } ,
38 |
39 | set : ( id , amount , name , callback ) => {
40 |
41 | return connection.transaction (( transaction ) => {
42 |
43 | transaction.executeSql (
44 | 'INSERT OR REPLACE INTO portfolio ( id , amount , name ) ' +
45 | 'VALUES ( ? , ? , ? );' ,
46 | [
47 | id ,
48 | amount ,
49 | name
50 | ] ,
51 | callback ,
52 | error
53 | );
54 | })
55 | } ,
56 |
57 | get : ( callback ) => {
58 |
59 | return connection.transaction (( transaction ) => {
60 |
61 | //transaction.executeSql ( "DROP TABLE IF EXISTS portfolio" );
62 |
63 | transaction.executeSql (
64 | 'SELECT * FROM portfolio' ,
65 | null ,
66 | callback ,
67 | error
68 |
69 | );
70 | });
71 | } ,
72 |
73 | delete : ( id , callback ) => {
74 |
75 | return connection.transaction (( transaction ) => {
76 |
77 | transaction.executeSql (
78 | 'DELETE FROM portfolio WHERE id = ?' ,
79 | [
80 | id
81 | ] ,
82 | callback ,
83 | error
84 |
85 | );
86 | });
87 | }
88 | } ,
89 |
90 | settings : {
91 |
92 | /**
93 | * Setup function to create the table to keep our saved settings in
94 | */
95 | setup : () => {
96 |
97 | return connection.transaction (( transaction ) => {
98 |
99 | transaction.executeSql (
100 | 'CREATE TABLE IF NOT EXISTS settings ( ' +
101 | 'id TEXT NOT NULL PRIMARY KEY , ' +
102 | 'value TEXT NOT NULL ' +
103 | ');'
104 | );
105 | });
106 | } ,
107 |
108 | set : ( id , value , callback ) => {
109 |
110 | return connection.transaction (( transaction ) => {
111 |
112 | transaction.executeSql (
113 | 'INSERT OR REPLACE INTO settings ( id , value ) ' +
114 | 'VALUES ( ? , ? );' ,
115 | [
116 | id ,
117 | value
118 | ] ,
119 | callback ,
120 | error
121 | );
122 | })
123 | } ,
124 |
125 | get : ( id , callback ) => {
126 |
127 | return connection.transaction (( transaction ) => {
128 |
129 | transaction.executeSql (
130 | 'SELECT * FROM settings WHERE id = ?' ,
131 | [
132 | id
133 | ] ,
134 | callback ,
135 | error
136 |
137 | );
138 | });
139 | }
140 | }
141 | };
--------------------------------------------------------------------------------
/configuration/environment.js:
--------------------------------------------------------------------------------
1 |
2 | const data = {
3 |
4 | mock : false
5 | };
6 |
7 |
8 | export default {
9 |
10 | data : {
11 |
12 | mock : ( function () {
13 |
14 | return ( __DEV__ && data.mock );
15 |
16 | } ())
17 | }
18 | };
--------------------------------------------------------------------------------
/configuration/store.js:
--------------------------------------------------------------------------------
1 |
2 | import { compose ,
3 | createStore ,
4 | applyMiddleware } from 'redux';
5 | import thunk from 'redux-thunk';
6 | //import { createLogger } from 'redux-logger';
7 | import reducers from '../reducers/index';
8 | import analytics from '../middleware/analytics';
9 | import currency from '../middleware/currency';
10 | import language from '../middleware/language';
11 | import portfolio from '../middleware/portfolio';
12 | import theme from '../middleware/theme';
13 |
14 | export default function ( state : Object = {}) {
15 |
16 | return createStore (
17 | reducers ,
18 | state ,
19 | compose (
20 | applyMiddleware (
21 | thunk ,
22 | analytics.navigate ,
23 | currency.get ,
24 | currency.save ,
25 | language.get ,
26 | language.save ,
27 | portfolio.delete ,
28 | portfolio.get ,
29 | portfolio.save ,
30 | theme.get ,
31 | theme.save
32 | //createLogger ()
33 | )
34 | )
35 | );
36 | };
37 |
--------------------------------------------------------------------------------
/constants/bull.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Constants across who is the Bull
4 | * @type {Object}
5 | */
6 | export default {
7 | error : 'bull:error' ,
8 | get : 'bull:get' ,
9 | set : 'bull:set'
10 | };
11 |
--------------------------------------------------------------------------------
/constants/currencies.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Constants across the currencies API
4 | * @type {Object}
5 | */
6 | export default {
7 | error : 'currencies:error' ,
8 | get : 'currencies:get' ,
9 | order : 'currencies:order' ,
10 | set : 'currencies:set'
11 | };
12 |
--------------------------------------------------------------------------------
/constants/currency.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Constants across the users preferred currency display
4 | * @type {Object}
5 | */
6 | export default {
7 | get : 'currency:get' ,
8 | save : 'currency:save' ,
9 | set : 'currency:set'
10 | };
11 |
--------------------------------------------------------------------------------
/constants/graphs.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Constants across the graphs API
4 | * @type {Object}
5 | */
6 | export default {
7 | error : 'graphs:error' ,
8 | get : 'graphs:get' ,
9 | reset : 'graphs:reset' ,
10 | set : 'graphs:set'
11 | };
12 |
--------------------------------------------------------------------------------
/constants/language.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Constants across the language
4 | * @type {Object}
5 | */
6 | export default {
7 | get : 'language:get' ,
8 | save : 'language:save' ,
9 | set : 'language:set'
10 | };
11 |
--------------------------------------------------------------------------------
/constants/navigation.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Constants across navigation routes
4 | * @type {Object}
5 | */
6 | export default {
7 | navigate : 'navigate'
8 | };
9 |
--------------------------------------------------------------------------------
/constants/news.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Constants across the news API
4 | * @type {Object}
5 | */
6 | export default {
7 | error : 'news:error' ,
8 | get : 'news:get' ,
9 | set : 'news:set'
10 | };
11 |
--------------------------------------------------------------------------------
/constants/portfolio.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Constants across the user's portfolio
4 | * @type {Object}
5 | */
6 | export default {
7 | delete : 'portfolio:delete' ,
8 | get : 'portfolio:get' ,
9 | reset : 'portfolio:reset' ,
10 | save : 'portfolio:save' ,
11 | set : 'portfolio:set' ,
12 | setup : 'portfolio:setup'
13 | };
14 |
--------------------------------------------------------------------------------
/constants/search.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Constants across the search functionality
4 | * @type {Object}
5 | */
6 | export default {
7 | on : 'search:on' ,
8 | set : 'search:set'
9 | };
10 |
--------------------------------------------------------------------------------
/constants/theme.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Constants across the theme
4 | * @type {Object}
5 | */
6 | export default {
7 | get : 'theme:get' ,
8 | save : 'theme:save' ,
9 | set : 'theme:set'
10 | };
11 |
--------------------------------------------------------------------------------
/middleware/analytics.js:
--------------------------------------------------------------------------------
1 |
2 | import analytics from '../utilities/analytics';
3 | import constants from '../constants/navigation';
4 |
5 | export default {
6 |
7 | navigate : store => next => action => {
8 |
9 | // This will track all screen navigation
10 | if ( action.type === constants.navigate ) {
11 |
12 | analytics.event ( action.previous , 'navigate' , action.current );
13 | analytics.screen ( action.current );
14 | next ( action );
15 | }
16 |
17 | else {
18 | next ( action );
19 | }
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/middleware/currency.js:
--------------------------------------------------------------------------------
1 |
2 | import actions from '../actions/currency';
3 | import currencies from '../actions/currencies';
4 | import constants from '../constants/currency';
5 | import database from '../configuration/database';
6 | import codes from '../properties/currencies';
7 |
8 | export default {
9 |
10 | get : store => next => action => {
11 |
12 | if ( action.type === constants.get ) {
13 |
14 | database.settings.get ( 'currency' , ( data , results ) => {
15 |
16 | let id = results.rows._array.length ? results.rows._array [ 0 ].value : codes [ 0 ].id;
17 |
18 | // If the currency code no longers exists in our property file set it the first in array
19 | if ( ! codes.find (( item ) => item.id === id )) {
20 |
21 | id = codes [ 0 ].id;
22 | }
23 |
24 | store.dispatch ( actions.set ( id ));
25 | store.dispatch ( currencies.get ( id ));
26 | next ( action );
27 | });
28 | }
29 |
30 | else {
31 | next ( action );
32 | }
33 | } ,
34 |
35 | save : store => next => action => {
36 |
37 | if ( action.type === constants.save ) {
38 |
39 | database.settings.set ( 'currency' , action.id , () => {
40 |
41 | store.dispatch ( actions.set ( action.id ));
42 | store.dispatch ( currencies.get ( action.id ));
43 | next ( action )
44 | });
45 | }
46 |
47 | else {
48 | next ( action );
49 | }
50 | }
51 | };
52 |
--------------------------------------------------------------------------------
/middleware/language.js:
--------------------------------------------------------------------------------
1 |
2 | import actions from '../actions/language';
3 | import constants from '../constants/language';
4 | import database from '../configuration/database';
5 | import languages from '../properties/languages';
6 |
7 | export default {
8 |
9 | get : store => next => action => {
10 |
11 | if ( action.type === constants.get ) {
12 |
13 | database.settings.get ( 'language' , ( data , results ) => {
14 |
15 | const language = results.rows._array.length ? results.rows._array [ 0 ].value : null;
16 |
17 | // Only set the theme again if one has been saved.
18 | // The default theme is already set in the reducer
19 | // Only set the theme if it is still present in our properties file
20 | if ( language && languages [ language ]) {
21 |
22 | store.dispatch ( actions.set ( language ));
23 | }
24 | next ( action );
25 |
26 | });
27 | }
28 |
29 | else {
30 | next ( action );
31 | }
32 | } ,
33 |
34 | save : store => next => action => {
35 |
36 | if ( action.type === constants.save ) {
37 |
38 | database.settings.set ( 'language' , action.id , () => {
39 |
40 | store.dispatch ( actions.set ( action.id ));
41 | next ( action )
42 | });
43 | }
44 |
45 | else {
46 | next ( action );
47 | }
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/middleware/portfolio.js:
--------------------------------------------------------------------------------
1 |
2 | import actions from '../actions/portfolio';
3 | import constants from '../constants/portfolio';
4 | import database from '../configuration/database';
5 |
6 | export default {
7 |
8 | get : store => next => action => {
9 |
10 | if ( action.type === constants.get ) {
11 |
12 | database.portfolio.get (( data , results ) => {
13 |
14 | const portfolio = results.rows._array.length ? results.rows._array : null;
15 |
16 | if ( portfolio ) {
17 |
18 | store.dispatch ( actions.setup ( portfolio ));
19 | }
20 | next ( action );
21 |
22 | });
23 | }
24 |
25 | else {
26 | next ( action );
27 | }
28 | } ,
29 |
30 | delete : store => next => action => {
31 |
32 | if ( action.type === constants.delete ) {
33 |
34 | database.portfolio.delete ( action.id , () => {
35 |
36 | store.dispatch ( actions.reset ( action.id ));
37 | next ( action )
38 | });
39 | }
40 |
41 | else {
42 | next ( action );
43 | }
44 | } ,
45 |
46 | save : store => next => action => {
47 |
48 | if ( action.type === constants.save ) {
49 |
50 | database.portfolio.set (
51 | action.id ,
52 | action.amount ,
53 | action.name ,
54 | () => {
55 |
56 | store.dispatch (
57 | actions.set (
58 | action.id ,
59 | action.amount ,
60 | action.name
61 | )
62 | );
63 | next ( action );
64 | }
65 | );
66 | }
67 |
68 | else {
69 | next ( action );
70 | }
71 | }
72 | };
73 |
--------------------------------------------------------------------------------
/middleware/theme.js:
--------------------------------------------------------------------------------
1 |
2 | import actions from '../actions/theme';
3 | import constants from '../constants/theme';
4 | import database from '../configuration/database';
5 | import themes from '../properties/themes';
6 |
7 | export default {
8 |
9 | get : store => next => action => {
10 |
11 | if ( action.type === constants.get ) {
12 |
13 | database.settings.get ( 'theme' , ( data , results ) => {
14 |
15 | const theme = results.rows._array.length ? results.rows._array [ 0 ].value : null;
16 |
17 | // Only set the theme again if one has been saved.
18 | // The default theme is already set in the reducer
19 | // Only set the theme if it is still present in our properties file
20 | if ( theme && themes [ theme ]) {
21 |
22 | store.dispatch ( actions.set ( theme ));
23 | }
24 | next ( action );
25 |
26 | });
27 | }
28 |
29 | else {
30 | next ( action );
31 | }
32 | } ,
33 |
34 | save : store => next => action => {
35 |
36 | if ( action.type === constants.save ) {
37 |
38 | database.settings.set ( 'theme' , action.id , () => {
39 |
40 | store.dispatch ( actions.set ( action.id ));
41 | next ( action )
42 | });
43 | }
44 |
45 | else {
46 | next ( action );
47 | }
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/navigations/router.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { StackNavigator ,
4 | TabNavigator } from 'react-navigation';
5 | import TabBar from '../components/navigations/tabbar-bottom';
6 | import Bull from '../screens/bull';
7 | import Converter from '../screens/converter';
8 | import Currency from '../screens/currency';
9 | import Currencies from '../screens/currencies';
10 | import Detail from '../screens/detail';
11 | import Exchanges from '../screens/exchanges';
12 | import Language from '../screens/language';
13 | import Languages from '../screens/languages';
14 | import News from '../screens/news';
15 | import Portfolio from '../screens/portfolio';
16 | import Settings from '../screens/settings';
17 | import Theme from '../screens/theme';
18 | import Themes from '../screens/themes';
19 | import Donate from '../screens/donate';
20 | import header from '../styles/header';
21 |
22 | const Root = TabNavigator (
23 |
24 | {
25 | bull : {
26 | screen : Bull
27 | } ,
28 |
29 | currencies : {
30 | screen : Currencies
31 | } ,
32 |
33 | portfolio : {
34 | screen : Portfolio
35 | } ,
36 |
37 | news : {
38 | screen : News
39 | } ,
40 |
41 | settings : {
42 | screen : Settings
43 | }
44 | } ,
45 |
46 | {
47 | animationEnabled : true ,
48 | swipeEnabled : true ,
49 | tabBarComponent : TabBar ,
50 | tabBarPosition : 'bottom' ,
51 | tabBarOptions : {
52 | showLabel : false
53 | }
54 | }
55 | );
56 |
57 | export default StackNavigator ({
58 | Root : {
59 | screen : Root
60 | } ,
61 |
62 | currency : {
63 | screen : Currency
64 | } ,
65 |
66 | detail : {
67 | screen : Detail
68 | } ,
69 |
70 | donate : {
71 | screen : Donate
72 | } ,
73 |
74 | exchanges : {
75 | screen : Exchanges
76 | } ,
77 |
78 | language : {
79 | screen : Language
80 | } ,
81 |
82 | converter : {
83 | screen : Converter
84 | } ,
85 |
86 | theme : {
87 | screen : Theme
88 | } ,
89 |
90 | themes : {
91 | screen : Themes
92 | } ,
93 |
94 | languages : {
95 | screen : Languages
96 | }
97 | } ,
98 |
99 | {
100 | navigationOptions : ({ screenProps }) => {
101 |
102 | const theme = screenProps.theme ,
103 | appearance = header ( theme ) ;
104 |
105 | return {
106 | headerStyle : appearance.header ,
107 | headerTitleStyle : appearance.title
108 | };
109 | }
110 | }
111 | );
112 |
113 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bullet",
3 | "version": "6.1.0",
4 | "description": "Find the latest Bull and on the crypto market.",
5 | "author": "Stuart Pretorius",
6 | "main": "node_modules/expo/AppEntry.js",
7 | "scripts": {
8 | "test": "jest"
9 | },
10 | "jest": {
11 | "preset": "jest-expo"
12 | },
13 | "dependencies": {
14 | "@expo/samples": "~1.0.4",
15 | "d3-array": "^1.2.0",
16 | "d3-scale": "^1.0.6",
17 | "d3-time": "^1.0.7",
18 | "d3-time-format": "^2.0.5",
19 | "expo": "^20.0.0",
20 | "react": "16.0.0-alpha.12",
21 | "react-native": "https://github.com/expo/react-native/archive/sdk-20.0.0.tar.gz",
22 | "react-native-xml2js": "^1.0.3",
23 | "react-navigation": "^1.0.0-beta.11",
24 | "react-redux": "^5.0.5",
25 | "redux": "^3.7.1",
26 | "redux-logger": "^3.0.6",
27 | "redux-thunk": "^2.2.0",
28 | "sentry-expo": "^1.6.0"
29 | },
30 | "devDependencies": {
31 | "jest-expo": "~20.0.0",
32 | "sentry-expo": "~1.6.0"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/properties/device.js:
--------------------------------------------------------------------------------
1 |
2 | import { Platform } from 'react-native';
3 | import Dimensions from 'Dimensions';
4 |
5 | export default {
6 |
7 | height : Dimensions.get ( 'window' ).height ,
8 |
9 | os : {
10 | android : Platform.OS === 'android' ,
11 | ios : Platform.OS === 'ios'
12 | } ,
13 |
14 | width : Dimensions.get ( 'window' ).width
15 |
16 | };
--------------------------------------------------------------------------------
/properties/exchanges.js:
--------------------------------------------------------------------------------
1 |
2 | export default [
3 |
4 | {
5 | brand : {
6 | primary : '#2b71b1' ,
7 | secondary : '#f8f8f8'
8 | } ,
9 | name : 'Coinbase' ,
10 | url : 'https://www.coinbase.com/join/591ea5b87e4844089e92e8ae'
11 | } ,
12 |
13 | {
14 | brand : {
15 | primary : '#2d6184' ,
16 | secondary : '#fff'
17 | } ,
18 | name : 'Coinmama' ,
19 | url : 'https://www.Coinmama.com/?ref=terminalpunk'
20 | } ,
21 |
22 | {
23 | brand : {
24 | primary : '#32343e' ,
25 | secondary : '#00bdca'
26 | } ,
27 | name : 'CEX.IO' ,
28 | url : 'https://cex.io/r/0/up107577742/0/'
29 | } ,
30 |
31 | {
32 | brand : {
33 | primary : '#f48435' ,
34 | secondary : '#fff'
35 | } ,
36 | name : 'Local Bitcoins' ,
37 | url : 'https://localbitcoins.com/?ch=dni3'
38 | } ,
39 |
40 | {
41 | brand : {
42 | primary : '#2498b9' ,
43 | secondary : '#fff'
44 | } ,
45 | name : 'BitPanda' ,
46 | url : 'https://www.bitpanda.com/?ref=1436947744937813050'
47 | } ,
48 |
49 | {
50 | brand : {
51 | primary : '#444' ,
52 | secondary : '#1abc9c'
53 | } ,
54 | name : 'PAXFUL' ,
55 | url : 'https://paxful.com/roots/buy-bitcoin/index?affiliate=MmKdXy2jYwo'
56 | } ,
57 |
58 | {
59 | brand : {
60 | primary : '#143603' ,
61 | secondary : '#ffdb3f'
62 | } ,
63 | name : '247 Exchange' ,
64 | url : 'https://www.247exchange.com/?rId=103493'
65 | }
66 |
67 | // {
68 | // brand : {
69 | // primary : #4f6587' ,
70 | // secondary : '#cad6e3'
71 | // } ,
72 | // name : 'Bitcoin DE' ,
73 | // url : ''
74 | // }
75 |
76 | // {
77 | // brand : {
78 | // primary : '#2e969e' ,
79 | // secondary : '#fff'
80 | // } ,
81 | // name : 'Coinhouse' ,
82 | // url : ''
83 | // }
84 |
85 | ];
--------------------------------------------------------------------------------
/properties/languages.js:
--------------------------------------------------------------------------------
1 |
2 | import chinese from './languages/chinese';
3 | import english from './languages/english';
4 | import french from './languages/french';
5 | import german from './languages/german';
6 | import malay from './languages/malay';
7 | import spanish from './languages/spanish';
8 | import turkish from './languages/turkish';
9 | import vietnamese from './languages/vietnamese';
10 |
11 | export default {
12 | ...english ,
13 | ...chinese ,
14 | ...french ,
15 | ...german ,
16 | ...malay ,
17 | //...spanish ,
18 | ...turkish ,
19 | ...vietnamese
20 | };
--------------------------------------------------------------------------------
/properties/languages/chinese.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | zh : {
5 |
6 | id : 'zh' ,
7 | names : {
8 | en : 'Chinese' ,
9 | es : 'Chino' ,
10 | de : 'Chinesisch' ,
11 | fr : 'Chinois' ,
12 | ms : 'Cina' ,
13 | tr : 'Çince' ,
14 | vi : 'Trung Quốc' ,
15 | zh : '中文'
16 | } ,
17 |
18 | actions : {
19 | ad : '赞助商' ,
20 | all : '所有' ,
21 | add : '加' ,
22 | calculating : '计算...' ,
23 | cancel : '取消' ,
24 | load : '加载' ,
25 | loading : '载入中...' ,
26 | more : '阅读更多' ,
27 | remove : '去掉' ,
28 | refresh : '刷新' ,
29 | return : '背部' ,
30 | share : '分享' ,
31 | search : '请输入一个搜索词' ,
32 | update : '更新'
33 | } ,
34 |
35 | denominations : {
36 |
37 | btc : {
38 | name : '比特币' ,
39 | symbol : 'BTC'
40 | }
41 | } ,
42 |
43 | errors : {
44 | 500 : '不可用' ,
45 | ajax : '检索数据时出错。请再试一次。' ,
46 | default : '糟糕 - 发生错误. 请重新启动应用程序。'
47 | } ,
48 |
49 | screens : {
50 |
51 | bull : {
52 |
53 | 404 : '看起来在某些数据中出现问题. 我们一直无法计算最新的牛逼' ,
54 |
55 | changes : {
56 | hour : '上一个小时' ,
57 | day : '最后一天' ,
58 | title : '运动' ,
59 | week : '上个星期'
60 | } ,
61 |
62 | description : '公牛评级是基于过去24小时内按市值,价格和最新趋势时间框架变化移动的硬币总量的数字。时间范围可以只有一小时或最多一天。 这个数字越高,货币越有可能在积极的方向发展。' ,
63 |
64 | notice : '此评级是根据前{{placeholder}}名加密货币计算的。' ,
65 |
66 | market : {
67 | available : '供应可用' ,
68 | cap : '市值' ,
69 | rank : '秩' ,
70 | title : '市场' ,
71 | total : '供应总量' ,
72 | updated : '最近更新时间' ,
73 | volume : '24小时音量'
74 | } ,
75 |
76 | rating : '公牛评级' ,
77 |
78 | title : '最新公牛' ,
79 |
80 | values : {
81 | title : '价格'
82 | }
83 |
84 | } ,
85 |
86 | currency : {
87 | title : '更改货币'
88 | } ,
89 |
90 | currencies : {
91 | title :'前{{length}}名货币' ,
92 | headers : {
93 | rank : '秩' ,
94 | change : '24小时' ,
95 | price : '价钱' ,
96 | rating : '评分'
97 | } ,
98 | none : '找不到搜索字词的货币'
99 | } ,
100 |
101 | converter : {
102 | title : '变流器' ,
103 | placeholder : '量'
104 | } ,
105 |
106 | detail : {
107 | title : '详情' ,
108 | add : '加入投资组合' ,
109 | update : '更新投资组合'
110 | } ,
111 |
112 | exchanges : {
113 | title : '采购'
114 | } ,
115 |
116 | language : {
117 | title : '改变语言'
118 | } ,
119 |
120 | news : {
121 | title : '新闻'
122 | } ,
123 |
124 | portfolio : {
125 | 404 : '您尚未将任何货币加入投资组合。 请从货币详细信息屏幕添加它们,我们将能够为您的硬币收集提供建议' ,
126 | description : '将{{placeholder}}添加到您的投资组合列表中,以跟踪所有硬币及其合并价值。' ,
127 | headers : {
128 | amount : '量' ,
129 | name : '名称' ,
130 | price : '价钱' ,
131 | total : '总'
132 | } ,
133 | title : '投资组合'
134 | } ,
135 |
136 | settings : {
137 | title : '设置'
138 | } ,
139 |
140 | share : {
141 | title : 'Cryptobullography' ,
142 | summary : '找到最新的斗牛市场上最大的噪音!#Cryptobullography'
143 | } ,
144 |
145 | theme : {
146 | title : '改变主题'
147 | } ,
148 |
149 | translations : {
150 | title : '提交翻译' ,
151 | action : '发送翻译' ,
152 | body : '此应用程序使用Google翻译为我们的用户提供多语言支持。 可能会好多了 如果您说另一种语言,并希望将其添加到应用程序中,请通过提交翻译来帮助。'
153 | } ,
154 |
155 | themes : {
156 | title : '提交主题',
157 | action : '发一个主题' ,
158 | body : '如果您想提交主题,请通过您的调色板和主题名称发送,我们将其添加到应用程序。'
159 | } ,
160 |
161 | donate : {
162 | action : '复制{{placeholder}}地址' ,
163 | title : '捐' ,
164 | body : '请帮助这个应用程序保持广告免费和进一步发展捐赠。 按下面的按钮将把钱包地址复制到剪贴板。' ,
165 | wallets : [
166 | {
167 | name : 'Ethereum' ,
168 | id : '0x790b032d497131296eae4250a4840785dfcfd83e'
169 | } ,
170 | {
171 | name : 'BitCoin' ,
172 | id : '1MGkY3ZtvPVZUrg68eMdeKcjAv5FwD7hhm'
173 | } ,
174 | {
175 | name : 'LiteCoin' ,
176 | id : 'Li5YUuaso9Dzmf1ZB9qrh9QBfy9TWeLTdJ'
177 | } ,
178 | {
179 | name : 'Groestlcoin' ,
180 | id : 'FqmnNi5CVUi3wPBhzCZkTWRyE666j6oYat'
181 | } ,
182 | {
183 | name : 'NEO' ,
184 | id : 'ARr1SNboRfbHEjnpnrdVkpApz9cNknS7hL'
185 | }
186 | ]
187 | }
188 | }
189 | }
190 | };
191 |
--------------------------------------------------------------------------------
/properties/languages/english.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | en : {
5 |
6 | id : 'en' ,
7 | names : {
8 | en : 'English' ,
9 | es : 'Inglés' ,
10 | de : 'Englisch' ,
11 | fr : 'Anglais' ,
12 | ms : 'Bahasa Inggeris' ,
13 | tr : 'Ingilizce' ,
14 | vi : 'Anh' ,
15 | zh : '英语'
16 | } ,
17 |
18 | actions : {
19 | ad : 'Sponsored' ,
20 | all : 'all' ,
21 | add : 'Add' ,
22 | calculating : 'Calculating...' ,
23 | cancel : 'Cancel' ,
24 | load : 'Load' ,
25 | loading : 'Loading...' ,
26 | more : 'Read more' ,
27 | refresh : 'Refresh' ,
28 | return : 'Back' ,
29 | remove : 'Remove' ,
30 | share : 'Share' ,
31 | search : 'Please enter a search term' ,
32 | update : 'Update'
33 | } ,
34 |
35 | denominations : {
36 |
37 | btc : {
38 | name : 'BitCoin' ,
39 | symbol : 'BTC'
40 | }
41 | } ,
42 |
43 | errors : {
44 | 500 : 'Unavailable' ,
45 | ajax : 'There was an error retrieving the data. Please try again.' ,
46 | default : 'Oops - an error has occurred. Please restart the application.'
47 | } ,
48 |
49 | screens : {
50 |
51 | bull : {
52 |
53 | 404 : 'Looks like there has been in a problem in some of the data. We have been unable to calculate the latest Bull - sorry!' ,
54 |
55 | changes : {
56 | hour : 'Last Hour' ,
57 | day : 'Last Day' ,
58 | title : 'Movement' ,
59 | week : 'Last Week'
60 | } ,
61 |
62 | description : 'The Bull rating is a number based off the total volume of coins moved in the last 24 hours by market cap, price and latest trending time frame change The time frame can be as little as an hour or at most a day. The higher this number is the more likely the currency is trending in a positive direction.' ,
63 |
64 | notice : 'This rating is based off of a calculation from the top {{placeholder}} crypto currencies.' ,
65 |
66 | market : {
67 | available : 'Supply Available' ,
68 | cap : 'Cap' ,
69 | rank : 'Rank' ,
70 | title : 'Market' ,
71 | total : 'Supply Total' ,
72 | updated : 'Last Updated' ,
73 | volume : '24 Hour Volume'
74 | } ,
75 |
76 | rating : 'Bull Rating' ,
77 |
78 | title : 'Latest Bull' ,
79 |
80 | values : {
81 | title : 'Prices'
82 | }
83 |
84 | } ,
85 |
86 | currency : {
87 | title : 'Change currency'
88 | } ,
89 |
90 | currencies : {
91 | title :'Top {{length}} Currencies' ,
92 | headers : {
93 | rank : 'Rank' ,
94 | change : '24H' ,
95 | price : 'Price' ,
96 | rating : 'Rating'
97 | } ,
98 | none : 'No currencies found for search term'
99 | } ,
100 |
101 | converter : {
102 | title : 'Converter' ,
103 | placeholder : 'Amount'
104 | } ,
105 |
106 | detail : {
107 | title : 'Detail' ,
108 | add : 'Add to portfolio' ,
109 | update : 'Update portfolio'
110 | } ,
111 |
112 | exchanges : {
113 | title : 'Purchase'
114 | } ,
115 |
116 | language : {
117 | title : 'Change language'
118 | } ,
119 |
120 | news : {
121 | title : 'News'
122 | } ,
123 |
124 | portfolio : {
125 | 404 : 'You have not added any currencies to your portfolio. Please add them from the currency detail screen and we\'ll be able to advise you of an estimated worth for your coin collection' ,
126 | description : 'Add {{placeholder}} to your portfolio list to keep a track of all your coins and their combined worth.' ,
127 | headers : {
128 | amount : 'Amount' ,
129 | name : 'Name' ,
130 | price : 'Price' ,
131 | total : 'Total'
132 | } ,
133 | title : 'Portfolio'
134 | } ,
135 |
136 | settings : {
137 | title : 'Settings'
138 | } ,
139 |
140 | share : {
141 | title : 'Cryptobullography' ,
142 | summary : 'Find the latest bull on the cryptocurrency market making the biggest noise! #Cryptobullography'
143 | } ,
144 |
145 | theme : {
146 | title : 'Change theme'
147 | } ,
148 |
149 | translations : {
150 | title : 'Submit translation' ,
151 | action : 'Send a translation' ,
152 | body : 'This application uses Google Translate to provide multilingual support for our users. It could probably be a lot better. If you speak another language and would like it added to the application please help by submitting a translation.'
153 | } ,
154 |
155 | themes : {
156 | title : 'Submit theme',
157 | action : 'Send a theme' ,
158 | body : 'If you would like to submit a theme please send through your colour palette and theme name and we\'ll add it to the application.'
159 | } ,
160 |
161 | donate : {
162 | action : 'Copy {{placeholder}} address' ,
163 | title : 'Donate' ,
164 | body : 'Please help this application stay ad free and further development by donating. Pressing the below buttons will copy the wallet address to your clipboard.' ,
165 | wallets : [
166 | {
167 | name : 'Ethereum' ,
168 | id : '0x790b032d497131296eae4250a4840785dfcfd83e'
169 | } ,
170 | {
171 | name : 'BitCoin' ,
172 | id : '1MGkY3ZtvPVZUrg68eMdeKcjAv5FwD7hhm'
173 | } ,
174 | {
175 | name : 'LiteCoin' ,
176 | id : 'Li5YUuaso9Dzmf1ZB9qrh9QBfy9TWeLTdJ'
177 | } ,
178 | {
179 | name : 'Groestlcoin' ,
180 | id : 'FqmnNi5CVUi3wPBhzCZkTWRyE666j6oYat'
181 | } ,
182 | {
183 | name : 'NEO' ,
184 | id : 'ARr1SNboRfbHEjnpnrdVkpApz9cNknS7hL'
185 | }
186 | ]
187 | }
188 | }
189 | }
190 | };
191 |
--------------------------------------------------------------------------------
/properties/languages/malay.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | ms : {
5 |
6 | id : 'ms' ,
7 | names : {
8 | en : 'Malay' ,
9 | es : 'Malayo' ,
10 | de : 'Malaiisch' ,
11 | fr : 'Malais' ,
12 | ms : 'Melayu' ,
13 | tr : 'Malaya' ,
14 | vi : 'Người Malay' ,
15 | zh : '马来语'
16 | } ,
17 |
18 | actions : {
19 | ad : 'Ditaja' ,
20 | all : 'Semua' ,
21 | add : 'Tambah' ,
22 | calculating : 'Menghitung...' ,
23 | cancel : 'Batalkan' ,
24 | load : 'Beban' ,
25 | loading : 'Memuatkan...' ,
26 | more : 'Baca lebih lanjut' ,
27 | refresh : 'Menyegarkan' ,
28 | return : 'Belakang' ,
29 | remove : 'Keluarkan' ,
30 | share : 'Berkongsi' ,
31 | search : 'Sila masukkan istilah carian' ,
32 | update : 'Kemas kini'
33 | } ,
34 |
35 | denominations : {
36 |
37 | btc : {
38 | name : 'BitCoin' ,
39 | symbol : 'BTC'
40 | }
41 | } ,
42 |
43 | errors : {
44 | 500 : 'Tidak ada' ,
45 | ajax : 'Terdapat ralat untuk mendapatkan semula data. Sila cuba lagi.' ,
46 | default : 'Oops - ralat telah berlaku. Sila mulakan semula aplikasi.'
47 | } ,
48 |
49 | screens : {
50 |
51 | bull : {
52 |
53 | 404 : 'Sepertinya terdapat masalah dalam beberapa data. Kami tidak dapat mengira Bull terbaru - maaf!' ,
54 |
55 | changes : {
56 | hour : 'Jam terakhir' ,
57 | day : 'Hari terakhir' ,
58 | title : 'Pergerakan' ,
59 | week : 'Minggu lepas'
60 | } ,
61 |
62 | description : 'Penarafan Bull adalah angka yang didasarkan pada jumlah keseluruhan koin yang dipindahkan dalam 24 jam terakhir oleh topi pasaran, harga dan perubahan jangka masa tren terkini. Rangka masa boleh menjadi kurang dari sejam atau paling banyak sehari. Semakin tinggi angka ini adalah semakin besar mata wang sedang berubah arah.' ,
63 |
64 | notice : 'Enarafan ini didasarkan pada pengiraan dari {{placeholder}} mata wang utama crypto.' ,
65 |
66 | market : {
67 | available : 'Bekalan disediakan' ,
68 | cap : 'Kapasiti' ,
69 | rank : 'Kedudukan' ,
70 | title : 'Pasaran' ,
71 | total : 'Bekalan Jumlah' ,
72 | updated : 'Terakhir Dikemaskini' ,
73 | volume : '24 Jam Jilid'
74 | } ,
75 |
76 | rating : 'Lembu Jantan Penilaian' ,
77 |
78 | title : 'Lembu jantan' ,
79 |
80 | values : {
81 | title : 'Harga'
82 | }
83 |
84 | } ,
85 |
86 | converter : {
87 | title : 'Penukar' ,
88 | placeholder : 'Jumlah'
89 | } ,
90 |
91 | currency : {
92 | title : 'Tukar mata wang'
93 | } ,
94 |
95 | currencies : {
96 | title :'{{length}} Mata Wang Teratas' ,
97 | headers : {
98 | rank : 'Kedudukan' ,
99 | change : '24 jam' ,
100 | price : 'Harga' ,
101 | rating : 'Penilaian'
102 | } ,
103 | none : 'Tiada mata wang yang dijumpai untuk istilah carian'
104 | } ,
105 |
106 | detail : {
107 | title : 'Terperinci' ,
108 | add : 'Tambah kepada portfolio' ,
109 | update : 'Kemas kini portfolio'
110 | } ,
111 |
112 | exchanges : {
113 | title : 'Pembelian'
114 | } ,
115 |
116 | language : {
117 | title : 'Tukar bahasa'
118 | } ,
119 |
120 | news : {
121 | title : 'Berita'
122 | } ,
123 |
124 | portfolio : {
125 | 404 : 'Anda belum menambahkan sebarang mata wang ke portfolio anda. Sila tambahkan mereka dari skrin terperinci mata wang dan kami akan dapat menasihati anda tentang nilai anggaran untuk pengumpulan duit syiling anda' ,
126 | description : 'Tambah {{placeholder}} ke senarai portfolio anda untuk menyimpan trek semua syiling anda dan nilai gabungan mereka.' ,
127 | headers : {
128 | amount : 'Jumlah' ,
129 | name : 'Nama' ,
130 | price : 'Harga' ,
131 | total : 'Jumlah'
132 | } ,
133 | title : 'Portfolio'
134 | } ,
135 |
136 | settings : {
137 | title : 'Tetapan'
138 | } ,
139 |
140 | share : {
141 | title : 'Cryptobullography' ,
142 | summary : 'Cari lembu terbaru di pasaran cryptocurrency yang membuat bunyi terbesar! #Cryptobullography'
143 | } ,
144 |
145 | theme : {
146 | title : 'Tukar tema'
147 | } ,
148 |
149 | translations : {
150 | title : 'Terjemahan' ,
151 | action : 'Hantar terjemahan' ,
152 | body : 'Aplikasi ini menggunakan Google Translate untuk memberikan sokongan berbilang bahasa kepada pengguna kami. Ini mungkin lebih baik. Sekiranya anda bercakap bahasa lain dan ingin menambahkannya kepada aplikasi, sila membantu dengan menyerahkan terjemahan.'
153 | } ,
154 |
155 | themes : {
156 | title : 'Hantar tema',
157 | action : 'Hantar tema' ,
158 | body : 'Jika anda ingin menyerahkan tema sila hantar melalui palet warna dan nama tema anda dan kami akan menambahkannya ke aplikasi.'
159 | } ,
160 |
161 | donate : {
162 | action : 'Salin alamat {{placeholder}}' ,
163 | title : 'Menderma' ,
164 | body : 'Tolong bantu aplikasi ini supaya iklan percuma dan pembangunan selanjutnya dengan menderma. Menekan butang di bawah akan menyalin alamat dompet ke clipboard anda.' ,
165 | wallets : [
166 | {
167 | name : 'Ethereum' ,
168 | id : '0x790b032d497131296eae4250a4840785dfcfd83e'
169 | } ,
170 | {
171 | name : 'BitCoin' ,
172 | id : '1MGkY3ZtvPVZUrg68eMdeKcjAv5FwD7hhm'
173 | } ,
174 | {
175 | name : 'LiteCoin' ,
176 | id : 'Li5YUuaso9Dzmf1ZB9qrh9QBfy9TWeLTdJ'
177 | } ,
178 | {
179 | name : 'Groestlcoin' ,
180 | id : 'FqmnNi5CVUi3wPBhzCZkTWRyE666j6oYat'
181 | } ,
182 | {
183 | name : 'NEO' ,
184 | id : 'ARr1SNboRfbHEjnpnrdVkpApz9cNknS7hL'
185 | }
186 | ]
187 | }
188 | }
189 | }
190 | };
191 |
--------------------------------------------------------------------------------
/properties/languages/spanish.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | es : {
5 | "id": "es",
6 | "actions": {
7 | "ad": "Patrocinado",
8 | "all": "todo",
9 | "add": "Añadir",
10 | "calculating": "Calculando...",
11 | "cancel": "Cancelar",
12 | "load": "Cargar",
13 | "loading": "Cargando...",
14 | "more": "Leer más",
15 | "refresh": "Actualizar",
16 | "return": "Volver",
17 | "remove": "Eliminar",
18 | "share": "Compartir",
19 | "search": "Por favor inserte un término de búsqueda ",
20 | "update": "Actualizar"
21 | },
22 | "denominations": {
23 | "btc": {
24 | "name": "BitCoin",
25 | "symbol": "BTC"
26 | }
27 | },
28 | "errors": {
29 | "500": "No disponible",
30 | "ajax": "Se ha producido un error al obtener los datos. Inténtalo de nuevo.",
31 | "default": "Oops - se produjo un error, por favor reinicia la aplicación."
32 | },
33 | "names": {
34 | "en": "Spanish",
35 | "es": "Español",
36 | "de": "Spanisch",
37 | "fr": "Espanol",
38 | "ms": "Sepanyol",
39 | "tr": "İspanyol",
40 | "vi": "người Tây Ban Nha",
41 | "zh": "西班牙语"
42 | },
43 | "screens": {
44 | "algorithms": {
45 | "title": "{{length}} Algoritmos ",
46 | "headers": {
47 | "name": "Nombre",
48 | "profit": "Beneficio",
49 | "symbol": "Symbol"
50 | },
51 | "none": "No se encontraron monedas para el término de búsqueda"
52 | },
53 | "detail": {
54 | "title": "Detalle"
55 | },
56 | "exchanges": {
57 | "title": "Compra"
58 | },
59 | "language": {
60 | "title": "Cambiar idioma"
61 | },
62 | "settings": {
63 | "title": "Ajustes"
64 | },
65 | "share": {
66 | "title": "Crypto Coin Miner",
67 | "summary": "Encuentra la criptomoneda más rentable para la minar #CryptoCoinMiner!"
68 | },
69 | "theme": {
70 | "title": "Cambiar Tema"
71 | },
72 | "translations": {
73 | "title": "Presentar traducción ",
74 | "action": "Enviar una traducción ",
75 | "body": "Esta aplicación utiliza Google Translate para proporcionar soporte multilingüe a nuestros usuarios. Probablemente podría ser mucho mejor. Si hablas otro idioma y deseas que se agregue a la aplicación, ayuda enviando una traducción."
76 | },
77 | "themes": {
78 | "title": "Presentar tema",
79 | "action": "Enviar un tema",
80 | "body": "Si desea enviar un tema, envíe su paleta de colores y el nombre del tema y lo agregaremos a la aplicación."
81 | },
82 | "donate": {
83 | "action": "Copiar dirección {{placeholder}} ",
84 | "title": "Donar",
85 | "body": "Por favor, ayuda a que esta aplicación permanezca libre de anuncios y desarrolle más mediante la donación. Al presionar los botones a continuación, se copiará la dirección de la billetera en el portal .",
86 | "wallets": [
87 | {
88 | "name": "Ethereum",
89 | "id": "0x790b032d497131296eae4250a4840785dfcfd83e"
90 | },
91 | {
92 | "name": "BitCoin",
93 | "id": "1MGkY3ZtvPVZUrg68eMdeKcjAv5FwD7hhm"
94 | },
95 | {
96 | "name": "LiteCoin",
97 | "id": "Li5YUuaso9Dzmf1ZB9qrh9QBfy9TWeLTdJ"
98 | },
99 | {
100 | "name": "Groestlcoin",
101 | "id": "FqmnNi5CVUi3wPBhzCZkTWRyE666j6oYat"
102 | },
103 | {
104 | "name": "NEO",
105 | "id": "ARr1SNboRfbHEjnpnrdVkpApz9cNknS7hL"
106 | }
107 | ]
108 | }
109 | }
110 | }
111 | };
112 |
--------------------------------------------------------------------------------
/properties/languages/turkish.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | tr : {
5 |
6 | id : 'tr' ,
7 | names : {
8 | en : 'Turkish' ,
9 | es : 'Turco' ,
10 | de : 'Türkisch' ,
11 | fr : 'Turc' ,
12 | ms : 'Turki' ,
13 | tr : 'Türk' ,
14 | vi : 'Thổ' ,
15 | zh : '土耳其'
16 | } ,
17 |
18 | actions : {
19 | ad : 'Sponsor' ,
20 | all : 'herşey' ,
21 | add : 'Eklemek' ,
22 | calculating : 'Hesaplama...' ,
23 | cancel : 'İptal etmek' ,
24 | load : 'Yük' ,
25 | loading : 'Yükleniyor...' ,
26 | more : 'Daha fazla oku' ,
27 | refresh : 'Yenile' ,
28 | return : 'Geri' ,
29 | remove : 'Kaldır' ,
30 | share : 'Pay' ,
31 | search : 'Lütfen bir arama terimi girin' ,
32 | update : 'Güncelleştirme'
33 | } ,
34 |
35 | denominations : {
36 |
37 | btc : {
38 | name : 'BitCoin' ,
39 | symbol : 'BTC'
40 | }
41 | } ,
42 |
43 | errors : {
44 | 500 : 'Kullanım dışı' ,
45 | ajax : 'Verilerin alınmasında bir hata oluştu. Lütfen tekrar deneyin.' ,
46 | default : 'Hata! Bir hata oluştu. Lütfen uygulamayı yeniden başlatın.'
47 | } ,
48 |
49 | screens : {
50 |
51 | bull : {
52 |
53 | 404 : 'Bazı verilerin birinde sorun yaşanıyor gibi görünüyor. En son Bull\'u hesaplayamadık - özür dilerim!' ,
54 |
55 | changes : {
56 | hour : 'Son saat' ,
57 | day : 'Son gün' ,
58 | title : 'Hareket' ,
59 | week : 'Geçen hafta'
60 | } ,
61 |
62 | description : 'Bull derecelendirmesi, son 24 saat içinde hareket eden madalyonların toplam hacminin piyasa değeri, fiyat ve son trend zaman aralığına göre değişen bir sayıdır. Zaman aralığı, bir saat veya en fazla bir gün olabilir. Bu sayı ne kadar yüksek olursa para olumlu yönde artacaktır.' ,
63 |
64 | notice : 'Bu derecelendirme, üst {{placeholder}} kripto para biriminden bir hesaplamaya dayanır.' ,
65 |
66 | market : {
67 | available : 'Tedarik Mevcut.' ,
68 | cap : 'Kapak' ,
69 | rank : 'Rütbe' ,
70 | title : 'Pazar' ,
71 | total : 'Tedarik Toplam' ,
72 | updated : 'Son Güncelleme' ,
73 | volume : '24 Saatlik Hacim'
74 | } ,
75 |
76 | rating : 'Boğa Değerlendirme' ,
77 |
78 | title : 'Son Boğa' ,
79 |
80 | values : {
81 | title : 'Fiyatlar'
82 | }
83 |
84 | } ,
85 |
86 | currency : {
87 | title : 'Para birimini değiştir'
88 | } ,
89 |
90 | currencies : {
91 | title :'En {{length}} Para Birimleri' ,
92 | headers : {
93 | rank : 'Rütbe' ,
94 | change : '24H' ,
95 | price : 'Fiyat' ,
96 | rating : 'Değerlendirme'
97 | } ,
98 | none : 'Arama terimi için para birimi bulunamadı'
99 | } ,
100 |
101 | converter : {
102 | title : 'Dönüştürücü' ,
103 | placeholder : 'Tutar'
104 | } ,
105 |
106 | detail : {
107 | title : 'Detay' ,
108 | add : 'Portföyüne ekle' ,
109 | update : 'Portföyü güncelle'
110 | } ,
111 |
112 | exchanges : {
113 | title : 'Satın alma'
114 | } ,
115 |
116 | language : {
117 | title : 'Dili değiştir'
118 | } ,
119 |
120 | news : {
121 | title : 'Haber'
122 | } ,
123 |
124 | portfolio : {
125 | 404 : 'Portföyünüze para birimi eklemediniz. Lütfen bunları para birimi detay ekranından ekleyin ve bozuk para grubunuz için tahmini bir değer önerebiliriz.' ,
126 | description : 'Tüm madeni paralarınızı ve bunların birleşik değerlerini takip etmek için portföy listesine {{placeholder}} ekleyin.' ,
127 | headers : {
128 | amount : 'Tutar' ,
129 | name : 'Isim' ,
130 | price : 'Fiyat' ,
131 | total : 'Toplam'
132 | } ,
133 | title : 'Portföy'
134 | } ,
135 |
136 | settings : {
137 | title : 'Ayarlar'
138 | } ,
139 |
140 | share : {
141 | title : 'Cryptobullography' ,
142 | summary : 'En büyük gürültüyü veren kriptokrasi pazarında en son boğayı bulun! #Cryptobullography'
143 | } ,
144 |
145 | theme : {
146 | title : 'Temayı değiştir'
147 | } ,
148 |
149 | translations : {
150 | title : 'Çeviriyi gönderin' ,
151 | action : 'Bir çeviri gönderin' ,
152 | body : 'Bu uygulama, kullanıcılarımız için çok dilde destek sağlamak için Google Çeviri\'yi kullanmaktadır. Muhtemelen daha iyi olabilir. Başka bir dil konuşursanız ve uygulamaya eklemesini isterseniz lütfen bir çeviri gönderin.'
153 | } ,
154 |
155 | themes : {
156 | title : 'Tema gönderin',
157 | action : 'Bir tema gönder' ,
158 | body : 'Bir tema göndermek isterseniz lütfen renk paletinize ve temanızın adını gönderin ve onu uygulamaya ekleyin.'
159 | } ,
160 |
161 | donate : {
162 | action : '{{Placeholder}} adresini kopyala' ,
163 | title : 'Bağışta bulunmak' ,
164 | body : 'Lütfen bu uygulamanın ücretsiz olarak kalmasına ve bağış yaparak daha da geliştirilmesine yardımcı olun. Aşağıdaki düğmelere basarak M-cüzdan adresini panonuza kopyalayabilirsiniz.' ,
165 | wallets : [
166 | {
167 | name : 'Ethereum' ,
168 | id : '0x790b032d497131296eae4250a4840785dfcfd83e'
169 | } ,
170 | {
171 | name : 'BitCoin' ,
172 | id : '1MGkY3ZtvPVZUrg68eMdeKcjAv5FwD7hhm'
173 | } ,
174 | {
175 | name : 'LiteCoin' ,
176 | id : 'Li5YUuaso9Dzmf1ZB9qrh9QBfy9TWeLTdJ'
177 | } ,
178 | {
179 | name : 'Groestlcoin' ,
180 | id : 'FqmnNi5CVUi3wPBhzCZkTWRyE666j6oYat'
181 | } ,
182 | {
183 | name : 'NEO' ,
184 | id : 'ARr1SNboRfbHEjnpnrdVkpApz9cNknS7hL'
185 | }
186 | ]
187 | }
188 | }
189 | }
190 | };
191 |
--------------------------------------------------------------------------------
/properties/languages/vietnamese.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | vi : {
5 |
6 | id : 'vi' ,
7 | names : {
8 | en : 'Vietnamese' ,
9 | es : 'Vietnamita' ,
10 | de : 'Vietnamesisch' ,
11 | fr : 'Vietnamien' ,
12 | ms : 'Bahasa Inggeris' ,
13 | vi : 'Tiếng Việt' ,
14 | tr : 'Vietnam' ,
15 | zh : '英语'
16 | } ,
17 |
18 | actions : {
19 | ad : 'Tài trợ' ,
20 | all : 'mọi điều' ,
21 | add : 'Thêm vào' ,
22 | calculating : 'Tính...' ,
23 | cancel : 'Hủy' ,
24 | load : 'Tải' ,
25 | loading : 'Tải...' ,
26 | more : 'Đọc thêm' ,
27 | refresh : 'Tải lại' ,
28 | return : 'Trở lại' ,
29 | remove : 'Tẩy' ,
30 | share : 'Chia sẻ' ,
31 | search : 'Vui lòng nhập cụm từ tìm kiếm' ,
32 | update : 'Cập nhật'
33 | } ,
34 |
35 | denominations : {
36 |
37 | btc : {
38 | name : 'BitCoin' ,
39 | symbol : 'BTC'
40 | }
41 | } ,
42 |
43 | errors : {
44 | 500 : 'Không có sẵn' ,
45 | ajax : 'Đã xảy ra lỗi khi truy xuất dữ liệu. Vui lòng thử lại.' ,
46 | default : 'Rất tiếc - đã xảy ra lỗi. Vui lòng khởi động lại ứng dụng.'
47 | } ,
48 |
49 | screens : {
50 |
51 | bull : {
52 |
53 | 404 : 'Có vẻ như đã xảy ra sự cố trong một số dữ liệu. Chúng tôi đã không thể tính được Bull mới nhất - xin lỗi!' ,
54 |
55 | changes : {
56 | hour : 'Giờ cuối cùng' ,
57 | day : 'Ngày cuối' ,
58 | title : 'Phong trào' ,
59 | week : 'Tuần trước'
60 | } ,
61 |
62 | description : 'Xếp hạng Bull là một con số dựa trên tổng khối lượng tiền xu di chuyển trong 24 giờ qua theo giới hạn thị trường, giá cả và sự thay đổi khung thời gian mới nhất Khung thời gian có thể chỉ là một giờ hoặc nhiều nhất một ngày. Con số này càng cao thì đồng tiền có xu hướng theo hướng tích cực.' ,
63 |
64 | notice : 'Xếp hạng này được dựa trên tính toán từ tiền tệ hàng đầu {{placeholder}} crypto.' ,
65 |
66 | market : {
67 | available : 'Cung cấp Có sẵn.' ,
68 | cap : 'Vượt qua' ,
69 | rank : 'Cấp' ,
70 | title : 'Thị trường' ,
71 | total : 'Tổng cung' ,
72 | updated : 'Cập nhật mới nhất' ,
73 | volume : 'Tốc độ 24 giờ'
74 | } ,
75 |
76 | rating : 'Đánh giá Bull' ,
77 |
78 | title : 'Mới nhất Bull' ,
79 |
80 | values : {
81 | title : 'Giá'
82 | }
83 |
84 | } ,
85 |
86 | currency : {
87 | title : 'Thay đổi tiền tệ'
88 | } ,
89 |
90 | currencies : {
91 | title :'Đầu trang {{length}} Tiền tệ' ,
92 | headers : {
93 | rank : 'Cấp' ,
94 | change : '24 giờ' ,
95 | price : 'Giá bán' ,
96 | rating : 'Xêp hạng'
97 | } ,
98 | none : 'Không tìm thấy đơn vị tiền tệ cho cụm từ tìm kiếm'
99 | } ,
100 |
101 | converter : {
102 | title : 'Chuyển đổi' ,
103 | placeholder : 'Số tiền'
104 | } ,
105 |
106 | detail : {
107 | title : 'Chi tiết' ,
108 | add : 'Thêm vào danh mục đầu tư' ,
109 | update : 'Cập nhật danh mục đầu tư'
110 | } ,
111 |
112 | exchanges : {
113 | title : 'Mua, tựa vào, bám vào'
114 | } ,
115 |
116 | language : {
117 | title : 'Thay đổi ngôn ngữ'
118 | } ,
119 |
120 | news : {
121 | title : 'Tin tức'
122 | } ,
123 |
124 | portfolio : {
125 | 404 : 'Bạn chưa thêm bất kỳ loại tiền tệ nào vào danh mục đầu tư của mình. Vui lòng thêm chúng từ màn hình chi tiết tiền tệ và chúng tôi sẽ có thể cho bạn biết giá trị ước tính cho bộ sưu tập tiền xu của bạn' ,
126 | description : 'Thêm {{placeholder}} vào danh mục danh mục đầu tư của bạn để theo dõi tất cả đồng tiền của bạn và giá trị kết hợp của chúng.' ,
127 | headers : {
128 | amount : 'Số tiền' ,
129 | name : 'Tên' ,
130 | price : 'Giá bán' ,
131 | total : 'Toàn bộ'
132 | } ,
133 | title : 'Danh mục'
134 | } ,
135 |
136 | settings : {
137 | title : 'Cài đặt'
138 | } ,
139 |
140 | share : {
141 | title : 'Cryptobullography' ,
142 | summary : 'Tìm con bò mới nhất trên thị trường cryptocurrency làm cho tiếng ồn lớn nhất! #Cryptobullography'
143 | } ,
144 |
145 | theme : {
146 | title : 'Thay đổi chủ đề'
147 | } ,
148 |
149 | translations : {
150 | title : 'Gửi bản dịch' ,
151 | action : 'Gửi bản dịch' ,
152 | body : 'Ứng dụng này sử dụng Google Translate cung cấp hỗ trợ đa ngôn ngữ cho người dùng của chúng tôi. Có lẽ nó sẽ tốt hơn rất nhiều. Nếu bạn nói một ngôn ngữ khác và muốn nó được thêm vào ứng dụng hãy giúp đỡ bằng cách gửi một bản dịch.'
153 | } ,
154 |
155 | themes : {
156 | title : 'Gửi chủ đề',
157 | action : 'Gửi một chủ đề' ,
158 | body : 'Nếu bạn muốn gửi chủ đề vui lòng gửi qua bảng màu và tên chủ đề của bạn và chúng tôi sẽ thêm nó vào ứng dụng.'
159 | } ,
160 |
161 | donate : {
162 | action : 'Sao chép địa chỉ {{placeholder}}' ,
163 | title : 'Tặng' ,
164 | body : 'Hãy giúp ứng dụng này ở lại miễn phí và phát triển hơn nữa bằng cách quyên góp. Nhấn các nút bên dưới sẽ sao chép địa chỉ ví với bộ nhớ tạm của bạn.' ,
165 | wallets : [
166 | {
167 | name : 'Ethereum' ,
168 | id : '0x790b032d497131296eae4250a4840785dfcfd83e'
169 | } ,
170 | {
171 | name : 'BitCoin' ,
172 | id : '1MGkY3ZtvPVZUrg68eMdeKcjAv5FwD7hhm'
173 | } ,
174 | {
175 | name : 'LiteCoin' ,
176 | id : 'Li5YUuaso9Dzmf1ZB9qrh9QBfy9TWeLTdJ'
177 | } ,
178 | {
179 | name : 'Groestlcoin' ,
180 | id : 'FqmnNi5CVUi3wPBhzCZkTWRyE666j6oYat'
181 | } ,
182 | {
183 | name : 'NEO' ,
184 | id : 'ARr1SNboRfbHEjnpnrdVkpApz9cNknS7hL'
185 | }
186 | ]
187 | }
188 | }
189 | }
190 | };
191 |
--------------------------------------------------------------------------------
/properties/themes.js:
--------------------------------------------------------------------------------
1 |
2 | import defaults from './themes/default';
3 | import midnight from './themes/midnight';
4 | import marley from './themes/marley';
5 | import amazon from './themes/amazon';
6 | import windows from './themes/windows';
7 | import heineken from './themes/heineken';
8 | import killbill from './themes/killbill';
9 | import facebook from './themes/facebook';
10 | import twitter from './themes/twitter';
11 | import mcdonalds from './themes/mcdonalds';
12 | import matrix from './themes/matrix';
13 | import google from './themes/google';
14 | import lego from './themes/lego';
15 | import lagoon from './themes/lagoon';
16 |
17 | export default {
18 | ...defaults ,
19 | ...midnight ,
20 | ...google ,
21 | ...marley ,
22 | ...heineken ,
23 | ...matrix ,
24 | ...facebook ,
25 | ...windows ,
26 | ...killbill ,
27 | ...twitter ,
28 | ...mcdonalds ,
29 | ...amazon ,
30 | ...lego ,
31 | ...lagoon
32 | };
33 |
--------------------------------------------------------------------------------
/properties/themes/amazon.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | amazon : {
5 | names : {
6 | en : 'Amazon' ,
7 | es : 'Amazonas' ,
8 | de : 'Amazonas' ,
9 | fr : 'Amazone' ,
10 | ms : 'Amazon' ,
11 | tr : 'Amazon' ,
12 | vi : 'Amazon' ,
13 | zh : '亚马逊'
14 | } ,
15 | id : 'amazon' ,
16 |
17 | base : '#f7f7f7' ,
18 | body : '#000' ,
19 | border : '#f99101' ,
20 | chrome : '#211e1f' ,
21 | disabled : '#666' ,
22 | negative : '#c6000f' ,
23 | positive : '#007a00' ,
24 | primary : '#f7f7f7' ,
25 | secondary : '#f99101' ,
26 | loader : '#f99101'
27 | }
28 | };
--------------------------------------------------------------------------------
/properties/themes/default.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | default : {
5 | names : {
6 | en : 'Jedi Knight' ,
7 | es : 'Caballero Jedi' ,
8 | de : 'Jedi Ritter' ,
9 | fr : 'Chevalier Jedi' ,
10 | ms : 'Knight Jedi' ,
11 | tr : 'Jedi Şövalye' ,
12 | vi : 'Hiệp sĩ Jedi' ,
13 | zh : '绝地骑士'
14 | } ,
15 | id : 'default' ,
16 |
17 | base : '#fff' ,
18 | body : '#333' ,
19 | border : '#cdcdcd' ,
20 | chrome : '#fff' ,
21 | disabled : '#acacac' ,
22 | negative : '#cc0000' ,
23 | positive : '#00cc00' ,
24 | primary : '#f9f9f9' ,
25 | secondary : '#00c9f1' ,
26 | loader : '#00c9f1'
27 | }
28 | };
--------------------------------------------------------------------------------
/properties/themes/facebook.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | facebook : {
5 | names : {
6 | en : 'TheFacebook' ,
7 | es : 'El Facebook' ,
8 | de : 'DasFacebook' ,
9 | fr : 'LeFacebook' ,
10 | ms : 'TheFacebook' ,
11 | tr : 'Facebook' ,
12 | vi : 'Mạng xã hội Facebook' ,
13 | zh : 'TheFacebook'
14 | } ,
15 | id : 'facebook' ,
16 |
17 | base : '#f7f7f7' ,
18 | body : '#111' ,
19 | border : '#8b9dc3' ,
20 | chrome : '#3b5998' ,
21 | disabled : '#444' ,
22 | negative : '#ec361b' ,
23 | positive : '#4ab200' ,
24 | primary : '#dfe3ee' ,
25 | secondary : '#dfe3ee' ,
26 | loader : '#3b5998'
27 | }
28 | };
--------------------------------------------------------------------------------
/properties/themes/google.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | google : {
5 | names : {
6 | en : 'Google' ,
7 | es : 'Google' ,
8 | de : 'Google' ,
9 | fr : 'Google' ,
10 | ms : 'Google' ,
11 | tr : 'Google' ,
12 | vi : 'Google' ,
13 | zh : '谷歌'
14 | } ,
15 | id : 'google' ,
16 |
17 | base : '#fff' ,
18 | body : '#545454' ,
19 | border : '#fad76f' ,
20 | chrome : '#fff' ,
21 | disabled : '#444' ,
22 | negative : '#da452f' ,
23 | positive : '#03a15b' ,
24 | primary : '#fff' ,
25 | secondary : '#176ced' ,
26 | loader : '#176ced'
27 | }
28 | };
--------------------------------------------------------------------------------
/properties/themes/heineken.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | heineken : {
5 | names : {
6 | en : 'Heineken' ,
7 | es : 'Heineken' ,
8 | de : 'Heineken' ,
9 | fr : 'Heineken' ,
10 | ms : 'Heineken' ,
11 | tr : 'Heineken' ,
12 | vi : 'Heineken' ,
13 | zh : '喜力'
14 | } ,
15 | id : 'heineken' ,
16 |
17 | base : '#fff' ,
18 | body : '#0b0e10' ,
19 | border : '#0b0e10' ,
20 | chrome : '#048743' ,
21 | disabled : '#444' ,
22 | negative : '#ed1c24' ,
23 | positive : '#048743' ,
24 | primary : '#ed1c24' ,
25 | secondary : '#fff' ,
26 | loader : '#ed1c24'
27 | }
28 | };
--------------------------------------------------------------------------------
/properties/themes/killbill.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | killbill : {
5 | names : {
6 | en : 'Kill Bill' ,
7 | es : 'Kill Bill' ,
8 | de : 'Kill Bill' ,
9 | fr : 'Kill Bill' ,
10 | ms : 'Bunuh Rang Undang-Undang' ,
11 | tr : 'Bill i öldür' ,
12 | vi : 'Giết Bill' ,
13 | zh : '杀死比尔'
14 | } ,
15 | id : 'killbill' ,
16 |
17 | base : '#f0db75' ,
18 | body : '#1c1209' ,
19 | border : '#a4070c' ,
20 | chrome : '#f6d904' ,
21 | disabled : '#444' ,
22 | negative : '#a4070c' ,
23 | positive : '#006e00' ,
24 | primary : '#dcb046' ,
25 | secondary : '#a4070c' ,
26 | loader : '#a4070c'
27 | }
28 | };
--------------------------------------------------------------------------------
/properties/themes/lagoon.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | lagoon : {
5 | names : {
6 | en : 'Blue Lagoon' ,
7 | es : 'Laguna Azul' ,
8 | de : 'Blaue Lagune' ,
9 | fr : 'Lagon bleu' ,
10 | ms : 'Lagun Biru' ,
11 | tr : 'Mavi Lagün' ,
12 | vi : 'Đầm xanh' ,
13 | zh : '蓝色泻湖'
14 | } ,
15 | id : 'lagoon' ,
16 |
17 | base : '#816cff' ,
18 | body : '#f9f9f9' ,
19 | border : '#4935bf' ,
20 | chrome : '#000063' ,
21 | disabled : '#444' ,
22 | negative : '#330000' ,
23 | positive : '#003300' ,
24 | primary : '#513fcc' ,
25 | secondary : '#ffb600' ,
26 | loader : '#ffb600'
27 | }
28 | };
--------------------------------------------------------------------------------
/properties/themes/lego.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | lego : {
5 | names : {
6 | en : 'Everything is awesome' ,
7 | es : 'Todo es asombroso' ,
8 | de : 'Hier ist alles super' ,
9 | fr : 'Tout est génial' ,
10 | ms : 'Semuanya hebat' ,
11 | tr : 'Herşey harika' ,
12 | vi : 'Mọi thứ đều tuyệt vời' ,
13 | zh : '一切都很棒'
14 | } ,
15 | id : 'lego' ,
16 |
17 | base : '#fff' ,
18 | body : '#000' ,
19 | border : '#ff0000' ,
20 | chrome : '#ff0000' ,
21 | disabled : '#444' ,
22 | negative : '#ff0000' ,
23 | positive : '#00ff00' ,
24 | primary : '#fff' ,
25 | secondary : '#ffff00' ,
26 | loader : '#ffff00'
27 | }
28 | };
--------------------------------------------------------------------------------
/properties/themes/marley.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | marley : {
5 | names : {
6 | en : 'Bob Marley' ,
7 | es : 'Bob Marley' ,
8 | de : 'Bob Marley' ,
9 | fr : 'Bob Marley' ,
10 | ms : 'Bob Marley' ,
11 | tr : 'Bob Marley' ,
12 | vi : 'Bob Marley' ,
13 | zh : '鲍勃·马利'
14 | } ,
15 | id : 'marley' ,
16 |
17 | base : '#f0d817' ,
18 | body : '#333' ,
19 | border : '#3eb308' ,
20 | chrome : '#3eb308' ,
21 | disabled : '#444' ,
22 | negative : '#730000' ,
23 | positive : '#3eb308' ,
24 | primary : '#e20d0d' ,
25 | secondary : '#dbc600' ,
26 | loader : '#3eb308'
27 | }
28 | };
--------------------------------------------------------------------------------
/properties/themes/matrix.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | matrix : {
5 | names : {
6 | en : 'The Matrix' ,
7 | es : 'La Matriz' ,
8 | de : 'Die Matrix' ,
9 | fr : 'La Matrice' ,
10 | ms : 'The Matrix' ,
11 | tr : 'Matrix' ,
12 | vi : 'Ma trận' ,
13 | zh : '矩阵'
14 | } ,
15 | id : 'matrix' ,
16 |
17 | base : '#000' ,
18 | body : '#00ff00' ,
19 | border : '#00ff00' ,
20 | chrome : '#000' ,
21 | disabled : '#444' ,
22 | negative : '#cc0000' ,
23 | positive : '#00ff00' ,
24 | primary : '#000' ,
25 | secondary : '#00ff00' ,
26 | loader : '#00ff00'
27 | }
28 | };
--------------------------------------------------------------------------------
/properties/themes/mcdonalds.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | mcdonalds : {
5 | names : {
6 | en : 'Mickey D\'s' ,
7 | es : 'Mickey D\'s' ,
8 | de : 'McDonald\'s' ,
9 | fr : 'McDonald\'s' ,
10 | ms : 'McDonald\'s' ,
11 | tr : 'McDonald\'s' ,
12 | vi : 'McDonald\'s' ,
13 | zh : '麦当劳'
14 | } ,
15 | id : 'mcdonalds' ,
16 |
17 | base : '#fff' ,
18 | body : '#333' ,
19 | border : '#ffc300' ,
20 | chrome : '#dd1021' ,
21 | disabled : '#444' ,
22 | negative : '#cc0000' ,
23 | positive : '#00cc00' ,
24 | primary : '#fff' ,
25 | secondary : '#ffc300' ,
26 | loader : '#ffc300'
27 | }
28 | };
--------------------------------------------------------------------------------
/properties/themes/midnight.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | midnight : {
5 | names : {
6 | en : 'Sith Lord' ,
7 | es : 'Lord Sith' ,
8 | de : 'Sith Herr' ,
9 | fr : 'Sith Lord' ,
10 | ms : 'Sith Tuan' ,
11 | tr : 'Sith Lord' ,
12 | vi : 'Sith Lord' ,
13 | zh : '西斯王'
14 | } ,
15 | id : 'midnight' ,
16 |
17 | base : '#121212' ,
18 | body : '#acacac' ,
19 | border : '#000' ,
20 | chrome : '#010101' ,
21 | disabled : '#444' ,
22 | negative : '#cc0000' ,
23 | positive : '#00cc00' ,
24 | primary : '#050505' ,
25 | secondary : '#00c4eb' ,
26 | loader : '#00c4eb'
27 | }
28 | };
--------------------------------------------------------------------------------
/properties/themes/twitter.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | twitter : {
5 | names : {
6 | en : 'Twitter' ,
7 | es : 'Gorjeo' ,
8 | de : 'Twitter' ,
9 | fr : 'Gazouillement' ,
10 | ms : 'Twitter' ,
11 | tr : 'Heyecan' ,
12 | vi : 'Twitter' ,
13 | zh : '推特'
14 | } ,
15 | id : 'twitter' ,
16 |
17 | base : '#fff' ,
18 | body : '#333' ,
19 | border : '#00aced' ,
20 | chrome : '#1dcaff' ,
21 | disabled : '#0084b4' ,
22 | negative : '#cc0000' ,
23 | positive : '#00cc00' ,
24 | primary : '#c0deed' ,
25 | secondary : '#fff' ,
26 | loader : '#1dcaff'
27 | }
28 | };
--------------------------------------------------------------------------------
/properties/themes/windows.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | windows : {
5 | names : {
6 | en : 'Windows 3.1' ,
7 | es : 'Windows 3.1' ,
8 | de : 'Windows 3.1' ,
9 | fr : 'Windows 3.1' ,
10 | ms : 'Windows 3.1' ,
11 | tr : 'Windows 3.1' ,
12 | vi : 'Windows 3.1' ,
13 | zh : 'Windows 3.1'
14 | } ,
15 | id : 'windows' ,
16 |
17 | base : '#fff' ,
18 | body : '#0e0e0e' ,
19 | border : '#6b6c6e' ,
20 | chrome : '#0001b3' ,
21 | disabled : '#c0c0c0' ,
22 | negative : '#b30001' ,
23 | positive : '#01b300' ,
24 | primary : '#c5c6cb' ,
25 | secondary : '#fff' ,
26 | loader : '#0001b3'
27 | }
28 | };
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | Crypto Bullography
2 | ==================
3 |
4 | This is the JavaScript and React Native souce code for the Crypto Bullography application available to download on the App Store, Google Play and Expo.io. Have Fun!
5 |
6 | * [Exponent](https://expo.io/@terminalpunk/bullet)
7 | * [iTunes Store](https://itunes.apple.com/gb/app/crypto-coin-bull/id1257246245?mt=8)
8 | * [Google Play Store](https://play.google.com/store/apps/details?id=com.webstew.bullet&hl=en)
9 |
10 |
--------------------------------------------------------------------------------
/reducers/bull.js:
--------------------------------------------------------------------------------
1 |
2 | import array from '../utilities/array';
3 | import constants from '../constants/bull';
4 |
5 | export default function (
6 |
7 | state = {
8 | change : {} ,
9 | competitors : 0 ,
10 | error : null ,
11 | id : null ,
12 | loading : true ,
13 | market : null ,
14 | name : null ,
15 | prices : {} ,
16 | rank : null ,
17 | rating : 0 ,
18 | supply : {} ,
19 | symbol : null ,
20 | updated : null ,
21 | volume : null
22 | } ,
23 |
24 | action = {}
25 |
26 | ) {
27 |
28 | switch ( action.type ) {
29 |
30 | case constants.error :
31 |
32 | return Object.assign (
33 | {} ,
34 | state ,
35 | {
36 | error : action.error ,
37 | loading : false
38 | }
39 | );
40 |
41 | case constants.get :
42 |
43 | return Object.assign (
44 | {} ,
45 | state ,
46 | {
47 | error : null ,
48 | loading : true
49 | }
50 | );
51 |
52 |
53 | case constants.set :
54 |
55 | let bull = {
56 | rating : 0
57 | };
58 |
59 | action.currencies.forEach (( currency ) => {
60 |
61 | if (
62 | ! isNaN ( currency.rating ) &&
63 | currency.rating > bull.rating
64 | ) {
65 |
66 | bull = currency;
67 | }
68 |
69 | });
70 |
71 | bull.loading = false;
72 | bull.competitors = action.currencies.length;
73 |
74 | return Object.assign (
75 | {} ,
76 | state ,
77 | bull
78 | );
79 |
80 | default :
81 | return state;
82 | }
83 | };
84 |
--------------------------------------------------------------------------------
/reducers/currencies.js:
--------------------------------------------------------------------------------
1 |
2 | import constants from '../constants/currencies';
3 |
4 | export default function (
5 |
6 | state = {
7 | error : null ,
8 | items : [] ,
9 | order : 'rank' ,
10 | loading : true
11 | } ,
12 |
13 | action = {}
14 |
15 | ) {
16 |
17 | switch ( action.type ) {
18 |
19 | case constants.error :
20 |
21 | return Object.assign (
22 | {} ,
23 | state ,
24 | {
25 | error : action.error ,
26 | loading : false
27 | }
28 | );
29 |
30 | case constants.get :
31 |
32 | return Object.assign (
33 | {} ,
34 | state ,
35 | {
36 | error : null ,
37 | loading : true
38 | }
39 | );
40 |
41 | case constants.set :
42 |
43 | return Object.assign (
44 | {} ,
45 | state ,
46 | {
47 | error : null ,
48 | items : action.items ,
49 | loading : false ,
50 | order : 'rank'
51 | }
52 | );
53 |
54 | case constants.order :
55 |
56 | const methods = {
57 |
58 | change ( a , b ) {
59 |
60 | return ( b.change.day - a.change.day );
61 |
62 | } ,
63 |
64 | price ( a , b ) {
65 |
66 | return ( b.prices.fiat - a.prices.fiat );
67 | } ,
68 |
69 | rank ( a , b ) {
70 |
71 | return ( a.rank - b.rank );
72 | } ,
73 |
74 | rating ( a , b ) {
75 |
76 | if ( isNaN ( a.rating ) || isNaN ( b.rating )) {
77 |
78 | return a.rating > b.rating ? 1 : -1;
79 | }
80 |
81 | return b.rating - a.rating;
82 | }
83 | } ,
84 |
85 | items = state.items.slice ( 0 );
86 |
87 |
88 | return Object.assign (
89 | {} ,
90 | state ,
91 | {
92 | ordering : false ,
93 | order : action.order ,
94 | items : items.sort ( methods [ action.order ])
95 | }
96 | );
97 |
98 | default :
99 | return state;
100 | }
101 | };
102 |
--------------------------------------------------------------------------------
/reducers/currency.js:
--------------------------------------------------------------------------------
1 |
2 | import constants from '../constants/currency';
3 | import currencies from '../properties/currencies';
4 |
5 | //const initial = languages [ Object.keys ( languages ) [ 0 ]];
6 |
7 | export default function (
8 |
9 | state = {
10 | id : null ,
11 | loading : true ,
12 | names : [] ,
13 | symbol : null
14 | } ,
15 |
16 | action = {}
17 |
18 | ) {
19 |
20 | switch ( action.type ) {
21 |
22 | case constants.set :
23 |
24 | const currency = currencies.find (( item ) => item.id === action.id );
25 |
26 | return Object.assign (
27 | {} ,
28 | state ,
29 | {
30 | ...currency ,
31 | loading : false
32 | } ,
33 | );
34 |
35 | default :
36 | return state;
37 | }
38 | };
39 |
--------------------------------------------------------------------------------
/reducers/graphs.js:
--------------------------------------------------------------------------------
1 |
2 | import constants from '../constants/graphs';
3 |
4 | export default function (
5 |
6 | state = {
7 | prices : {} ,
8 | market : {} ,
9 | loading : true
10 | } ,
11 |
12 | action = {}
13 |
14 | ) {
15 |
16 | switch ( action.type ) {
17 |
18 | case constants.error :
19 |
20 | return Object.assign (
21 | {} ,
22 | state ,
23 | {
24 | error : action.error ,
25 | loading : false
26 | }
27 | );
28 |
29 | case constants.get :
30 |
31 | return Object.assign (
32 | {} ,
33 | state ,
34 | {
35 | error : null ,
36 | loading : true
37 | }
38 | );
39 |
40 |
41 | case constants.reset :
42 |
43 | return Object.assign (
44 | {} ,
45 | state ,
46 | {
47 | loading : true ,
48 | prices : {} ,
49 | volume : {}
50 | }
51 | );
52 |
53 |
54 | case constants.set :
55 |
56 | return Object.assign (
57 | {} ,
58 | state ,
59 | {
60 | loading : false ,
61 | prices : action.prices ,
62 | volume : action.volume
63 | }
64 | );
65 |
66 | default :
67 | return state;
68 | }
69 | };
70 |
--------------------------------------------------------------------------------
/reducers/index.js:
--------------------------------------------------------------------------------
1 |
2 | import { combineReducers } from 'redux';
3 | import bull from './bull';
4 | import currency from './currency';
5 | import currencies from './currencies';
6 | import graphs from './graphs';
7 | import language from './language';
8 | import navigation from './navigation';
9 | import news from './news';
10 | import portfolio from './portfolio';
11 | import search from './search';
12 | import theme from './theme';
13 |
14 | export default combineReducers ({
15 | bull ,
16 | currency ,
17 | currencies ,
18 | graphs ,
19 | language ,
20 | navigation ,
21 | news ,
22 | portfolio ,
23 | search ,
24 | theme
25 | });
26 |
--------------------------------------------------------------------------------
/reducers/language.js:
--------------------------------------------------------------------------------
1 |
2 | import constants from '../constants/language';
3 | import languages from '../properties/languages';
4 |
5 | const initial = languages [ Object.keys ( languages ) [ 0 ]];
6 |
7 | export default function (
8 |
9 | // We set the default to the first theme in our theme properties
10 | state = {
11 | id : initial.id ,
12 | actions : initial.actions ,
13 | denominations : initial.denominations ,
14 | errors : initial.errors ,
15 | names : initial.names ,
16 | screens : initial.screens
17 | } ,
18 |
19 | action = {}
20 |
21 | ) {
22 |
23 | switch ( action.type ) {
24 |
25 | case constants.set :
26 |
27 | return Object.assign (
28 | {} ,
29 | state ,
30 | languages [ action.id ]
31 | );
32 |
33 | default :
34 | return state;
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/reducers/navigation.js:
--------------------------------------------------------------------------------
1 |
2 | import constants from '../constants/navigation';
3 |
4 | export default function (
5 |
6 | state = {
7 | previous : null ,
8 | current : null
9 | } ,
10 |
11 | action = {}
12 |
13 | ) {
14 |
15 | switch ( action.type ) {
16 |
17 | case constants.navigate :
18 |
19 | return Object.assign (
20 | {} ,
21 | state ,
22 | {
23 | current : action.current ,
24 | previous : action.previous
25 | }
26 | );
27 |
28 | default :
29 | return state;
30 | }
31 | };
32 |
--------------------------------------------------------------------------------
/reducers/news.js:
--------------------------------------------------------------------------------
1 |
2 | import constants from '../constants/news';
3 |
4 | export default function (
5 |
6 | state = {
7 | error : null ,
8 | items : [] ,
9 | loading : true
10 | } ,
11 |
12 | action = {}
13 |
14 | ) {
15 |
16 | switch ( action.type ) {
17 |
18 | case constants.error :
19 |
20 | return Object.assign (
21 | {} ,
22 | state ,
23 | {
24 | error : action.error ,
25 | loading : false
26 | }
27 | );
28 |
29 | case constants.get :
30 |
31 | return Object.assign (
32 | {} ,
33 | state ,
34 | {
35 | error : null ,
36 | loading : true
37 | }
38 | );
39 |
40 | case constants.set :
41 |
42 | return Object.assign (
43 | {} ,
44 | state ,
45 | {
46 | error : null ,
47 | items : action.items ,
48 | loading : false
49 | }
50 | );
51 |
52 | default :
53 | return state;
54 | }
55 | };
56 |
--------------------------------------------------------------------------------
/reducers/portfolio.js:
--------------------------------------------------------------------------------
1 |
2 | import constants from '../constants/portfolio';
3 |
4 | export default function (
5 |
6 | state = {
7 | error : null ,
8 | items : [] ,
9 | loading : true
10 | } ,
11 |
12 | action = {}
13 |
14 | ) {
15 |
16 | switch ( action.type ) {
17 |
18 | case constants.reset :
19 |
20 | return Object.assign (
21 | {} ,
22 | state ,
23 | {
24 | error : null ,
25 | items : state.items.filter (( item ) => item.id !== action.id ) ,
26 | loading : false
27 | }
28 | );
29 |
30 | case constants.setup :
31 |
32 | return Object.assign (
33 | {} ,
34 | state ,
35 | {
36 | error : null ,
37 | items : action.items ,
38 | loading : false
39 | }
40 | );
41 |
42 | case constants.set :
43 |
44 | const items = state.items ,
45 | index = items.findIndex (( item ) => item.id === action.id );
46 |
47 | if ( index > -1 ) {
48 | items [ index ].amount = action.amount;
49 | }
50 |
51 | else {
52 | items.push ({
53 | amount : action.amount ,
54 | id : action.id ,
55 | name : action.name
56 | });
57 | }
58 |
59 | return Object.assign (
60 | {} ,
61 | state ,
62 | {
63 | error : null ,
64 | items : items ,
65 | loading : false
66 | }
67 | );
68 |
69 | default :
70 | return state;
71 | }
72 | };
73 |
--------------------------------------------------------------------------------
/reducers/search.js:
--------------------------------------------------------------------------------
1 |
2 | import constants from '../constants/search';
3 |
4 | export default function (
5 |
6 | state = {
7 | on : false ,
8 | value : null
9 | } ,
10 |
11 | action = {}
12 |
13 | ) {
14 |
15 | switch ( action.type ) {
16 |
17 | case constants.on :
18 |
19 | return Object.assign (
20 | {} ,
21 | state ,
22 | {
23 | on : action.value ,
24 | value : null
25 | }
26 | );
27 |
28 | case constants.set :
29 |
30 | return Object.assign (
31 | {} ,
32 | state ,
33 | {
34 | value : action.value
35 | }
36 | );
37 |
38 | default :
39 | return state;
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/reducers/theme.js:
--------------------------------------------------------------------------------
1 |
2 | import constants from '../constants/theme';
3 | import themes from '../properties/themes';
4 |
5 | const initial = themes [ Object.keys ( themes ) [ 0 ]];
6 |
7 | export default function (
8 |
9 | // We set the default to the first theme in our theme properties
10 | state = {
11 | id : initial.id ,
12 | base : initial.base ,
13 | body : initial.body ,
14 | border : initial.border ,
15 | chrome : initial.chrome ,
16 | disabled : initial.disabled ,
17 | names : initial.names ,
18 | negative : initial.negative ,
19 | positive : initial.positive ,
20 | primary : initial.primary ,
21 | secondary : initial.secondary
22 | } ,
23 |
24 | action = {}
25 |
26 | ) {
27 |
28 | switch ( action.type ) {
29 |
30 | case constants.set :
31 |
32 | return Object.assign (
33 | {} ,
34 | state ,
35 | themes [ action.id ]
36 | );
37 |
38 | default :
39 | return state;
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/schematics/currency.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | /**
5 | * Re-writes the API response to our prefferd data schema.
6 | *
7 | * @param {Array} data The currencies results from the API we are calling.
8 | */
9 | get ( data , code ) {
10 |
11 | return data.map ( function ( currency ) {
12 |
13 | const fiat = {
14 | market : currency [ 'market_cap_' + code ] ? currency [ 'market_cap_' + code ] : currency [ 'market_cap_usd' ] ,
15 | price : currency [ 'price_' + code ] ? currency [ 'price_' + code ] : currency [ 'price_usd' ] ,
16 | volume : currency [ '24h_volume_' + code ] ? currency [ '24h_volume_' + code ] : currency [ '24h_volume_usd' ]
17 | };
18 |
19 | return {
20 |
21 | change : {
22 | day : currency [ 'percent_change_24h' ] ? parseFloat ( currency [ 'percent_change_24h' ]) : null ,
23 | hour : currency [ 'percent_change_1h' ] ? parseFloat ( currency [ 'percent_change_1h' ]) : null ,
24 | week : currency [ 'percent_change_7d' ] ? parseFloat ( currency [ 'percent_change_7d' ]) : null
25 | } ,
26 |
27 | id : currency [ 'id' ] ,
28 |
29 | market : fiat.market ? parseFloat ( fiat.market ).toFixed ( 2 ) : null ,
30 |
31 | name : currency [ 'name' ] ,
32 |
33 | prices : {
34 | btc : currency [ 'price_btc' ] ? parseFloat ( currency [ 'price_btc' ]) : null ,
35 | fiat : fiat.price ? parseFloat ( fiat.price ) : null
36 | } ,
37 |
38 | rank : parseFloat ( currency [ 'rank' ]) ,
39 | rating : fiat.market && ( currency [ 'percent_change_1h' ] || currency [ 'percent_change_24h' ]) ? ( parseFloat ( fiat.volume ) / parseFloat ( fiat.market ) * parseFloat ( fiat.price ) * ( currency [ 'percent_change_1h' ] ? parseFloat ( currency [ 'percent_change_1h' ]) : parseFloat ( currency [ 'percent_change_24h' ]))).toFixed ( 2 ) : null ,
40 |
41 | supply : {
42 | available : currency [ 'available_supply' ] ? parseFloat ( currency [ 'available_supply' ]).toFixed ( 2 ) : null ,
43 | total : currency [ 'total_supply' ] ? parseFloat ( currency [ 'total_supply' ]).toFixed ( 2 ) : null
44 | } ,
45 |
46 | symbol : currency [ 'symbol' ] ,
47 | updated : new Date ( parseInt ( currency [ 'last_updated' ])).toLocaleDateString () ,
48 |
49 | volume : fiat.volume ? parseFloat ( fiat.volume ).toFixed ( 2 ) : null
50 | };
51 | });
52 | }
53 | };
--------------------------------------------------------------------------------
/schematics/graphs.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | export default {
4 |
5 | /**
6 | * Re-writes the API response to our prefferd data schema.
7 | *
8 | * @param {Array} data The graph results from the API we are calling.
9 | */
10 | get ( data ) {
11 |
12 | return {
13 |
14 | market : {
15 | cap : data [ 'market_cap_by_available_supply' ] ,
16 | volume : data [ 'volume_usd' ]
17 | } ,
18 |
19 | prices : {
20 | btc : data [ 'price_btc' ] ,
21 | usd : data [ 'price_usd' ]
22 | }
23 | }
24 |
25 | }
26 |
27 | };
--------------------------------------------------------------------------------
/schematics/news.js:
--------------------------------------------------------------------------------
1 |
2 | import { parseString } from 'react-native-xml2js';
3 |
4 | export default {
5 |
6 | get ( xml ) {
7 |
8 | let data;
9 |
10 | parseString (
11 | xml ,
12 | {
13 | emptyTag : null ,
14 | explicitArray : false ,
15 | normalize : true ,
16 | normalizeTags : true
17 | } ,
18 | ( error , json ) => data = json.rss.channel.item
19 | );
20 |
21 | return data;
22 | }
23 | };
--------------------------------------------------------------------------------
/screens/bull.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { Platform ,
5 | ScrollView ,
6 | Share ,
7 | View } from 'react-native';
8 | import { Ionicons } from '@expo/vector-icons';
9 | import Error from '../components/errors/ajax';
10 | import Action from '../components/utilities/header-action';
11 | import Button from '../components/utilities/button';
12 | import Loader from '../components/utilities/loader';
13 | import Header from '../components/bull/header';
14 | import Overview from '../components/bull/overview';
15 | import NotFound from '../components/bull/404';
16 | import Refresh from '../components/bull/refresh';
17 | import actions from '../actions/currencies';
18 | import scene from '../styles/scene';
19 | import style from '../styles/bull';
20 | import api from '../api/currencies';
21 | import analytics from '../utilities/analytics';
22 | import application from '../configuration/application';
23 |
24 | export default connect (
25 |
26 | state => ({
27 | bull : state.bull ,
28 | currency : state.currency ,
29 | language : state.language ,
30 | theme : state.theme
31 | })
32 |
33 | ) ( class Bull extends React.Component {
34 |
35 | static navigationOptions = ({ navigation , screenProps }) => {
36 |
37 | const language = screenProps.language ,
38 | theme = screenProps.theme ;
39 |
40 | return {
41 | headerLeft : {
44 |
45 | const platform = Platform.OS;
46 |
47 | analytics.event ( 'cryptobullography' , 'share' , 'open' , platform );
48 | Share.share (
49 | {
50 | message : language.screens.share.summary ,
51 | title : language.screens.share.title ,
52 | url : application.store ()
53 | } ,
54 | {
55 | dialogTitle : language.screens.share.title ,
56 | tintColor : theme.chrome
57 | }
58 | )
59 | .then (() => analytics.event ( 'cryptobullography' , 'share' , 'success' , platform ))
60 | .catch (( error ) => analytics.event ( 'cryptobullography' , 'share' , 'error' , platform ));
61 | }}
62 | value = { language.actions.share }
63 | /> ,
64 | headerRight : ,
65 | headerTitle : ,
66 | tabBarIcon : ({ focused }) => {
67 |
68 | return (
69 |
74 | );
75 | } ,
76 | title : language.screens.bull.title
77 | };
78 | };
79 |
80 | constructor ( props ) {
81 | super ( props );
82 |
83 | this.refresh = this.refresh.bind ( this );
84 | }
85 |
86 | refresh () {
87 |
88 | const action = this.props.bull.competitors > api.limit ? 'stream' : 'get';
89 |
90 | analytics.event (
91 | 'bull' ,
92 | 'load' ,
93 | action ,
94 | 'application'
95 | );
96 | this.props.dispatch ( actions [ action ] ( this.props.currency.id ));
97 | }
98 |
99 | render () {
100 |
101 | const language = this.props.language ,
102 | theme = this.props.theme ,
103 | scenery = scene ( theme ) ,
104 | appearance = style ( theme ) ;
105 |
106 | if ( this.props.bull.loading ) {
107 |
108 | return (
109 |
110 |
115 |
116 | );
117 | }
118 |
119 | if ( this.props.bull.error ) {
120 |
121 | analytics.screen ( 'bull:500' );
122 | return (
123 |
130 | );
131 |
132 | }
133 |
134 | if ( this.props.bull.rating === 0 ) {
135 |
136 | analytics.screen ( 'bull:404' );
137 | return (
138 |
143 | );
144 | }
145 |
146 | return (
147 |
148 |
154 |
155 |
168 |
169 | );
170 | }
171 | });
172 |
--------------------------------------------------------------------------------
/screens/currency.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { ScrollView ,
5 | Text ,
6 | TouchableOpacity } from 'react-native';
7 | import { Ionicons } from '@expo/vector-icons';
8 | import Back from '../components/utilities/back';
9 | import currencies from '../properties/currencies.js';
10 | import style from '../styles/list-control';
11 | import scene from '../styles/scene';
12 | import strings from '../utilities/string';
13 | import actions from '../actions/currency';
14 | import analytics from '../utilities/analytics';
15 |
16 | export default connect (
17 |
18 | state => ({
19 | currency : state.currency ,
20 | language : state.language ,
21 | theme : state.theme
22 | })
23 |
24 | ) ( class Currency extends React.Component {
25 |
26 | static navigationOptions = ({ navigation , screenProps }) => {
27 |
28 | const language = screenProps.language ,
29 | theme = screenProps.theme ;
30 |
31 | return {
32 | headerLeft : navigation.goBack ()}
34 | value = { language.actions.return }
35 | /> ,
36 | title : strings.capitalise ( language.screens.currency.title )
37 | };
38 | };
39 |
40 | currencies () {
41 |
42 | const current = this.props.currency ,
43 | language = this.props.language ,
44 | theme = this.props.theme ,
45 | appearance = style ( theme ) ;
46 |
47 | return currencies.map (( currency , index ) => {
48 |
49 | const icon = currency.id === current.id ? 'ios-radio-button-on-outline' : 'ios-radio-button-off-outline' ,
50 | background = index % 2 === 0 ? theme.primary : theme.base;
51 |
52 | return (
53 | {
56 |
57 | analytics.event ( 'currency' , 'set' , currency.names [ 'en' ]);
58 | this.props.dispatch ( actions.save ( currency.id ));
59 | }}
60 | style = {{
61 | ...appearance.control ,
62 | ...{
63 | backgroundColor : background
64 | }
65 | }}
66 | >
67 |
68 | { currency.names [ language.id ]}
69 |
70 |
75 |
76 | );
77 | });
78 | }
79 |
80 | render () {
81 |
82 | const theme = this.props.theme ,
83 | scenery = scene ( theme ) ;
84 |
85 | return (
86 |
87 | { this.currencies ()}
88 |
89 | );
90 | }
91 | });
92 |
--------------------------------------------------------------------------------
/screens/donate.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Clipboard ,
4 | Linking ,
5 | ScrollView ,
6 | TouchableOpacity ,
7 | Text ,
8 | View } from 'react-native';
9 | import { connect } from 'react-redux';
10 | import { Ionicons } from '@expo/vector-icons';
11 | import Back from '../components/utilities/back';
12 | import Button from '../components/utilities/button';
13 | import Code from '../components/utilities/code';
14 | import Heading from '../components/utilities/headings';
15 | import scene from '../styles/scene';
16 | import style from '../styles/help';
17 | import analytics from '../utilities/analytics';
18 |
19 | export default connect (
20 |
21 | state => ({
22 | language : state.language ,
23 | theme : state.theme
24 | })
25 |
26 | ) ( class Donate extends React.Component {
27 |
28 | static navigationOptions = ({ navigation , screenProps }) => {
29 |
30 | const language = screenProps.language ,
31 | theme = screenProps.theme ;
32 |
33 | return {
34 | title : language.screens.donate.title ,
35 | headerLeft : navigation.goBack ()}
37 | theme = { theme }
38 | value = { language.actions.return }
39 | />
40 | };
41 | };
42 |
43 | wallets () {
44 |
45 | const language = this.props.language ,
46 | theme = this.props.theme ;
47 |
48 | return language.screens.donate.wallets.map (( wallet , index ) => {
49 |
50 | const label = language.screens.donate.action.replace ( '{{placeholder}}' , wallet.name );
51 |
52 | return (
53 |
54 |
59 |
63 |
74 | );
75 | });
76 | }
77 |
78 | render () {
79 |
80 | const language = this.props.language ,
81 | theme = this.props.theme ,
82 | scenery = scene ( theme ) ,
83 | appearance = style ( theme ) ;
84 |
85 | return (
86 |
87 |
92 |
93 |
94 | { language.screens.donate.body }
95 |
96 | { this.wallets ()}
97 |
98 |
99 | );
100 | }
101 | });
102 |
--------------------------------------------------------------------------------
/screens/exchanges.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { Linking ,
5 | ScrollView ,
6 | Text ,
7 | TouchableOpacity ,
8 | View } from 'react-native';
9 | import { Ionicons } from '@expo/vector-icons';
10 | import Back from '../components/utilities/back';
11 | import scene from '../styles/scene';
12 | import exchanges from '../properties/exchanges';
13 | import style from '../styles/list-control';
14 | import analytics from '../utilities/analytics';
15 |
16 | export default connect (
17 |
18 | state => ({
19 | language : state.language ,
20 | theme : state.theme
21 | })
22 |
23 | ) ( class Exchanges extends React.Component {
24 |
25 | static navigationOptions = ({ navigation , screenProps }) => {
26 |
27 | const language = screenProps.language ,
28 | theme = screenProps.theme ;
29 |
30 | return {
31 | title : language.screens.exchanges.title ,
32 | headerLeft : navigation.goBack ()}
34 | theme = { theme }
35 | value = { language.actions.return }
36 | />
37 | // tabBarIcon : ({ focused }) => {
38 |
39 | // return (
40 | //
45 | // );
46 | // }
47 | };
48 | };
49 |
50 | exchanges () {
51 |
52 | const theme = this.props.theme;
53 |
54 | return exchanges.map (( exchange , index ) => {
55 |
56 | const background = index % 2 === 0 ? theme.primary : theme.base ,
57 | appearance = style ( theme );
58 |
59 | return (
60 | {
63 |
64 | analytics.event ( 'exchange' , 'open' , exchange.name );
65 | Linking.openURL ( exchange.url );
66 | }}
67 | style = {{
68 | ...appearance.control ,
69 | ...{
70 | backgroundColor : background
71 | }
72 | }}
73 | >
74 |
75 | { exchange.name }
76 |
77 |
82 |
83 | );
84 | });
85 | }
86 |
87 | render () {
88 |
89 | const theme = this.props.theme ,
90 | scenery = scene ( theme ) ;
91 |
92 | return (
93 |
94 | { this.exchanges ()}
95 |
96 | );
97 | }
98 | });
99 |
--------------------------------------------------------------------------------
/screens/language.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Linking ,
4 | ScrollView ,
5 | TouchableOpacity ,
6 | Text ,
7 | View } from 'react-native';
8 | import { connect } from 'react-redux';
9 | import { Ionicons } from '@expo/vector-icons';
10 | import application from '../configuration/application';
11 | import Back from '../components/utilities/back';
12 | import Button from '../components/utilities/button';
13 | import Heading from '../components/utilities/headings';
14 | import scene from '../styles/scene';
15 | import style from '../styles/help';
16 | import strings from '../utilities/string';
17 | import analytics from '../utilities/analytics';
18 |
19 | export default connect (
20 |
21 | state => ({
22 | language : state.language ,
23 | theme : state.theme
24 | })
25 |
26 | ) ( class Translations extends React.Component {
27 |
28 | static navigationOptions = ({ navigation , screenProps }) => {
29 |
30 | const language = screenProps.language ,
31 | theme = screenProps.theme ;
32 |
33 | return {
34 | title : strings.capitalise ( language.screens.translations.title ) ,
35 | headerLeft : navigation.goBack ()}
37 | theme = { theme }
38 | value = { language.actions.return }
39 | />
40 | };
41 | };
42 |
43 | constructor ( props ) {
44 | super ( props );
45 |
46 | this.translations = this.translations.bind ( this );
47 | }
48 |
49 | translations () {
50 |
51 | const language = this.props.language;
52 |
53 | analytics.event ( 'language' , 'send' , 'email' );
54 | Linking.openURL (
55 | 'mailto://' + application.email + '?subject=Translation Request&body=' + JSON.stringify (
56 | language ,
57 | null ,
58 | '\t'
59 | ));
60 | }
61 |
62 | render () {
63 |
64 | const language = this.props.language ,
65 | theme = this.props.theme ,
66 | scenery = scene ( theme ) ,
67 | appearance = style ( theme ) ;
68 |
69 | return (
70 |
71 |
76 |
77 |
78 | { language.screens.translations.body }
79 |
80 |
85 |
86 |
87 | );
88 | }
89 | });
90 |
--------------------------------------------------------------------------------
/screens/languages.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { ScrollView ,
5 | Text ,
6 | TouchableOpacity } from 'react-native';
7 | import { Ionicons } from '@expo/vector-icons';
8 | import Back from '../components/utilities/back';
9 | import actions from '../actions/language';
10 | import languages from '../properties/languages';
11 | import scene from '../styles/scene';
12 | import style from '../styles/list-control';
13 | import strings from '../utilities/string';
14 | import analytics from '../utilities/analytics';
15 |
16 | export default connect (
17 |
18 | state => ({
19 | language : state.language ,
20 | theme : state.theme
21 | })
22 |
23 | ) ( class Language extends React.Component {
24 |
25 | static navigationOptions = ({ navigation , screenProps }) => {
26 |
27 | const language = screenProps.language;
28 |
29 | return {
30 | headerLeft : navigation.goBack ()}
32 | value = { language.actions.return }
33 | /> ,
34 | title : strings.capitalise ( language.screens.language.title )
35 |
36 | };
37 | };
38 |
39 | languages () {
40 |
41 | const current = this.props.language ,
42 | theme = this.props.theme ,
43 | appearance = style ( theme ) ;
44 |
45 | return Object.keys ( languages ).map (( language , index ) => {
46 |
47 | const icon = language === current.id ? 'ios-radio-button-on-outline' : 'ios-radio-button-off-outline' ,
48 | background = index % 2 === 0 ? theme.primary : theme.base;
49 |
50 | return (
51 | {
54 |
55 | analytics.event ( 'languages' , 'set' , language );
56 | this.props.dispatch ( actions.save ( language ));
57 | }}
58 | style = {{
59 | ...appearance.control ,
60 | ...{
61 | backgroundColor : background
62 | }
63 | }}
64 | >
65 |
66 | { languages [ language ].names [ current.id ]}
67 |
68 |
73 |
74 | );
75 | });
76 | }
77 |
78 | render () {
79 |
80 | const theme = this.props.theme ,
81 | scenery = scene ( theme ) ;
82 |
83 | return (
84 |
85 | { this.languages ()}
86 |
87 | );
88 | }
89 | });
90 |
--------------------------------------------------------------------------------
/screens/main.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Platform ,
4 | StatusBar ,
5 | View } from 'react-native';
6 | import { connect } from 'react-redux';
7 | import Navigation from '../navigations/router';
8 | import styles from '../styles/main';
9 | import analytics from '../utilities/analytics';
10 | import routes from '../utilities/routes';
11 | import actions from '../actions/navigation';
12 |
13 | export default connect (
14 |
15 | state => ({
16 | language : state.language ,
17 | theme : state.theme
18 | })
19 |
20 | ) ( class Main extends React.Component {
21 |
22 | constructor ( props ) {
23 | super ( props );
24 |
25 | // Only fire the application load data once
26 | analytics.event ( 'cryptobullography' , 'loaded' );
27 | this.dimensions ();
28 |
29 | this.navigate = this.navigate.bind ( this );
30 | }
31 |
32 | dimensions () {
33 |
34 | // Everytime a theme or language is changed update the GA dimension
35 | analytics.dimension ( 'language' , this.props.language.names.en );
36 | analytics.dimension ( 'theme' , this.props.theme.names.en );
37 | }
38 |
39 | // When react-navigation is moved into redux we can remove this approach for screen tracking feature
40 | // And just use the middleware
41 | navigate ( last , next ) {
42 |
43 | const current = routes.name ( next ) ,
44 | previous = routes.name ( last ) ;
45 |
46 | this.props.dispatch ( actions.navigate ( previous , current ));
47 | }
48 |
49 | render () {
50 |
51 | const theme = this.props.theme ,
52 | language = this.props.language ,
53 | appearance = styles ( theme ) ;
54 |
55 | this.dimensions ();
56 | return (
57 |
58 | { Platform.OS === 'ios' && }
59 | { Platform.OS === 'android' && }
60 |
67 |
68 | );
69 | }
70 | });
71 |
--------------------------------------------------------------------------------
/screens/settings.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { connect } from 'react-redux';
4 | import { Linking ,
5 | Platform ,
6 | ScrollView ,
7 | Share ,
8 | Text ,
9 | TouchableOpacity ,
10 | View } from 'react-native';
11 | import { Ionicons } from '@expo/vector-icons';
12 | import Action from '../components/utilities/header-action';
13 | import scene from '../styles/scene';
14 | import style from '../styles/list-control';
15 | import application from '../configuration/application';
16 | import analytics from '../utilities/analytics';
17 |
18 | export default connect (
19 |
20 | state => ({
21 | language : state.language ,
22 | theme : state.theme
23 | })
24 |
25 | ) ( class Settings extends React.Component {
26 |
27 | static navigationOptions = ({ screenProps }) => {
28 |
29 | const language = screenProps.language ,
30 | theme = screenProps.theme ;
31 |
32 | return {
33 | headerLeft : {
36 |
37 | const platform = Platform.OS;
38 |
39 | analytics.event ( 'cryptobullography' , 'share' , 'open' , platform );
40 | Share.share (
41 | {
42 | message : language.screens.share.summary ,
43 | title : language.screens.share.title ,
44 | url : application.store ()
45 | } ,
46 | {
47 | dialogTitle : language.screens.share.title ,
48 | tintColor : theme.chrome
49 | }
50 | )
51 | .then (() => analytics.event ( 'cryptobullography' , 'share' , 'success' , platform ))
52 | .catch (( error ) => analytics.event ( 'cryptobullography' , 'share' , 'error' , platform ));
53 | }}
54 | value = { language.actions.share }
55 | /> ,
56 | title : language.screens.settings.title ,
57 | tabBarIcon : ({ focused }) => {
58 |
59 | return (
60 |
65 | );
66 |
67 | }
68 | };
69 | };
70 |
71 | settings () {
72 |
73 | const language = this.props.language;
74 |
75 | return [
76 | {
77 | name : language.screens.exchanges.title ,
78 | url : 'exchanges'
79 | } ,
80 | {
81 | name : language.screens.converter.title ,
82 | url : 'converter'
83 | } ,
84 | {
85 | name : language.screens.currency.title ,
86 | url : 'currency'
87 | } ,
88 |
89 | {
90 | name : language.screens.theme.title ,
91 | url : 'themes'
92 | } ,
93 |
94 | {
95 | name : language.screens.language.title ,
96 | url : 'languages'
97 | } ,
98 |
99 | {
100 | name : language.screens.themes.title ,
101 | url : 'theme'
102 | } ,
103 |
104 | {
105 | name : language.screens.translations.title ,
106 | url : 'language'
107 | } ,
108 |
109 | {
110 | name : language.screens.donate.title ,
111 | url : 'donate'
112 | }
113 | ];
114 | }
115 |
116 | contents () {
117 |
118 | const navigate = this.props.navigation.navigate ,
119 | theme = this.props.theme ,
120 | appearance = style ( theme ) ;
121 |
122 | return this.settings ().map (( setting , index ) => {
123 |
124 | const background = index % 2 === 0 ? theme.base : theme.primary;
125 |
126 | return (
127 | navigate ( setting.url )}
130 | style = {{
131 | ...appearance.control ,
132 | ...{
133 | backgroundColor : background
134 | }
135 | }}
136 | >
137 |
138 | { setting.name }
139 |
140 |
145 |
146 | );
147 | });
148 |
149 | }
150 |
151 | render () {
152 |
153 | const theme = this.props.theme ,
154 | scenery = scene ( theme ) ,
155 | appearance = style ( theme ) ;
156 |
157 | return (
158 |
159 | Linking.openURL ( application.cryptocoinminer ())}
161 | style = {{
162 | ...appearance.control ,
163 | ...{
164 | backgroundColor : theme.primary
165 | }
166 | }}
167 | >
168 |
169 | Crypto Coin Miner
170 |
171 |
176 |
177 | { this.contents ()}
178 |
179 | );
180 |
181 | }
182 | });
183 |
--------------------------------------------------------------------------------
/screens/theme.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Linking ,
4 | ScrollView ,
5 | TouchableOpacity ,
6 | Text ,
7 | View } from 'react-native';
8 | import { connect } from 'react-redux';
9 | import { Ionicons } from '@expo/vector-icons';
10 | import application from '../configuration/application';
11 | import Back from '../components/utilities/back';
12 | import Button from '../components/utilities/button';
13 | import Heading from '../components/utilities/headings';
14 | import scene from '../styles/scene';
15 | import style from '../styles/help';
16 | import strings from '../utilities/string';
17 | import analytics from '../utilities/analytics';
18 |
19 | export default connect (
20 |
21 | state => ({
22 | language : state.language ,
23 | theme : state.theme
24 | })
25 |
26 | ) ( class Theme extends React.Component {
27 |
28 | static navigationOptions = ({ navigation , screenProps }) => {
29 |
30 | const language = screenProps.language ,
31 | theme = screenProps.theme ;
32 |
33 | return {
34 | title : strings.capitalise ( language.screens.themes.title ) ,
35 | headerLeft : navigation.goBack ()}
37 | theme = { theme }
38 | value = { language.actions.return }
39 | />
40 | };
41 | };
42 |
43 | constructor ( props ) {
44 | super ( props );
45 |
46 | this.themes = this.themes.bind ( this );
47 | }
48 |
49 | themes () {
50 |
51 | const theme = this.props.theme;
52 |
53 | analytics.event ( 'theme' , 'send' , 'email' );
54 | Linking.openURL (
55 | 'mailto://' + application.email + '?subject=Theme Request&body=' + JSON.stringify (
56 | theme ,
57 | null ,
58 | '\t'
59 | ));
60 | }
61 |
62 | render () {
63 |
64 | const language = this.props.language ,
65 | theme = this.props.theme ,
66 | scenery = scene ( theme ) ,
67 | appearance = style ( theme ) ;
68 |
69 | return (
70 |
71 |
76 |
77 |
78 | { language.screens.themes.body }
79 |
80 |
85 |
86 |
87 | );
88 | }
89 | });
90 |
--------------------------------------------------------------------------------
/screens/themes.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { ScrollView ,
4 | Text ,
5 | TouchableOpacity } from 'react-native';
6 | import { connect } from 'react-redux';
7 | import { Ionicons } from '@expo/vector-icons';
8 | import Back from '../components/utilities/back';
9 | import actions from '../actions/theme';
10 | import themes from '../properties/themes';
11 | import scene from '../styles/scene';
12 | import style from '../styles/list-control';
13 | import strings from '../utilities/string';
14 | import analytics from '../utilities/analytics';
15 |
16 | export default connect (
17 |
18 | state => ({
19 | language : state.language ,
20 | theme : state.theme
21 | })
22 |
23 | ) ( class Themes extends React.Component {
24 |
25 | static navigationOptions = ({ navigation , screenProps }) => {
26 |
27 | const language = screenProps.language ,
28 | theme = screenProps.theme ;
29 |
30 | return {
31 | title : strings.capitalise ( language.screens.theme.title ) ,
32 | headerLeft : navigation.goBack ()}
34 | theme = { theme }
35 | value = { language.actions.return }
36 | />
37 | };
38 | };
39 |
40 | themes () {
41 |
42 | const current = this.props.theme ,
43 | language = this.props.language;
44 |
45 | return Object.keys ( themes ).map (( theme , index ) => {
46 |
47 | const icon = theme === current.id ? 'ios-radio-button-on-outline' : 'ios-radio-button-off-outline' ,
48 | background = index % 2 === 0 ? current.primary : current.base ,
49 | appearance = style ( current ) ;
50 |
51 | return (
52 | {
55 |
56 | analytics.event ( 'themes' , 'set' , theme );
57 | this.props.dispatch ( actions.save ( theme ));
58 | }}
59 | style = {{
60 | ...appearance.control ,
61 | ...{
62 | backgroundColor : background
63 | }
64 | }}
65 | >
66 |
67 | { themes [ theme ].names [ language.id ]}
68 |
69 |
74 |
75 | );
76 | });
77 | }
78 |
79 | render () {
80 |
81 | const theme = this.props.theme ,
82 | scenery = scene ( theme ) ;
83 |
84 | return (
85 |
86 | { this.themes ()}
87 |
88 | );
89 | }
90 | });
91 |
--------------------------------------------------------------------------------
/styles/adverts.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | export default ( theme ) => {
4 |
5 | return {
6 |
7 | body : {
8 | marginBottom : 5 ,
9 | paddingBottom : 5 ,
10 | paddingHorizontal : 15
11 | } ,
12 |
13 | button : {
14 |
15 | cta : {
16 | color : theme.secondary ,
17 | fontSize : 14 ,
18 | marginRight : 15
19 | } ,
20 |
21 | column : {
22 | flex : 1 ,
23 | flexDirection : 'column'
24 | } ,
25 |
26 | content : {
27 | backgroundColor : theme.chrome ,
28 | borderColor : theme.border ,
29 | borderRadius : 5 ,
30 | borderWidth : 1 ,
31 | flexDirection : 'row' ,
32 | padding : 5
33 | } ,
34 |
35 | description : {
36 | color : theme.body ,
37 | flex : 1 ,
38 | fontSize : 14 ,
39 | marginBottom : 4
40 | } ,
41 |
42 | icon : {
43 | borderColor : theme.border ,
44 | borderWidth : 1 ,
45 | height : 70 ,
46 | marginRight : 5 ,
47 | borderRadius : 5 ,
48 | width : 70
49 | } ,
50 |
51 | label : {
52 | color : theme.body ,
53 | fontSize : 12 ,
54 | marginBottom : 5 ,
55 | marginRight : 5 ,
56 | textAlign : 'right'
57 | } ,
58 |
59 | title : {
60 | color : theme.body ,
61 | flex : 1 ,
62 | fontSize : 18 ,
63 | fontWeight : 'bold' ,
64 | marginBottom : 3 ,
65 | paddingTop : 1
66 | } ,
67 |
68 | view : {
69 | flexDirection : 'column' ,
70 | marginBottom : 10
71 | }
72 | }
73 | };
74 | };
--------------------------------------------------------------------------------
/styles/bull.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | '404' : {
7 | backgroundColor : theme.base
8 | } ,
9 |
10 | '500' : {
11 | color : theme.negative ,
12 | borderColor : theme.negative ,
13 | borderRadius : 5 ,
14 | borderWidth : 1 ,
15 | fontSize : 21 ,
16 | fontWeight : 'bold' ,
17 | marginBottom : 25 ,
18 | marginTop : 15 ,
19 | padding : 15 ,
20 | textAlign : 'center'
21 | } ,
22 |
23 | button : {
24 | paddingHorizontal : 15
25 | } ,
26 |
27 | icon : {
28 | height : 32 ,
29 | marginLeft : 15 ,
30 | marginTop : 8 ,
31 | width : 32
32 | } ,
33 |
34 | notice : {
35 | color : theme.body ,
36 | paddingBottom : 15
37 | } ,
38 |
39 | rating : {
40 | color : theme.positive ,
41 | flexDirection : 'row' ,
42 | fontSize : 100 ,
43 | paddingBottom : 15 ,
44 | textAlign : 'center'
45 | } ,
46 |
47 | stripe : {
48 | backgroundColor : theme.primary
49 | } ,
50 |
51 | view : {
52 | backgroundColor : theme.base ,
53 | borderColor : theme.border ,
54 | borderTopWidth : 1 ,
55 | paddingBottom : 5 ,
56 | paddingHorizontal : 15 ,
57 | paddingTop : 15
58 | }
59 | };
60 | };
--------------------------------------------------------------------------------
/styles/button.js:
--------------------------------------------------------------------------------
1 |
2 | import color from '../utilities/colors'
3 |
4 | const common = {
5 |
6 | text : {
7 | fontSize : 18 ,
8 | fontWeight : 'bold'
9 | } ,
10 |
11 | view : {
12 | alignItems : 'center' ,
13 | borderRadius : 5 ,
14 | borderWidth : 1 ,
15 | marginBottom : 15 ,
16 | paddingHorizontal : 15 ,
17 | paddingVertical : 10
18 | }
19 | };
20 |
21 | export default ( theme ) => {
22 |
23 | return {
24 |
25 | primary : {
26 |
27 | text : {
28 | ...{
29 | color : theme.chrome
30 | } ,
31 | ...common.text
32 | } ,
33 |
34 | view : {
35 | ...{
36 | backgroundColor : theme.secondary ,
37 | borderColor : color.shade ( theme.secondary , -0.5 )
38 | } ,
39 | ...common.view
40 | }
41 | } ,
42 |
43 | secondary : {
44 |
45 | text : {
46 | ...{
47 | color : theme.body
48 | } ,
49 | ...common.text
50 | } ,
51 |
52 | view : {
53 | ...{
54 | backgroundColor : theme.primary ,
55 | borderColor : color.shade ( theme.primary , -0.5 )
56 | } ,
57 | ...common.view
58 | }
59 | } ,
60 |
61 | tertiary : {
62 |
63 | text : {
64 | ...{
65 | color : theme.body
66 | } ,
67 | ...common.text
68 | } ,
69 |
70 | view : {
71 | ...{
72 | backgroundColor : theme.negative ,
73 | borderColor : color.shade ( theme.negative , -0.5 )
74 | } ,
75 | ...common.view
76 | }
77 | }
78 | };
79 | };
--------------------------------------------------------------------------------
/styles/code.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 | text : {
6 | color : theme.body ,
7 | fontFamily : 'space-mono' ,
8 | fontSize : 12
9 | } ,
10 |
11 | view : {
12 | borderColor : theme.border ,
13 | borderRadius : 5 ,
14 | borderStyle : 'dotted' ,
15 | borderWidth : 1 ,
16 | marginBottom : 15 ,
17 | padding : 10
18 | }
19 | };
20 | };
--------------------------------------------------------------------------------
/styles/converter.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | icon : {
7 | backgroundColor : theme.primary ,
8 | flexDirection : 'column' ,
9 | justifyContent : 'center' ,
10 | paddingLeft : 7 ,
11 | paddingRight : 5
12 | } ,
13 |
14 | input : {
15 | backgroundColor : theme.base ,
16 | borderColor : theme.border ,
17 | borderRadius : 5 ,
18 | borderWidth : 1 ,
19 | color : theme.body ,
20 | height : 40 ,
21 | fontSize : 24 ,
22 | marginHorizontal : 15 ,
23 | paddingHorizontal : 5 ,
24 | textAlign : 'center'
25 | } ,
26 |
27 | picker : {
28 | borderWidth : 0
29 | } ,
30 |
31 | pickers : {
32 |
33 | column : {
34 | backgroundColor : theme.base ,
35 | borderColor : theme.border ,
36 | borderRadius : 5 ,
37 | borderWidth : 1 ,
38 | flex : 1 ,
39 | flexDirection : 'column' ,
40 | justifyContent : 'center'
41 | } ,
42 |
43 | view : {
44 | backgroundColor : theme.primary ,
45 | flex : 2 ,
46 | flexDirection : 'row'
47 | }
48 | } ,
49 |
50 | result : {
51 | backgroundColor : theme.primary ,
52 | borderWidth : 0 ,
53 | color : theme.disabled ,
54 | flex : 1 ,
55 | fontSize : 24 ,
56 | height : 40 ,
57 | textAlign : 'center'
58 | }
59 | };
60 | };
--------------------------------------------------------------------------------
/styles/currencies.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | 404 : {
7 |
8 | term : {
9 | fontWeight : 'bold'
10 | } ,
11 |
12 | text : {
13 | color : theme.body ,
14 | fontSize : 18
15 | } ,
16 |
17 | view : {
18 | paddingHorizontal : 10 ,
19 | paddingVertical : 20
20 | }
21 | } ,
22 |
23 | head : {
24 | alignItems : 'center' ,
25 | flex : 2 ,
26 | flexDirection : 'row'
27 | } ,
28 |
29 | icon : {
30 | height : 24 ,
31 | marginRight : 10 ,
32 | width : 24
33 | } ,
34 |
35 | name : {
36 | flex : 1 ,
37 | fontWeight : 'bold'
38 | } ,
39 |
40 | price : {
41 | textAlign : 'right'
42 | } ,
43 |
44 | text : {
45 | textAlign : 'right'
46 | }
47 | };
48 | };
--------------------------------------------------------------------------------
/styles/detail.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | button : {
7 | backgroundColor : theme.primary ,
8 | paddingTop : 15 ,
9 | paddingHorizontal : 15
10 | } ,
11 |
12 | icon : {
13 | height : 32 ,
14 | marginLeft : 15 ,
15 | marginTop : 8 ,
16 | width : 32
17 | }
18 | };
19 | };
--------------------------------------------------------------------------------
/styles/errors.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | export default ( theme ) => {
4 |
5 | return {
6 | ajax : {
7 |
8 | text : {
9 | color : theme.body ,
10 | fontSize : 16 ,
11 | textAlign : 'center'
12 | } ,
13 |
14 | view : {
15 | alignItems : 'center' ,
16 | backgroundColor : theme.base ,
17 | borderColor : theme.border ,
18 | borderBottomWidth : 1 ,
19 | borderTopWidth : 1 ,
20 | flex : 1 ,
21 | flexDirection : 'column' ,
22 | justifyContent : 'center' ,
23 | paddingBottom : 25 ,
24 | paddingHorizontal : 15 ,
25 | paddingTop : 15
26 | }
27 | }
28 | };
29 |
30 | };
--------------------------------------------------------------------------------
/styles/graphs.js:
--------------------------------------------------------------------------------
1 |
2 | import device from '../properties/device';
3 |
4 | const height = Math.round ( device.height / 3 ) + 8;
5 |
6 | export default ( theme ) => {
7 |
8 | return {
9 |
10 | tree : {
11 |
12 | axis : {
13 |
14 | y : {
15 |
16 | cell : {
17 | flex : 1 ,
18 | flexDirection : 'column' ,
19 | justifyContent : 'center'
20 | } ,
21 |
22 | text : {
23 | color : theme.body ,
24 | fontSize : 10 ,
25 | padding : 2 ,
26 | textAlign : 'right'
27 | } ,
28 |
29 | view : {
30 | borderColor : theme.border ,
31 | borderBottomWidth : 1 ,
32 | borderRightWidth : 1 ,
33 | borderTopWidth : 1 ,
34 | height : height ,
35 | flexDirection : 'column' ,
36 | paddingLeft : 10 ,
37 | paddingRight : 5
38 | }
39 | }
40 | } ,
41 |
42 | bar : {
43 |
44 | highlight : {
45 | backgroundColor : theme.primary ,
46 | borderColor : theme.border ,
47 | borderRadius : 5 ,
48 | borderWidth : 1 ,
49 | width : 10
50 | } ,
51 |
52 | view : {
53 | borderBottomWidth : 1 ,
54 | borderColor : theme.border ,
55 | borderTopWidth : 1 ,
56 | flex : 1 ,
57 | flexDirection : 'column' ,
58 | justifyContent : 'flex-end' ,
59 | paddingHorizontal : 1 ,
60 | paddingVertical : 2
61 | }
62 | } ,
63 |
64 | chart : {
65 | flex : 1 ,
66 | height : height
67 | } ,
68 |
69 | loading : {
70 | flex : 1 ,
71 | flexDirection : 'column' ,
72 | height : height ,
73 | justifyContent : 'center' ,
74 | paddingVertical : 15
75 | } ,
76 |
77 | section : {
78 |
79 | text : {
80 | color : theme.body ,
81 | bottom : - ( Math.round ( height / 2.3 )) ,
82 | fontSize : 14 ,
83 | fontWeight : 'bold' ,
84 | left : - ( Math.round ( height / 2.225 )) ,
85 | transform : [{
86 | rotate : '270deg'
87 | }] ,
88 | width : height
89 | } ,
90 |
91 | view : {
92 | backgroundColor : theme.primary ,
93 | borderLeftWidth : 1 ,
94 | borderRightWidth : 1 ,
95 | borderColor : theme.border ,
96 | height : height ,
97 | width : 25
98 | }
99 | } ,
100 |
101 | view : {
102 | flexDirection : 'row' ,
103 | flex : 1
104 | }
105 | }
106 | };
107 | };
--------------------------------------------------------------------------------
/styles/header.js:
--------------------------------------------------------------------------------
1 |
2 | //import theme from '../configuration/palette';
3 |
4 | export default ( theme ) => {
5 |
6 | return {
7 |
8 | back : {
9 |
10 | control : {
11 | alignItems : 'center' ,
12 | flexDirection : 'row' ,
13 | paddingLeft : 10
14 | } ,
15 |
16 | text : {
17 | color : theme.secondary ,
18 | fontSize : 18 ,
19 | fontWeight : 'normal' ,
20 | paddingLeft : 5
21 | }
22 |
23 | } ,
24 |
25 | left : {
26 |
27 | control : {
28 | alignItems : 'center' ,
29 | flexDirection : 'row' ,
30 | paddingLeft : 5 ,
31 | paddingRight : 10
32 | } ,
33 |
34 | text : {
35 | color : theme.secondary ,
36 | fontSize : 18 ,
37 | fontWeight : 'normal' ,
38 | paddingLeft : 5
39 | }
40 | } ,
41 |
42 | right : {
43 |
44 | control : {
45 | alignItems : 'center' ,
46 | flexDirection : 'row' ,
47 | paddingRight : 10
48 | } ,
49 |
50 | text : {
51 | color : theme.secondary ,
52 | fontSize : 18 ,
53 | fontWeight : 'normal' ,
54 | paddingLeft : 5
55 | } ,
56 |
57 | icon : {
58 | marginHorizontal : 15 ,
59 | marginTop : 2
60 | }
61 | } ,
62 |
63 | header : {
64 | backgroundColor : theme.chrome ,
65 | borderBottomColor : theme.chrome ,
66 | elevation : 0
67 | } ,
68 |
69 | icon : {
70 | height : 16 ,
71 | marginRight : 10 ,
72 | width : 16
73 | } ,
74 |
75 | title : {
76 | alignSelf : 'center' ,
77 | color : theme.body ,
78 | fontSize : 18 ,
79 | fontWeight : 'normal' ,
80 | textAlign : 'center'
81 | }
82 | };
83 | };
--------------------------------------------------------------------------------
/styles/headings.js:
--------------------------------------------------------------------------------
1 |
2 | import colour from '../utilities/colors';
3 |
4 | export default ( theme ) => {
5 |
6 | return {
7 |
8 | // H1
9 | 1 : {
10 | color : colour.shade ( theme.body , -0.25 ) ,
11 | fontSize : 21 ,
12 | fontWeight : 'bold' ,
13 | paddingHorizontal : 15 ,
14 | paddingVertical : 12
15 |
16 | } ,
17 |
18 | // H2
19 | 2 : {
20 |
21 | color : colour.shade ( theme.body , -0.25 ) ,
22 | fontSize : 18 ,
23 | fontWeight : 'bold' ,
24 | marginBottom : 12
25 |
26 | }
27 | };
28 |
29 | };
--------------------------------------------------------------------------------
/styles/help.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | body : {
7 | paddingBottom : 15 ,
8 | paddingHorizontal : 15
9 | } ,
10 |
11 | text : {
12 | color : theme.body ,
13 | fontSize : 16 ,
14 | marginBottom : 15
15 | }
16 | };
17 | };
--------------------------------------------------------------------------------
/styles/integer.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | default : {
7 | fontWeight : 'bold'
8 | } ,
9 |
10 | negative : {
11 | color : theme.negative
12 | } ,
13 |
14 | positive : {
15 | color : theme.positive
16 | }
17 | };
18 | };
--------------------------------------------------------------------------------
/styles/layout.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | center : {
7 | flex : 1 ,
8 | flexDirection : 'column' ,
9 | justifyContent : 'center'
10 | } ,
11 |
12 | fill : {
13 | flex : 1
14 | } ,
15 |
16 | row : {
17 | flexDirection : 'row'
18 | }
19 | };
20 |
21 | };
--------------------------------------------------------------------------------
/styles/list-control.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | control : {
7 | borderColor : theme.border ,
8 | borderBottomWidth : 1 ,
9 | flex : 1 ,
10 | flexDirection : 'row' ,
11 | justifyContent : 'space-between' ,
12 | padding : 15
13 | } ,
14 |
15 | text : {
16 | color : theme.body ,
17 | fontSize : 16
18 | }
19 | };
20 | };
--------------------------------------------------------------------------------
/styles/list.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | body : {
7 | flex : 1
8 | } ,
9 |
10 | cell : {
11 | flex : 1 ,
12 | paddingHorizontal : 2 ,
13 | paddingVertical : 10
14 | } ,
15 |
16 | 'cell-text' : {
17 | color : theme.body ,
18 | fontSize : 14
19 | } ,
20 |
21 | head : {
22 | backgroundColor : theme.chrome ,
23 | borderBottomColor : theme.border ,
24 | borderBottomWidth : 1 ,
25 | flexDirection : 'row' ,
26 | paddingHorizontal : 10
27 | } ,
28 |
29 | 'head-text' : {
30 | color : theme.secondary ,
31 | flex : 1 ,
32 | fontSize : 16
33 | } ,
34 |
35 | row : {
36 | alignItems : 'center' ,
37 | flex : 1 ,
38 | flexDirection : 'row' ,
39 | paddingHorizontal : 8
40 | }
41 | };
42 | };
--------------------------------------------------------------------------------
/styles/loader.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | alignItems : 'center' ,
7 | flex : 1 ,
8 | flexDirection : 'column' ,
9 | justifyContent : 'center'
10 |
11 | };
12 | };
--------------------------------------------------------------------------------
/styles/main.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | main : {
7 | backgroundColor : theme.primary ,
8 | flex : 1
9 | } ,
10 |
11 | statusbar : {
12 | backgroundColor : theme.primary ,
13 | height : 24
14 | }
15 | }
16 | };
--------------------------------------------------------------------------------
/styles/modal.js:
--------------------------------------------------------------------------------
1 |
2 | import device from '../properties/device';
3 |
4 | export default ( theme ) => {
5 |
6 | return {
7 |
8 | field : {
9 | flexDirection : 'row' ,
10 | marginBottom : 15
11 | } ,
12 |
13 | input : {
14 | borderColor : theme.border ,
15 | borderWidth : 1 ,
16 | borderRadius : 5 ,
17 | color : theme.body ,
18 | flex : 1 ,
19 | fontSize : 14 ,
20 | height : 40 ,
21 | padding : 5 ,
22 | textAlign : 'right'
23 | } ,
24 |
25 | label : {
26 |
27 | text : {
28 | fontSize : 14 ,
29 | color : theme.body ,
30 | textAlign : 'right'
31 | } ,
32 |
33 | view : {
34 | flexDirection : 'column' ,
35 | justifyContent : 'center' ,
36 | paddingRight : 10
37 | }
38 | } ,
39 |
40 | overlay : {
41 | backgroundColor : 'rgba( 51 , 51 , 51 , 0.8 )' ,
42 | flexDirection : 'column' ,
43 | height : device.height ,
44 | justifyContent : 'center'
45 | } ,
46 |
47 | text : {
48 | fontSize : 14 ,
49 | color : theme.body ,
50 | marginBottom : 15
51 | } ,
52 |
53 | view : {
54 | backgroundColor : theme.base ,
55 | borderColor : theme.border ,
56 | borderRadius : 5 ,
57 | borderWidth : 1 ,
58 | marginHorizontal : 15 ,
59 | paddingHorizontal : 15 ,
60 | paddingTop : 15
61 | }
62 | };
63 | };
--------------------------------------------------------------------------------
/styles/news.js:
--------------------------------------------------------------------------------
1 |
2 | import colour from '../utilities/colors';
3 |
4 | export default ( theme ) => {
5 |
6 | return {
7 |
8 | date : {
9 | color : theme.disabled ,
10 | fontSize : 14 ,
11 | marginBottom : 15
12 | } ,
13 |
14 | description : {
15 | color : theme.body ,
16 | fontSize : 16 ,
17 | marginBottom : 13
18 | } ,
19 |
20 | icon : {
21 | flexDirection : 'row'
22 | } ,
23 |
24 | link : {
25 | color : theme.secondary ,
26 | flex : 1 ,
27 | fontSize : 14 ,
28 | marginRight : 5 ,
29 | paddingTop : 1 ,
30 | textAlign : 'right'
31 | } ,
32 |
33 | title : {
34 | color : colour.shade ( theme.body , -0.25 ) ,
35 | fontSize : 18 ,
36 | fontWeight : 'bold' ,
37 | marginBottom : 5
38 | } ,
39 |
40 | view : {
41 | padding : 10
42 | }
43 |
44 | };
45 | };
--------------------------------------------------------------------------------
/styles/notification.js:
--------------------------------------------------------------------------------
1 |
2 | import colors from '../utilities/colors'
3 |
4 | export default ( theme ) => {
5 |
6 | return {
7 |
8 | default : {
9 | borderWidth : 1 ,
10 | borderRadius : 3 ,
11 | marginHorizontal : 15 ,
12 | marginVertical : 15 ,
13 | paddingHorizontal : 15 ,
14 | paddingVertical : 10
15 |
16 | } ,
17 |
18 | notification : {
19 | backgroundColor : theme.positive ,
20 | borderColor : colors.shade ( theme.positive , -0.15 ) ,
21 | }
22 | };
23 | };
--------------------------------------------------------------------------------
/styles/portfolio.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | 404 : {
7 | text : {
8 | color : theme.body ,
9 | fontSize : 14
10 | } ,
11 |
12 | view : {
13 | padding : 15
14 | }
15 | } ,
16 |
17 | cell : {
18 | textAlign : 'right'
19 | } ,
20 |
21 | missing : {
22 |
23 | icon : {
24 | marginLeft : 15 ,
25 | marginTop : 2
26 | } ,
27 |
28 | row : {
29 | alignItems : 'center' ,
30 | flexDirection : 'row' ,
31 | justifyContent : 'center'
32 | } ,
33 |
34 | text : {
35 | color : theme.body ,
36 | flex : 1 ,
37 | fontSize : 14
38 | } ,
39 |
40 | view : {
41 | alignItems : 'center' ,
42 | flexDirection : 'row' ,
43 | justifyContent : 'center' ,
44 | paddingHorizontal : 10
45 | }
46 | } ,
47 |
48 | head : {
49 | alignItems : 'center' ,
50 | flex : 2 ,
51 | flexDirection : 'row'
52 | } ,
53 |
54 | icon : {
55 | height : 24 ,
56 | marginRight : 10 ,
57 | width : 24
58 | } ,
59 |
60 | name : {
61 | flex : 1 ,
62 | fontWeight : 'bold'
63 | } ,
64 |
65 | total : {
66 |
67 | col : {
68 | color : theme.body ,
69 | flex : 3 ,
70 | fontSize : 14 ,
71 | fontWeight : 'bold' ,
72 | textAlign : 'right'
73 | } ,
74 |
75 | head : {
76 | color : theme.body ,
77 | flex : 1 ,
78 | fontSize : 14 ,
79 | fontWeight : 'bold' ,
80 | paddingLeft : 20
81 | } ,
82 |
83 | view : {
84 | backgroundColor : theme.chrome ,
85 | borderTopColor : theme.border ,
86 | borderTopWidth : 1 ,
87 | paddingHorizontal : 10 ,
88 | paddingVertical : 15 ,
89 | flexDirection : 'row'
90 | }
91 | }
92 | };
93 | };
--------------------------------------------------------------------------------
/styles/push-notifications.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | container : {
7 | backgroundColor : theme.primary
8 | } ,
9 |
10 | text : {
11 | color : theme.body
12 | }
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/styles/scene.js:
--------------------------------------------------------------------------------
1 |
2 | import device from '../properties/device';
3 |
4 | export default ( theme ) => {
5 |
6 | return {
7 |
8 | body : {
9 | backgroundColor : theme.base ,
10 | borderTopWidth : 1 ,
11 | borderTopColor : theme.border ,
12 | flex : 1 ,
13 | flexDirection : 'column' ,
14 | height : device.height
15 | } ,
16 |
17 | header : {
18 | backgroundColor : theme.primary
19 | }
20 | };
21 | };
22 |
--------------------------------------------------------------------------------
/styles/search.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | close : {
7 | marginLeft : 5 ,
8 | marginRight : 10 ,
9 | marginTop : 3
10 | } ,
11 |
12 | refresh : {
13 | marginLeft : 10 ,
14 | marginRight : 5 ,
15 | marginTop : 3
16 | } ,
17 |
18 | field : {
19 | flex : 1 ,
20 | flexDirection : 'row' ,
21 | } ,
22 |
23 | icon : {
24 | marginHorizontal : 15
25 | } ,
26 |
27 | input : {
28 | backgroundColor : theme.base ,
29 | borderColor : theme.border ,
30 | borderRadius : 3 ,
31 | borderWidth : 1 ,
32 | color : theme.body ,
33 | flex : 1 ,
34 | fontSize : 14 ,
35 | margin : 5 ,
36 | paddingHorizontal : 5
37 | } ,
38 |
39 | view : {
40 | backgroundColor : theme.chrome ,
41 | borderBottomColor : theme.border ,
42 | borderBottomWidth : 1 ,
43 | height : 40
44 | }
45 | };
46 | };
--------------------------------------------------------------------------------
/styles/section.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | body : {
7 | borderColor : theme.border ,
8 | borderBottomWidth : 1 ,
9 | padding : 15
10 | } ,
11 |
12 | row : {
13 | color : theme.body ,
14 | paddingBottom : 3
15 | } ,
16 |
17 | stripe : {
18 | backgroundColor : theme.primary
19 | } ,
20 |
21 | view : {
22 | borderTopColor : theme.border ,
23 | borderTopWidth : 1
24 | }
25 | };
26 | };
--------------------------------------------------------------------------------
/styles/seperators.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 | backgroundColor : theme.border ,
6 | height : 1
7 | };
8 | };
--------------------------------------------------------------------------------
/styles/stripe.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | primary : {
7 | backgroundColor : theme.primary
8 | } ,
9 |
10 | secondary : {
11 | backgroundColor : theme.base
12 | }
13 | };
14 | };
--------------------------------------------------------------------------------
/styles/tabbar.js:
--------------------------------------------------------------------------------
1 |
2 | export default ( theme ) => {
3 |
4 | return {
5 |
6 | tabBar : {
7 | borderTopWidth : 1 ,
8 | borderTopColor : theme.border ,
9 | backgroundColor : theme.chrome ,
10 | flexDirection : 'row' ,
11 | height : 49
12 | } ,
13 | tab : {
14 | alignItems : 'stretch' ,
15 | flex : 1 ,
16 | justifyContent : 'flex-end'
17 | } ,
18 | icon : {
19 | flexGrow : 1
20 | } ,
21 | label : {
22 | backgroundColor : 'transparent' ,
23 | fontSize : 10 ,
24 | marginBottom : 1.5 ,
25 | textAlign : 'center'
26 | }
27 | }
28 |
29 | };
30 |
--------------------------------------------------------------------------------
/utilities/__tests__/cache.js:
--------------------------------------------------------------------------------
1 |
2 | describe ( 'example test' , () => {
3 |
4 | it ( 'works' , () => {
5 |
6 | expect ( 1 ).toBe ( 1 );
7 |
8 | });
9 |
10 | });
11 |
--------------------------------------------------------------------------------
/utilities/analytics.js:
--------------------------------------------------------------------------------
1 |
2 | import { Platform } from 'react-native';
3 | import { Amplitude ,
4 | Constants } from 'expo';
5 | import application from '../configuration/application';
6 | import strings from './string';
7 |
8 | let dimensions = {};
9 |
10 | const configuration = {
11 | agent : Platform.OS === 'ios' ? Constants.platform.ios.model : 'Android : ' + Constants.deviceName ,
12 | client : Constants.deviceId ,
13 | name : Constants.deviceName ,
14 | version : Platform.Version
15 | } ,
16 |
17 | amplitude = {
18 | uid : 'bdc55b3cbe9aa875abea6c2c4645b912' ,
19 | } ,
20 |
21 | google = {
22 | endpoint : '' ,
23 | options : {
24 | method : 'POST' ,
25 | headers : {
26 | 'User-Agent': configuration.agent ,
27 | }
28 | } ,
29 | uid : 'UA-105110895-1' ,
30 | url : 'https://www.google-analytics.com/collect' ,
31 | version : 1
32 | } ,
33 |
34 | error = ( error ) => {
35 |
36 | console.log ( 'Error in Google Analytics' );
37 | } ,
38 |
39 | parametise = () => {
40 |
41 | let data = '';
42 |
43 | Object.keys ( dimensions ).forEach (( value , index ) => {
44 | data += '&cd' + ( index + 1 ) + '=' + value + ':' + dimensions [ value ];
45 | });
46 |
47 | return data;
48 | };
49 |
50 | export default {
51 |
52 | setup () {
53 |
54 | if ( ! __DEV__ ) {
55 |
56 | // Setup Amplitude Tracking
57 | Amplitude.initialize ( amplitude.uid );
58 | Amplitude.setUserId ( configuration.client );
59 | Amplitude.setUserProperties ({
60 | agent : configuration.agent ,
61 | application : application.version ,
62 | name : configuration.name ,
63 | version : configuration.version
64 | });
65 |
66 | // Setup Google Analytics
67 | google.endpoint += google.url + '?v=' + google.version;
68 | google.endpoint += '&tid=' + google.uid;
69 | google.endpoint += '&cid=' + configuration.client;
70 | google.endpoint += '&z=' + Date.now ();
71 | google.endpoint += '&an=' + strings.datalise ( application.name );
72 | google.endpoint += '&av=' + application.version;
73 | }
74 | } ,
75 |
76 | dimension ( property , value ) {
77 |
78 | dimensions [ strings.datalise ( property )] = strings.datalise ( value );
79 | } ,
80 |
81 | event ( category , action , label , value ) {
82 |
83 | let parameters = '';
84 |
85 | if ( ! __DEV__ ) {
86 |
87 | parameters += label ? '&el=' + strings.datalise ( label ) : '';
88 | parameters += value ? '&cd' + ( Object.keys ( dimensions ).length + 1 ) + '=' + strings.datalise ( value ) : '';
89 |
90 | Amplitude.logEventWithProperties (
91 | strings.datalise ( category ) ,
92 | {
93 | ...dimensions ,
94 | action : strings.datalise ( action ) ,
95 | category : strings.datalise ( category ) ,
96 | label : strings.datalise ( label ) ,
97 | value : strings.datalise ( value )
98 | }
99 | );
100 |
101 | // Send analytics to Google
102 | fetch (
103 | encodeURI ( google.endpoint + '&t=event&ec=' + strings.datalise ( category ) + '&ea=' + strings.datalise ( action ) + parameters + parametise ()) ,
104 | google.options
105 | ).catch ( error );
106 | }
107 | } ,
108 |
109 | screen ( screen ) {
110 |
111 | if ( ! __DEV__ ) {
112 |
113 | // Send analytics to Amplitude
114 | Amplitude.logEventWithProperties (
115 | 'screen' ,
116 | {
117 | ...dimensions ,
118 | view : strings.datalise ( screen )
119 | }
120 | );
121 |
122 | // Send analytics to Google
123 | fetch (
124 | encodeURI ( google.endpoint + '&t=screenview&cd=' + strings.datalise ( screen ) + parametise ()) ,
125 | google.options
126 | ).catch ( error );
127 | }
128 | }
129 | };
--------------------------------------------------------------------------------
/utilities/array.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | alphabeticalise ( array , property ) {
5 |
6 | return array.sort (( a , b ) => {
7 |
8 | if ( a [ property ] < b [ property ]) {
9 |
10 | return -1;
11 | }
12 |
13 | if ( a [ property ] > b [ property ]) {
14 |
15 | return 1;
16 | }
17 |
18 | return 0;
19 | });
20 | } ,
21 |
22 | /**
23 | * Get the maximum value of a property from an array of objects.
24 | */
25 | max ( array , property ) {
26 |
27 | return Math.max.apply ( Math , array.map ( function ( currency ) {
28 |
29 | return currency [ property ];
30 |
31 | }));
32 | }
33 | };
--------------------------------------------------------------------------------
/utilities/cache.js:
--------------------------------------------------------------------------------
1 |
2 | import { Image } from 'react-native';
3 | import { Asset ,
4 | Font } from 'expo';
5 |
6 |
7 | const cache = {
8 |
9 | fonts ( fonts ) {
10 |
11 | return fonts.map ( font => Font.loadAsync ( font ));
12 | } ,
13 |
14 | images ( images ) {
15 |
16 | return images.map ( image => {
17 |
18 | if ( typeof image === 'string' ) {
19 |
20 | return Image.prefetch ( image );
21 | }
22 |
23 | else {
24 |
25 | return Asset.fromModule ( image ).downloadAsync ();
26 | }
27 | });
28 | }
29 | }
30 |
31 | export default {
32 |
33 | assets ({
34 | images = [] ,
35 | fonts = []
36 | }) {
37 |
38 | return Promise.all ([
39 | ...cache.images ( images ) ,
40 | ...cache.fonts ( fonts )
41 | ]);
42 | }
43 |
44 | };
45 |
--------------------------------------------------------------------------------
/utilities/colors.js:
--------------------------------------------------------------------------------
1 |
2 | // https://stackoverflow.com/a/13542669
3 | const blend = {
4 |
5 | rgb ( color , mixer , percent ) {
6 |
7 | let f = color.split ( ',' ) ,
8 | t = mixer.split ( ',' ) ,
9 | R = parseInt ( f [ 0 ].slice ( 4 )) ,
10 | G = parseInt ( f [ 1 ]) ,
11 | B = parseInt ( f [ 2 ]);
12 |
13 | return 'rgb(' + ( Math.round (( parseInt ( t [ 0 ].slice ( 4 )) - R ) * percent ) + R ) + ',' + ( Math.round (( parseInt ( t [ 1 ]) - G ) * percent ) + G ) + ',' + ( Math.round (( parseInt ( t [ 2 ]) - B ) * percent ) + B ) + ')';
14 |
15 | } ,
16 |
17 | hex ( color , mixer , percent ) {
18 |
19 | let f = parseInt ( color.slice ( 1 ) , 16 ) ,
20 | t = parseInt ( mixer.slice ( 1 ) , 16 ) ,
21 | R1 = f >> 16 ,
22 | G1 = f >> 8 & 0x00FF ,
23 | B1 = f & 0x0000FF ,
24 | R2 = t >> 16 ,
25 | G2 = t >> 8 & 0x00FF ,
26 | B2 = t & 0x0000FF;
27 |
28 | return '#' + ( 0x1000000 + ( Math.round (( R2 - R1 ) * percent ) + R1 ) * 0x10000 + ( Math.round (( G2 - G1 ) * percent ) + G1 ) * 0x100 + ( Math.round (( B2 - B1 ) * percent ) + B1 )).toString ( 16 ).slice ( 1 );
29 | }
30 |
31 | } ,
32 |
33 | shade = {
34 |
35 | rgb ( color , percent ) {
36 |
37 | let f = color.split ( ',' ) ,
38 | t = percent < 0 ? 0 : 255 ,
39 | p = percent < 0 ? percent * -1 : percent ,
40 | R = parseInt ( f [ 0 ].slice ( 4 )) ,
41 | G = parseInt ( f [ 1 ]) ,
42 | B = parseInt ( f [ 2 ]);
43 |
44 | return 'rgb(' + ( Math.round (( t - R ) * p ) + R ) + ',' + ( Math.round (( t - G ) * p ) + G ) + ',' + ( Math.round (( t - B ) * p ) + B ) + ')';
45 | } ,
46 |
47 | hex ( color , percent ) {
48 |
49 | const f = parseInt ( color.slice ( 1 ) , 16 ) ,
50 | t = percent < 0 ? 0 : 255 ,
51 | p = percent < 0 ? percent * -1 : percent ,
52 | R = f >> 16 ,
53 | G = f >> 8 & 0x00FF ,
54 | B = f & 0x0000FF;
55 |
56 | return '#' + ( 0x1000000 + ( Math.round (( t - R ) * p ) + R ) * 0x10000 + ( Math.round (( t - G ) * p ) + G ) * 0x100 + ( Math.round (( t - B ) * p ) + B )).toString ( 16 ).slice ( 1 );
57 | }
58 |
59 | };
60 |
61 | export default {
62 |
63 | blend ( color , mixer , percent ) {
64 |
65 | const type = color.length > 7 ? 'rgb' : 'hex';
66 |
67 | return blend [ type ] ( color , mixer , percent );
68 | } ,
69 |
70 | shade ( color , percent ) {
71 |
72 | const type = color.length > 7 ? 'rgb' : 'hex';
73 |
74 | return shade [ type ] ( color , percent );
75 | }
76 |
77 | };
78 |
--------------------------------------------------------------------------------
/utilities/numbers.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | /**
5 | * Returns a number with the appropriate position suffix.
6 | *
7 | * @param {Number} number The number to add a positioning suffix to.
8 | * @returns {String} The number with the positioning string added to it.
9 | */
10 | rank ( number ) {
11 |
12 | let suffix;
13 |
14 | // Assign according to the last number
15 | switch ( number.toString ().slice ( -1 )) {
16 |
17 | case '0' :
18 | case '4' :
19 | case '5' :
20 | case '6' :
21 | case '7' :
22 | case '8' :
23 | case '9' :
24 | suffix = 'th';
25 | break;
26 |
27 | case '1' :
28 | suffix = 'st';
29 | break;
30 |
31 | case '2' :
32 | suffix = 'nd';
33 | break;
34 |
35 | case '3' :
36 | suffix = 'rd';
37 | break;
38 |
39 | // Returns the number unchanged
40 | default :
41 | suffix = '';
42 | break;
43 | }
44 |
45 | return number + suffix;
46 | } ,
47 |
48 | /**
49 | * Returns a formatted number with comma deliminated thousands.
50 | *
51 | * @param {Number} number The number to format.
52 | * @returns {String} The formatted number.
53 | */
54 | format ( number ) {
55 |
56 | let parts;
57 |
58 | if ( ! number ) {
59 |
60 | return number;
61 | }
62 |
63 | parts = number.toString ().split ( '.' );
64 | parts [ 0 ] = parts [ 0 ].replace ( /\B(?=(\d{3})+(?!\d))/g , ',' );
65 |
66 | return parts.join ( '.' );
67 | }
68 |
69 | };
--------------------------------------------------------------------------------
/utilities/routes.js:
--------------------------------------------------------------------------------
1 |
2 | const name = ( state ) => {
3 |
4 | if ( ! state ) {
5 | return null;
6 | }
7 |
8 | const route = state.routes [ state.index ];
9 |
10 | // Nested navigators
11 | if ( route.routes ) {
12 | return name ( route );
13 | }
14 |
15 | return route.routeName;
16 | };
17 |
18 |
19 | export default {
20 |
21 | name : name
22 | };
--------------------------------------------------------------------------------
/utilities/string.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | capitalise ( phrase ) {
5 |
6 | return phrase.replace ( /\w\S*/g , ( word ) => {
7 |
8 | return word.charAt ( 0 ).toUpperCase () + word.substr ( 1 ).toLowerCase ();
9 | });
10 | } ,
11 |
12 | contains ( subject , characters ) {
13 |
14 | return ( subject.indexOf ( characters ) > -1 );
15 | } ,
16 |
17 | datalise ( phrase ) {
18 |
19 | return phrase ? phrase.toLowerCase ().replace ( /\s/g , '-' ) : null;
20 | }
21 | };
--------------------------------------------------------------------------------