{
26 | this.webview = webview;
27 | }}
28 | scalesPageToFit
29 | startInLoadingState
30 | source={{ html: HtmlHelper.getHtml(content) }}
31 | style={{ height: Dimensions.get('window').height, backgroundColor: 'white' }}
32 | onNavigationStateChange={(event) => {
33 | if (event.url.startsWith('http')) {
34 | Helper.openLink(event.url);
35 | this.webview.stopLoading();
36 | }
37 | }}
38 | injectedJavaScript=""
39 | />
40 | );
41 | }
42 | }
43 |
44 | export default PatternDetailView;
45 |
--------------------------------------------------------------------------------
/src/containers/skill-tree/www/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | end_of_line = lf
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.md]
13 | insert_final_newline = false
14 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/src/containers/skill-tree/www/.gitignore:
--------------------------------------------------------------------------------
1 | platforms/*
2 | plugins/*
3 |
--------------------------------------------------------------------------------
/src/containers/skill-tree/www/README.md:
--------------------------------------------------------------------------------
1 | # React Native + Cordova Test
2 |
3 | This project was a test for intergrate legacy cordova, ionic project with React Native.
4 |
5 | [Cordova Part](https://github.com/phodal/rn-cordova-test-web)
6 |
7 | [Native Part](https://github.com/phodal/rn-cordova-test-native)
8 |
9 | License
10 | ---
11 |
12 | MIT
13 |
--------------------------------------------------------------------------------
/src/containers/skill-tree/www/js/controllers.js:
--------------------------------------------------------------------------------
1 | var app = angular.module('moTree', []);
2 | app.controller('HomeCtrl', function ($scope) {
3 | $scope.loadDataFinish = false;
4 | init();
5 |
6 | function init() {
7 | $scope.skillInfo = [];
8 | $scope.loadDataFinish = true;
9 |
10 | document.addEventListener('message', function (e) {
11 | window.postMessage(JSON.stringify(e));
12 | });
13 | }
14 |
15 | $scope.$on('$ionicView.beforeEnter', function () {
16 | init();
17 | });
18 |
19 | $scope.openSkill = function (event) {
20 | var id = event.srcElement.parentElement.getAttribute('id');
21 | window.postMessage(JSON.stringify({id: id}));
22 | };
23 |
24 | $scope.canAddPoint = function () {
25 |
26 | };
27 |
28 | $scope.hasPoint = function (skill_id) {
29 |
30 | };
31 | });
32 |
--------------------------------------------------------------------------------
/src/icon/GrowthFont.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | import { createIconSetFromIcoMoon } from 'react-native-vector-icons';
4 | import icoMoonConfig from '../../assets/glyphmaps/growth.json';
5 |
6 | const Icon = createIconSetFromIcoMoon(icoMoonConfig, 'growth');
7 |
8 | export default Icon;
9 |
10 | export const Button = Icon.Button;
11 | export const TabBarItem = Icon.TabBarItem;
12 | export const TabBarItemIOS = Icon.TabBarItemIOS;
13 | export const ToolbarAndroid = Icon.ToolbarAndroid;
14 | export const getImageSource = Icon.getImageSource;
15 |
16 |
--------------------------------------------------------------------------------
/src/images/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/growth/df6e8ac6624ad1b8d0f27d3f6347c13012f3d2d4/src/images/.gitkeep
--------------------------------------------------------------------------------
/src/lib/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/growth/df6e8ac6624ad1b8d0f27d3f6347c13012f3d2d4/src/lib/.gitkeep
--------------------------------------------------------------------------------
/src/lib/analytics.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Custom Redux Middleware to track Redux Actions
3 | *
4 | * React Native Starter App
5 | * https://github.com/mcnamee/react-native-starter-app
6 | */
7 | import { GoogleAnalyticsTracker } from 'react-native-google-analytics-bridge';
8 |
9 | import AppConfig from '../constants/config';
10 |
11 | // Google Analytics
12 | const GoogleAnalytics = new GoogleAnalyticsTracker(AppConfig.gaTrackingId);
13 |
14 | const track = store => next => (action) => {
15 | // Track each screen view to Redux
16 | // - Requires that each Scene in RNRF have a 'analyticsDesc' prop
17 | switch (action.type) {
18 | case 'REACT_NATIVE_ROUTER_FLUX_FOCUS' :
19 | if (action && action.scene && action.scene.analyticsDesc) {
20 | try {
21 | const screenName = (action.scene.title)
22 | ? `${action.scene.analyticsDesc} - ${action.scene.title}`
23 | : action.scene.analyticsDesc;
24 |
25 | // Send to Google Analytics
26 | GoogleAnalytics.trackScreenView(screenName);
27 | } catch (err) {
28 | console.log(store);
29 | console.log(err);
30 | }
31 | }
32 | break;
33 |
34 | default :
35 | }
36 | return next(action);
37 | };
38 |
39 | export default track;
40 |
--------------------------------------------------------------------------------
/src/redux/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/growth/df6e8ac6624ad1b8d0f27d3f6347c13012f3d2d4/src/redux/.gitkeep
--------------------------------------------------------------------------------
/src/redux/article/actions.js:
--------------------------------------------------------------------------------
1 | export const SAVE_ARTICLE = 'SAVE_ARTICLE';
2 |
3 | export function saveArticle(regex) {
4 | return {
5 | type: SAVE_ARTICLE, regex,
6 | };
7 | }
8 |
--------------------------------------------------------------------------------
/src/redux/article/reducer.js:
--------------------------------------------------------------------------------
1 | import { SAVE_ARTICLE } from './actions';
2 |
3 | const initialState = {
4 | article: {},
5 | };
6 |
7 | export default function reducer(state = initialState, action = {}) {
8 | switch (action.type) {
9 | // focus action is dispatched when a new screen comes into focus
10 | case SAVE_ARTICLE:
11 | return {
12 | ...state,
13 | regex: action.regex,
14 | };
15 |
16 | default:
17 | return state;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/redux/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import routes from './routes/reducer';
3 | import sections from './section/reducer';
4 | import regex from './regex/reducer';
5 | import article from './article/reducer';
6 |
7 | const appReducer = combineReducers({
8 | routes,
9 | sections,
10 | regex,
11 | article,
12 | // ... other reducers
13 | });
14 |
15 | // Setup root reducer
16 | const rootReducer = (state, action) => {
17 | const newState = (action.type === 'RESET') ? undefined : state;
18 | return appReducer(newState, action);
19 | };
20 |
21 | export default rootReducer;
22 |
--------------------------------------------------------------------------------
/src/redux/regex/actions.js:
--------------------------------------------------------------------------------
1 | export const CHOICE_REGEX = 'CHOICE_REGEX';
2 |
3 | export function choiceRegex(regex) {
4 | return {
5 | type: CHOICE_REGEX, regex,
6 | };
7 | }
8 |
--------------------------------------------------------------------------------
/src/redux/regex/reducer.js:
--------------------------------------------------------------------------------
1 | import { CHOICE_REGEX } from './actions';
2 |
3 | const initialState = {
4 | regex: {
5 | name: '',
6 | regex: /1-9a-Z/,
7 | descriptions: '',
8 | tag: '',
9 | },
10 | };
11 |
12 | export default function reducer(state = initialState, action = {}) {
13 | switch (action.type) {
14 | // focus action is dispatched when a new screen comes into focus
15 | case CHOICE_REGEX:
16 | return {
17 | ...state,
18 | regex: action.regex,
19 | };
20 |
21 | default:
22 | return state;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/redux/routes/reducer.js:
--------------------------------------------------------------------------------
1 | import { ActionConst } from 'react-native-router-flux';
2 |
3 | const initialState = {
4 | scene: {},
5 | };
6 |
7 | export default function reducer(state = initialState, action = {}) {
8 | switch (action.type) {
9 | // focus action is dispatched when a new screen comes into focus
10 | case ActionConst.FOCUS:
11 | return {
12 | ...state,
13 | scene: action.scene,
14 | };
15 |
16 | default:
17 | return state;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/redux/section/actions.js:
--------------------------------------------------------------------------------
1 | export const SAVE_SECTION = 'SAVE_SECTION';
2 |
3 | export function saveSection(section) {
4 | return {
5 | type: SAVE_SECTION, section,
6 | };
7 | }
8 |
--------------------------------------------------------------------------------
/src/redux/section/reducer.js:
--------------------------------------------------------------------------------
1 | import { SAVE_SECTION } from './actions';
2 |
3 | const initialState = {
4 | section: {},
5 | };
6 |
7 | export default function reducer(state = initialState, action = {}) {
8 | switch (action.type) {
9 | // focus action is dispatched when a new screen comes into focus
10 | case SAVE_SECTION:
11 | return {
12 | ...state,
13 | section: action.section,
14 | };
15 |
16 | default:
17 | return state;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/redux/store/configureStore.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable global-require,no-unused-vars */
2 | import { createStore, applyMiddleware, compose } from 'redux';
3 | import createLogger from 'redux-logger';
4 | import thunk from 'redux-thunk';
5 |
6 | import reducers from '../';
7 | import Analytics from '../../lib/analytics';
8 |
9 | const loggerMiddleware = createLogger({
10 | predicate: (getState, action) => __DEV__,
11 | });
12 |
13 | export default function configureStore() {
14 | let middleware = [
15 | Analytics,
16 | thunk,
17 | ];
18 |
19 | if (__DEV__) {
20 | // Dev-only middleware
21 | middleware = [
22 | ...middleware,
23 | loggerMiddleware,
24 | ];
25 | }
26 |
27 | const enhancer = compose(
28 | applyMiddleware(...middleware),
29 | );
30 |
31 | return createStore(reducers, enhancer);
32 | }
33 |
--------------------------------------------------------------------------------
/src/theme/colors.js:
--------------------------------------------------------------------------------
1 | const app = {
2 | background: '#E9EBEE',
3 | cardBackground: '#FFFFFF',
4 | listItemBackground: '#FFFFFF',
5 | };
6 |
7 | const brand = {
8 | brand: {
9 | primary: '#03a9f4',
10 | secondary: '#17233D',
11 | },
12 | };
13 |
14 | const text = {
15 | textPrimary: '#222',
16 | textSecondary: '#666',
17 | headingPrimary: brand.brand.primary,
18 | headingSecondary: brand.brand.primary,
19 | };
20 |
21 | const borders = {
22 | border: '#D0D1D5',
23 | };
24 |
25 | const tabbar = {
26 | tabbar: {
27 | background: '#ffffff',
28 | iconDefault: '#BABDC2',
29 | iconSelected: brand.brand.primary,
30 | },
31 | };
32 |
33 | export default {
34 | ...app,
35 | ...brand,
36 | ...text,
37 | ...borders,
38 | ...tabbar,
39 | };
40 |
--------------------------------------------------------------------------------
/src/theme/fonts.js:
--------------------------------------------------------------------------------
1 | import { Platform } from 'react-native';
2 |
3 | function lineHeight(fontSize) {
4 | const multiplier = (fontSize > 20) ? 0.1 : 0.33;
5 | return parseInt(fontSize + (fontSize * multiplier), 10);
6 | }
7 |
8 | const base = {
9 | size: 14,
10 | lineHeight: lineHeight(14),
11 | ...Platform.select({
12 | ios: {
13 | family: 'HelveticaNeue',
14 | },
15 | android: {
16 | family: 'Roboto',
17 | },
18 | }),
19 | };
20 |
21 | export default {
22 | base: { ...base },
23 | h1: { ...base, size: base.size * 1.75, lineHeight: lineHeight(base.size * 2) },
24 | h2: { ...base, size: base.size * 1.5, lineHeight: lineHeight(base.size * 1.75) },
25 | h3: { ...base, size: base.size * 1.25, lineHeight: lineHeight(base.size * 1.5) },
26 | h4: { ...base, size: base.size * 1.1, lineHeight: lineHeight(base.size * 1.25) },
27 | h5: { ...base },
28 | };
29 |
--------------------------------------------------------------------------------
/src/theme/sizes.js:
--------------------------------------------------------------------------------
1 | import { Dimensions, Platform } from 'react-native';
2 |
3 | const { width, height } = Dimensions.get('window');
4 | const screenHeight = width < height ? height : width;
5 | const screenWidth = width < height ? width : height;
6 |
7 | const AppSizes = {
8 | // Window Dimensions
9 | screen: {
10 | height: screenHeight,
11 | width: screenWidth,
12 |
13 | widthHalf: screenWidth * 0.5,
14 | widthThird: screenWidth * 0.333,
15 | widthTwoThirds: screenWidth * 0.666,
16 | widthQuarter: screenWidth * 0.25,
17 | widthThreeQuarters: screenWidth * 0.75,
18 | },
19 | navbarHeight: (Platform.OS === 'ios') ? 64 : 54,
20 | basicNavbarHeight: (Platform.OS === 'ios') ? 24 : 14,
21 | statusBarHeight: (Platform.OS === 'ios') ? 16 : 0,
22 | tabbarHeight: 51,
23 |
24 | padding: 20,
25 | paddingSml: 10,
26 |
27 | borderRadius: 2,
28 | };
29 |
30 | export default AppSizes;
31 |
--------------------------------------------------------------------------------
/src/utils/AsyncStorageHelper.js:
--------------------------------------------------------------------------------
1 | import { AsyncStorage } from 'react-native';
2 |
3 | class AsyncStorageHelper {
4 |
5 | static add(key, value, call) {
6 | AsyncStorage.setItem(key, value, () => (
7 | AsyncStorage.getItem(key, (err, result) => (
8 | call(result)))));
9 | }
10 |
11 | static set(key, value) {
12 | AsyncStorage.setItem(key, value);
13 | }
14 |
15 | static get(key, callback) {
16 | AsyncStorage.getItem(key, callback);
17 | }
18 |
19 | static del(key, call) {
20 | AsyncStorage.removeItem(
21 | key, () => (
22 | this.query(key, call)));
23 | }
24 |
25 | static update(key, value, call) {
26 | AsyncStorage.mergeItem(
27 | key, value, () => (
28 | this.query(key, call)));
29 | }
30 |
31 | static query(key, call) {
32 | AsyncStorage.getItem(
33 | key, (err, result) => (
34 | call(result)));
35 | }
36 |
37 | }
38 | export default AsyncStorageHelper;
39 |
--------------------------------------------------------------------------------
/src/utils/BookmarkHelper.js:
--------------------------------------------------------------------------------
1 | import { AsyncStorage } from 'react-native';
2 |
3 | const STORE_KEY = 'bookmark';
4 |
5 | export default class BookmarkHelper {
6 | slug: null;
7 |
8 | static getSlug() {
9 | return this.slug;
10 | }
11 |
12 | static setSlug(slug) {
13 | this.slug = slug;
14 | }
15 |
16 | static makeBookmark() {
17 | const slug = this.slug;
18 | AsyncStorage.getItem(STORE_KEY, (result) => {
19 | console.log(result);
20 | if (!result) {
21 | return AsyncStorage.setItem(STORE_KEY, JSON.stringify([slug]));
22 | }
23 |
24 | const data = JSON.parse(result);
25 | if (data.includes(slug)) {
26 | return AsyncStorage.setItem(STORE_KEY, JSON.stringify(data.push(slug)));
27 | }
28 |
29 | return null;
30 | });
31 | }
32 |
33 | static isArticleRead(callback) {
34 | const slug = this.slug;
35 | AsyncStorage.getItem(STORE_KEY, (result) => {
36 | if (!result) {
37 | return callback(false);
38 | }
39 |
40 | const data = JSON.parse(result);
41 | if (data.includes(slug)) {
42 | return callback(true);
43 | }
44 |
45 | return callback(false);
46 | });
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/utils/EditorWebViewServices.js:
--------------------------------------------------------------------------------
1 | export default class EditorWebViewServices {
2 | webView: null;
3 |
4 | static getWebView(): null {
5 | return this.webView;
6 | }
7 |
8 | static setWebView(value: null) {
9 | this.webView = value;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/utils/MarkdownHelper.js:
--------------------------------------------------------------------------------
1 | const hljs = require('highlightjs'); // https://highlightjs.org/
2 |
3 | const md = require('markdown-it')({
4 | highlight(str, lang) {
5 | if (lang && hljs.getLanguage(lang)) {
6 | try {
7 | return `${
8 | hljs.highlight(lang, str, true).value
9 | }
`.replace(/\n/g, '
');
10 | } catch (__) {
11 | console.error(__);
12 | }
13 | }
14 |
15 | return `${md.utils.escapeHtml(str).replace(/\n/g, '
')}
`;
16 | },
17 | });
18 |
19 | export default class MarkdownHelper {
20 | static convert(str) {
21 | return md.render(str);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/utils/MoregexWebViewServices.js:
--------------------------------------------------------------------------------
1 | export default class MoregexWebViewServices {
2 | webView: null;
3 |
4 | static getWebView(): null {
5 | return this.webView;
6 | }
7 |
8 | static setWebView(value: null) {
9 | this.webView = value;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/utils/SectionPageHelper.js:
--------------------------------------------------------------------------------
1 | export default class SectionPageHelper {
2 | section: null;
3 |
4 | static getSection(): null {
5 | return this.section;
6 | }
7 |
8 | static setSection(value: null) {
9 | this.section = value;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/utils/TodoListHelper.js:
--------------------------------------------------------------------------------
1 | import AsyncStorageHelper from './AsyncStorageHelper';
2 |
3 | export const KEY = 'todo-';
4 |
5 | class TodoListHelper {
6 | static key(value) {
7 | return KEY.concat(value.name);
8 | }
9 |
10 | static init(key, value, call) {
11 | AsyncStorageHelper.query(
12 | key, (result) => {
13 | if (!result) {
14 | AsyncStorageHelper.add(
15 | key, JSON.stringify(value), data => (
16 | call(JSON.parse(data))));
17 | } else {
18 | call(JSON.parse(result));
19 | }
20 | });
21 | }
22 |
23 | static changeTodoState(key, value, call) {
24 | AsyncStorageHelper.update(
25 | key, JSON.stringify(value), data => (
26 | call(JSON.parse(data))));
27 | }
28 |
29 | }
30 | export default TodoListHelper;
31 |
--------------------------------------------------------------------------------
/src/utils/api.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | axios.defaults.baseURL = 'https://phodal.coding.me/growth/';
4 |
5 | axios.defaults.headers.common = ['token'];
6 |
7 | class Api {
8 | static ROADMAP_LIST = 'awesome/api/all.json';
9 | static PROJECT_LIST = 'project/api/all.json';
10 | static TOOLBOX_LIST = 'toolbox/api/all.json';
11 | static TOOLBOX_DETAIL = 'toolbox/';
12 | static ARTICLE_LIST = 'articles/api/all.json';
13 | static ARTICLE_DETAIL = 'articles/';
14 | static GROWTH_IN_ACTION = 'growth-in-action/api/all.json';
15 | static IDEA_BOOK = 'ideabook/api/all.json';
16 | static FE = 'fe/api/all.json';
17 | static GITBOOK = 'gitbook/api.json';
18 | static AWESOMES = 'awesomes/api/awesomes.json';
19 |
20 | static get = url => axios.get(url);
21 |
22 | }
23 | export default Api;
24 |
--------------------------------------------------------------------------------
/test/helpers/html-transform.js:
--------------------------------------------------------------------------------
1 | const babel = require('babel-core');
2 | const jestPreset = require('babel-preset-jest');
3 |
4 | module.exports = {
5 | process(src, filename) {
6 | if (babel.util.canCompile(filename)) {
7 | return babel.transform(src, {
8 | filename,
9 | presets: [jestPreset],
10 | retainLines: true,
11 | }).code;
12 | }
13 | return src;
14 | },
15 | };
16 |
--------------------------------------------------------------------------------