From a5f716823f220cf17dcbf0f6c2cd714df7e4a88b Mon Sep 17 00:00:00 2001 From: Wenxuan Shi Date: Sun, 13 Aug 2017 02:47:01 +0800 Subject: [PATCH] Improve profile contact UI (#277) --- .gitignore | 3 + package-lock.json | 1018 +--------------------------- package.json | 1 + scripts/build/config/webpack.js | 1 + scripts/build/runWebpack.js | 3 + vj4/handler/user.py | 13 +- vj4/locale/zh_CN.yaml | 9 +- vj4/template.py | 11 +- vj4/ui/breakpoints.json | 3 +- vj4/ui/common/common.inc.styl | 1 + vj4/ui/constant/model.js | 6 + vj4/ui/misc/grid.styl | 2 +- vj4/ui/pages/home_messages.page.js | 60 +- vj4/ui/pages/user_detail.page.js | 23 + vj4/ui/pages/user_detail.page.styl | 52 +- vj4/ui/templates/user_detail.html | 66 +- vj4/ui/utils/parseQueryString.js | 11 + vj4/ui/utils/request.js | 131 ++-- 18 files changed, 280 insertions(+), 1134 deletions(-) create mode 100644 vj4/ui/pages/user_detail.page.js create mode 100644 vj4/ui/utils/parseQueryString.js diff --git a/.gitignore b/.gitignore index d5fad542a..4bfe91692 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,7 @@ vj4/constant # Generated locales vj4/ui/static/locale +# Webpack Stats +.webpackStats.json + !.gitkeep diff --git a/package-lock.json b/package-lock.json index 29fce3170..906d12259 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1330,6 +1330,11 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, + "base-64": { + "version": "0.1.0", + "resolved": "http://registry.npm.taobao.org/base-64/download/base-64-0.1.0.tgz", + "integrity": "sha1-eAqZyE59YAJgNhURxId2E78k9rs=" + }, "base64-js": { "version": "1.2.1", "resolved": "http://registry.npm.taobao.org/base64-js/download/base64-js-1.2.1.tgz", @@ -1651,7 +1656,6 @@ "requires": { "anymatch": "1.3.2", "async-each": "1.0.1", - "fsevents": "1.1.2", "glob-parent": "2.0.0", "inherits": "2.0.3", "is-binary-path": "1.0.1", @@ -3685,1018 +3689,6 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "fsevents": { - "version": "1.1.2", - "resolved": "http://registry.npm.taobao.org/fsevents/download/fsevents-1.1.2.tgz", - "integrity": "sha1-MoK3E/s62A7eDp/PRhG1qm/AM/Q=", - "dev": true, - "optional": true, - "requires": { - "nan": "2.6.2", - "node-pre-gyp": "0.6.36" - }, - "dependencies": { - "abbrev": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", - "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=", - "dev": true, - "optional": true - }, - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true, - "optional": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "aproba": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz", - "integrity": "sha1-ldNgDwdxCqDpKYxyatXs8urLq6s=", - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", - "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", - "dev": true, - "optional": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.2.9" - } - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true, - "optional": true - }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "dev": true, - "optional": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "dev": true, - "optional": true - }, - "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", - "dev": true, - "optional": true - }, - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", - "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", - "dev": true, - "requires": { - "balanced-match": "0.4.2", - "concat-map": "0.0.1" - } - }, - "buffer-shims": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", - "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true, - "optional": true - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "combined-stream": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", - "dev": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true, - "optional": true, - "requires": { - "boom": "2.10.1" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true - } - } - }, - "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", - "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", - "dev": true, - "optional": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true, - "optional": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true, - "optional": true - }, - "extsprintf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", - "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true, - "optional": true - }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "dev": true, - "optional": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.15" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.1" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", - "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", - "dev": true, - "optional": true, - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "optional": true, - "requires": { - "aproba": "1.1.1", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true - } - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", - "dev": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", - "dev": true, - "optional": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true, - "optional": true - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true, - "optional": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.0", - "sshpk": "1.13.0" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ini": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", - "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true, - "optional": true - }, - "jodid25519": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", - "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true, - "optional": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "optional": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true, - "optional": true - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true, - "optional": true - }, - "jsprim": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", - "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.0.2", - "json-schema": "0.2.3", - "verror": "1.3.6" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true - } - } - }, - "mime-db": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", - "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=", - "dev": true - }, - "mime-types": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", - "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", - "dev": true, - "requires": { - "mime-db": "1.27.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true, - "optional": true - }, - "node-pre-gyp": { - "version": "0.6.36", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz", - "integrity": "sha1-22BBEst04NR3VU6bUFsXq936t4Y=", - "dev": true, - "optional": true, - "requires": { - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.0", - "rc": "1.2.1", - "request": "2.81.0", - "rimraf": "2.6.1", - "semver": "5.3.0", - "tar": "2.2.1", - "tar-pack": "3.4.0" - } - }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "dev": true, - "optional": true, - "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" - } - }, - "npmlog": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.0.tgz", - "integrity": "sha512-ocolIkZYZt8UveuiDS0yAkkIjid1o7lPG8cYm05yNYzBn8ykQtaiPMEGp8fY9tKdDgm8okpdKzkvu1y9hUYugA==", - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", - "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", - "dev": true, - "optional": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true, - "optional": true - }, - "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", - "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", - "dev": true, - "optional": true, - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", - "integrity": "sha1-z3jsb0ptHrQ9JkiMrJfwQudLf8g=", - "dev": true, - "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "1.0.1", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.0.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.6.0", - "uuid": "3.0.1" - } - }, - "rimraf": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, - "safe-buffer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", - "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=", - "dev": true - }, - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true, - "optional": true - }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true, - "optional": true, - "requires": { - "hoek": "2.16.3" - } - }, - "sshpk": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.0.tgz", - "integrity": "sha1-/yo+T9BEl1Vf7Zezmg/YL6+zozw=", - "dev": true, - "optional": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jodid25519": "1.0.2", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true - } - } - }, - "string_decoder": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz", - "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=", - "dev": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", - "dev": true, - "optional": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "optional": true - }, - "tar": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", - "dev": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "tar-pack": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", - "integrity": "sha1-I74tf2cagzk3bL2wuP4/3r8xeYQ=", - "dev": true, - "optional": true, - "requires": { - "debug": "2.6.8", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.2.9", - "rimraf": "2.6.1", - "tar": "2.2.1", - "uid-number": "0.0.6" - } - }, - "tough-cookie": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", - "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", - "dev": true, - "optional": true, - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", - "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", - "dev": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "uuid": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", - "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=", - "dev": true, - "optional": true - }, - "verror": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", - "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", - "dev": true, - "optional": true, - "requires": { - "extsprintf": "1.0.2" - } - }, - "wide-align": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", - "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", - "dev": true, - "optional": true, - "requires": { - "string-width": "1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - } - } - }, "fstream": { "version": "1.0.11", "resolved": "http://registry.npm.taobao.org/fstream/download/fstream-1.0.11.tgz", diff --git a/package.json b/package.json index 96af627fc..fbf0fdb06 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ }, "dependencies": { "babel-runtime": "^6.25.0", + "base-64": "^0.1.0", "classnames": "^2.2.5", "clipboard": "^1.7.1", "codemirror": "^5.28.0", diff --git a/scripts/build/config/webpack.js b/scripts/build/config/webpack.js index 3f2b1655a..63c7a78a4 100644 --- a/scripts/build/config/webpack.js +++ b/scripts/build/config/webpack.js @@ -83,6 +83,7 @@ export default function (env = {}) { const config = { bail: true, + profile: true, context: root('vj4/ui'), devtool: env.production ? 'source-map' : false, entry: { diff --git a/scripts/build/runWebpack.js b/scripts/build/runWebpack.js index 11f9c0dc6..09b923af6 100644 --- a/scripts/build/runWebpack.js +++ b/scripts/build/runWebpack.js @@ -1,4 +1,6 @@ +import fs from 'fs'; import webpack from 'webpack'; +import root from './utils/root.js'; import webpackConfig from './config/webpack.js'; export default function ({ watch, production }) { @@ -21,6 +23,7 @@ export default function ({ watch, production }) { return; } console.log(stats.toString(outputOptions)); + fs.writeFileSync(root('./.webpackStats.json'), JSON.stringify(stats.toJson(), null, 2)); if (!watch && stats.hasErrors()) { process.exitCode = 1; } diff --git a/vj4/handler/user.py b/vj4/handler/user.py index 02f84d161..3f702c1ef 100644 --- a/vj4/handler/user.py +++ b/vj4/handler/user.py @@ -188,10 +188,6 @@ async def get(self, *, uid: int): raise error.UserNotFoundError(uid) dudoc, sdoc = await asyncio.gather(domain.get_user(self.domain_id, udoc['_id']), token.get_most_recent_session_by_uid(udoc['_id'])) - email = self.get_udoc_setting(udoc, 'mail') - if email: - email = email.replace('@', random.choice([' [at] ', '#'])) - bg = random.randint(1, 21) rdocs = record.get_multi(get_hidden=self.has_priv(builtin.PRIV_VIEW_HIDDEN_RECORD), uid=uid).sort([('_id', -1)]) rdocs = await rdocs.limit(10).to_list() @@ -206,7 +202,7 @@ async def get(self, *, uid: int): dcount = await ddocs.count() ddocs = await ddocs.limit(10).to_list() self.render('user_detail.html', is_self_profile=is_self_profile, - udoc=udoc, dudoc=dudoc, sdoc=sdoc, email=email, bg=bg, + udoc=udoc, dudoc=dudoc, sdoc=sdoc, rdocs=rdocs, pdocs=pdocs, pcount=pcount, psdocs=psdocs, pscount=pscount, ddocs=ddocs, dcount=dcount) @@ -225,8 +221,11 @@ def modify_udoc(self, udict, key): @base.get_argument @base.route_argument @base.sanitize - async def get(self, *, q: str): - udocs = await user.get_prefix_list(q, user.PROJECTION_PUBLIC, 20) + async def get(self, *, q: str, exact_match: bool=False): + if exact_match: + udocs = [] + else: + udocs = await user.get_prefix_list(q, user.PROJECTION_PUBLIC, 20) try: udoc = await user.get_by_uid(int(q), user.PROJECTION_PUBLIC) if udoc: diff --git a/vj4/locale/zh_CN.yaml b/vj4/locale/zh_CN.yaml index 96c57bfcc..7f9878696 100644 --- a/vj4/locale/zh_CN.yaml +++ b/vj4/locale/zh_CN.yaml @@ -648,5 +648,12 @@ Menu: 菜单 Code copied to clipboard!: 代码已复制到剪贴板! No comments so far...: 目前还没有评论... No solutions so far...: 目前还没有题解... +Edit Profile: 编辑个人信息 +Send Message: 发送站内信息 +Copy Email: 复制电子邮件 +Copy QQ Number: 复制QQ号 +Copy WeChat Account: 复制微信号 +'"{data}" copied to clipboard!': “{data}”已复制到剪贴板! +'Copy "{data}" failed :(': '复制“{data}”失败 :(' Introduce: 简介 -Introduce must not exceed 500 characters and it will be shown in the list view.: 简介不能超过 500 个字符,将显示在列表页面中。 +Introduce must not exceed 500 characters and it will be shown in the list view.: 简介不能超过 500 个字符,将显示在列表页面中。 \ No newline at end of file diff --git a/vj4/template.py b/vj4/template.py index 3d23ada8a..402f2c3ae 100644 --- a/vj4/template.py +++ b/vj4/template.py @@ -1,3 +1,4 @@ +import base64 import hashlib import hoedown import re @@ -44,6 +45,7 @@ def __init__(self): self.filters['json'] = json.encode self.filters['gravatar_url'] = gravatar_url self.filters['format_size'] = format_size + self.filters['base64_encode'] = base64_encode @@ -114,6 +116,11 @@ def format_size(size, base=1, ndigits=3): unit_names = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] for unit_name in unit_names: if size < unit: - return '{0}{1}'.format(round(size, ndigits=ndigits), unit_name) + return '{0} {1}'.format(round(size, ndigits=ndigits), unit_name) size /= unit - return '{0}{1}'.format(round(size * unit, ndigits=ndigits), unit_names[-1]) + return '{0} {1}'.format(round(size * unit, ndigits=ndigits), unit_names[-1]) + + +def base64_encode(str): + encoded = base64.b64encode(str.encode()) + return encoded.decode() diff --git a/vj4/ui/breakpoints.json b/vj4/ui/breakpoints.json index d6e25ceaf..386c14cb0 100644 --- a/vj4/ui/breakpoints.json +++ b/vj4/ui/breakpoints.json @@ -1,4 +1,5 @@ { "mobile": 450, - "desktop": 1000 + "desktop": 1000, + "hd": 1250 } diff --git a/vj4/ui/common/common.inc.styl b/vj4/ui/common/common.inc.styl index 854dcabfa..ff72825e0 100644 --- a/vj4/ui/common/common.inc.styl +++ b/vj4/ui/common/common.inc.styl @@ -2,6 +2,7 @@ breakpoints = json('../breakpoints.json', { hash: true }) rupture.mobile-cutoff = unit(breakpoints.mobile, 'px') rupture.desktop-cutoff = unit(breakpoints.desktop, 'px') +rupture.hd-cutoff = unit(breakpoints.hd, 'px') @import '~vj/misc/.iconfont/webicon.inc.styl' @import 'color.inc.styl' diff --git a/vj4/ui/constant/model.js b/vj4/ui/constant/model.js index f7e8b4649..634ef6136 100644 --- a/vj4/ui/constant/model.js +++ b/vj4/ui/constant/model.js @@ -10,3 +10,9 @@ export const USER_GENDER_RANGE = { [USER_GENDER_OTHER]: 'Other', }; attachObjectMeta(USER_GENDER_RANGE, 'intKey', true); +export const USER_GENDER_ICONS = { + [USER_GENDER_MALE]: '♂', + [USER_GENDER_FEMALE]: '♀', + [USER_GENDER_OTHER]: '?', +}; +attachObjectMeta(USER_GENDER_ICONS, 'intKey', true); diff --git a/vj4/ui/misc/grid.styl b/vj4/ui/misc/grid.styl index 529baf562..53d6d38dd 100644 --- a/vj4/ui/misc/grid.styl +++ b/vj4/ui/misc/grid.styl @@ -13,7 +13,7 @@ Options: margin-right: auto +desktop() max-width: 980px - +above(1250px) + +hd() max-width: 1200px .row::before, diff --git a/vj4/ui/pages/home_messages.page.js b/vj4/ui/pages/home_messages.page.js index 995503976..aa1b7a542 100644 --- a/vj4/ui/pages/home_messages.page.js +++ b/vj4/ui/pages/home_messages.page.js @@ -1,17 +1,38 @@ import _ from 'lodash'; import { NamedPage } from 'vj/misc/PageLoader'; +import request from 'vj/utils/request'; import loadReactRedux from 'vj/utils/loadReactRedux'; +import parseQueryString from 'vj/utils/parseQueryString'; import { ActionDialog } from 'vj/components/dialog'; import UserSelectAutoComplete from 'vj/components/autocomplete/UserSelectAutoComplete'; const page = new NamedPage('home_messages', () => { + let reduxStore; + + function createDialog(user) { + const id = _.uniqueId('PLACEHOLDER_'); + reduxStore.dispatch({ + type: 'DIALOGUES_CREATE', + payload: { + id, + user, + }, + }); + reduxStore.dispatch({ + type: 'DIALOGUES_SWITCH_TO', + payload: id, + }); + } + async function mountComponent() { const SockJs = await System.import('sockjs-client'); const { default: MessagePadApp } = await System.import('../components/messagepad'); const { default: MessagePadReducer } = await System.import('../components/messagepad/reducers'); const { React, render, Provider, store } = await loadReactRedux(MessagePadReducer); + reduxStore = store; + const sock = new SockJs('/home/messages-conn'); sock.onmessage = (message) => { const msg = JSON.parse(message.data); @@ -46,25 +67,38 @@ const page = new NamedPage('home_messages', () => { return; } const user = userSelector.value(); - const id = _.uniqueId('PLACEHOLDER_'); - store.dispatch({ - type: 'DIALOGUES_CREATE', - payload: { - id, - user, - }, - }); - store.dispatch({ - type: 'DIALOGUES_SWITCH_TO', - payload: id, - }); + createDialog(user); }} /> , $('#messagePad').get(0), ); } - mountComponent(); + + /** + * A target user id may be assigned in the query string. + */ + async function loadSendTarget() { + const queryString = parseQueryString(); + if (!queryString.target_uid) { + return; + } + const user = await request.get('/user/search', { + q: queryString.target_uid, + exact_match: true, + }); + if (!user || user.length === 0) { + return; + } + createDialog(user[0]); + } + + async function init() { + await mountComponent(); + await loadSendTarget(); + } + + init(); }); export default page; diff --git a/vj4/ui/pages/user_detail.page.js b/vj4/ui/pages/user_detail.page.js new file mode 100644 index 000000000..cc54d0169 --- /dev/null +++ b/vj4/ui/pages/user_detail.page.js @@ -0,0 +1,23 @@ +import base64 from 'base-64'; +import Clipboard from 'clipboard'; + +import { NamedPage } from 'vj/misc/PageLoader'; +import substitute from 'vj/utils/substitute'; +import Notification from 'vj/components/notification'; +import i18n from 'vj/utils/i18n'; + +const page = new NamedPage('user_detail', async () => { + $('[name="profile_contact_copy"]').get().forEach((el) => { + const data = $(el).attr('data-content'); + const decoded = base64.decode(data); + const clip = new Clipboard(el, { text: () => decoded }); + clip.on('success', () => { + Notification.success(substitute(i18n('"{data}" copied to clipboard!'), { data: decoded }), 2000); + }); + clip.on('error', () => { + Notification.error(substitute(i18n('Copy "{data}" failed :('), { data: decoded })); + }); + }); +}); + +export default page; diff --git a/vj4/ui/pages/user_detail.page.styl b/vj4/ui/pages/user_detail.page.styl index 8d55405ef..501c9ece8 100644 --- a/vj4/ui/pages/user_detail.page.styl +++ b/vj4/ui/pages/user_detail.page.styl @@ -6,27 +6,16 @@ position: relative background-position: center center background-size: cover - min-height: 220px + min-height: rem(270px) + padding-bottom: rem($tab-item-height + 20px) - &:after - padding-top: 30% - display: block - content: '' - - +mobile() - padding-top: 0 - padding-bottom: rem($tab-item-height) + +mobile() + padding-bottom: rem($tab-item-height) .profile-header__content - position: absolute - left: 0 - top: 0 - width: 100% + position: relative padding: rem($section-gap-v) rem($section-gap-h) 0 - +mobile() - position: relative - .media__left padding-right: rem(30px) @@ -43,6 +32,37 @@ .media__body margin-top: rem(20px) + .profile-header__contact-bar + position: absolute + right: rem($section-gap-h + 5px) + top: rem($section-gap-v + 20px) + color: #FFF + text-align: right + + +below(rupture.hd-cutoff) + position: relative + right: 0 + top: 0 + text-align: left + margin: rem(15px 0) + + .profile-header__contact-item + vertical-align: top + color: rgba(#FFF, 0.8) + margin-left: rem(10px) + + +below(rupture.hd-cutoff) + margin-left: 0 + margin-right: rem(15px) + + &:hover + color: rgba(#FFF, 1) + text-decoration: none + + &.badge--mod + background-color: rgba(#FFF, 0.8) !important + color: rgba(#000, 0.8) !important + .profile-header__main h1 line-height: 40px diff --git a/vj4/ui/templates/user_detail.html b/vj4/ui/templates/user_detail.html index 065de59c9..6ef2c479b 100644 --- a/vj4/ui/templates/user_detail.html +++ b/vj4/ui/templates/user_detail.html @@ -5,13 +5,13 @@
{% endblock %} diff --git a/vj4/ui/utils/parseQueryString.js b/vj4/ui/utils/parseQueryString.js new file mode 100644 index 000000000..631015425 --- /dev/null +++ b/vj4/ui/utils/parseQueryString.js @@ -0,0 +1,11 @@ +export default function parseQueryString(str) { + const obj = {}; + (str || document.location.search) + .replace(/(^\?)/, '') + .split('&') + .forEach((n) => { + const s = n.split('=').map(v => decodeURIComponent(v)); + obj[s[0]] = s[1]; + }); + return obj; +} diff --git a/vj4/ui/utils/request.js b/vj4/ui/utils/request.js index b3032f76d..8298dd3cf 100644 --- a/vj4/ui/utils/request.js +++ b/vj4/ui/utils/request.js @@ -1,66 +1,79 @@ import i18n from './i18n'; -const request = {}; +const request = { + /** + * @param {object} options + */ + async ajax(options) { + return new Promise((resolve, reject) => { + $ + .ajax({ + dataType: 'json', + ...options, + }) + .fail((jqXHR, textStatus, errorThrown) => { + if (textStatus === 'abort') { + const err = new Error(i18n('Aborted')); + err.aborted = true; + reject(err); + } else if (jqXHR.readyState === 0) { + reject(new Error(i18n('Network error'))); + } else if (errorThrown instanceof Error) { + reject(errorThrown); + } else if (typeof jqXHR.responseJSON === 'object' && jqXHR.responseJSON.error) { + reject(new Error(jqXHR.responseJSON.error.message)); + } else { + reject(new Error(textStatus)); + } + }) + .done(resolve); + }); + }, -request.ajax = async function (options) { - return new Promise((resolve, reject) => { - $ - .ajax({ - dataType: 'json', - ...options, - }) - .fail((jqXHR, textStatus, errorThrown) => { - if (textStatus === 'abort') { - const err = new Error(i18n('Aborted')); - err.aborted = true; - reject(err); - } else if (jqXHR.readyState === 0) { - reject(new Error(i18n('Network error'))); - } else if (errorThrown instanceof Error) { - reject(errorThrown); - } else if (typeof jqXHR.responseJSON === 'object' && jqXHR.responseJSON.error) { - reject(new Error(jqXHR.responseJSON.error.message)); - } else { - reject(new Error(textStatus)); - } - }) - .done(resolve); - }); -}; - -request.post = function (url, dataOrForm = {}, options = {}) { - let postData; - if (dataOrForm instanceof jQuery && dataOrForm.is('form')) { - // $form - postData = dataOrForm.serialize(); - } else if (dataOrForm instanceof Node && $(dataOrForm).is('form')) { - // form - postData = $(dataOrForm).serialize(); - } else if (typeof dataOrForm === 'string') { - // foo=bar&box=boz - postData = dataOrForm; - } else { - // {foo: 'bar'} - postData = $.param({ - csrf_token: UiContext.csrf_token, - ...dataOrForm, - }, true); - } - return request.ajax({ - url, - method: 'post', - data: postData, - ...options, - }); -}; + /** + * @param {string} url + * @param {JQueryStatic | Node | string | object} dataOrForm + * @param {object} options + */ + post(url, dataOrForm = {}, options = {}) { + let postData; + if (dataOrForm instanceof jQuery && dataOrForm.is('form')) { + // $form + postData = dataOrForm.serialize(); + } else if (dataOrForm instanceof Node && $(dataOrForm).is('form')) { + // form + postData = $(dataOrForm).serialize(); + } else if (typeof dataOrForm === 'string') { + // foo=bar&box=boz + postData = dataOrForm; + } else { + // {foo: 'bar'} + postData = $.param({ + csrf_token: UiContext.csrf_token, + ...dataOrForm, + }, true); + } + return request.ajax({ + url, + method: 'post', + data: postData, + ...options, + }); + }, -request.get = function (url, qs = {}, options = {}) { - return request.ajax({ - url, - data: qs, - method: 'get', - ...options, - }); + /** + * @param {string} url + * @param {object} qs + * @param {object} options + */ + get(url, qs = {}, options = {}) { + return request.ajax({ + url, + data: qs, + method: 'get', + ...options, + }); + }, }; export default request;