diff --git a/.editorconfig b/.editorconfig index b4458e14..192641a7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,8 +2,8 @@ root = true [*] -indent_style = tab -indent_size = 4 +indent_style = space +indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 1ac595da..00000000 --- a/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -/node_modules/ -/web/output/ diff --git a/.eslintrc b/.eslintrc index 9d061b2c..f51a3770 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,379 +1,62 @@ { - "parser": "babel-eslint", - "ecmaFeatures": { - "jsx": true - }, - "env": { - "es6": true, - "jasmine": true, - }, - "plugins": [ - "react" - ], - // Map from global var to bool specifying if it can be redefined - "globals": { - "__DEV__": true, - "__dirname": false, - "__fbBatchedBridgeConfig": false, - "cancelAnimationFrame": false, - "clearImmediate": true, - "clearInterval": false, - "clearTimeout": false, - "console": false, - "document": false, - "escape": false, - "exports": false, - "fetch": false, - "global": false, - "jest": false, - "Map": true, - "module": false, - "navigator": false, - "process": false, - "Promise": true, - "requestAnimationFrame": true, - "require": false, - "Set": true, - "setImmediate": true, - "setInterval": false, - "setTimeout": false, - "window": false, - "XMLHttpRequest": false, - "pit": false - }, - "rules": { - "comma-dangle": 0, - // disallow trailing commas in object literals - "no-cond-assign": 1, - // disallow assignment in conditional expressions - "no-console": 0, - // disallow use of console (off by default in the node environment) - "no-constant-condition": 0, - // disallow use of constant expressions in conditions - "no-control-regex": 1, - // disallow control characters in regular expressions - "no-debugger": 1, - // disallow use of debugger - "no-dupe-keys": 1, - // disallow duplicate keys when creating object literals - "no-empty": 0, - // disallow empty statements - "no-empty-class": 1, - // disallow the use of empty character classes in regular expressions - "no-ex-assign": 1, - // disallow assigning to the exception in a catch block - "no-extra-boolean-cast": 1, - // disallow double-negation boolean casts in a boolean context - "no-extra-parens": 0, - // disallow unnecessary parentheses (off by default) - "no-extra-semi": 1, - // disallow unnecessary semicolons - "no-func-assign": 1, - // disallow overwriting functions written as function declarations - "no-inner-declarations": 0, - // disallow function or variable declarations in nested blocks - "no-invalid-regexp": 1, - // disallow invalid regular expression strings in the RegExp constructor - "no-negated-in-lhs": 1, - // disallow negation of the left operand of an in expression - "no-obj-calls": 1, - // disallow the use of object properties of the global object (Math and JSON) as functions - "no-regex-spaces": 1, - // disallow multiple spaces in a regular expression literal - "no-reserved-keys": 0, - // disallow reserved words being used as object literal keys (off by default) - "no-sparse-arrays": 1, - // disallow sparse arrays - "no-unreachable": 1, - // disallow unreachable statements after a return, throw, continue, or break statement - "use-isnan": 1, - // disallow comparisons with the value NaN - "valid-jsdoc": 0, - // Ensure JSDoc comments are valid (off by default) - "valid-typeof": 1, - // Ensure that the results of typeof are compared against a valid string - - // Best Practices - // These are rules designed to prevent you from making mistakes. They either prescribe a better way of doing something or help you avoid footguns. - - "block-scoped-var": 0, - // treat var statements as if they were block scoped (off by default) - "complexity": 0, - // specify the maximum cyclomatic complexity allowed in a program (off by default) - "consistent-return": 0, - // require return statements to either always or never specify values - "curly": 1, - // specify curly brace conventions for all control statements - "default-case": 0, - // require default case in switch statements (off by default) - "dot-notation": 1, - // encourages use of dot notation whenever possible - "eqeqeq": 1, - // require the use of === and !== - "guard-for-in": 0, - // make sure for-in loops have an if statement (off by default) - "no-alert": 1, - // disallow the use of alert, confirm, and prompt - "no-caller": 1, - // disallow use of arguments.caller or arguments.callee - "no-div-regex": 1, - // disallow division operators explicitly at beginning of regular expression (off by default) - "no-else-return": 0, - // disallow else after a return in an if (off by default) - "no-empty-label": 1, - // disallow use of labels for anything other then loops and switches - "no-eq-null": 0, - // disallow comparisons to null without a type-checking operator (off by default) - "no-eval": 1, - // disallow use of eval() - "no-extend-native": 1, - // disallow adding to native types - "no-extra-bind": 1, - // disallow unnecessary function binding - "no-fallthrough": 1, - // disallow fallthrough of case statements - "no-floating-decimal": 1, - // disallow the use of leading or trailing decimal points in numeric literals (off by default) - "no-implied-eval": 1, - // disallow use of eval()-like methods - "no-labels": 1, - // disallow use of labeled statements - "no-iterator": 1, - // disallow usage of __iterator__ property - "no-lone-blocks": 1, - // disallow unnecessary nested blocks - "no-loop-func": 0, - // disallow creation of functions within loops - "no-multi-str": 0, - // disallow use of multiline strings - "no-native-reassign": 0, - // disallow reassignments of native objects - "no-new": 1, - // disallow use of new operator when not part of the assignment or comparison - "no-new-func": 1, - // disallow use of new operator for Function object - "no-new-wrappers": 1, - // disallows creating new instances of String,Number, and Boolean - "no-octal": 1, - // disallow use of octal literals - "no-octal-escape": 1, - // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251"; - "no-proto": 1, - // disallow usage of __proto__ property - "no-redeclare": 0, - // disallow declaring the same variable more then once - "no-return-assign": 1, - // disallow use of assignment in return statement - "no-script-url": 1, - // disallow use of javascript: urls. - "no-self-compare": 1, - // disallow comparisons where both sides are exactly the same (off by default) - "no-sequences": 1, - // disallow use of comma operator - "no-unused-expressions": 0, - // disallow usage of expressions in statement position - "no-void": 1, - // disallow use of void operator (off by default) - "no-warning-comments": 0, - // disallow usage of configurable warning terms in comments": 1, // e.g. TODO or FIXME (off by default) - "no-with": 1, - // disallow use of the with statement - "radix": 1, - // require use of the second argument for parseInt() (off by default) - "semi-spacing": 1, - // require a space after a semi-colon - "vars-on-top": 0, - // requires to declare all vars on top of their containing scope (off by default) - "wrap-iife": 0, - // require immediate function invocation to be wrapped in parentheses (off by default) - "yoda": 1, - // require or disallow Yoda conditions - - // Strict Mode - // These rules relate to using strict mode. - - "global-strict": [ - 2, - "always" - ], - // require or disallow the "use strict" pragma in the global scope (off by default in the node environment) - "no-extra-strict": 1, - // disallow unnecessary use of "use strict"; when already in strict mode - "strict": 0, - // require that all functions are run in strict mode - - // Variables - // These rules have to do with variable declarations. - - "no-catch-shadow": 1, - // disallow the catch clause parameter name being the same as a variable in the outer scope (off by default in the node environment) - "no-delete-var": 1, - // disallow deletion of variables - "no-label-var": 1, - // disallow labels that share a name with a variable - "no-shadow": 1, - // disallow declaration of variables already declared in the outer scope - "no-shadow-restricted-names": 1, - // disallow shadowing of names such as arguments - "no-undef": 2, - // disallow use of undeclared variables unless mentioned in a /*global */ block - "no-undefined": 0, - // disallow use of undefined variable (off by default) - "no-undef-init": 1, - // disallow use of undefined when initializing variables - "no-unused-vars": [ - 1, - { - "vars": "all", - "args": "none" - } - ], - // disallow declaration of variables that are not used in the code - "no-use-before-define": 0, - // disallow use of variables before they are defined - - // Node.js - // These rules are specific to JavaScript running on Node.js. - - "handle-callback-err": 1, - // enforces error handling in callbacks (off by default) (on by default in the node environment) - "no-mixed-requires": 1, - // disallow mixing regular variable and require declarations (off by default) (on by default in the node environment) - "no-new-require": 1, - // disallow use of new operator with the require function (off by default) (on by default in the node environment) - "no-path-concat": 1, - // disallow string concatenation with __dirname and __filename (off by default) (on by default in the node environment) - "no-process-exit": 0, - // disallow process.exit() (on by default in the node environment) - "no-restricted-modules": 1, - // restrict usage of specified node modules (off by default) - "no-sync": 0, - // disallow use of synchronous methods (off by default) - - // Stylistic Issues - // These rules are purely matters of style and are quite subjective. - - "key-spacing": 0, - "comma-spacing": 0, - "no-multi-spaces": 0, - "brace-style": 0, - // enforce one true brace style (off by default) - "camelcase": 0, - // require camel case names - "consistent-this": [ - 1, - "self" - ], - // enforces consistent naming when capturing the current execution context (off by default) - "eol-last": 1, - // enforce newline at the end of file, with no multiple empty lines - "func-names": 0, - // require function expressions to have a name (off by default) - "func-style": 0, - // enforces use of function declarations or expressions (off by default) - "new-cap": 0, - // require a capital letter for constructors - "new-parens": 1, - // disallow the omission of parentheses when invoking a constructor with no arguments - "no-nested-ternary": 0, - // disallow nested ternary expressions (off by default) - "no-array-constructor": 1, - // disallow use of the Array constructor - "no-lonely-if": 0, - // disallow if as the only statement in an else block (off by default) - "no-new-object": 1, - // disallow use of the Object constructor - "no-spaced-func": 1, - // disallow space between function identifier and application - "no-space-before-semi": 1, - // disallow space before semicolon - "no-ternary": 0, - // disallow the use of ternary operators (off by default) - "no-trailing-spaces": 1, - // disallow trailing whitespace at the end of lines - "no-underscore-dangle": 0, - // disallow dangling underscores in identifiers - "no-wrap-func": 1, - // disallow wrapping of non-IIFE statements in parens - "no-mixed-spaces-and-tabs": 1, - // disallow mixed spaces and tabs for indentation - "quotes": [ - 1, - "single", - "avoid-escape" - ], - // specify whether double or single quotes should be used - "quote-props": 0, - // require quotes around object literal property names (off by default) - "semi": 1, - // require or disallow use of semicolons instead of ASI - "sort-vars": 0, - // sort variables within the same declaration block (off by default) - "space-after-keywords": 1, - // require a space after certain keywords (off by default) - "space-in-brackets": 0, - // require or disallow spaces inside brackets (off by default) - "space-in-parens": 0, - // require or disallow spaces inside parentheses (off by default) - "space-infix-ops": 1, - // require spaces around operators - "space-return-throw-case": 1, - // require a space after return, throw, and case - "space-unary-ops": [ - 1, - { - "words": true, - "nonwords": false - } - ], - // require or disallow spaces before/after unary operators (words on by default, nonwords off by default) - "max-nested-callbacks": 0, - // specify the maximum depth callbacks can be nested (off by default) - "one-var": 0, - // allow just one var statement per function (off by default) - "wrap-regex": 0, - // require regex literals to be wrapped in parentheses (off by default) - - // Legacy - // The following rules are included for compatibility with JSHint and JSLint. While the names of the rules may not match up with the JSHint/JSLint counterpart, the functionality is the same. - - "max-depth": 0, - // specify the maximum depth that blocks can be nested (off by default) - "max-len": 0, - // specify the maximum length of a line in your program (off by default) - "max-params": 0, - // limits the number of parameters that can be used in the function declaration. (off by default) - "max-statements": 0, - // specify the maximum number of statement allowed in a function (off by default) - "no-bitwise": 1, - // disallow use of bitwise operators (off by default) - "no-plusplus": 0, - // disallow use of unary operators, ++ and -- (off by default) - - "react/display-name": 0, - "react/jsx-boolean-value": 0, - "react/jsx-quotes": [ - 1, - "double", - "avoid-escape" - ], - "react/jsx-no-undef": 1, - "react/jsx-sort-props": 0, - "react/jsx-uses-react": 0, - "react/jsx-uses-vars": 1, - "react/no-did-mount-set-state": [ - 1, - "allow-in-func" - ], - "react/no-did-update-set-state": [ - 1, - "allow-in-func" - ], - "react/no-multi-comp": 0, - "react/no-unknown-property": 0, - "react/prop-types": 0, - "react/react-in-jsx-scope": 0, - "react/self-closing-comp": 1, - "react/wrap-multilines": 0 - } + "extends": ["standard", "standard-react"], + "globals": { + "__DEV__": true, + "__dirname": false, + "__fbBatchedBridgeConfig": false, + "alert": false, + "cancelAnimationFrame": false, + "cancelIdleCallback": false, + "clearImmediate": true, + "clearInterval": false, + "clearTimeout": false, + "console": false, + "document": false, + "escape": false, + "Event": false, + "EventTarget": false, + "exports": false, + "fetch": false, + "FormData": false, + "global": false, + "jest": false, + "Map": true, + "module": false, + "navigator": false, + "process": false, + "Promise": true, + "requestAnimationFrame": true, + "requestIdleCallback": true, + "require": false, + "Set": true, + "setImmediate": true, + "setInterval": false, + "setTimeout": false, + "window": false, + "XMLHttpRequest": false, + "pit": false, + + // Flow global types. + "ReactComponent": false, + "ReactClass": false, + "ReactElement": false, + "ReactPropsCheckType": false, + "ReactPropsChainableTypeChecker": false, + "ReactPropTypes": false, + "SyntheticEvent": false, + "$Either": false, + "$All": false, + "$ArrayBufferView": false, + "$Tuple": false, + "$Supertype": false, + "$Subtype": false, + "$Shape": false, + "$Diff": false, + "$Keys": false, + "$Enum": false, + "$Exports": false, + "$FlowIssue": false, + "$FlowFixMe": false, + "$FixMe": false + } } diff --git a/android/app/src/main/java/com/noder/MainApplication.java b/android/app/src/main/java/com/noder/MainApplication.java index 88bcb8f8..20b619e2 100644 --- a/android/app/src/main/java/com/noder/MainApplication.java +++ b/android/app/src/main/java/com/noder/MainApplication.java @@ -21,7 +21,7 @@ public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override - protected boolean getUseDeveloperSupport() { + public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } diff --git a/index.android.js b/index.android.js index e537718a..a4f1b94e 100644 --- a/index.android.js +++ b/index.android.js @@ -1,6 +1,5 @@ -import React from 'react'; -import {AppRegistry} from 'react-native'; -import Noder from './src'; +import React from 'react' +import {AppRegistry} from 'react-native' +import Noder from './src' - -AppRegistry.registerComponent('noder', () => Noder); +AppRegistry.registerComponent('noder', () => Noder) diff --git a/index.ios.js b/index.ios.js index e537718a..a4f1b94e 100644 --- a/index.ios.js +++ b/index.ios.js @@ -1,6 +1,5 @@ -import React from 'react'; -import {AppRegistry} from 'react-native'; -import Noder from './src'; +import React from 'react' +import {AppRegistry} from 'react-native' +import Noder from './src' - -AppRegistry.registerComponent('noder', () => Noder); +AppRegistry.registerComponent('noder', () => Noder) diff --git a/index.web.js b/index.web.js index c163b595..10e80533 100644 --- a/index.web.js +++ b/index.web.js @@ -1,13 +1,12 @@ -import 'babel-polyfill'; -import {AppRegistry} from 'react-native'; -import Noder from './src'; +import 'babel-polyfill' +import {AppRegistry} from 'react-native' +import Noder from './src' +AppRegistry.registerComponent('noder', () => Noder) -AppRegistry.registerComponent('noder', () => Noder); - -var app = document.createElement('div'); -document.body.appendChild(app); +var app = document.createElement('div') +document.body.appendChild(app) AppRegistry.runApplication('noder', { - rootTag: app -}); + rootTag: app +}) diff --git a/ios/noder/AppDelegate.m b/ios/noder/AppDelegate.m index e4807abd..25ec878e 100644 --- a/ios/noder/AppDelegate.m +++ b/ios/noder/AppDelegate.m @@ -45,7 +45,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( // jsCodeLocation = [CodePush bundleURL]; #ifdef DEBUG - jsCodeLocation = [NSURL URLWithString:@"http://192.168.1.105:8081/index.ios.bundle?platform=ios&dev=true"]; + jsCodeLocation = [NSURL URLWithString:@"http://172.18.118.191:8081/index.ios.bundle?platform=ios&dev=true"]; #else jsCodeLocation = [CodePush bundleURL]; #endif diff --git a/ios/noder/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/noder/Images.xcassets/AppIcon.appiconset/Contents.json index 8d83298a..b22f5b62 100644 --- a/ios/noder/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/noder/Images.xcassets/AppIcon.appiconset/Contents.json @@ -2,85 +2,105 @@ "images" : [ { "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x", - "filename" : "Icon-Small@2x.png" + "size" : "20x20", + "scale" : "2x" }, { "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x", - "filename" : "Icon-Small@3x.png" + "size" : "20x20", + "scale" : "3x" }, { + "size" : "29x29", "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x", - "filename" : "Icon-40@2x.png" + "filename" : "Icon-Small@2x.png", + "scale" : "2x" }, { + "size" : "29x29", "idiom" : "iphone", + "filename" : "Icon-Small@3x.png", + "scale" : "3x" + }, + { "size" : "40x40", - "scale" : "3x", - "filename" : "Icon-40@3x.png" + "idiom" : "iphone", + "filename" : "Icon-40@2x.png", + "scale" : "2x" }, { + "size" : "40x40", "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x", - "filename" : "Icon-60@2x.png" + "filename" : "Icon-40@3x.png", + "scale" : "3x" }, { + "size" : "60x60", "idiom" : "iphone", + "filename" : "Icon-60@2x.png", + "scale" : "2x" + }, + { "size" : "60x60", - "scale" : "3x", - "filename" : "Icon-60@3x.png" + "idiom" : "iphone", + "filename" : "Icon-60@3x.png", + "scale" : "3x" }, { "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x", - "filename" : "Icon-Small.png" + "size" : "20x20", + "scale" : "1x" }, { "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x", - "filename" : "Icon-Small@2x.png" + "size" : "20x20", + "scale" : "2x" }, { + "size" : "29x29", "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x", - "filename" : "Icon-40.png" + "filename" : "Icon-Small.png", + "scale" : "1x" }, { + "size" : "29x29", "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x", - "filename" : "Icon-40@2x.png" + "filename" : "Icon-Small@2x.png", + "scale" : "2x" }, { + "size" : "40x40", "idiom" : "ipad", - "size" : "76x76", - "scale" : "1x", - "filename" : "Icon-76.png" + "filename" : "Icon-40.png", + "scale" : "1x" }, { + "size" : "40x40", "idiom" : "ipad", + "filename" : "Icon-40@2x.png", + "scale" : "2x" + }, + { "size" : "76x76", - "scale" : "2x", - "filename" : "Icon-76@2x.png" + "idiom" : "ipad", + "filename" : "Icon-76.png", + "scale" : "1x" }, { + "size" : "76x76", "idiom" : "ipad", + "filename" : "Icon-76@2x.png", + "scale" : "2x" + }, + { "size" : "83.5x83.5", - "scale" : "2x", - "filename" : "Icon-83.5@2x.png" + "idiom" : "ipad", + "filename" : "Icon-83.5@2x.png", + "scale" : "2x" } ], "info" : { "version" : 1, - "author" : "makeappicon" + "author" : "xcode" } } \ No newline at end of file diff --git a/package.json b/package.json index 781ace33..8615d077 100644 --- a/package.json +++ b/package.json @@ -17,38 +17,39 @@ "push-android-prod": "code-push release-react Noder android --deploymentName Production", "push-ios-prod": "code-push release-react Noder ios --deploymentName Production", "push-key": "code-push deployment ls Noder -k", - "i-taobao": "npm i --registry=https://registry.npm.taobao.org" + "i-taobao": "npm i --registry=https://registry.npm.taobao.org", + "standard": "eslint --fix '**/*.js'" }, "dependencies": { - "flux-standard-action": "^0.6.1", + "flux-standard-action": "^1.2.0", "lodash": "^4.15.0", "markdown": "0.5.0", "moment": "^2.14.1", "query-string": "^4.2.3", - "react": "15.2.1", - "react-addons-pure-render-mixin": "15.2.1", - "react-dom": "15.2.1", - "react-native": "0.31.0", + "react": "16.0.0-alpha.6", + "react-addons-pure-render-mixin": "^15.5.2", + "react-dom": "^15.5.3", + "react-native": "^0.43.2", "react-native-barcodescanner": "^3.1.1", - "react-native-blur": "^1.0.1", + "react-native-blur": "^2.0.0", "react-native-button": "^1.6.0", - "react-native-camera": "git+https://github.com/lwansbrough/react-native-camera.git", - "react-native-cli": "^1.0.0", - "react-native-code-push": "^1.14.5-beta", + "react-native-camera": "^0.6.0", + "react-native-cli": "^2.0.1", + "react-native-code-push": "^2.0.1-beta", "react-native-html-render": "^1.0.4", - "react-native-scrollable-tab-view": "^0.5.3", - "react-native-vector-icons": "^2.0.3", - "react-redux": "^4.4.5", + "react-native-scrollable-tab-view": "^0.7.4", + "react-native-vector-icons": "^4.0.1", + "react-redux": "^5.0.4", "react-web": "git+https://github.com/flyskywhy/react-web.git", "redux": "^3.5.2", - "redux-actions": "^0.11.0", - "redux-logger": "^2.6.1", + "redux-actions": "^2.0.1", + "redux-logger": "^3.0.1", "redux-promise": "^0.5.3", "redux-thunk": "^2.1.0", "webpack": "^1.13.1" }, "devDependencies": { - "babel-eslint": "^6.1.2", + "babel-eslint": "^7.2.1", "babel-loader": "^6.2.4", "babel-polyfill": "^6.13.0", "babel-preset-es2015": "^6.6.0", @@ -56,8 +57,13 @@ "babel-preset-stage-1": "^6.5.0", "coffee-script": "^1.9.2", "dev-ip": "^1.0.1", - "eslint": "^3.3.1", - "eslint-plugin-react": "^6.1.2", + "eslint": "^3.19.0", + "eslint-config-standard": "^10.2.0", + "eslint-config-standard-react": "^4.3.0", + "eslint-plugin-flowtype": "^2.30.4", + "eslint-plugin-promise": "^3.5.0", + "eslint-plugin-react": "^6.10.3", + "eslint-plugin-standard": "^3.0.1", "file-loader": "^0.9.0", "gulp": "^3.9.1", "gulp-replace": "^0.5.4", @@ -68,6 +74,8 @@ "react-native-cli": "^0.2.0", "redux-devtools": "^3.3.1", "run-sequence": "^1.2.2", + "snazzy": "^7.0.0", + "standard": "^10.0.1", "url-loader": "^0.5.7", "webpack": "^1.13.2", "webpack-dev-server": "^1.14.1", @@ -77,5 +85,16 @@ "node": ">= 4.x", "npm": ">= 3.x" }, - "bundleId": "org.reactjs.native.example.noder" + "bundleId": "org.reactjs.native.example.noder", + "standard": { + "ignore": [], + "globals": [ + "__DEV__" + ], + "parser": "babel-eslint", + "plugins": [ + "flowtype", + "react" + ] + } } diff --git a/post_npm_install/react-native-blur/src/BlurView.js b/post_npm_install/react-native-blur/src/BlurView.js index 2d2c0b0a..25da3ea4 100644 --- a/post_npm_install/react-native-blur/src/BlurView.js +++ b/post_npm_install/react-native-blur/src/BlurView.js @@ -1,3 +1,3 @@ -import {Platform} from 'react-native'; +import {Platform} from 'react-native' -module.exports = Platform.OS === 'web' ? require('./BlurView.ios') : require('./BlurView.' + Platform.OS); +module.exports = Platform.OS === 'web' ? require('./BlurView.ios') : require('./BlurView.' + Platform.OS) diff --git a/post_npm_install/react-native-blur/src/VibrancyView.js b/post_npm_install/react-native-blur/src/VibrancyView.js index 2046710b..c7db4b72 100644 --- a/post_npm_install/react-native-blur/src/VibrancyView.js +++ b/post_npm_install/react-native-blur/src/VibrancyView.js @@ -1,3 +1,3 @@ -import {Platform} from 'react-native'; +import {Platform} from 'react-native' -module.exports = Platform.OS === 'web' ? require('./VibrancyView.ios') : require('./VibrancyView.' + Platform.OS); +module.exports = Platform.OS === 'web' ? require('./VibrancyView.ios') : require('./VibrancyView.' + Platform.OS) diff --git a/post_npm_install/react-native-scrollable-tab-view/Button.js b/post_npm_install/react-native-scrollable-tab-view/Button.js index 71ec0031..9b2a5055 100644 --- a/post_npm_install/react-native-scrollable-tab-view/Button.js +++ b/post_npm_install/react-native-scrollable-tab-view/Button.js @@ -1,3 +1,3 @@ -import {Platform} from 'react-native'; +import {Platform} from 'react-native' -module.exports = Platform.OS === 'web' ? require('./Button.ios') : require('./Button.' + Platform.OS); +module.exports = Platform.OS === 'web' ? require('./Button.ios') : require('./Button.' + Platform.OS) diff --git a/src/actions/index.js b/src/actions/index.js index 25de39f0..6a5a4da9 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -1,14 +1,11 @@ -import * as user from './user'; -import * as utils from './utils'; -import * as message from './message'; -import * as topic from './topic'; - +import * as user from './user' +import * as utils from './utils' +import * as message from './message' +import * as topic from './topic' export default { - ...user, - ...utils, - ...message, - ...topic -}; - - + ...user, + ...utils, + ...message, + ...topic +} diff --git a/src/actions/message.js b/src/actions/message.js index bbc3158a..3fc81228 100644 --- a/src/actions/message.js +++ b/src/actions/message.js @@ -1,28 +1,25 @@ -import {createAction} from 'redux-actions'; -import * as types from '../constants/ActionTypes'; -import * as messageService from '../services/messageService'; +import {createAction} from 'redux-actions' +import * as types from '../constants/ActionTypes' +import * as messageService from '../services/messageService' +export const getUnreadMessageCount = createAction(types.GET_UNREAD_MESSAGE_COUNT, async() => { + return await messageService.getUnreadMessageCount() +}) -export const getUnreadMessageCount = createAction(types.GET_UNREAD_MESSAGE_COUNT, async()=> { - return await messageService.getUnreadMessageCount(); -}); - - -export const markAsRead = createAction(types.MARK_AS_READ, async()=> { - return await messageService.markAsRead(); +export const markAsRead = createAction(types.MARK_AS_READ, async() => { + return await messageService.markAsRead() }, function (resolved, rejected) { - return { - resolved, - rejected, - sync: 'message' - } -}); - + return { + resolved, + rejected, + sync: 'message' + } +}) -export const getMessageList = createAction(types.GET_MESSAGES_LIST, async()=> { - return await messageService.getMessages(); -}, ()=> { - return { - sync: 'message' - } -}); +export const getMessageList = createAction(types.GET_MESSAGES_LIST, async() => { + return await messageService.getMessages() +}, () => { + return { + sync: 'message' + } +}) diff --git a/src/actions/topic.js b/src/actions/topic.js index d75723db..fabba35f 100644 --- a/src/actions/topic.js +++ b/src/actions/topic.js @@ -1,78 +1,64 @@ -import {createAction} from 'redux-actions'; -import * as markdown from 'markdown'; -import * as types from '../constants/ActionTypes'; -import * as topicService from '../services/topicService'; - - -function setMetaId(id) { - return { - id - } -} - - -export const getTopicsByTab = createAction(types.GET_TOPICS_BY_TAB, async(tab, params)=> { - return await topicService.getTopicsByTab(tab, params); -}, (tab)=> { - return { - tab - } -}); - - -export const updateTopicsByTab = createAction(types.UPDATE_TOPICS_BY_TAB, async(tab)=> { - return await topicService.getTopicsByTab(tab, { - page: 1 - }); -}, (tab)=> { - return { - tab, - sync: 'topic' - } -}); - - -export const getTopicById = createAction(types.GET_TOPIC_BY_ID, topicService.getTopicById, (id)=> { - return { - id, - sync: 'topic' - } -}); - - -export const removeTopicCacheById = createAction(types.REMOVE_TOPIC_CACHE_BY_ID, (id)=> { - return { - id - } -}); - - -export const replyTopicById = createAction(types.REPLY_TOPIC_BY_ID, topicService.reply, ({topicId, content, replyId, user}, resolved, rejected)=> { - return { - id: topicId, - content: markdown.parse(content), - replyId, - resolved, - rejected, - user - } -}); - - -export const upReply = createAction(types.UP_REPLY, topicService.upReply, ({topicId, replyId, userId, resolved, rejected})=> { - return { - id: topicId, - replyId, - userId, - resolved, - rejected - } -}); - - -export const publish = createAction(types.PUBLISH, topicService.publish, ({resolved, rejected})=> { - return { - resolved, - rejected - } -}); +import {createAction} from 'redux-actions' +import * as markdown from 'markdown' +import * as types from '../constants/ActionTypes' +import * as topicService from '../services/topicService' + +export const getTopicsByTab = createAction(types.GET_TOPICS_BY_TAB, async(tab, params) => { + return await topicService.getTopicsByTab(tab, params) +}, (tab) => { + return { + tab + } +}) + +export const updateTopicsByTab = createAction(types.UPDATE_TOPICS_BY_TAB, async(tab) => { + return await topicService.getTopicsByTab(tab, { + page: 1 + }) +}, (tab) => { + return { + tab, + sync: 'topic' + } +}) + +export const getTopicById = createAction(types.GET_TOPIC_BY_ID, topicService.getTopicById, (id) => { + return { + id, + sync: 'topic' + } +}) + +export const removeTopicCacheById = createAction(types.REMOVE_TOPIC_CACHE_BY_ID, (id) => { + return { + id + } +}) + +export const replyTopicById = createAction(types.REPLY_TOPIC_BY_ID, topicService.reply, ({topicId, content, replyId, user}, resolved, rejected) => { + return { + id: topicId, + content: markdown.parse(content), + replyId, + resolved, + rejected, + user + } +}) + +export const upReply = createAction(types.UP_REPLY, topicService.upReply, ({topicId, replyId, userId, resolved, rejected}) => { + return { + id: topicId, + replyId, + userId, + resolved, + rejected + } +}) + +export const publish = createAction(types.PUBLISH, topicService.publish, ({resolved, rejected}) => { + return { + resolved, + rejected + } +}) diff --git a/src/actions/user.js b/src/actions/user.js index 1e1a21a6..409f2df6 100644 --- a/src/actions/user.js +++ b/src/actions/user.js @@ -1,78 +1,74 @@ -import {createAction} from 'redux-actions'; -import * as types from '../constants/ActionTypes'; -import * as userService from '../services/userService'; -import * as tokenService from '../services/token'; -import * as storageService from '../services/storage'; +import {createAction} from 'redux-actions' +import * as types from '../constants/ActionTypes' +import * as userService from '../services/userService' +import * as tokenService from '../services/token' +import * as storageService from '../services/storage' -export const checkToken = createAction(types.CHECK_TOKEN, async(token)=> { - const userLoginInfo = await userService.checkToken(token); - const user = await userService +export const checkToken = createAction(types.CHECK_TOKEN, async(token) => { + const userLoginInfo = await userService.checkToken(token) + const user = await userService .getUserInfo(userLoginInfo.loginname) - .then((data)=> { - return { - secret: userLoginInfo, - publicInfo: data - }; - }); - tokenService.setToken(token); - return user; -}, (token, resolved)=> { - return { - resolved: resolved, - sync: 'user' - } -}); + .then((data) => { + return { + secret: userLoginInfo, + publicInfo: data + } +}) + tokenService.setToken(token) + return user +}, (token, resolved) => { + return { + resolved: resolved, + sync: 'user' + } +}) +export const updateClientUserInfo = createAction(types.UPDATE_CLIENT_USER_INFO, async(user) => { + return await userService.getUserInfo(user.secret.loginname) + .then(userInfo => { + if (userInfo) { + return userInfo + } + throw 'getUserInfoError' +}) +}, () => { + return { + sync: 'user' + } +}) -export const updateClientUserInfo = createAction(types.UPDATE_CLIENT_USER_INFO, async(user)=> { - return await userService.getUserInfo(user.secret.loginname) - .then(userInfo=> { - if (userInfo) { - return userInfo; - } - throw 'getUserInfoError' - }); -}, ()=> { - return { - sync: 'user' - } -}); - - -export const getUserInfo = createAction(types.GET_USER_INFO, async(loginName)=> { - return await userService.getUserInfo(loginName) - .then(userInfo=> { - if (userInfo) { - return userInfo; - } - throw 'getUserInfoError' - }); -}, (userName)=> { - return { - userName, - sync: 'user' - } -}); - +export const getUserInfo = createAction(types.GET_USER_INFO, async(loginName) => { + return await userService.getUserInfo(loginName) + .then(userInfo => { + if (userInfo) { + return userInfo + } + throw 'getUserInfoError' +}) +}, (userName) => { + return { + userName, + sync: 'user' + } +}) export const logout = function () { - return { - type: types.LOGOUT, - meta: { - sync: 'user' - } - } -}; + return { + type: types.LOGOUT, + meta: { + sync: 'user' + } + } +} export const clear = function () { - try { - storageService.removeItem('topic'); - storageService.removeItem('message'); - } - catch (err) { - console.warn(err); - } - return { - type: types.CLEAR - } -}; + try { + storageService.removeItem('topic') + storageService.removeItem('message') + } catch (err) { + console.warn(err) + } + return { + type: types.CLEAR + } +} diff --git a/src/actions/utils.js b/src/actions/utils.js index c68ed403..d42c5dbe 100644 --- a/src/actions/utils.js +++ b/src/actions/utils.js @@ -1,38 +1,35 @@ -import {createAction} from 'redux-actions'; -import * as types from '../constants/ActionTypes'; -import * as storageService from '../services/storage'; +import {createAction} from 'redux-actions' +import * as types from '../constants/ActionTypes' +import * as storageService from '../services/storage' +const syncReducer = ['user', 'message', 'topic'] -const syncReducer = ['user', 'message', 'topic']; +export const toast = createAction(types.TOAST, (text, timeout) => { + return { + text, + timeout, + id: new Date().getTime() + } +}) - -export const toast = createAction(types.TOAST, (text, timeout)=> { - return { - text, - timeout, - id: new Date().getTime() - } -}); - - -export const getReducerFromAsyncStorage = createAction(types.GET_REDUCER_FROM_ASYNC_STORAGE, async()=> { - return storageService.multiGet(syncReducer) - .then(arr=> { - let ob = {}; - arr.forEach(item=> { - ob[item[0]] = item[1]; - }); - if (ob.user && ob.user.secret) { - global.token = ob.user.secret.token; - } - return ob; - }) - .catch(err=> { - console.warn(err); - }); -}, (resolved, rejected)=> { - return { - resolved, - rejected - } -}); +export const getReducerFromAsyncStorage = createAction(types.GET_REDUCER_FROM_ASYNC_STORAGE, async() => { + return storageService.multiGet(syncReducer) + .then(arr => { + let ob = {} + arr.forEach(item => { + ob[item[0]] = item[1] + }) + if (ob.user && ob.user.secret) { + global.token = ob.user.secret.token + } + return ob +}) + .catch(err => { + console.warn(err) +}) +}, (resolved, rejected) => { + return { + resolved, + rejected + } +}) diff --git a/src/components/CommentHtml.js b/src/components/CommentHtml.js index dfb7fff8..8cb0ae3e 100644 --- a/src/components/CommentHtml.js +++ b/src/components/CommentHtml.js @@ -1,180 +1,176 @@ -import React, {Component} from 'react'; -import {StyleSheet, Image, Dimensions} from 'react-native'; -import Html from './base/Html'; - - -const {height, width} = Dimensions.get('window'); +import React, {Component} from 'react' +import {StyleSheet, Image, Dimensions} from 'react-native' +import Html from './base/Html' +const {width} = Dimensions.get('window') class CommentHtml extends Component { - constructor(props) { - super(props); - if (this.props.style) { - this.styles = Object.assign({}, styles, this.props.style) - } - } + constructor (props) { + super(props) + if (this.props.style) { + this.styles = Object.assign({}, styles, this.props.style) + } + } - render() { - return ( - - ) - } + render () { + return ( + + ) + } } - -const fontSize = 14; -const titleMargin = 5; +const fontSize = 14 +const titleMargin = 5 const styles = StyleSheet.create({ - p: { - //lineHeight: fontSize * 1.4, - fontSize: fontSize, - color: 'rgba(0,0,0,0.8)' - }, - pwrapper: { - marginTop: 5, - marginBottom: 5 - }, - - a: { - color: '#3498DB', - fontSize: fontSize, - paddingLeft: 4, - paddingRight: 4, - marginRight: 10, - marginLeft: 10 - }, - h1: { - fontSize: fontSize * 1.6, - fontWeight: "bold", - color: 'rgba(0,0,0,0.8)' - }, - h1wrapper: { - marginTop: titleMargin, - marginBottom: titleMargin - }, - h2: { - fontSize: fontSize * 1.5, - fontWeight: 'bold', - color: 'rgba(0,0,0,0.85)' - }, - h2wrapper: { - marginBottom: titleMargin, - marginTop: titleMargin - }, - h3: { - fontWeight: 'bold', - fontSize: fontSize * 1.4, - color: 'rgba(0,0,0,0.8)' - }, - h3wrapper: { - marginBottom: titleMargin - 2, - marginTop: titleMargin - 2 - }, - h4: { - fontSize: fontSize * 1.3, - color: 'rgba(0,0,0,0.7)', - fontWeight: 'bold' - }, - h4wrapper: { - marginBottom: titleMargin - 2, - marginTop: titleMargin - 2, - }, - h5: { - fontSize: fontSize * 1.2, - color: 'rgba(0,0,0,0.7)', - fontWeight: 'bold' - }, - h5wrapper: { - marginBottom: titleMargin - 3, - marginTop: titleMargin - 3, - }, - h6: { - fontSize: fontSize * 1.1, - color: 'rgba(0,0,0,0.7)', - fontWeight: 'bold' - }, - h6wrapper: { - marginBottom: titleMargin - 3, - marginTop: titleMargin - 3, - }, - li: { - fontSize: fontSize * 0.9, - color: 'rgba(0,0,0,0.7)' - }, - liwrapper: { - paddingLeft: 20, - marginBottom: 10 - }, - strong: { - fontWeight: 'bold' - }, - em: { - fontStyle: 'italic' - }, - codeScrollView: { - backgroundColor: '#333', - flexDirection: 'column', - marginBottom: 15 - }, - codeRow: { - flex: 1, - flexDirection: 'row', - height: 25, - alignItems: 'center' - }, - codeFirstRow: { - paddingTop: 20, - height: 25 + 20 - }, - codeLastRow: { - paddingBottom: 20, - height: 25 + 20 - }, - codeFirstAndLastRow: { - paddingBottom: 20, - height: 25 + 40, - paddingTop: 20 - }, - lineNum: { - width: 55, - color: 'rgba(255,255,255,0.5)', - }, - lineNumWrapper: { - width: 55, - height: 25, - backgroundColor: 'rgba(0,0,0,0.1)', - flexDirection: 'row', - alignItems: 'center', - paddingLeft: 20 - }, - codeWrapper: { - flexDirection: 'column' - }, - codeLineWrapper: { - height: 25, - flexDirection: 'row', - alignItems: 'center', - paddingLeft: 20, - paddingRight: 20 - }, - blockquotewrapper: { - paddingLeft: 20, - borderLeftColor: '#3498DB', - borderLeftWidth: 3 - }, - img: { - width: width - 80 - 20, - height: width - 80 - 20, - resizeMode: Image.resizeMode.contain, - margin: 10 - } -}); + p: { + // lineHeight: fontSize * 1.4, + fontSize: fontSize, + color: 'rgba(0,0,0,0.8)' + }, + pwrapper: { + marginTop: 5, + marginBottom: 5 + }, + a: { + color: '#3498DB', + fontSize: fontSize, + paddingLeft: 4, + paddingRight: 4, + marginRight: 10, + marginLeft: 10 + }, + h1: { + fontSize: fontSize * 1.6, + fontWeight: 'bold', + color: 'rgba(0,0,0,0.8)' + }, + h1wrapper: { + marginTop: titleMargin, + marginBottom: titleMargin + }, + h2: { + fontSize: fontSize * 1.5, + fontWeight: 'bold', + color: 'rgba(0,0,0,0.85)' + }, + h2wrapper: { + marginBottom: titleMargin, + marginTop: titleMargin + }, + h3: { + fontWeight: 'bold', + fontSize: fontSize * 1.4, + color: 'rgba(0,0,0,0.8)' + }, + h3wrapper: { + marginBottom: titleMargin - 2, + marginTop: titleMargin - 2 + }, + h4: { + fontSize: fontSize * 1.3, + color: 'rgba(0,0,0,0.7)', + fontWeight: 'bold' + }, + h4wrapper: { + marginBottom: titleMargin - 2, + marginTop: titleMargin - 2 + }, + h5: { + fontSize: fontSize * 1.2, + color: 'rgba(0,0,0,0.7)', + fontWeight: 'bold' + }, + h5wrapper: { + marginBottom: titleMargin - 3, + marginTop: titleMargin - 3 + }, + h6: { + fontSize: fontSize * 1.1, + color: 'rgba(0,0,0,0.7)', + fontWeight: 'bold' + }, + h6wrapper: { + marginBottom: titleMargin - 3, + marginTop: titleMargin - 3 + }, + li: { + fontSize: fontSize * 0.9, + color: 'rgba(0,0,0,0.7)' + }, + liwrapper: { + paddingLeft: 20, + marginBottom: 10 + }, + strong: { + fontWeight: 'bold' + }, + em: { + fontStyle: 'italic' + }, + codeScrollView: { + backgroundColor: '#333', + flexDirection: 'column', + marginBottom: 15 + }, + codeRow: { + flex: 1, + flexDirection: 'row', + height: 25, + alignItems: 'center' + }, + codeFirstRow: { + paddingTop: 20, + height: 25 + 20 + }, + codeLastRow: { + paddingBottom: 20, + height: 25 + 20 + }, + codeFirstAndLastRow: { + paddingBottom: 20, + height: 25 + 40, + paddingTop: 20 + }, + lineNum: { + width: 55, + color: 'rgba(255,255,255,0.5)' + }, + lineNumWrapper: { + width: 55, + height: 25, + backgroundColor: 'rgba(0,0,0,0.1)', + flexDirection: 'row', + alignItems: 'center', + paddingLeft: 20 + }, + codeWrapper: { + flexDirection: 'column' + }, + codeLineWrapper: { + height: 25, + flexDirection: 'row', + alignItems: 'center', + paddingLeft: 20, + paddingRight: 20 + }, + blockquotewrapper: { + paddingLeft: 20, + borderLeftColor: '#3498DB', + borderLeftWidth: 3 + }, + img: { + width: width - 80 - 20, + height: width - 80 - 20, + resizeMode: Image.resizeMode.contain, + margin: 10 + } +}) -export default CommentHtml; +export default CommentHtml diff --git a/src/components/CommentList.js b/src/components/CommentList.js index 71568a4a..120f62f0 100644 --- a/src/components/CommentList.js +++ b/src/components/CommentList.js @@ -1,310 +1,292 @@ -import React, {Component, PropTypes} from 'react'; -import {Dimensions, View, Text, ListView, StyleSheet, Image, TouchableOpacity, RefreshControl} from 'react-native'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; -import Icon from 'react-native-vector-icons/Ionicons'; -import moment from 'moment'; -import CommentHtml from './CommentHtml'; -import CommentUp from './CommentUp'; -import * as Constants from '../constants'; +import React, {Component, PropTypes} from 'react' +import {Dimensions, View, Text, ListView, StyleSheet, Image, TouchableOpacity, RefreshControl} from 'react-native' +import PureRenderMixin from 'react-addons-pure-render-mixin' +import Icon from 'react-native-vector-icons/Ionicons' +import moment from 'moment' +import CommentHtml from './CommentHtml' +import CommentUp from './CommentUp' +import * as Constants from '../constants' -import {parseImgUrl} from '../utils'; - - -const {height, width} = Dimensions.get('window'); +import {parseImgUrl} from '../utils' +const {width} = Dimensions.get('window') class CommentList extends Component { - static propTypes = { - data: PropTypes.array, - focusedReply: PropTypes.string, - router: PropTypes.object, - user: PropTypes.object, - onReplyPress: PropTypes.func, - onAuthorNamePress: PropTypes.func, - onPullRefresh: PropTypes.func, - pending: PropTypes.bool - }; - - - static defaultProps = { - onReplyPress: ()=>null, - onAuthorNamePress: ()=>null, - pending: false - }; - - - constructor(props) { - super(props); - const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); - this.state = { - ds: ds.cloneWithRows(props.data.concat([]).reverse()) - }; - this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); - } - - - componentWillReceiveProps(nextProps) { - if (nextProps.data !== this.props.data) { - this.setState({ - ds: this.state.ds.cloneWithRows(nextProps.data.concat([]).reverse()) - }) - } - } - - - componentDidMount() { - setTimeout(() => this._scrollToReply(), 0); - } - - - scrollToTop() { - this._listView.scrollTo({ - x: 0, - y: 0 - }); - } - - - _scrollToReply() { - const {focusedReply} = this.props; - if (focusedReply) { - let row = this[focusedReply]; - if (row && row.measure) { - row.measure((x, y, width, height, pageX, pageY) => { - this._listView.setNativeProps({ - contentOffset: { - x: 0, - y: y - } - }); - }); - - row.setNativeProps({ - styles: { - backgroundColor: 'red' - } - }); - } - } - } - - _renderFooter(comment, authorName) { - if (this.props.user) { - return ( - - null, + onAuthorNamePress: () => null, + pending: false + }; + + constructor (props) { + super(props) + const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}) + this.state = { + ds: ds.cloneWithRows(props.data.concat([]).reverse()) + } + this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this) + } + + componentWillReceiveProps (nextProps) { + if (nextProps.data !== this.props.data) { + this.setState({ + ds: this.state.ds.cloneWithRows(nextProps.data.concat([]).reverse()) + }) + } + } + + componentDidMount () { + setTimeout(() => this._scrollToReply(), 0) + } + + scrollToTop () { + this._listView.scrollTo({ + x: 0, + y: 0 + }) + } + + _scrollToReply () { + const {focusedReply} = this.props + if (focusedReply) { + let row = this[focusedReply] + if (row && row.measure) { + row.measure((x, y, width, height, pageX, pageY) => { + this._listView.setNativeProps({ + contentOffset: { + x: 0, + y: y + } + }) + }) + + row.setNativeProps({ + styles: { + backgroundColor: 'red' + } + }) + } + } + } + + _renderFooter (comment, authorName) { + if (this.props.user) { + return ( + + - this.props.onReplyPress(comment.id, authorName) }> - this.props.onReplyPress(comment.id, authorName)}> + - - - ); - } - } - - - _renderRow(comment, sectionID, rowID, highlightRow) { - const authorName = comment.author.loginname; - const date = moment(comment.create_at).startOf('minute').fromNow(); - const commentNum = this.props.data.length - parseInt(rowID); - let focusStyle = {}; - if (this.props.focusedReply) { - let replyId = this.props.focusedReply; - if (replyId == comment.id) { - focusStyle = { - backgroundColor: 'rgba(0,2,125,0.07)' - } - } - } - - - return ( - this[comment.id]=view} - key={comment.id} - style={[styles.commentWrapper,focusStyle]}> - - { - this.props.router.toUser({ - userName: authorName - }) - }}> - - - - - - {commentNum} 楼 + + + ) + } + } + + _renderRow (comment, sectionID, rowID, highlightRow) { + const authorName = comment.author.loginname + const date = moment(comment.create_at).startOf('minute').fromNow() + const commentNum = this.props.data.length - parseInt(rowID) + let focusStyle = {} + if (this.props.focusedReply) { + let replyId = this.props.focusedReply + if (replyId === comment.id) { + focusStyle = { + backgroundColor: 'rgba(0,2,125,0.07)' + } + } + } + + return ( + this[comment.id] = view} + key={comment.id} + style={[styles.commentWrapper, focusStyle]}> + + { + this.props.router.toUser({ + userName: authorName + }) + }}> + + + + + {commentNum} 楼 - - - - - - this.props.onAuthorNamePress(authorName)}> - - {authorName} - - - - - - - {date} - - - - - - - { this._renderFooter(comment, authorName) } - - - ) - } - - - render() { - return ( - this._listView=view} - style={{backgroundColor:'rgba(255,255,255,1)'}} - showsVerticalScrollIndicator={true} - initialListSize={10} - pagingEnabled={false} - removeClippedSubviews={true} - dataSource={this.state.ds} - renderRow={this._renderRow.bind(this)} - refreshControl={ - + + + + + + this.props.onAuthorNamePress(authorName)}> + + {authorName} + + + + + + + {date} + + + + + + + { this._renderFooter(comment, authorName) } + + + ) + } + + render () { + return ( + this._listView = view} + style={{backgroundColor: 'rgba(255,255,255,1)'}} + showsVerticalScrollIndicator + initialListSize={10} + pagingEnabled={false} + removeClippedSubviews + dataSource={this.state.ds} + renderRow={this._renderRow.bind(this)} + refreshControl={ + } /> - ) - } + ) + } } -const authorImgSize = 35; -const commentContentOffset = 15 * 2 + authorImgSize; -const commentIconSize = 12; - +const authorImgSize = 35 +const commentContentOffset = 15 * 2 + authorImgSize +const commentIconSize = 12 const commentHtmlStyle = StyleSheet.create({ - img: { - width: width - commentContentOffset - 15, - height: width - commentContentOffset - 15, - resizeMode: Image.resizeMode.contain - } -}); - + img: { + width: width - commentContentOffset - 15, + height: width - commentContentOffset - 15, + resizeMode: Image.resizeMode.contain + } +}) const styles = StyleSheet.create({ - commentWrapper: { - borderBottomColor: 'rgba(0,0,0,0.02)', - borderBottomWidth: 1, - padding: 15, - flexDirection: 'row', - }, - - commentHeader: { - flexDirection: 'row', - alignItems: 'center' - }, - - date: { - flexDirection: 'row', - flex: 1 - }, - - author: { - flex: 1 - }, - authorText: { - color: 'rgba(0,0,0,0.3)', - fontSize: 12 - }, - - dateIcon: { - height: commentIconSize, - width: commentIconSize, - flexDirection: 'row' - }, - - dateText: { - color: 'rgba(0,0,0,0.3)', - fontSize: 12, - textAlign: 'right', - flex: 1 - }, - - commentIcon: { - height: commentIconSize, - width: commentIconSize - }, - - - imageWrapper: { - width: authorImgSize + 15 - }, - - commentNumText: { - marginTop: 15, - fontSize: 12, - color: 'rgba(0,0,0,0.3)', - textAlign: 'center', - width: authorImgSize - - }, - - commentContentWrapper: { - width: width - commentContentOffset - 15, - }, - - authorImg: { - height: authorImgSize, - width: authorImgSize, - borderRadius: authorImgSize / 2 - - }, - commentFooter: { - flexDirection: 'row', - flex: 1, - justifyContent: 'space-between', - alignItems: 'flex-end' - }, - replyIcon: { - width: 15, - flex: 1 - }, - up: { - width: 40, - flexDirection: 'row', - justifyContent: 'flex-start', - alignItems: 'center' - } -}); - - -export default CommentList; + commentWrapper: { + borderBottomColor: 'rgba(0,0,0,0.02)', + borderBottomWidth: 1, + padding: 15, + flexDirection: 'row' + }, + + commentHeader: { + flexDirection: 'row', + alignItems: 'center' + }, + + date: { + flexDirection: 'row', + flex: 1 + }, + + author: { + flex: 1 + }, + authorText: { + color: 'rgba(0,0,0,0.3)', + fontSize: 12 + }, + + dateIcon: { + height: commentIconSize, + width: commentIconSize, + flexDirection: 'row' + }, + + dateText: { + color: 'rgba(0,0,0,0.3)', + fontSize: 12, + textAlign: 'right', + flex: 1 + }, + + commentIcon: { + height: commentIconSize, + width: commentIconSize + }, + + imageWrapper: { + width: authorImgSize + 15 + }, + + commentNumText: { + marginTop: 15, + fontSize: 12, + color: 'rgba(0,0,0,0.3)', + textAlign: 'center', + width: authorImgSize + + }, + + commentContentWrapper: { + width: width - commentContentOffset - 15 + }, + + authorImg: { + height: authorImgSize, + width: authorImgSize, + borderRadius: authorImgSize / 2 + + }, + commentFooter: { + flexDirection: 'row', + flex: 1, + justifyContent: 'space-between', + alignItems: 'flex-end' + }, + replyIcon: { + width: 15, + flex: 1 + }, + up: { + width: 40, + flexDirection: 'row', + justifyContent: 'flex-start', + alignItems: 'center' + } +}) + +export default CommentList diff --git a/src/components/CommentOverlay.js b/src/components/CommentOverlay.js index eb6a09fb..a61f74e6 100644 --- a/src/components/CommentOverlay.js +++ b/src/components/CommentOverlay.js @@ -1,77 +1,72 @@ -import React, {Component, PropTypes} from 'react'; -import {View, StyleSheet, Text} from 'react-native'; -import Icon from 'react-native-vector-icons/Ionicons'; -import OverlayButton from './base/OverlayButton'; - - -const overlaySize = 45; -const iconSize = 12; +import React, {Component, PropTypes} from 'react' +import {View, StyleSheet, Text} from 'react-native' +import Icon from 'react-native-vector-icons/Ionicons' +import OverlayButton from './base/OverlayButton' +const overlaySize = 45 +const iconSize = 12 class CommentOverlay extends Component { - static propTypes = { - onPress: PropTypes.func, - replyCount: PropTypes.number - }; - + static propTypes = { + onPress: PropTypes.func, + replyCount: PropTypes.number + }; - static defaultProps = { - replyCount: 0 - }; + static defaultProps = { + replyCount: 0 + }; + _renderCommentReplyCount (count) { + if (count > 999) { + return '1k+' + } + return count + } - _renderCommentReplyCount(count) { - if (count > 999) { - return '1k+' - } - return count - } - - render() { - return ( - - - + + - - {this._renderCommentReplyCount(this.props.replyCount)} - - - - ) - } + + {this._renderCommentReplyCount(this.props.replyCount)} + + + + ) + } } const styles = StyleSheet.create({ - position: { - right: 20, - bottom: 20 - }, - commentText: { - textAlign: 'center', - color: 'white', - fontSize: 12, - paddingLeft: 4 - }, - content: { - height: overlaySize, - width: overlaySize, - borderRadius: overlaySize / 2, - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center' - }, - commentIcon: { - height: iconSize, - width: iconSize, - } -}); - + position: { + right: 20, + bottom: 20 + }, + commentText: { + textAlign: 'center', + color: 'white', + fontSize: 12, + paddingLeft: 4 + }, + content: { + height: overlaySize, + width: overlaySize, + borderRadius: overlaySize / 2, + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center' + }, + commentIcon: { + height: iconSize, + width: iconSize + } +}) -export default CommentOverlay; +export default CommentOverlay diff --git a/src/components/CommentUp.js b/src/components/CommentUp.js index c5205b50..012e0594 100644 --- a/src/components/CommentUp.js +++ b/src/components/CommentUp.js @@ -1,111 +1,102 @@ -import React, {Component, PropTypes} from 'react'; -import {View, TouchableOpacity, StyleSheet, Text} from 'react-native'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; -import Icon from 'react-native-vector-icons/Ionicons'; -import Spinner from './base/Spinner'; - +import React, {Component, PropTypes} from 'react' +import {View, TouchableOpacity, StyleSheet, Text} from 'react-native' +import PureRenderMixin from 'react-addons-pure-render-mixin' +import Icon from 'react-native-vector-icons/Ionicons' +import Spinner from './base/Spinner' class CommentUp extends Component { - static propTypes = { - pending: PropTypes.bool, - disabled: PropTypes.bool, - replyId: PropTypes.string, - ups: PropTypes.array, - userId: PropTypes.string - }; - - - static defaultProps = { - pending: false, - disabled: false, - ups: [] - }; - - - constructor(props){ - super(props); - this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); - } - - - _onUpPress() { - const {disabled, pending, upReply, replyId, userId, topicId} = this.props; - if (disabled) { - return window.alert('不能给自己点赞哦!') - } - if (pending) return; - - upReply({ - replyId, - userId, - topicId - }); - } - - - _isUped() { - return this.props.ups.some(item=> { - return item == this.props.userId - }) - } - - - _renderUpIcon() { - if (this.props.pending) { - return ( - { + return item === this.props.userId + }) + } + + _renderUpIcon () { + if (this.props.pending) { + return ( + - ) - } - return ( - - ) - } - - - render() { - const {ups} = this.props; - let count = ups.length; - return ( - - - - {this._renderUpIcon()} - - {count == 0 ? null : ( - - {count} - + ) + } + + render () { + const {ups} = this.props + let count = ups.length + return ( + + + + {this._renderUpIcon()} + + {count === 0 ? null : ( + + {count} + )} - - - ) - } + + + ) + } } - const styles = StyleSheet.create({ - textWrapper: { - paddingLeft: 7 - }, - text: { - fontSize: 12, - color: 'rgba(0,0,0,0.2)', - height: 12 - }, - loading: { - height: 12, - width: 16 - } -}); - - -export default CommentUp; + textWrapper: { + paddingLeft: 7 + }, + text: { + fontSize: 12, + color: 'rgba(0,0,0,0.2)', + height: 12 + }, + loading: { + height: 12, + width: 16 + } +}) + +export default CommentUp diff --git a/src/components/MarkAsReadOverlay.js b/src/components/MarkAsReadOverlay.js index 9b38857f..f2aa3a5b 100644 --- a/src/components/MarkAsReadOverlay.js +++ b/src/components/MarkAsReadOverlay.js @@ -1,77 +1,69 @@ -import React, {Component, PropTypes} from 'react'; -import {View, StyleSheet, Text} from 'react-native'; -import OverlayButton from './base/OverlayButton'; -import Spinner from './base/Spinner'; - - -const overlaySize = 45; +import React, {Component, PropTypes} from 'react' +import {View, StyleSheet, Text} from 'react-native' +import OverlayButton from './base/OverlayButton' +import Spinner from './base/Spinner' +const overlaySize = 45 class MarkAsReadOverlay extends Component { - static propTypes = { - hasNotRead: PropTypes.array, - markAsRead: PropTypes.func, - pending: PropTypes.bool - }; - - - _onPress() { - if (this.props.hasNotRead.length == 0) { - window.alert('暂无未读消息!') - } - else { - this.props.markAsRead() - } - } + static propTypes = { + hasNotRead: PropTypes.array, + markAsRead: PropTypes.func, + pending: PropTypes.bool + }; + _onPress () { + if (this.props.hasNotRead.length === 0) { + window.alert('暂无未读消息!') + } else { + this.props.markAsRead() + } + } - _renderContent() { - if (this.props.pending) { - return ( - - ) - } - return ( - + _renderContent () { + if (this.props.pending) { + return ( + + ) + } + return ( + 已读 - ) - } + ) + } - - render() { - return ( - - - {this._renderContent()} - - - ) - } + render () { + return ( + + + {this._renderContent()} + + + ) + } } - const styles = StyleSheet.create({ - position: { - right: 20, - bottom: 20 - }, - text: { - textAlign: 'center', - color: 'white', - fontSize: 12 - }, - content: { - height: overlaySize, - width: overlaySize, - borderRadius: overlaySize / 2, - flexDirection: 'column', - justifyContent: 'center' - } -}); - + position: { + right: 20, + bottom: 20 + }, + text: { + textAlign: 'center', + color: 'white', + fontSize: 12 + }, + content: { + height: overlaySize, + width: overlaySize, + borderRadius: overlaySize / 2, + flexDirection: 'column', + justifyContent: 'center' + } +}) -export default MarkAsReadOverlay; +export default MarkAsReadOverlay diff --git a/src/components/MessageList.js b/src/components/MessageList.js index 849bda7f..a2b06c36 100644 --- a/src/components/MessageList.js +++ b/src/components/MessageList.js @@ -1,238 +1,225 @@ -import React, {Component, PropTypes} from 'react'; -import {View, StyleSheet, Text, Image, ListView, TouchableHighlight, Dimensions, RefreshControl} from 'react-native'; -import moment from 'moment'; -import {parseImgUrl} from '../utils'; -import * as Constants from '../constants'; - - -const {height, width} = Dimensions.get('window'); +import React, {Component, PropTypes} from 'react' +import {View, StyleSheet, Text, Image, ListView, TouchableHighlight, Dimensions, RefreshControl} from 'react-native' +import moment from 'moment' +import {parseImgUrl} from '../utils' +import * as Constants from '../constants' +const {height, width} = Dimensions.get('window') class MessageList extends Component { - static propTypes = { - data: PropTypes.array, - pending: PropTypes.bool, - getMessageList: PropTypes.func, - didFocus: PropTypes.bool - }; - - - static defaultProps = { - pending: false, - didFocus: false, - data: [] - }; - - - constructor(props) { - super(props); - this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); - this.state = { - ds: this.ds.cloneWithRows(props.data) - } - } - - - componentWillReceiveProps(nextProps) { - if (nextProps.data !== this.props.data) { - this.setState({ - ds: this.state.ds.cloneWithRows(nextProps.data) - }); - } - } - - - _onRowPress(message) { - this.props.router.toComment({ - topic: message.topic, - from: 'message', - reply: message.reply, - id: message.topic.id - }); - } - - - _renderRowFooter(message) { - const date = moment(message.reply.create_at).startOf('minute').fromNow(); - - return ( - - - - {message.author.loginname} - - - {message.type == 'reply' ? ' 回复' : ' @'} - - - - - {date} - - - ) - } - - - _renderRow(message) { - var topic = message.topic; - - - return ( - {this._onRowPress(message)}} - underlayColor='#3498DB' - key={message.id}> - - r1 !== r2}) + this.state = { + ds: this.ds.cloneWithRows(props.data) + } + } + + componentWillReceiveProps (nextProps) { + if (nextProps.data !== this.props.data) { + this.setState({ + ds: this.state.ds.cloneWithRows(nextProps.data) + }) + } + } + + _onRowPress (message) { + this.props.router.toComment({ + topic: message.topic, + from: 'message', + reply: message.reply, + id: message.topic.id + }) + } + + _renderRowFooter (message) { + const date = moment(message.reply.create_at).startOf('minute').fromNow() + + return ( + + + + {message.author.loginname} + + + {message.type == 'reply' ? ' 回复' : ' @'} + + + + + {date} + + + ) + } + + _renderRow (message) { + var topic = message.topic + + return ( + { this._onRowPress(message) }} + underlayColor='#3498DB' + key={message.id}> + + - - - {topic.title} - - - - {this._renderRowFooter(message)} - - - - - ) - } - - - _renderHeader() { - const {data, didFocus} = this.props; - if (!data.length && didFocus) { - return ( - - + + + {topic.title} + + + + {this._renderRowFooter(message)} + + + + + ) + } + + _renderHeader () { + const {data, didFocus} = this.props + if (!data.length && didFocus) { + return ( + + 空空哒 - - ) - } - return null; - } - - - render() { - const {pending, didFocus, getMessageList} = this.props; - return ( - {getMessageList()}} - {...Constants.refreshControl} + + ) + } + return null + } + + render () { + const {pending, didFocus, getMessageList} = this.props + return ( + { getMessageList() }} + {...Constants.refreshControl} /> } /> - ) - } + ) + } } - const styles = StyleSheet.create({ - "row": { - "height": 90, - "flexDirection": "row", - "borderBottomColor": "rgba(0, 0, 0, 0.02)", - "borderBottomWidth": 1, - "paddingTop": 25, - "paddingRight": 20, - "paddingBottom": 25, - "paddingLeft": 20, - alignItems: 'center' - }, - "img": { - "height": 40, - "width": 40, - "borderRadius": 20, - marginRight: 20 - }, - "topic": { - flexDirection: 'column', - width: width - 20 * 3 - 40 - }, - "title": { - "fontSize": 15 - }, - "topicFooter": { - "marginTop": 10, - "flexDirection": "row", - width: width - (20 + 90) - }, - "topicFooter text": { - "fontSize": 11, - "color": "rgba(0, 0, 0, 0.4)" - }, - "topicFooter date": { - "position": "absolute", - "right": 0, - "top": 0 - }, - "topicFooter count": { - "marginRight": 15 - }, - "topicFooter top": { - "fontSize": 11, - "marginTop": 1, - "marginRight": 0, - "marginBottom": 0, - "marginLeft": 10, - "color": "#E74C3C" - }, - "topicFooter good": { - "fontSize": 11, - "marginTop": 1, - "marginRight": 0, - "marginBottom": 0, - "marginLeft": 10, - "color": "#2ECC71" - }, - "topicFooter tab": { - "fontSize": 11, - "marginTop": 1, - "marginRight": 0, - "marginBottom": 0, - "marginLeft": 10 - }, - "loading": { - "marginTop": 250 - }, - rowFooterText: { - fontSize: 13, - color: 'rgba(0,0,0,0.7)' - }, - atText: { - color: '#E74C3C' - }, - replyText: { - color: '#2980B9' - }, - emptyMessage: { - marginTop: 80, - flex: 1 - }, - emptyMessageText: { - textAlign: 'center', - color: '#3498DB', - fontSize: 24 - } - -}); - - -export default MessageList; + 'row': { + 'height': 90, + 'flexDirection': 'row', + 'borderBottomColor': 'rgba(0, 0, 0, 0.02)', + 'borderBottomWidth': 1, + 'paddingTop': 25, + 'paddingRight': 20, + 'paddingBottom': 25, + 'paddingLeft': 20, + alignItems: 'center' + }, + 'img': { + 'height': 40, + 'width': 40, + 'borderRadius': 20, + marginRight: 20 + }, + 'topic': { + flexDirection: 'column', + width: width - 20 * 3 - 40 + }, + 'title': { + 'fontSize': 15 + }, + 'topicFooter': { + 'marginTop': 10, + 'flexDirection': 'row', + width: width - (20 + 90) + }, + 'topicFooter text': { + 'fontSize': 11, + 'color': 'rgba(0, 0, 0, 0.4)' + }, + 'topicFooter date': { + 'position': 'absolute', + 'right': 0, + 'top': 0 + }, + 'topicFooter count': { + 'marginRight': 15 + }, + 'topicFooter top': { + 'fontSize': 11, + 'marginTop': 1, + 'marginRight': 0, + 'marginBottom': 0, + 'marginLeft': 10, + 'color': '#E74C3C' + }, + 'topicFooter good': { + 'fontSize': 11, + 'marginTop': 1, + 'marginRight': 0, + 'marginBottom': 0, + 'marginLeft': 10, + 'color': '#2ECC71' + }, + 'topicFooter tab': { + 'fontSize': 11, + 'marginTop': 1, + 'marginRight': 0, + 'marginBottom': 0, + 'marginLeft': 10 + }, + 'loading': { + 'marginTop': 250 + }, + rowFooterText: { + fontSize: 13, + color: 'rgba(0,0,0,0.7)' + }, + atText: { + color: '#E74C3C' + }, + replyText: { + color: '#2980B9' + }, + emptyMessage: { + marginTop: 80, + flex: 1 + }, + emptyMessageText: { + textAlign: 'center', + color: '#3498DB', + fontSize: 24 + } + +}) + +export default MessageList diff --git a/src/components/MessageOverlay.js b/src/components/MessageOverlay.js index fb9c5a05..f6f1f907 100644 --- a/src/components/MessageOverlay.js +++ b/src/components/MessageOverlay.js @@ -1,109 +1,102 @@ -import React, {Component, PropTypes} from 'react'; -import {View, Text, StyleSheet, Dimensions} from 'react-native'; -import OverlayButton from './base/OverlayButton'; -import Icon from 'react-native-vector-icons/Ionicons'; - +import React, {Component, PropTypes} from 'react' +import {View, Text, StyleSheet, Dimensions} from 'react-native' +import OverlayButton from './base/OverlayButton' +import Icon from 'react-native-vector-icons/Ionicons' class MessageOverlay extends Component { - static propTypes = { - count: PropTypes.number, - toMessage: PropTypes.func - }; - - - static defaultProps = { - count: 0, - toMessage: ()=>null - }; - - - _renderMessageCount() { - const count = this.props.count; - - if (count > 0) { - return ( - - - {count > 999 ? '1k+' : count} - - - ) - } - - return null - } - - - render() { - if (this.props.user) { - return ( - this.props.toMessage() }> - - - - - {this._renderMessageCount()} - - ) - } - return null - } + static propTypes = { + count: PropTypes.number, + toMessage: PropTypes.func + }; + + static defaultProps = { + count: 0, + toMessage: () => null + }; + + _renderMessageCount () { + const count = this.props.count + + if (count > 0) { + return ( + + + {count > 999 ? '1k+' : count} + + + ) + } + + return null + } + + render () { + if (this.props.user) { + return ( + this.props.toMessage()}> + + + + + {this._renderMessageCount()} + + ) + } + return null + } } - -const overlaySize = 45; -const countBoxSize = 20; -const countTextSize = 10; - +const overlaySize = 45 +const countBoxSize = 20 +const countTextSize = 10 const styles = StyleSheet.create({ - iconWrapper: { - height: overlaySize, - width: overlaySize, - borderRadius: overlaySize / 2, - flex: 1, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center' - }, - icon: { - flex: 1, - textAlign: 'center' - }, - countWrapper: { - height: countBoxSize, - width: countBoxSize, - borderRadius: countBoxSize / 2, - backgroundColor: 'red', - position: 'absolute', - right: -5, - top: -5 - }, - countText: { - color: 'rgba(255,255,255,0.8)', - fontSize: countTextSize, - lineHeight: countBoxSize - countTextSize / 2, - textAlign: 'center', - height: countBoxSize, - width: countBoxSize, - borderRadius: countBoxSize / 2, - backgroundColor: 'transparent' - }, - position: { - right: 20, - bottom: 20 - }, - container: { - backgroundColor: 'blue', - borderRadius: overlaySize / 2 - } -}); - - -export default MessageOverlay; + iconWrapper: { + height: overlaySize, + width: overlaySize, + borderRadius: overlaySize / 2, + flex: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center' + }, + icon: { + flex: 1, + textAlign: 'center' + }, + countWrapper: { + height: countBoxSize, + width: countBoxSize, + borderRadius: countBoxSize / 2, + backgroundColor: 'red', + position: 'absolute', + right: -5, + top: -5 + }, + countText: { + color: 'rgba(255,255,255,0.8)', + fontSize: countTextSize, + lineHeight: countBoxSize - countTextSize / 2, + textAlign: 'center', + height: countBoxSize, + width: countBoxSize, + borderRadius: countBoxSize / 2, + backgroundColor: 'transparent' + }, + position: { + right: 20, + bottom: 20 + }, + container: { + backgroundColor: 'blue', + borderRadius: overlaySize / 2 + } +}) + +export default MessageOverlay diff --git a/src/components/Nav.js b/src/components/Nav.js index 7f113e3b..ebfed161 100644 --- a/src/components/Nav.js +++ b/src/components/Nav.js @@ -1,98 +1,93 @@ -import React, {Component} from 'react'; -import {View, Text, StyleSheet, TouchableOpacity, Dimensions, Platform} from 'react-native'; - - -const {height, width} = Dimensions.get('window'); +import React, {Component} from 'react' +import {View, Text, StyleSheet, TouchableOpacity, Dimensions, Platform} from 'react-native' +const {height, width} = Dimensions.get('window') class Nav extends Component { - _renderNavContent() { - let navs = this.props.navs || {}; - - return ['Left', 'Center', 'Right'].map((position)=> { - let nav = navs[position]; - if (nav) { - return ( - - - - {nav.text} - - - - ) - } - return ( - - - - ) - }) - } + _renderNavContent () { + let navs = this.props.navs || {} + return ['Left', 'Center', 'Right'].map((position) => { + let nav = navs[position] + if (nav) { + return ( + + + + {nav.text} + + + + ) + } + return ( + + + + ) + }) + } - render() { - return ( - this.nav=view} - style={styles.nav}> + render () { + return ( + this.nav = view} + style={styles.nav}> - {this._renderNavContent()} + {this._renderNavContent()} - - ) - } + + ) + } } - -const navHeight = 55; -const statusBarHeight = Platform.OS === 'ios' ? 20 : 0; - +const navHeight = 55 +const statusBarHeight = Platform.OS === 'ios' ? 20 : 0 const styles = StyleSheet.create({ - nav: { - height: navHeight + statusBarHeight, - width: width, - borderBottomColor: 'rgba(0,0,0,0.03)', - backgroundColor: '#292829', - borderBottomWidth: 1, - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - paddingTop: statusBarHeight, - paddingLeft: 15, - paddingRight: 15 - }, - navItem: { - color: 'rgba(255,255,255,0.7)', - fontSize: 16 - }, - textWrapper: { - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - }, - textWrapperRight: { - flex: 1, - alignItems: 'flex-end' - }, - textWrapperLeft: { - flex: 1, - alignItems: 'flex-start' - }, - textWrapperCenter: { - flex: 2, - }, - navLeft: {}, - navRight: {}, - navCenter: { - color: 'rgba(241,196,15,0.9)', - } -}); + nav: { + height: navHeight + statusBarHeight, + width: width, + borderBottomColor: 'rgba(0,0,0,0.03)', + backgroundColor: '#292829', + borderBottomWidth: 1, + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + paddingTop: statusBarHeight, + paddingLeft: 15, + paddingRight: 15 + }, + navItem: { + color: 'rgba(255,255,255,0.7)', + fontSize: 16 + }, + textWrapper: { + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center' + }, + textWrapperRight: { + flex: 1, + alignItems: 'flex-end' + }, + textWrapperLeft: { + flex: 1, + alignItems: 'flex-start' + }, + textWrapperCenter: { + flex: 2 + }, + navLeft: {}, + navRight: {}, + navCenter: { + color: 'rgba(241,196,15,0.9)' + } +}) -Nav.navHeight = navHeight; +Nav.navHeight = navHeight -export default Nav; +export default Nav diff --git a/src/components/ScrollableTabs.js b/src/components/ScrollableTabs.js index c9ceff72..3cbeccc1 100644 --- a/src/components/ScrollableTabs.js +++ b/src/components/ScrollableTabs.js @@ -1,4 +1,4 @@ -import React, {Component, PropTypes} from 'react'; +import React, {Component, PropTypes} from 'react' import { View, Dimensions, @@ -9,316 +9,294 @@ import { ScrollView, Animated, ViewPagerAndroid -} from 'react-native'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; - - -const {height, width} = Dimensions.get('window'); +} from 'react-native' +import PureRenderMixin from 'react-addons-pure-render-mixin' +const {height, width} = Dimensions.get('window') class ScrollableTabs extends Component { - static propTypes = { - tabs: PropTypes.array, - tabNavItemWidth: PropTypes.number, - index: PropTypes.number, - onPageChanged: PropTypes.func, - onPageChangedAndAnimateEnd: PropTypes.func - }; - - static defaultProps = { - tabs: [], - tabNavItemWidth: 60 - }; - - - constructor(props) { - super(props); - const {tabNavItemWidth, tabs} = props; - this.space = (width - tabNavItemWidth * 3) / 2; - this.navContentWidth = (tabs.length + 2) * tabNavItemWidth + this.space * (tabs.length + 1); - this.index = props.index || Math.floor(tabs.length / 2); - const offset = this.index * (this.space + tabNavItemWidth); - this.state = { - x: new Animated.Value(-offset) - }; - this.state.x.addListener((e)=> { - if (e.value % (this.space + tabNavItemWidth) === 0) { - let index = Math.abs(e.value / (this.space + tabNavItemWidth)); - this._scrolling = false; - typeof this.props.onPageChangedAndAnimateEnd === 'function' && this.props.onPageChangedAndAnimateEnd(index, this.isScrolling()); - } - }); - this._navs = {}; - this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); - } - - - isScrolling() { - return ()=> this._scrolling; - } - - - _updateNavScale(offset) { - const space = this.space + this.props.tabNavItemWidth; - this.props.tabs.forEach((item, index)=> { - let min = (index - 1) * space; - let max = (index + 1) * space; - let center = index * space; - - let scale = 0; - if (offset > min && offset < center) { - scale = (offset - min) / space; - } - if (offset > center && offset < max) { - scale = (max - offset) / space; - } - - if (offset === center) { - scale = 1; - } - this._navs[index].setNativeProps({ - style: this._getActiveNavItemStyle(scale) - }); - } - ); - } - - - _animateScroll(x) { - const {tabNavItemWidth} = this.props; - const navContentOffset = (this.space + tabNavItemWidth) / width * x; - Animated.event( - [{ - offset: this.state.x - }] + static propTypes = { + tabs: PropTypes.array, + tabNavItemWidth: PropTypes.number, + index: PropTypes.number, + onPageChanged: PropTypes.func, + onPageChangedAndAnimateEnd: PropTypes.func + }; + + static defaultProps = { + tabs: [], + tabNavItemWidth: 60 + }; + + constructor (props) { + super(props) + const {tabNavItemWidth, tabs} = props + this.space = (width - tabNavItemWidth * 3) / 2 + this.navContentWidth = (tabs.length + 2) * tabNavItemWidth + this.space * (tabs.length + 1) + this.index = props.index || Math.floor(tabs.length / 2) + const offset = this.index * (this.space + tabNavItemWidth) + this.state = { + x: new Animated.Value(-offset) + } + this.state.x.addListener((e) => { + if (e.value % (this.space + tabNavItemWidth) === 0) { + let index = Math.abs(e.value / (this.space + tabNavItemWidth)) + this._scrolling = false + typeof this.props.onPageChangedAndAnimateEnd === 'function' && this.props.onPageChangedAndAnimateEnd(index, this.isScrolling()) + } + }) + this._navs = {} + this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this) + } + + isScrolling () { + return () => this._scrolling + } + + _updateNavScale (offset) { + const space = this.space + this.props.tabNavItemWidth + this.props.tabs.forEach((item, index) => { + let min = (index - 1) * space + let max = (index + 1) * space + let center = index * space + + let scale = 0 + if (offset > min && offset < center) { + scale = (offset - min) / space + } + if (offset > center && offset < max) { + scale = (max - offset) / space + } + + if (offset === center) { + scale = 1 + } + this._navs[index].setNativeProps({ + style: this._getActiveNavItemStyle(scale) + }) + } + ) + } + + _animateScroll (x) { + const {tabNavItemWidth} = this.props + const navContentOffset = (this.space + tabNavItemWidth) / width * x + Animated.event( + [{ + offset: this.state.x + }] )({ - offset: -navContentOffset - }); - this._updateNavScale(navContentOffset); - } + offset: -navContentOffset +}) + this._updateNavScale(navContentOffset) + } - - _onScroll(e) { - const {contentSize={}} = e.nativeEvent; + _onScroll (e) { + const {contentSize = {}} = e.nativeEvent // 一下一行代码为解决一个奇葩的bug - if (contentSize.height === 0 && contentSize.width === 0) return; - - - const {x} = e.nativeEvent.contentOffset; - this._scrolling = true; - this._animateScroll(x); - } - - - _onMomentumScrollBegin(e) { - this._scrolling = true; - const offsetX = e.nativeEvent.contentOffset.x; - this._animateScroll(offsetX); - } - - - _onMomentumScrollEnd(e) { - this._scrolling = true; - const offsetX = e.nativeEvent.contentOffset.x; - const page = parseInt(offsetX / width, 10); - this._animateScroll(offsetX); - if (page !== this.index) { - typeof this.props.onPageChanged === 'function' && this.props.onPageChanged(page, this.isScrolling()); - } - this.index = page; - } - - - _onPageSelected(e) { - const {position} = e.nativeEvent; - this.index = position; - if (position === undefined) { - return; - } - typeof this.props.onPageChanged === 'function' && this.props.onPageChanged(position, this.isScrolling()); - } - - - _onAndroidPageScroll(e) { - const {offset, position} = e.nativeEvent; - let x = (position + offset) * width; - this._animateScroll(x); - } - - - _onNavItemPress(index) { - if (Platform.OS === 'ios') { - this.scrollView.scrollTo({ - x: width * index, - y: 0, - animated: true - }); - } - else { - this.viewPager.setPage(index); - } - } - - - _getActiveNavItemStyle(opacity) { - return { - borderTopColor: 'rgba(241,196,15,' + opacity + ')' - }; - } - - - _renderNavs() { - return this.props.tabs.map((item, index)=> { - let activeStyle = this._getActiveNavItemStyle(0); - if (index === this.index) { - activeStyle = this._getActiveNavItemStyle(1) - } - - return ( - - this._navs[index]=view} key={index} - style={[styles.navItem, { width: this.props.tabNavItemWidth }, activeStyle]}> - - - { item } - - - - - ); - }); - } - - - _renderChildren() { - return this.props.children.map((pageContent, index)=> { - return ( - - { pageContent } - - ); - }); - } - - - _renderPageScroll() { - const initContentOffset = { - x: this.index * width, - y: 0 - }; - - if (Platform.OS === 'ios') { - return ( - this.scrollView=view} - contentOffset={ initContentOffset } - alwaysBounceVertical={false} - automaticallyAdjustContentInsets={false} - showsHorizontalScrollIndicator={false} - showsVerticalScrollIndicator={false} - scrollsToTop={false} - scrollEventThrottle={16} - onScroll={this._onScroll.bind(this)} - onMomentumScrollBegin={this._onMomentumScrollBegin.bind(this)} - onMomentumScrollEnd={this._onMomentumScrollEnd.bind(this)} - keyboardDismissMode="on-drag" + if (contentSize.height === 0 && contentSize.width === 0) { return } + + const {x} = e.nativeEvent.contentOffset + this._scrolling = true + this._animateScroll(x) + } + + _onMomentumScrollBegin (e) { + this._scrolling = true + const offsetX = e.nativeEvent.contentOffset.x + this._animateScroll(offsetX) + } + + _onMomentumScrollEnd (e) { + this._scrolling = true + const offsetX = e.nativeEvent.contentOffset.x + const page = parseInt(offsetX / width, 10) + this._animateScroll(offsetX) + if (page !== this.index) { + typeof this.props.onPageChanged === 'function' && this.props.onPageChanged(page, this.isScrolling()) + } + this.index = page + } + + _onPageSelected (e) { + const {position} = e.nativeEvent + this.index = position + if (position === undefined) { + return + } + typeof this.props.onPageChanged === 'function' && this.props.onPageChanged(position, this.isScrolling()) + } + + _onAndroidPageScroll (e) { + const {offset, position} = e.nativeEvent + let x = (position + offset) * width + this._animateScroll(x) + } + + _onNavItemPress (index) { + if (Platform.OS === 'ios') { + this.scrollView.scrollTo({ + x: width * index, + y: 0, + animated: true + }) + } else { + this.viewPager.setPage(index) + } + } + + _getActiveNavItemStyle (opacity) { + return { + borderTopColor: 'rgba(241,196,15,' + opacity + ')' + } + } + + _renderNavs () { + return this.props.tabs.map((item, index) => { + let activeStyle = this._getActiveNavItemStyle(0) + if (index === this.index) { + activeStyle = this._getActiveNavItemStyle(1) + } + + return ( + + this._navs[index] = view} key={index} + style={[styles.navItem, { width: this.props.tabNavItemWidth }, activeStyle]}> + + + { item } + + + + + ) + }) + } + + _renderChildren () { + return this.props.children.map((pageContent, index) => { + return ( + + { pageContent } + + ) + }) + } + + _renderPageScroll () { + const initContentOffset = { + x: this.index * width, + y: 0 + } + + if (Platform.OS === 'ios') { + return ( + this.scrollView = view} + contentOffset={initContentOffset} + alwaysBounceVertical={false} + automaticallyAdjustContentInsets={false} + showsHorizontalScrollIndicator={false} + showsVerticalScrollIndicator={false} + scrollsToTop={false} + scrollEventThrottle={16} + onScroll={this._onScroll.bind(this)} + onMomentumScrollBegin={this._onMomentumScrollBegin.bind(this)} + onMomentumScrollEnd={this._onMomentumScrollEnd.bind(this)} + keyboardDismissMode='on-drag' > - { this._renderChildren() } - - - ); - } - return ( - this.viewPager=view} - initialPage={this.index} - style={styles.scrollableContentAndroid} - onPageSelected={this._onPageSelected.bind(this)} - onPageScroll={ this._onAndroidPageScroll.bind(this)}> - - { this._renderChildren() } - - - ); - } - - - render() { - return ( - - - - - - - { this._renderNavs() } - - - - - - { this._renderPageScroll() } - - - ); - } + { this._renderChildren() } + + + ) + } + return ( + this.viewPager = view} + initialPage={this.index} + style={styles.scrollableContentAndroid} + onPageSelected={this._onPageSelected.bind(this)} + onPageScroll={this._onAndroidPageScroll.bind(this)}> + + { this._renderChildren() } + + + ) + } + + render () { + return ( + + + + + + + { this._renderNavs() } + + + + + + { this._renderPageScroll() } + + + ) + } } - -const statusBarSpace = Platform.OS === 'ios' ? 20 : 0; - +const statusBarSpace = Platform.OS === 'ios' ? 20 : 0 const styles = StyleSheet.create({ - navWrapper: { - height: 40 + statusBarSpace, - backgroundColor: 'rgba(0,0,0,0.8)' - }, - statusBarSpace: { - height: statusBarSpace - }, - nav: { - height: 40, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - }, - navItem: { - height: 40, - borderTopWidth: 4, - borderTopColor: 'transparent', - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center' - }, - itemText: { - textAlign: 'center', - color: 'rgba(255,255,255,0.7)', - flex: 1 - }, - page: { - width, - backgroundColor: 'white' - }, - scrollableContentAndroid: { - flex: 1, - backgroundColor: 'white' - }, - container: { - flex: 1 - } -}); - - -export default ScrollableTabs; + navWrapper: { + height: 40 + statusBarSpace, + backgroundColor: 'rgba(0,0,0,0.8)' + }, + statusBarSpace: { + height: statusBarSpace + }, + nav: { + height: 40, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between' + }, + navItem: { + height: 40, + borderTopWidth: 4, + borderTopColor: 'transparent', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center' + }, + itemText: { + textAlign: 'center', + color: 'rgba(255,255,255,0.7)', + flex: 1 + }, + page: { + width, + backgroundColor: 'white' + }, + scrollableContentAndroid: { + flex: 1, + backgroundColor: 'white' + }, + container: { + flex: 1 + } +}) + +export default ScrollableTabs diff --git a/src/components/Setting.js b/src/components/Setting.js index 878e8463..d65bd66b 100644 --- a/src/components/Setting.js +++ b/src/components/Setting.js @@ -1,196 +1,184 @@ -import React, {Component, PropTypes} from 'react'; -import {View, StyleSheet, Text, TouchableOpacity, Dimensions, Platform} from 'react-native'; -import Icon from 'react-native-vector-icons/Ionicons'; -import Modal from './base/Modal'; -import Button from 'react-native-button'; -import Row from './base/Row'; - - -const {height, width} = Dimensions.get('window'); -const iconSize = 17; -const rowHeight = 50; +import React, {Component, PropTypes} from 'react' +import {View, StyleSheet, Text, TouchableOpacity, Dimensions, Platform} from 'react-native' +import Icon from 'react-native-vector-icons/Ionicons' +import Modal from './base/Modal' +import Button from 'react-native-button' +import Row from './base/Row' +const {height, width} = Dimensions.get('window') +const iconSize = 17 +const rowHeight = 50 class Setting extends Component { - static propTypes = { - router: PropTypes.object, - actions: PropTypes.object - }; - - - constructor(props) { - super(props); - this.state = { - visible: false - }; - } - - - onAboutPress() { - this.props.router.toAbout() - } - - - onLogoutPress() { - this.props.router.pop(); - this.props.actions.logout(); - } - - - onClearPress() { - const {actions} = this.props; - actions.clear(); - actions.toast('缓存清除成功'); - } - - - show() { - this.setState({ - visible: true - }); - } - - - render() { - if (!this.state.visible) return null; - return ( - - - - - - - + static propTypes = { + router: PropTypes.object, + actions: PropTypes.object + }; + + constructor (props) { + super(props) + this.state = { + visible: false + } + } + + onAboutPress () { + this.props.router.toAbout() + } + + onLogoutPress () { + this.props.router.pop() + this.props.actions.logout() + } + + onClearPress () { + const {actions} = this.props + actions.clear() + actions.toast('缓存清除成功') + } + + show () { + this.setState({ + visible: true + }) + } + + render () { + if (!this.state.visible) { return null } + return ( + + + + + + + 设置 - - - - - - - - + + + + + + + 关于 - - - - - - - + + + + + + + 清除缓存 - - - - - - - - + + + + + + + 退出 - - - - - this.setState({ visible : false })}> - - - - - ) - } + + + + + this.setState({ visible: false })}> + + + + + ) + } } - const styles = StyleSheet.create({ - container: { - flex: 1, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center' - }, - modalStyle: { - flex: 1, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center' - }, - contentWrapper: { - flex: 4, - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - backgroundColor: Platform.OS === 'ios' ? 'rgba(0,0,0,0.3)' : '#292829', - borderRadius: 3, - margin: 30 - }, - body: { - flex: 1, - height: rowHeight * 6, - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center' - }, - closeButton: { - flex: 1, - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center' - }, - row: { - flex: 1, - flexDirection: 'row', - justifyContent: 'flex-start', - alignItems: 'center', - borderRadius: 2, - paddingLeft: 20, - backgroundColor: 'rgba(0,0,0,0.1)', - width: width - 30 * 2 - }, - rowText: { - color: 'rgba(255,255,255,0.7)' - }, - rowTextWrapper: { - paddingLeft: 20 - }, - logoutText: { - color: '#E74C3C' - }, - header: { - backgroundColor: 'rgba(0,0,0,0.5)' - }, - closeIcon: { - height: 30, - width: 30, - borderRadius: 30 / 2, - borderColor: 'rgba(255,255,255,0.2)', - textAlign: 'center' - } -}); - - -export default Setting; + container: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center' + }, + modalStyle: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center' + }, + contentWrapper: { + flex: 4, + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: Platform.OS === 'ios' ? 'rgba(0,0,0,0.3)' : '#292829', + borderRadius: 3, + margin: 30 + }, + body: { + flex: 1, + height: rowHeight * 6, + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center' + }, + closeButton: { + flex: 1, + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center' + }, + row: { + flex: 1, + flexDirection: 'row', + justifyContent: 'flex-start', + alignItems: 'center', + borderRadius: 2, + paddingLeft: 20, + backgroundColor: 'rgba(0,0,0,0.1)', + width: width - 30 * 2 + }, + rowText: { + color: 'rgba(255,255,255,0.7)' + }, + rowTextWrapper: { + paddingLeft: 20 + }, + logoutText: { + color: '#E74C3C' + }, + header: { + backgroundColor: 'rgba(0,0,0,0.5)' + }, + closeIcon: { + height: 30, + width: 30, + borderRadius: 30 / 2, + borderColor: 'rgba(255,255,255,0.2)', + textAlign: 'center' + } +}) + +export default Setting diff --git a/src/components/TabBar.js b/src/components/TabBar.js index 1fa90b80..9e233e24 100644 --- a/src/components/TabBar.js +++ b/src/components/TabBar.js @@ -1,99 +1,92 @@ -import React, {Component, PropTypes} from 'react'; -import {StyleSheet, Text, TouchableOpacity, View, Dimensions, Animated} from 'react-native'; +import React, {Component, PropTypes} from 'react' +import {StyleSheet, Text, TouchableOpacity, View, Dimensions, Animated} from 'react-native' - -const { width } = Dimensions.get('window'); -const underLineColor = '#3498DB'; -const activeTabTextColor = 'rgba(0,0,0,9)'; -const normalTabTextColor = 'rgba(0,0,0,0.4)'; +const { width } = Dimensions.get('window') +const underLineColor = '#3498DB' +const activeTabTextColor = 'rgba(0,0,0,9)' +const normalTabTextColor = 'rgba(0,0,0,0.4)' class TabBar extends Component { - static propTypes = { - goToPage: PropTypes.func, - activeTab: PropTypes.number, - tabs: PropTypes.array, - tabStyle: PropTypes.object, - tabTextStyle: PropTypes.object, - activeTabTextColor: PropTypes.string, - normalTabTextColor: PropTypes.string - }; - - - static defaultProps = { - activeTabTextColor, - normalTabTextColor - }; - - - constructor(props) { - super(props); - } - - - renderTabOption(name, page) { - const isTabActive = this.props.activeTab === page; - const textStyle = { - color: isTabActive ? this.props.activeTabTextColor : this.props.normalTabTextColor, - fontWeight: isTabActive ? 'bold' : 'normal' - }; - - return ( - this.props.goToPage(page)}> - - - { name } - - - - ); - } - - - render() { - var numberOfTabs = this.props.tabs.length; - var tabUnderlineStyle = { - position: 'absolute', - width: width / numberOfTabs, - height: 4, - backgroundColor: underLineColor, - bottom: 0 - }; - - var left = this.props.scrollValue.interpolate({ - inputRange: [0, 1], outputRange: [0, width / numberOfTabs] - }); - - return ( - - {this.props.tabs.map((tab, i) => this.renderTabOption(tab, i))} - - - ); - } + static propTypes = { + goToPage: PropTypes.func, + activeTab: PropTypes.number, + tabs: PropTypes.array, + tabStyle: PropTypes.object, + tabTextStyle: PropTypes.object, + activeTabTextColor: PropTypes.string, + normalTabTextColor: PropTypes.string + }; + + static defaultProps = { + activeTabTextColor, + normalTabTextColor + }; + + constructor (props) { + super(props) + } + + renderTabOption (name, page) { + const isTabActive = this.props.activeTab === page + const textStyle = { + color: isTabActive ? this.props.activeTabTextColor : this.props.normalTabTextColor, + fontWeight: isTabActive ? 'bold' : 'normal' + } + + return ( + this.props.goToPage(page)}> + + + { name } + + + + ) + } + + render () { + var numberOfTabs = this.props.tabs.length + var tabUnderlineStyle = { + position: 'absolute', + width: width / numberOfTabs, + height: 4, + backgroundColor: underLineColor, + bottom: 0 + } + + var left = this.props.scrollValue.interpolate({ + inputRange: [0, 1], outputRange: [0, width / numberOfTabs] + }) + + return ( + + {this.props.tabs.map((tab, i) => this.renderTabOption(tab, i))} + + + ) + } } - const styles = StyleSheet.create({ - tab: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - paddingBottom: 10, - paddingTop: 10 - }, - - tabs: { - height: 50 + 4, - flexDirection: 'row', - marginTop: 0, - borderWidth: 1, - borderTopWidth: 0, - borderLeftWidth: 0, - borderRightWidth: 0, - borderBottomColor: 'rgba(0,0,0,0.06)', - justifyContent: 'space-around' - } -}); - - -export default TabBar; + tab: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + paddingBottom: 10, + paddingTop: 10 + }, + + tabs: { + height: 50 + 4, + flexDirection: 'row', + marginTop: 0, + borderWidth: 1, + borderTopWidth: 0, + borderLeftWidth: 0, + borderRightWidth: 0, + borderBottomColor: 'rgba(0,0,0,0.06)', + justifyContent: 'space-around' + } +}) + +export default TabBar diff --git a/src/components/TopicRow.js b/src/components/TopicRow.js index ac06b156..ca71951b 100644 --- a/src/components/TopicRow.js +++ b/src/components/TopicRow.js @@ -1,97 +1,89 @@ -import React, {Component, PropTypes} from 'react'; -import {View, StyleSheet, Text, Image, TouchableHighlight, Dimensions} from 'react-native'; -import { parseImgUrl } from '../utils'; - - -const { width } = Dimensions.get('window'); +import React, {Component, PropTypes} from 'react' +import {View, StyleSheet, Text, Image, TouchableHighlight, Dimensions} from 'react-native' +import { parseImgUrl } from '../utils' +const { width } = Dimensions.get('window') class TopicRow extends Component { - static propTypes = { - topic: PropTypes.object, - footer: PropTypes.node, - onPress: PropTypes.func - }; - - - static defaultProps = { - onPress: ()=>null - }; - - - render() { - const { topic } = this.props; - - - return ( - {this.props.onPress(topic)}} - underlayColor='#3498DB' - key={topic.id}> - - - - this.imgView=view} - style={styles.img} - source={{uri: parseImgUrl(topic.author.avatar_url) }}> - - - - - this.titleText=view} - numberOfLines={1} - style={[styles.title]}> - { topic.title } - - - - { this.props.footer } - - - - - ) - } + static propTypes = { + topic: PropTypes.object, + footer: PropTypes.node, + onPress: PropTypes.func + }; + + static defaultProps = { + onPress: () => null + }; + + render () { + const { topic } = this.props + + return ( + { this.props.onPress(topic) }} + underlayColor='#3498DB' + key={topic.id}> + + + + this.imgView = view} + style={styles.img} + source={{uri: parseImgUrl(topic.author.avatar_url) }} /> + + + + this.titleText = view} + numberOfLines={1} + style={[styles.title]}> + { topic.title } + + + + { this.props.footer } + + + + + ) + } } - var styles = StyleSheet.create({ - "row": { - "height": 90, - "flexDirection": "row", - "borderBottomColor": "rgba(0, 0, 0, 0.02)", - "borderBottomWidth": 1, - "paddingTop": 25, - "paddingRight": 0, - "paddingBottom": 25, - "paddingLeft": 20 - }, - "imgWrapper": { - "width": 90, - "position": "absolute", - "left": 20, - "top": 25, - "height": 65 - }, - "img": { - "height": 40, - "width": 40, - "borderRadius": 20 - }, - "topic": { - "marginLeft": 60, - width: width - 100 - }, - "title": { - "fontSize": 15 - }, - "topicFooter": { - "marginTop": 12, - "flexDirection": "row" - } -}); - - -export default TopicRow; + 'row': { + 'height': 90, + 'flexDirection': 'row', + 'borderBottomColor': 'rgba(0, 0, 0, 0.02)', + 'borderBottomWidth': 1, + 'paddingTop': 25, + 'paddingRight': 0, + 'paddingBottom': 25, + 'paddingLeft': 20 + }, + 'imgWrapper': { + 'width': 90, + 'position': 'absolute', + 'left': 20, + 'top': 25, + 'height': 65 + }, + 'img': { + 'height': 40, + 'width': 40, + 'borderRadius': 20 + }, + 'topic': { + 'marginLeft': 60, + width: width - 100 + }, + 'title': { + 'fontSize': 15 + }, + 'topicFooter': { + 'marginTop': 12, + 'flexDirection': 'row' + } +}) + +export default TopicRow diff --git a/src/components/UserOverlay.js b/src/components/UserOverlay.js index 073dc305..4023bc35 100644 --- a/src/components/UserOverlay.js +++ b/src/components/UserOverlay.js @@ -1,85 +1,78 @@ -import React, {Component, PropTypes} from 'react'; -import {StyleSheet, Image, View} from 'react-native'; -import Icon from 'react-native-vector-icons/Ionicons.js'; -import OverlayButton from './base/OverlayButton'; -import { parseImgUrl } from '../utils'; - +import React, {Component, PropTypes} from 'react' +import {StyleSheet, Image, View} from 'react-native' +import Icon from 'react-native-vector-icons/Ionicons.js' +import OverlayButton from './base/OverlayButton' +import { parseImgUrl } from '../utils' class UserOverlay extends Component { - static propTypes = { - toLogin: PropTypes.func, - toUser: PropTypes.func - }; - - - _onPress() { - const { user, toLogin, toUser } = this.props; - if (user) { - toUser(); - } - else { - toLogin(); - } - } + static propTypes = { + toLogin: PropTypes.func, + toUser: PropTypes.func + }; + _onPress () { + const { user, toLogin, toUser } = this.props + if (user) { + toUser() + } else { + toLogin() + } + } - _renderOverlayContent() { - if (this.props.user) { - const uri = parseImgUrl(this.props.user.avatar_url); - return ( - - - ) - } + _renderOverlayContent () { + if (this.props.user) { + const uri = parseImgUrl(this.props.user.avatar_url) + return ( + + ) + } - return ( - - + - - ) - } + + ) + } - - render() { - return ( - - {this._renderOverlayContent()} - - ) - } + render () { + return ( + + {this._renderOverlayContent()} + + ) + } } - const styles = StyleSheet.create({ - userImg: { - borderWidth: 2, - borderColor: 'rgba(241,196,15,0.9)', - width: 45, - height: 45, - borderRadius: 45 / 2 - }, - iconWrapper: { - flex: 1, - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - height: 45, - width: 45 - }, - icon: { - flex: 1, - textAlign: 'center' - } -}); + userImg: { + borderWidth: 2, + borderColor: 'rgba(241,196,15,0.9)', + width: 45, + height: 45, + borderRadius: 45 / 2 + }, + iconWrapper: { + flex: 1, + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + height: 45, + width: 45 + }, + icon: { + flex: 1, + textAlign: 'center' + } +}) -export default UserOverlay; +export default UserOverlay diff --git a/src/components/UserTopicList.js b/src/components/UserTopicList.js index 9a244c13..8010ee6b 100644 --- a/src/components/UserTopicList.js +++ b/src/components/UserTopicList.js @@ -1,109 +1,99 @@ -import React, {Component} from 'react'; -import {View, StyleSheet, Text, ListView, Dimensions} from 'react-native'; -import moment from 'moment'; -import TopicRow from './TopicRow'; - - -const { width } = Dimensions.get('window'); +import React, {Component} from 'react' +import {View, StyleSheet, Text, ListView, Dimensions} from 'react-native' +import moment from 'moment' +import TopicRow from './TopicRow' +const { width } = Dimensions.get('window') class UserTopicList extends Component { - constructor(props) { - super(props); - this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); - this.data = this.props.data || []; - this.state = { - ds: this.ds.cloneWithRows(this.data) - } - } - - - componentWillReceiveProps(nextProps) { - if (nextProps.data != this.props.data) { - this.setState({ - ds: this.ds.cloneWithRows(nextProps.data) - }) - } - } - - - _onRowPress(topic) { - this.props.router.toTopic({ - topic: topic, - id: topic.id - }) - } - - - _renderRowFooter(topic) { - var date = moment(topic.last_reply_at).startOf('minute').fromNow(); - let dateItem = ( - - {date} - - ); - - let authorItem = ( - - {topic.author.loginname} - - ); - - - return [dateItem, authorItem] - } - + constructor (props) { + super(props) + this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}) + this.data = this.props.data || [] + this.state = { + ds: this.ds.cloneWithRows(this.data) + } + } + + componentWillReceiveProps (nextProps) { + if (nextProps.data != this.props.data) { + this.setState({ + ds: this.ds.cloneWithRows(nextProps.data) + }) + } + } + + _onRowPress (topic) { + this.props.router.toTopic({ + topic: topic, + id: topic.id + }) + } + + _renderRowFooter (topic) { + var date = moment(topic.last_reply_at).startOf('minute').fromNow() + let dateItem = ( + + {date} + + ) - _renderRow(topic) { - return ( - + let authorItem = ( + + {topic.author.loginname} + ) - } + return [dateItem, authorItem] + } - render() { - return ( - - - - ) - } + _renderRow (topic) { + return ( + + ) + } + + render () { + return ( + + + + ) + } } - var styles = StyleSheet.create({ - container: { - width: width - }, - "topicFooter text": { - "fontSize": 11, - "color": "rgba(0, 0, 0, 0.3)" - }, - "topicFooter date": { - "position": "absolute", - "right": 0, - "top": 0 - }, - "topicFooter author": { - position: 'absolute', - left: 0, - top: 0 - } -}); - - -export default UserTopicList; + container: { + width: width + }, + 'topicFooter text': { + 'fontSize': 11, + 'color': 'rgba(0, 0, 0, 0.3)' + }, + 'topicFooter date': { + 'position': 'absolute', + 'right': 0, + 'top': 0 + }, + 'topicFooter author': { + position: 'absolute', + left: 0, + top: 0 + } +}) + +export default UserTopicList diff --git a/src/components/base/CustomImage.js b/src/components/base/CustomImage.js index 7246263f..692f784d 100644 --- a/src/components/base/CustomImage.js +++ b/src/components/base/CustomImage.js @@ -1,135 +1,128 @@ import React, { Component, PropTypes -} from 'react'; +} from 'react' import { Image, View, StyleSheet, Text, TouchableOpacity -} from 'react-native'; -import Icon from 'react-native-vector-icons/Ionicons'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; - +} from 'react-native' +import Icon from 'react-native-vector-icons/Ionicons' +import PureRenderMixin from 'react-addons-pure-render-mixin' export default class CustomImage extends Component { - static propTypes = { - defaultSize: PropTypes.object, - maxImageWidth: PropTypes.number, - uri: PropTypes.string - }; - - - static defaultProps = { - defaultSize: { - height: 200, - width: 200 - } - }; + static propTypes = { + defaultSize: PropTypes.object, + maxImageWidth: PropTypes.number, + uri: PropTypes.string + }; + static defaultProps = { + defaultSize: { + height: 200, + width: 200 + } + }; - constructor(props) { - super(props); - this.state = { - size: props.defaultSize, - isLoaded: false, - error: false - }; - this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); - this._loadImg(props.uri, props.maxImageWidth); - } + constructor (props) { + super(props) + this.state = { + size: props.defaultSize, + isLoaded: false, + error: false + } + this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this) + this._loadImg(props.uri, props.maxImageWidth) + } + _loadImg (uri, maxImageWidth) { + if (!uri) { return } + const startTime = new Date().getTime() + Image.getSize(uri, (w, h) => { + if (w >= maxImageWidth) { + h = (maxImageWidth / w) * h + w = maxImageWidth + } + let leftTime = 500 - (new Date().getTime() - startTime) + if (leftTime > 0) { + setTimeout(() => { + this.setState({ + size: { + width: w, + height: h + }, + isLoaded: true + }) + }, leftTime) + } else { + this.setState({ + size: { + width: w, + height: h + }, + isLoaded: true + }) + } + }, () => { + this.setState({ + error: true + }) + }) + } - _loadImg(uri, maxImageWidth) { - if (!uri) return; - const startTime = new Date().getTime(); - Image.getSize(uri, (w, h)=> { - if (w >= maxImageWidth) { - h = (maxImageWidth / w) * h; - w = maxImageWidth; - } - let leftTime = 500 - (new Date().getTime() - startTime); - if (leftTime > 0) { - setTimeout(()=> { - this.setState({ - size: { - width: w, - height: h - }, - isLoaded: true - }); - }, leftTime); - } - else { - this.setState({ - size: { - width: w, - height: h - }, - isLoaded: true - }); - } - }, ()=> { - this.setState({ - error: true - }); - }); - } - - - render() { - const {uri, style} = this.props; - const {size, isLoaded, error} = this.state; - if (isLoaded) { - return ( - - ); - } + ) + } - if (error) { - return ( - this._loadImg(this.props.uri, this.props.maxImageWidth)}> - - this._loadImg(this.props.uri, this.props.maxImageWidth)}> + + - + 点击重新加载图片 - - - ); - } + + + ) + } - return ( - - + - - ); - } + + ) + } } - const styles = StyleSheet.create({ - container: { - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - backgroundColor: 'rgba(0,0,0,0.05)', - borderRadius: 5, - margin: 10 - }, - icon: { - color: 'rgba(0,0,0,0.5)' - } -}); + container: { + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + backgroundColor: 'rgba(0,0,0,0.05)', + borderRadius: 5, + margin: 10 + }, + icon: { + color: 'rgba(0,0,0,0.5)' + } +}) diff --git a/src/components/base/ErrorHandle.js b/src/components/base/ErrorHandle.js index d144a26f..6fed8179 100644 --- a/src/components/base/ErrorHandle.js +++ b/src/components/base/ErrorHandle.js @@ -1,65 +1,60 @@ -import React, {Component, PropTypes} from 'react'; -import {View, StyleSheet, TouchableOpacity, Text} from 'react-native'; - +import React, {Component, PropTypes} from 'react' +import {View, StyleSheet, TouchableOpacity, Text} from 'react-native' class ErrorHandle extends Component { - static propTypes = { - infoText: PropTypes.string, - onPress: PropTypes.func, - buttonText: PropTypes.string - }; - - - static defaultProps = { - infoText: '网络出错啦, 请点击按钮重新加载', - buttonText: '重新获取' - }; - - - render() { - return ( - - - { this.props.infoText } - - - - - { this.props.buttonText } - - - - - ) - } + static propTypes = { + infoText: PropTypes.string, + onPress: PropTypes.func, + buttonText: PropTypes.string + }; + + static defaultProps = { + infoText: '网络出错啦, 请点击按钮重新加载', + buttonText: '重新获取' + }; + + render () { + return ( + + + { this.props.infoText } + + + + + { this.props.buttonText } + + + + + ) + } } - const styles = StyleSheet.create({ - container: { - height: 130, - flexDirection: 'column', - justifyContent: 'space-between', - alignItems: 'center', - backgroundColor: 'white', - padding: 20 - }, - button: { - width: 150, - height: 40, - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - borderRadius: 5, - backgroundColor: '#E74C3C' - }, - infoText: { - fontSize: 20 - }, - buttonText: { - color: 'white' - } -}); - - -export default ErrorHandle; + container: { + height: 130, + flexDirection: 'column', + justifyContent: 'space-between', + alignItems: 'center', + backgroundColor: 'white', + padding: 20 + }, + button: { + width: 150, + height: 40, + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + borderRadius: 5, + backgroundColor: '#E74C3C' + }, + infoText: { + fontSize: 20 + }, + buttonText: { + color: 'white' + } +}) + +export default ErrorHandle diff --git a/src/components/base/Html.js b/src/components/base/Html.js index 43f7c9c9..4eb68036 100644 --- a/src/components/base/Html.js +++ b/src/components/base/Html.js @@ -1,126 +1,118 @@ -import React, {Component, PropTypes} from 'react'; -import {StyleSheet, Image, Dimensions} from 'react-native'; -import _ from 'lodash'; -import HtmlRender from 'react-native-html-render'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; -import CustomImage from './CustomImage'; -import {parseImgUrl, link} from '../../utils'; +import React, {Component, PropTypes} from 'react' +import {StyleSheet, Image, Dimensions} from 'react-native' +import _ from 'lodash' +import HtmlRender from 'react-native-html-render' +import PureRenderMixin from 'react-addons-pure-render-mixin' +import CustomImage from './CustomImage' +import {parseImgUrl, link} from '../../utils' - -const {width, height} = Dimensions.get('window'); -const defaultMaxImageWidth = width - 30 - 20; +const {width} = Dimensions.get('window') +const defaultMaxImageWidth = width - 30 - 20 const regs = { - http: { - topic: /^https?:\/\/cnodejs\.org\/topic\/\w*/, - user: /^https?:\/\/cnodejs\.org\/user\/\w*/ - }, - gif: /.*\.gif$/ -}; - + http: { + topic: /^https?:\/\/cnodejs\.org\/topic\/\w*/, + user: /^https?:\/\/cnodejs\.org\/user\/\w*/ + }, + gif: /.*\.gif$/ +} const styles = StyleSheet.create({ - defaultImg: { - height: defaultMaxImageWidth, - width: defaultMaxImageWidth, - resizeMode: Image.resizeMode.cover, - borderRadius: 5, - margin: 10 - } -}); - + defaultImg: { + height: defaultMaxImageWidth, + width: defaultMaxImageWidth, + resizeMode: Image.resizeMode.cover, + borderRadius: 5, + margin: 10 + } +}) class Html extends Component { - static propTypes = { - router: PropTypes.object, - imgStyle: PropTypes.object - }; - - - static defaultProps = { - maxImageWidth: defaultMaxImageWidth - }; - - constructor(props) { - super(props); - this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); - } - - - _onLinkPress(url) { - let router = this.props.router; - - if (/^\/user\/\w*/.test(url)) { - let authorName = url.replace(/^\/user\//, ''); - - router.toUser({ - userName: authorName - }); - } - - if (/^https?:\/\/.*/.test(url)) { - if (regs.http.topic.test(url)) { - let topicId = url.replace(/^https?:\/\/cnodejs\.org\/topic\//, ''); - - return router.toTopic({ - id: topicId - }); - } - - if (regs.http.user.test(url)) { - let userName = url.replace(/^https?:\/\/cnodejs\.org\/user\//, ''); - - return router.toUser({ - userName: userName - }); - } - - link(url); - } - - if (/^mailto:\w*/.test(url)) { - link(url); - } - } - - - _renderNode(node, index, parent, type) { - const name = node.name; - const {imgStyle=styles.defaultImg, maxImageWidth} = this.props; - - - if (node.type == 'block' && type == 'block') { - if (name == 'img') { - const uri = parseImgUrl(node.attribs.src); - if (regs.gif.test(uri)) return null; - const imageId = _.uniqueId('image_'); - return ( - - ); - } - } - } - - - render() { - return ( - - ); - } + ) + } } -export default Html; +export default Html diff --git a/src/components/base/Loading.js b/src/components/base/Loading.js index 5247787c..81235330 100644 --- a/src/components/base/Loading.js +++ b/src/components/base/Loading.js @@ -1,63 +1,58 @@ -import React, {Component, PropTypes} from 'react'; -import {View, StyleSheet, Text, Dimensions, Animated} from 'react-native'; -import Spinner from './Spinner'; +import React, {Component, PropTypes} from 'react' +import {StyleSheet, Dimensions, Animated} from 'react-native' +import Spinner from './Spinner' - -const { height, width } = Dimensions.get('window'); -const toastWidth = width * 0.7; +const { height, width } = Dimensions.get('window') +const toastWidth = width * 0.7 class Loading extends Component { - static propTypes = { - show: PropTypes.bool - }; - - - static defaultProps = { - show: false - }; - - - constructor(props) { - super(props); - this.state = { - fadeAnim: new Animated.Value(0.4) - }; - } - - - render() { - const opacity = { - opacity: this.state.fadeAnim - }; - if (!this.props.show) return null; - return ( - - - - ) - } + static propTypes = { + show: PropTypes.bool + }; + + static defaultProps = { + show: false + }; + + constructor (props) { + super(props) + this.state = { + fadeAnim: new Animated.Value(0.4) + } + } + + render () { + const opacity = { + opacity: this.state.fadeAnim + } + if (!this.props.show) { return null } + return ( + + + + ) + } } const styles = StyleSheet.create({ - container: { - position: 'absolute', - backgroundColor: 'rgba(0,0,0,0.8)', - borderRadius: 5, - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - width: toastWidth, - left: (width - toastWidth) / 2, - top: (height - 60) / 2, - padding: 20 - }, - text: { - flex: 1, - color: 'white', - fontSize: 16, - textAlign: 'center' - } -}); - - -export default Loading; + container: { + position: 'absolute', + backgroundColor: 'rgba(0,0,0,0.8)', + borderRadius: 5, + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + width: toastWidth, + left: (width - toastWidth) / 2, + top: (height - 60) / 2, + padding: 20 + }, + text: { + flex: 1, + color: 'white', + fontSize: 16, + textAlign: 'center' + } +}) + +export default Loading diff --git a/src/components/base/Modal.js b/src/components/base/Modal.js index 431f09f2..4882bbd8 100644 --- a/src/components/base/Modal.js +++ b/src/components/base/Modal.js @@ -1,102 +1,92 @@ -import React, {Component, PropTypes} from 'react'; -import {Dimensions, View, StyleSheet, Animated, Easing, Platform, TouchableWithoutFeedback, findNodeHandle} from 'react-native'; +import React, {Component, PropTypes} from 'react' +import {Dimensions, View, StyleSheet, Animated, Easing, Platform, TouchableWithoutFeedback} from 'react-native' + if (Platform.OS !== 'web') { - var BlurView = require('react-native-blur').BlurView; + var BlurView = require('react-native-blur').BlurView } - -const {height, width} = Dimensions.get('window'); - +const {height, width} = Dimensions.get('window') class Modal extends Component { - static propTypes = { - blur: PropTypes.bool, - blurType: PropTypes.string, - onPressBackdrop: PropTypes.func - }; - - - static defaultProps = { - blur: false, - blurType: 'dark' - }; - - - constructor(props) { - super(props); - this.state = { - fadeAnim: new Animated.Value(0.2) - }; - } - - - componentDidMount() { - Animated.timing(this.state.fadeAnim, { - toValue: 1, - easing: Easing.quad, - duration: 100 - }).start(); - } - - - _onPress(e) { - const {pageY} = e.nativeEvent; - const {onPressBackdrop} = this.props; - if (height - pageY > 200) { - typeof onPressBackdrop == 'function' && onPressBackdrop(); - } - } - - - _renderChildren() { - if (this.props.blur) { - if (Platform.OS === 'ios') { - return ( - - { this.props.children } - - ) - } - return ( - - { this.props.children } - - ); - - } - return this.props.children; - } - - - render() { - return ( - - - { this._renderChildren() } - - - ) - } + static propTypes = { + blur: PropTypes.bool, + blurType: PropTypes.string, + onPressBackdrop: PropTypes.func + }; + + static defaultProps = { + blur: false, + blurType: 'dark' + }; + + constructor (props) { + super(props) + this.state = { + fadeAnim: new Animated.Value(0.2) + } + } + + componentDidMount () { + Animated.timing(this.state.fadeAnim, { + toValue: 1, + easing: Easing.quad, + duration: 100 + }).start() + } + + _onPress (e) { + const {pageY} = e.nativeEvent + const {onPressBackdrop} = this.props + if (height - pageY > 200) { + typeof onPressBackdrop === 'function' && onPressBackdrop() + } + } + + _renderChildren () { + if (this.props.blur) { + if (Platform.OS === 'ios') { + return ( + + { this.props.children } + + ) + } + return ( + + { this.props.children } + + ) + } + return this.props.children + } + + render () { + return ( + + + { this._renderChildren() } + + + ) + } } - const styles = StyleSheet.create({ - container: { - position: 'absolute', - flex: 1, - top: 0, - left: 0, - height, - width - }, - blur: { - height, - width - }, - opacity: { - backgroundColor: 'rgba(0,0,0,0.4)' - } -}); - - -export default Modal; + container: { + position: 'absolute', + flex: 1, + top: 0, + left: 0, + height, + width + }, + blur: { + height, + width + }, + opacity: { + backgroundColor: 'rgba(0,0,0,0.4)' + } +}) + +export default Modal diff --git a/src/components/base/OverlayButton.js b/src/components/base/OverlayButton.js index b1616716..cd2ac799 100644 --- a/src/components/base/OverlayButton.js +++ b/src/components/base/OverlayButton.js @@ -1,40 +1,37 @@ -import React, {Component} from 'react'; -import {View, StyleSheet, TouchableOpacity} from 'react-native'; - -const overlayButtonSize = 45; +import React, {Component} from 'react' +import {View, StyleSheet, TouchableOpacity} from 'react-native' +const overlayButtonSize = 45 class OverlayButton extends Component { - render() { - return ( - - - {this.props.children} - - - ) - } + render () { + return ( + + + {this.props.children} + + + ) + } } - const styles = StyleSheet.create({ - container: { - height: overlayButtonSize, - width: overlayButtonSize, - position: 'absolute', - borderRadius: overlayButtonSize / 2, - backgroundColor: 'rgba(0,0,0,0.7)', - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center' - }, - defaultPosition: { - left: 20, - bottom: 20 - } -}); - + container: { + height: overlayButtonSize, + width: overlayButtonSize, + position: 'absolute', + borderRadius: overlayButtonSize / 2, + backgroundColor: 'rgba(0,0,0,0.7)', + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center' + }, + defaultPosition: { + left: 20, + bottom: 20 + } +}) -export default OverlayButton; +export default OverlayButton diff --git a/src/components/base/Return.js b/src/components/base/Return.js index 608d026a..f1a7074a 100644 --- a/src/components/base/Return.js +++ b/src/components/base/Return.js @@ -1,49 +1,44 @@ -import React, {Component} from 'react'; -import {StyleSheet, Dimensions, View} from 'react-native'; -import OverlayButton from './OverlayButton'; -import Icon from 'react-native-vector-icons/Ionicons'; - - -const returnSize = 45; +import React, {Component} from 'react' +import {StyleSheet, View} from 'react-native' +import OverlayButton from './OverlayButton' +import Icon from 'react-native-vector-icons/Ionicons' +const returnSize = 45 class Return extends Component { - _onPress() { - this.props.router && this.props.router.pop && this.props.router.pop() - } - - - render() { - return ( - - - - - - ) - } + _onPress () { + this.props.router && this.props.router.pop && this.props.router.pop() + } + + render () { + return ( + + + + + + ) + } } - const styles = StyleSheet.create({ - returnIcon: { - flex: 1, - textAlign: 'center' - }, - iconWrapper: { - flex: 1, - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - height: returnSize, - width: returnSize - } -}); - - -export default Return; + returnIcon: { + flex: 1, + textAlign: 'center' + }, + iconWrapper: { + flex: 1, + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + height: returnSize, + width: returnSize + } +}) + +export default Return diff --git a/src/components/base/Row.js b/src/components/base/Row.js index aae3007f..36955151 100644 --- a/src/components/base/Row.js +++ b/src/components/base/Row.js @@ -1,32 +1,29 @@ -import React, {Component, PropTypes} from 'react'; -import {View, StyleSheet, TouchableHighlight} from 'react-native'; - +import React, {Component} from 'react' +import {View, StyleSheet, TouchableHighlight} from 'react-native' class Row extends Component { - render() { - return ( - - - { this.props.children } - - - ) - } + render () { + return ( + + + { this.props.children } + + + ) + } } - const styles = StyleSheet.create({ - container: { - flex: 1, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center' - }, - content: { - flex: 1, - flexDirection: 'row' - } -}); - + container: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center' + }, + content: { + flex: 1, + flexDirection: 'row' + } +}) -export default Row; +export default Row diff --git a/src/components/base/Spinner.js b/src/components/base/Spinner.js index 15eb22e1..984eb2ff 100644 --- a/src/components/base/Spinner.js +++ b/src/components/base/Spinner.js @@ -1,22 +1,18 @@ -import React, {Component, PropTypes} from 'react'; -import {ActivityIndicator} from 'react-native'; - +import React, {Component} from 'react' +import {ActivityIndicator} from 'react-native' class Spinner extends Component { - static defaultProps={ - color: 'rgba(241,196,15, 1.0)' - }; + static defaultProps={ + color: 'rgba(241,196,15, 1.0)' + }; - render() { - return ( - - ) - } + render () { + return ( + + ) + } } - -export default Spinner; - - +export default Spinner diff --git a/src/components/base/Toast.js b/src/components/base/Toast.js index 238f9f84..dc660607 100644 --- a/src/components/base/Toast.js +++ b/src/components/base/Toast.js @@ -1,101 +1,94 @@ -import React, {Component, PropTypes} from 'react'; -import {View, StyleSheet, Text, Dimensions, Animated} from 'react-native'; - -const {height, width} = Dimensions.get('window'); -const toastWidth = width * 0.7; -const defaultText = 'Toast'; -const defaultTimeout = 2000; +import React, {Component, PropTypes} from 'react' +import {StyleSheet, Text, Dimensions, Animated} from 'react-native' +const {height, width} = Dimensions.get('window') +const toastWidth = width * 0.7 +const defaultText = 'Toast' +const defaultTimeout = 2000 class Toast extends Component { - static propTypes = { - duration: PropTypes.number - }; - - - static defaultProps = { - duration: 300 - }; - - - constructor(props) { - super(props); - this.state = { - fadeAnim: new Animated.Value(0.4), - show: false, - text: defaultText, - timeout: defaultTimeout - }; - } - - - componentWillUnmount() { - clearTimeout(this.timeout); - } - - - show(text = defaultText, timeout = defaultTimeout) { - const {duration} = this.props; - Animated.timing(this.state.fadeAnim, { - toValue: 1, - duration: duration - }).start(); - - this.setState({ - show: true, - text, - timeout - }); - - this.timeout = setTimeout(()=> { - Animated.timing(this.state.fadeAnim, { - toValue: 0, - duration: duration - }).start(()=> { - this.setState({ - show: false - }); - }); - }, timeout - duration); - } - - - render() { - const opacity = { - opacity: this.state.fadeAnim - }; - if (!this.state.show) return null; - return ( - - - {this.state.text} - - - ) - } + static propTypes = { + duration: PropTypes.number + }; + + static defaultProps = { + duration: 300 + }; + + constructor (props) { + super(props) + this.state = { + fadeAnim: new Animated.Value(0.4), + show: false, + text: defaultText, + timeout: defaultTimeout + } + } + + componentWillUnmount () { + clearTimeout(this.timeout) + } + + show (text = defaultText, timeout = defaultTimeout) { + const {duration} = this.props + Animated.timing(this.state.fadeAnim, { + toValue: 1, + duration: duration + }).start() + + this.setState({ + show: true, + text, + timeout + }) + + this.timeout = setTimeout(() => { + Animated.timing(this.state.fadeAnim, { + toValue: 0, + duration: duration + }).start(() => { + this.setState({ + show: false + }) + }) + }, timeout - duration) + } + + render () { + const opacity = { + opacity: this.state.fadeAnim + } + if (!this.state.show) { return null } + return ( + + + {this.state.text} + + + ) + } } const styles = StyleSheet.create({ - container: { - position: 'absolute', - backgroundColor: 'rgba(0,0,0,0.8)', - borderRadius: 5, - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - width: toastWidth, - left: (width - toastWidth) / 2, - top: (height - 60) / 2, - padding: 20 - }, - text: { - flex: 1, - color: 'white', - fontSize: 16, - textAlign: 'center', - lineHeight: 16 * 1.5 - } -}); - - -export default Toast; + container: { + position: 'absolute', + backgroundColor: 'rgba(0,0,0,0.8)', + borderRadius: 5, + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + width: toastWidth, + left: (width - toastWidth) / 2, + top: (height - 60) / 2, + padding: 20 + }, + text: { + flex: 1, + color: 'white', + fontSize: 16, + textAlign: 'center', + lineHeight: 16 * 1.5 + } +}) + +export default Toast diff --git a/src/configs/Router.js b/src/configs/Router.js index 6f2628f3..4d0085eb 100644 --- a/src/configs/Router.js +++ b/src/configs/Router.js @@ -1,126 +1,114 @@ -import React from 'react'; -import ReactNative from 'react-native'; -import {Navigator, Platform, BackAndroid} from 'react-native'; -import _ from 'lodash'; -import * as About from '../layouts/About'; -import * as QRCode from '../layouts/QRCode'; -import * as Login from '../layouts/Login'; -import * as User from '../layouts/User'; -import * as Message from '../layouts/Message'; -import * as Topic from '../layouts/Topic'; -import * as Comment from '../layouts/Comment'; -import * as Publish from '../layouts/Publish'; -import * as HomeComponent from '../layouts/Home'; -import * as CustomSceneConfigs from '../configs/sceneConfig'; -import connectComponent from '../utils/connectComponent'; - - -const Home = connectComponent(HomeComponent); +import React from 'react' +import ReactNative from 'react-native' +import {Navigator, Platform, BackAndroid} from 'react-native' +import _ from 'lodash' +import * as About from '../layouts/About' +import * as QRCode from '../layouts/QRCode' +import * as Login from '../layouts/Login' +import * as User from '../layouts/User' +import * as Message from '../layouts/Message' +import * as Topic from '../layouts/Topic' +import * as Comment from '../layouts/Comment' +import * as Publish from '../layouts/Publish' +import * as HomeComponent from '../layouts/Home' +import * as CustomSceneConfigs from '../configs/sceneConfig' +import connectComponent from '../utils/connectComponent' + +const Home = connectComponent(HomeComponent) const { - SceneConfigs, -} = ReactNative; + SceneConfigs +} = ReactNative class Router { - constructor(navigator) { - this.navigator = navigator; - if (Platform.OS === 'android') { - BackAndroid.addEventListener('hardwareBackPress', ()=> { - const routesList = this.navigator.getCurrentRoutes(); - const currentRoute = routesList[routesList.length - 1]; - if (currentRoute.name !== 'home') { - navigator.pop(); - return true; - } - return false; - }); - } - } - - - push(props = {}, route) { - let routesList = this.navigator.getCurrentRoutes(); - let nextIndex = routesList[routesList.length - 1].index + 1; - route.props = props; - route.index = nextIndex; - route.sceneConfig = route.sceneConfig ? route.sceneConfig : CustomSceneConfigs.customFloatFromRight; - route.id = _.uniqueId(); - route.component = connectComponent(route.component); - this.navigator.push(route); - } - - - pop() { - this.navigator.pop(); - } - - - toAbout(props) { - this.push(props, { - component: About, - name: 'about', - sceneConfig: CustomSceneConfigs.customFloatFromBottom - }); - } - - - toLogin(props) { - this.push(props, { - component: Login, - name: 'login', - sceneConfig: CustomSceneConfigs.customFloatFromBottom - }) - } - - - toQRCode(props) { - this.push(props, { - component: QRCode, - name: 'qrcode', - sceneConfig: CustomSceneConfigs.customFloatFromBottom - }); - } - - - toUser(props) { - this.push(props, { - component: User, - name: 'user' - }); - } - - - toMessage(props) { - this.push(props, { - component: Message, - name: 'message' - }) - } - - - toTopic(props) { - this.push(props, { - component: Topic, - name: 'topic' - }) - } - - - toComment(props) { - this.push(props, { - component: Comment, - name: 'comment' - }) - } - - - toPublish(props) { - this.push(props, { - component: Publish, - name: 'publish' - }) - } + constructor (navigator) { + this.navigator = navigator + if (Platform.OS === 'android') { + BackAndroid.addEventListener('hardwareBackPress', () => { + const routesList = this.navigator.getCurrentRoutes() + const currentRoute = routesList[routesList.length - 1] + if (currentRoute.name !== 'home') { + navigator.pop() + return true + } + return false + }) + } + } + + push (props = {}, route) { + let routesList = this.navigator.getCurrentRoutes() + let nextIndex = routesList[routesList.length - 1].index + 1 + route.props = props + route.index = nextIndex + route.sceneConfig = route.sceneConfig ? route.sceneConfig : CustomSceneConfigs.customFloatFromRight + route.id = _.uniqueId() + route.component = connectComponent(route.component) + this.navigator.push(route) + } + + pop () { + this.navigator.pop() + } + + toAbout (props) { + this.push(props, { + component: About, + name: 'about', + sceneConfig: CustomSceneConfigs.customFloatFromBottom + }) + } + + toLogin (props) { + this.push(props, { + component: Login, + name: 'login', + sceneConfig: CustomSceneConfigs.customFloatFromBottom + }) + } + + toQRCode (props) { + this.push(props, { + component: QRCode, + name: 'qrcode', + sceneConfig: CustomSceneConfigs.customFloatFromBottom + }) + } + + toUser (props) { + this.push(props, { + component: User, + name: 'user' + }) + } + + toMessage (props) { + this.push(props, { + component: Message, + name: 'message' + }) + } + + toTopic (props) { + this.push(props, { + component: Topic, + name: 'topic' + }) + } + + toComment (props) { + this.push(props, { + component: Comment, + name: 'comment' + }) + } + + toPublish (props) { + this.push(props, { + component: Publish, + name: 'publish' + }) + } } - -export default Router; +export default Router diff --git a/src/configs/animations.js b/src/configs/animations.js index aae1c108..90865acd 100644 --- a/src/configs/animations.js +++ b/src/configs/animations.js @@ -1,33 +1,33 @@ -import React from 'react'; -import {LayoutAnimation} from 'react-native'; +import React from 'react' +import {LayoutAnimation} from 'react-native' -var animations = {}; +var animations = {} animations.keyboard = { - layout: { - spring: { - duration: 400, - create: { - duration: 300, - type: LayoutAnimation.Types.easeInEaseOut, - property: LayoutAnimation.Properties.opacity, - }, - update: { - type: LayoutAnimation.Types.spring, - springDamping: 400 - } - }, - easeInEaseOut: { - duration: 400, - create: { - type: LayoutAnimation.Types.easeInEaseOut, - property: LayoutAnimation.Properties.scaleXY, - }, - update: { - type: LayoutAnimation.Types.easeInEaseOut, - } - } - } -}; + layout: { + spring: { + duration: 400, + create: { + duration: 300, + type: LayoutAnimation.Types.easeInEaseOut, + property: LayoutAnimation.Properties.opacity + }, + update: { + type: LayoutAnimation.Types.spring, + springDamping: 400 + } + }, + easeInEaseOut: { + duration: 400, + create: { + type: LayoutAnimation.Types.easeInEaseOut, + property: LayoutAnimation.Properties.scaleXY + }, + update: { + type: LayoutAnimation.Types.easeInEaseOut + } + } + } +} -export default animations; +export default animations diff --git a/src/configs/index.js b/src/configs/index.js index bef24e3d..7b2c0044 100644 --- a/src/configs/index.js +++ b/src/configs/index.js @@ -1,16 +1,16 @@ -import packageJson from '../../package.json'; +import packageJson from '../../package.json' export default { - domain: 'https://cnodejs.org/', - apiPath: '/api/v1', - bgImgUri: 'http://7lrzfj.com1.z0.glb.clouddn.com/soliury213H.png', - replySuffix: '\nFrom [Noder](https://github.com/soliury/noder-react-native)', - sourceInGithub: 'https://github.com/soliury/noder-react-native', - package: packageJson, - author: { - blog: 'http://lingyong.me/about', - cnodeName: 'soliury' - }, - cnodeAbout: 'https://cnodejs.org/about', - RNWebPage: 'http://facebook.github.io/react-native/' + domain: 'https://cnodejs.org/', + apiPath: '/api/v1', + bgImgUri: 'http://7lrzfj.com1.z0.glb.clouddn.com/soliury213H.png', + replySuffix: '\nFrom [Noder](https://github.com/soliury/noder-react-native)', + sourceInGithub: 'https://github.com/soliury/noder-react-native', + package: packageJson, + author: { + blog: 'http://lingyong.me/about', + cnodeName: 'soliury' + }, + cnodeAbout: 'https://cnodejs.org/about', + RNWebPage: 'http://facebook.github.io/react-native/' } diff --git a/src/configs/sceneConfig.js b/src/configs/sceneConfig.js index 8b2f9d1d..3e00d2d5 100644 --- a/src/configs/sceneConfig.js +++ b/src/configs/sceneConfig.js @@ -1,28 +1,25 @@ -import React from 'react'; -import {Dimensions, Navigator} from 'react-native'; +import React from 'react' +import {Dimensions, Navigator} from 'react-native' -const { width } = Dimensions.get('window'); +const { width } = Dimensions.get('window') -const baseConfig = Navigator.SceneConfigs.FloatFromRight; +const baseConfig = Navigator.SceneConfigs.FloatFromRight const popGestureConfig = Object.assign({}, baseConfig.gestures.pop, { - edgeHitWidth: width / 3 -}); - + edgeHitWidth: width / 3 +}) const fullPopGestureConfig = Object.assign({}, Navigator.SceneConfigs.FloatFromBottom.gestures.pop, { - edgeHitWidth: width -}); - + edgeHitWidth: width +}) export const customFloatFromRight = Object.assign({}, baseConfig, { - gestures: { - pop: popGestureConfig - } -}); - + gestures: { + pop: popGestureConfig + } +}) export const customFloatFromBottom = Object.assign({}, Navigator.SceneConfigs.FloatFromBottom, { - gestures: { - pop: fullPopGestureConfig - } -}); + gestures: { + pop: fullPopGestureConfig + } +}) diff --git a/src/constants/ActionTypes.js b/src/constants/ActionTypes.js index 778568c6..c1859ed5 100644 --- a/src/constants/ActionTypes.js +++ b/src/constants/ActionTypes.js @@ -1,40 +1,36 @@ // Home -export const OPEN_LOGIN_MODAL = 'OPEN_LOGIN_MODAL'; -export const CLOSE_LOGIN_MODAL = 'CLOSE_LOGIN_MODAL'; -export const UPDATE_TAB = 'UPDATE_TAB'; - +export const OPEN_LOGIN_MODAL = 'OPEN_LOGIN_MODAL' +export const CLOSE_LOGIN_MODAL = 'CLOSE_LOGIN_MODAL' +export const UPDATE_TAB = 'UPDATE_TAB' // user -export const CHECK_TOKEN = 'CHECK_TOKEN'; -export const GET_USER_FROM_STORAGE = 'GET_USER_FROM_STORAGE'; -export const UPDATE_CLIENT_USER_INFO = 'UPDATE_CLIENT_USER_INFO'; -export const LOGOUT = 'LOGOUT'; -export const GET_USER_INFO = 'GET_USER_INFO'; -export const CLEAR = "CLEAR"; +export const CHECK_TOKEN = 'CHECK_TOKEN' +export const GET_USER_FROM_STORAGE = 'GET_USER_FROM_STORAGE' +export const UPDATE_CLIENT_USER_INFO = 'UPDATE_CLIENT_USER_INFO' +export const LOGOUT = 'LOGOUT' +export const GET_USER_INFO = 'GET_USER_INFO' +export const CLEAR = 'CLEAR' // utils -export const TOAST = 'TOAST'; -export const OPEN_TOAST = 'OPEN_TOAST'; -export const CLOSE_TOAST = 'CLOSE_TOAST'; - +export const TOAST = 'TOAST' +export const OPEN_TOAST = 'OPEN_TOAST' +export const CLOSE_TOAST = 'CLOSE_TOAST' // message -export const GET_UNREAD_MESSAGE_COUNT = 'GET_UNREAD_MESSAGE_COUNT'; -export const MARK_AS_READ = 'MARK_AS_READ'; -export const GET_MESSAGES_LIST = 'GET_MESSAGES_LIST'; - +export const GET_UNREAD_MESSAGE_COUNT = 'GET_UNREAD_MESSAGE_COUNT' +export const MARK_AS_READ = 'MARK_AS_READ' +export const GET_MESSAGES_LIST = 'GET_MESSAGES_LIST' // topic -export const GET_TOPICS_FROM_STORAGE = 'GET_TOPICS_FROM_STORAGE'; -export const GET_TOPICS_BY_TAB = 'GET_TOPICS_BY_TAB'; -export const UPDATE_TOPICS_BY_TAB = 'UPDATE_TOPICS_BY_TAB'; -export const GET_TOPIC_BY_ID = 'GET_TOPIC_BY_ID'; -export const REMOVE_TOPIC_CACHE_BY_ID = 'REMOVE_TOPIC_CACHE_BY_ID'; -export const REPLY_TOPIC_BY_ID = 'REPLY_TOPIC_BY_ID'; -export const UP_REPLY = 'UP_REPLY'; -export const PUBLISH = 'PUBLISH'; - +export const GET_TOPICS_FROM_STORAGE = 'GET_TOPICS_FROM_STORAGE' +export const GET_TOPICS_BY_TAB = 'GET_TOPICS_BY_TAB' +export const UPDATE_TOPICS_BY_TAB = 'UPDATE_TOPICS_BY_TAB' +export const GET_TOPIC_BY_ID = 'GET_TOPIC_BY_ID' +export const REMOVE_TOPIC_CACHE_BY_ID = 'REMOVE_TOPIC_CACHE_BY_ID' +export const REPLY_TOPIC_BY_ID = 'REPLY_TOPIC_BY_ID' +export const UP_REPLY = 'UP_REPLY' +export const PUBLISH = 'PUBLISH' // middleware -export const SYNC_REDUCER_TO_ASYNC_STORAGE = 'SYNC_REDUCER_TO_ASYNC_STORAGE'; -export const GET_REDUCER_FROM_ASYNC_STORAGE = 'GET_REDUCER_FROM_ASYNC_STORAGE'; +export const SYNC_REDUCER_TO_ASYNC_STORAGE = 'SYNC_REDUCER_TO_ASYNC_STORAGE' +export const GET_REDUCER_FROM_ASYNC_STORAGE = 'GET_REDUCER_FROM_ASYNC_STORAGE' diff --git a/src/constants/Tabs.js b/src/constants/Tabs.js index d9694d66..780b7185 100644 --- a/src/constants/Tabs.js +++ b/src/constants/Tabs.js @@ -1 +1 @@ -export const tabs = ['good', 'ask', 'all', 'share', 'job']; +export const tabs = ['good', 'ask', 'all', 'share', 'job'] diff --git a/src/constants/index.js b/src/constants/index.js index ba30d1dc..51f70a6a 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -1,6 +1,6 @@ export const refreshControl = { - tintColor: "rgba(241,196,15, 1)", - title: "正在加载...", - colors: ["rgba(241,196,15, 1)", "rgba(241,196,15, 0.9)", "rgba(241,196,15, 0.8)"], - progressBackgroundColor: "#292829", -}; + tintColor: 'rgba(241,196,15, 1)', + title: '正在加载...', + colors: ['rgba(241,196,15, 1)', 'rgba(241,196,15, 0.9)', 'rgba(241,196,15, 0.8)'], + progressBackgroundColor: '#292829' +} diff --git a/src/index.js b/src/index.js index b8f5ab7d..becedc75 100644 --- a/src/index.js +++ b/src/index.js @@ -1,23 +1,20 @@ -import React,{ +import React, { Component -} from 'react'; -import { Provider } from 'react-redux'; -import configureStore from './store/configureStore'; -import Navigation from './layouts/Navigation'; - - -const store = configureStore(); +} from 'react' +import { Provider } from 'react-redux' +import configureStore from './store/configureStore' +import Navigation from './layouts/Navigation' +const store = configureStore() class App extends Component { - render() { - return ( - - - - ); - } + render () { + return ( + + + + ) + } } - -export default App; +export default App diff --git a/src/layouts/About.js b/src/layouts/About.js index 31201b6e..6422b984 100644 --- a/src/layouts/About.js +++ b/src/layouts/About.js @@ -1,132 +1,127 @@ -import React, {Component, PropTypes} from 'react'; -import {View, Text, StyleSheet, Image, TouchableOpacity, Dimensions, ScrollView} from 'react-native'; -import Icon from 'react-native-vector-icons/Ionicons'; -import config from '../configs'; -import * as utils from '../utils'; - -const {height, width} = Dimensions.get('window'); +import React, {Component, PropTypes} from 'react' +import {View, Text, StyleSheet, Image, TouchableOpacity, Dimensions, ScrollView} from 'react-native' +import Icon from 'react-native-vector-icons/Ionicons' +import config from '../configs' +import * as utils from '../utils' +const {height, width} = Dimensions.get('window') class About extends Component { - _onSourceInGithubPress() { - utils.link(config.sourceInGithub); - } - - - render() { - return ( - - + - Noder - - {' v' + config.package.version} + Noder + + {' v' + config.package.version} - - - utils.link(config.cnodeAbout)}> - For CNodejs.org - - - - - - - - - this.props.router.toUser({ - userName:config.author.cnodeName - })}> - @soliury - - - - utils.link(config.author.blog)}> - - - - - utils.link(config.RNWebPage)}> - Power By + + + utils.link(config.cnodeAbout)}> + For CNodejs.org + + + + + + + + this.props.router.toUser({ + userName: config.author.cnodeName + })}> + @soliury + + + + utils.link(config.author.blog)}> + + + + + utils.link(config.RNWebPage)}> + Power By React-Native {'v' + config.package.dependencies['react-native']} - - - - ) - } + + + + ) + } } - const styles = StyleSheet.create({ - bgWall: { - height: height, - width: width - }, - noderLogo: { - height: 150, - width: 150 - }, - container: { - width: width, - height: height, - flexDirection: 'column', - alignItems: 'center', - paddingTop: 30, - backgroundColor: '#292829' - }, - title: { - marginTop: 20, - fontSize: 30, - color: 'rgba(255,255,255,0.7)', - borderBottomWidth: 1, - borderBottomColor: 'rgba(255,255,255,0.1)', - - }, - subTitle: { - marginTop: 10, - fontSize: 16, - color: 'rgba(255,255,255,0.5)' - }, - row: { - flexDirection: 'row', - justifyContent: 'center', - height: 40, - alignItems: 'center' - }, - rowIcon: { - height: 40, - width: 40 - }, - footer: { - position: 'absolute', - bottom: 0, - width: width, - flexDirection: 'column', - justifyContent: 'space-around', - alignItems: 'center', - height: 100 - }, - blog: { - height: 20, - width: 100, - opacity: 0.5 - }, - reactNative: { - fontSize: 16, - color: 'rgba(255,255,255,0.3)' - } -}); - - -export const LayoutComponent = About; -export function mapStateToProps(state) { - return {}; + bgWall: { + height: height, + width: width + }, + noderLogo: { + height: 150, + width: 150 + }, + container: { + width: width, + height: height, + flexDirection: 'column', + alignItems: 'center', + paddingTop: 30, + backgroundColor: '#292829' + }, + title: { + marginTop: 20, + fontSize: 30, + color: 'rgba(255,255,255,0.7)', + borderBottomWidth: 1, + borderBottomColor: 'rgba(255,255,255,0.1)' + + }, + subTitle: { + marginTop: 10, + fontSize: 16, + color: 'rgba(255,255,255,0.5)' + }, + row: { + flexDirection: 'row', + justifyContent: 'center', + height: 40, + alignItems: 'center' + }, + rowIcon: { + height: 40, + width: 40 + }, + footer: { + position: 'absolute', + bottom: 0, + width: width, + flexDirection: 'column', + justifyContent: 'space-around', + alignItems: 'center', + height: 100 + }, + blog: { + height: 20, + width: 100, + opacity: 0.5 + }, + reactNative: { + fontSize: 16, + color: 'rgba(255,255,255,0.3)' + } +}) + +export const LayoutComponent = About +export function mapStateToProps (state) { + return {} } diff --git a/src/layouts/Comment.js b/src/layouts/Comment.js index 3cda0715..74f6b56a 100644 --- a/src/layouts/Comment.js +++ b/src/layouts/Comment.js @@ -1,339 +1,324 @@ -import React, {Component} from 'react'; -import {View, StyleSheet, Text, Image, ListView, TouchableOpacity, TextInput, LayoutAnimation, Dimensions, Keyboard, Platform} from 'react-native'; -import Icon from 'react-native-vector-icons/Ionicons'; -import Nav from '../components/Nav'; -import Spinner from '../components/base/Spinner'; -import CommentList from './../components/CommentList'; -import animations from '../configs/animations'; -import {parseImgUrl} from '../utils'; -import config from '../configs'; - - -const {width, height} = Dimensions.get('window'); -const authorImgSize = 35; -const replyFormHeight = 55; -const commentsHeight = height - 40 - 20 - replyFormHeight - 20; -const submitButtonWidth = 55; - +import React, {Component} from 'react' +import {View, StyleSheet, Text, Image, ListView, TouchableOpacity, TextInput, LayoutAnimation, Dimensions, Keyboard, Platform} from 'react-native' +import Icon from 'react-native-vector-icons/Ionicons' +import Nav from '../components/Nav' +import Spinner from '../components/base/Spinner' +import CommentList from './../components/CommentList' +import animations from '../configs/animations' +import {parseImgUrl} from '../utils' +import config from '../configs' + +const {width, height} = Dimensions.get('window') +const authorImgSize = 35 +const replyFormHeight = 55 +const commentsHeight = height - 40 - 20 - replyFormHeight - 20 +const submitButtonWidth = 55 class Comment extends Component { - constructor(props) { - super(props); - this.state = { - didFocus: false - }; - Keyboard.addListener('keyboardWillShow', this.updateKeyboardSpace.bind(this)); - Keyboard.addListener('keyboardWillHide', this.resetKeyboardSpace.bind(this)); - - if (Platform.OS === 'android') { - Keyboard.addListener('keyboardDidShow', this.updateKeyboardSpace.bind(this)); - Keyboard.addListener('keyboardDidHide', this.resetKeyboardSpace.bind(this)); - } - } - - updateKeyboardSpace(e) { - LayoutAnimation.configureNext(animations.keyboard.layout.spring); - this.commentsView && this.commentsView.setNativeProps({ - style: { - height: commentsHeight - e.endCoordinates.height - } - }) - } - - resetKeyboardSpace() { - LayoutAnimation.configureNext(animations.keyboard.layout.spring); - this.commentsView && this.commentsView.setNativeProps({ - style: { - height: commentsHeight - } - }) - } - - - componentDidMount() { - const {topic, actions} = this.props; - actions.getTopicById(topic.id); - } - - - componentDidFocus(haveFocus) { - if (!haveFocus) { - setTimeout(()=> { - this.setState({ - didFocus: true - }); - }); - } - } - - - _resetReplyForm() { - this.replyId = null; - this.textInput.setNativeProps({ - text: '' - }); - this.textInputValue = ''; - this.textInput.blur(); - } - - - _doReply() { - var content = this.textInputValue; - if (this.props.replyPending || content == '' || content == null) { - return - } - let {topic, user} = this.props; - content = content + config.replySuffix; - this.props.actions.replyTopicById({ - topicId: topic.id, - content: content, - replyId: this.replyId, - user: { - loginname: user.loginname, - avatar_url: user.avatar_url - } - }, ()=> { + constructor (props) { + super(props) + this.state = { + didFocus: false + } + Keyboard.addListener('keyboardWillShow', this.updateKeyboardSpace.bind(this)) + Keyboard.addListener('keyboardWillHide', this.resetKeyboardSpace.bind(this)) + + if (Platform.OS === 'android') { + Keyboard.addListener('keyboardDidShow', this.updateKeyboardSpace.bind(this)) + Keyboard.addListener('keyboardDidHide', this.resetKeyboardSpace.bind(this)) + } + } + + updateKeyboardSpace (e) { + LayoutAnimation.configureNext(animations.keyboard.layout.spring) + this.commentsView && this.commentsView.setNativeProps({ + style: { + height: commentsHeight - e.endCoordinates.height + } + }) + } + + resetKeyboardSpace () { + LayoutAnimation.configureNext(animations.keyboard.layout.spring) + this.commentsView && this.commentsView.setNativeProps({ + style: { + height: commentsHeight + } + }) + } + + componentDidMount () { + const {topic, actions} = this.props + actions.getTopicById(topic.id) + } + + componentDidFocus (haveFocus) { + if (!haveFocus) { + setTimeout(() => { + this.setState({ + didFocus: true + }) + }) + } + } + + _resetReplyForm () { + this.replyId = null + this.textInput.setNativeProps({ + text: '' + }) + this.textInputValue = '' + this.textInput.blur() + } + + _doReply () { + var content = this.textInputValue + if (this.props.replyPending || content == '' || content == null) { + return + } + let {topic, user} = this.props + content = content + config.replySuffix + this.props.actions.replyTopicById({ + topicId: topic.id, + content: content, + replyId: this.replyId, + user: { + loginname: user.loginname, + avatar_url: user.avatar_url + } + }, () => { // resolved - this._resetReplyForm(); - }, ()=> { + this._resetReplyForm() + }, () => { // rejected - }); - } - - - _onReplyPress(id, authorName) { - if (!this.props.user) return; - this.textInput.focus(); - let text = `@${authorName} `; - this.textInput.setNativeProps({ - text: text - }); - this.replyId = id; - this.textInputValue = text - } - - - _onAuthorTextPress(authorName) { - if (!this.props.user) return; - let text = (this.textInputValue || '') + ` @${authorName} `; - - this.textInput.setNativeProps({ - text: text - }); - this.textInputValue = text; - } - - - _renderReplySubmiteIcon() { - if (this.props.replyPending) { - return ( - - + - - ); - } - return ( - + ) + } + return ( + - ); - } - - - _renderReplyForm() { - const {user} = this.props; - if (!user) return null; - - const userImg = parseImgUrl(user.avatar_url); - let replyFormBorder = {}; - if (Platform.OS === 'android') { - replyFormBorder = { - borderTopWidth: 1, - borderTopColor: 'rgba(0,0,0,0.08)' - }; - } - - return ( - - - this.props.router.toUser({ - userName: user.loginname - })}> - - - - - - this.textInput=view} - value={this.state.textInput} - multiline={true} - placeholder='嘿,说点啥吧' - style={styles.replyInput} - onChangeText={(text) => { - this.textInput.setNativeProps({ - text: text - }); - this.textInputValue = text - }} + ) + } + + _renderReplyForm () { + const {user} = this.props + if (!user) { return null } + + const userImg = parseImgUrl(user.avatar_url) + let replyFormBorder = {} + if (Platform.OS === 'android') { + replyFormBorder = { + borderTopWidth: 1, + borderTopColor: 'rgba(0,0,0,0.08)' + } + } + + return ( + + + this.props.router.toUser({ + userName: user.loginname + })}> + + + + + + this.textInput = view} + value={this.state.textInput} + multiline + placeholder='嘿,说点啥吧' + style={styles.replyInput} + onChangeText={(text) => { + this.textInput.setNativeProps({ + text: text + }) + this.textInputValue = text + }} /> - - - - this._doReply()}> - {this._renderReplySubmiteIcon()} - - - - ) - } - - - render() { - const {replies, reply={}, router, user, actions, topic, loadPending, count} = this.props; - - let navs = { - Left: { - text: '返回', - onPress: ()=> { - router.pop() - } - }, - Center: { - text: '评论 ' + count, - onPress: ()=> { - if (count > 0) { - this.commentList.scrollToTop(); - } - } - } - }; - - - if (this.state.didFocus && this.props.reply && topic) { - navs = { - ...navs, - Right: { - text: '正文', - onPress: ()=> { - router.toTopic({ - topic: topic, - id: topic.id, - from: 'comment' - }) - } - } - } - } - - - return ( - -