From 35449991762fb7e9703c2d5e959abb96e29bb6d1 Mon Sep 17 00:00:00 2001 From: Haoyu-Mac Date: Tue, 15 Dec 2015 01:36:07 -0500 Subject: [PATCH 1/3] Coding for Xblock --- chatRecords.json | 17 ++++ comments.json | 27 +++++- public/css/base.css | 186 ++++++++++++++++++++++++++++++++++++++ public/index.html | 4 - public/scripts/example.js | 88 +++++++++++++++++- requirements.txt | 1 - server.go | 110 ---------------------- server.js | 28 ++++++ server.php | 52 ----------- server.pl | 36 -------- server.py | 36 -------- server.rb | 46 ---------- users.json | 17 ++++ 13 files changed, 361 insertions(+), 287 deletions(-) create mode 100644 chatRecords.json delete mode 100644 requirements.txt delete mode 100644 server.go delete mode 100644 server.php delete mode 100644 server.pl delete mode 100644 server.py delete mode 100644 server.rb create mode 100644 users.json diff --git a/chatRecords.json b/chatRecords.json new file mode 100644 index 00000000..99408138 --- /dev/null +++ b/chatRecords.json @@ -0,0 +1,17 @@ +[ + { + "userID": 1, + "name": "Pete Hunt", + "body": "Lorem ipsum dolor sit amet, id malorum perpetua has, id eum case appetere." + }, + { + "userID": 2, + "name": "Haoyu Hunt", + "body": "Tantas vituperata et pri, eu appareat inimicus eam" + }, + { + "userID": 3, + "name": "Chen Hunt", + "body": "ius audiam voluptaria qualisque. Sed no feugait accusam. Et sed nostrud invidunt, duo tractatos deterruisset ei, nam in voluptua invenire" + } +] \ No newline at end of file diff --git a/comments.json b/comments.json index 7bef77ad..008b92c2 100644 --- a/comments.json +++ b/comments.json @@ -8,5 +8,30 @@ "id": 1420070400000, "author": "Paul O’Shannessy", "text": "React is *great*!" + }, + { + "id": 1450122878637, + "author": "adfdf", + "text": "fdfd" + }, + { + "id": 1450122891262, + "author": "Haoyu", + "text": "This is great" + }, + { + "id": 1450123949686, + "author": "adf", + "text": "dfd" + }, + { + "id": 1450125550564, + "author": "adbdfd", + "text": "sdfd" + }, + { + "id": 1450125903112, + "author": "df", + "text": "df" } -] +] \ No newline at end of file diff --git a/public/css/base.css b/public/css/base.css index bf382be3..0867bded 100644 --- a/public/css/base.css +++ b/public/css/base.css @@ -60,3 +60,189 @@ p, ul { ul { padding-left: 30px; } + + +.online-user-list { + border: 1px solid #c7b89e; +} +.avatar-chat { + border: 2px solid #c7b89e; + display: inline-block; +} + +.avatar-chat, +.avatar-chat img { + width: 30px; + height: 30px; + -webkit-border-radius: 30px; + /* Saf3+, Chrome */ + border-radius: 30px; + /* Opera 10.5, IE 9 */ + /*-moz-border-radius: 30px; Disabled for FF1+ */ +} + + +/* Chat Records*/ + +.chat-thread { + margin: 24px auto 0 auto; + padding: 0 20px 0 0; + list-style: none; + overflow-y: scroll; + overflow-x: hidden; +} + +.chat-thread li { + position: relative; + clear: both; + display: inline-block; + padding: 16px 40px 16px 20px; + margin: 0 0 20px 0; + font: 16px/20px 'Noto Sans', sans-serif; + border-radius: 10px; + background-color: rgba(25,147,147,0.2); +} + +/* Chat - Avatar */ +.chat-thread li:before { + position: absolute; + top: 0; + width: 50px; + height: 50px; + border-radius: 50px; + content: ''; +} + +/* Chat - Speech Bubble Arrow */ +.chat-thread li:after { + position: absolute; + top: 15px; + content: ''; + width: 0; + height: 0; + border-top: 15px solid rgba(25,147,147,0.2); +} + +.chat-thread .self-chat { + animation: show-chat-odd 0.15s 1 ease-in; + -moz-animation: show-chat-odd 0.15s 1 ease-in; + -webkit-animation: show-chat-odd 0.15s 1 ease-in; + float: right; + margin-right: 80px; + color: #0AD5C1; +} + +.chat-thread .self-chat:before { + right: -80px; + background-size:cover; +} + +.chat-thread .self-chat:after { + border-right: 15px solid transparent; + right: -15px; +} + +.chat-thread .others-chat { + animation: show-chat-even 0.15s 1 ease-in; + -moz-animation: show-chat-even 0.15s 1 ease-in; + -webkit-animation: show-chat-even 0.15s 1 ease-in; + float: left; + margin-left: 80px; + color: #0EC879; +} + +.chat-thread .others-chat:before { + left: -80px; + background-size:cover; +} + +.chat-thread .others-chat:after { + border-left: 15px solid transparent; + left: -15px; +} + + + +// A tiny bit responsive... +// -------------------------------------- +/* Small screens */ +@media all and (max-width: 767px) { + .chat-thread { + width: 90%; + height: 260px; + } + + .chat-window { + left: 5%; + width: 90%; + } +} + +/* Medium and large screens */ +@media all and (min-width: 768px) { + .chat-thread { + width: 50%; + height: 320px; + } + + .chat-window { + left: 25%; + width: 50%; + } +} + +// Animation +// -------------------------------------- +@keyframes show-chat-even { + 0% { + margin-left: -480px; + } + 100% { + margin-left: 0; + } +} + +@-moz-keyframes show-chat-even { + 0% { + margin-left: -480px; + } + 100% { + margin-left: 0; + } +} + +@-webkit-keyframes show-chat-even { + 0% { + margin-left: -480px; + } + 100% { + margin-left: 0; + } +} + +@keyframes show-chat-odd { + 0% { + margin-right: -480px; + } + 100% { + margin-right: 0; + } +} + +@-moz-keyframes show-chat-odd { + 0% { + margin-right: -480px; + } + 100% { + margin-right: 0; + } +} + +@-webkit-keyframes show-chat-odd { + 0% { + margin-right: -480px; + } + 100% { + margin-right: 0; + } +} \ No newline at end of file diff --git a/public/index.html b/public/index.html index c6494446..3cbb635b 100644 --- a/public/index.html +++ b/public/index.html @@ -14,9 +14,5 @@
- diff --git a/public/scripts/example.js b/public/scripts/example.js index c249427a..0ad4c215 100644 --- a/public/scripts/example.js +++ b/public/scripts/example.js @@ -10,6 +10,73 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +var OnlineUserAvatar = React.createClass({ + render: function() { + return ( +
+ +
+ ); + } +}); + +var OnlineUserList = React.createClass({ + render: function() { + var onlineUserListNode = this.props.data.map(function(user) { + return ( + + ); + }); + return ( +
+ {onlineUserListNode} +
+ ); + } +}); + + +// var ChatRecord = React.createClass({ +// render: function() { +// return ( +//
+//

+// {this.props.name} +//

+// +//
+// ); +// } +// }); + +var ChatRecords = React.createClass({ + render: function() { + var divStyle = { + backgroundImage: 'url(' + imgUrl + ')', + } + return ( + + ); +} + +var ChatRecordsList = React.createClass({ + render: function() { + var divStyle = { + backgroundImage: 'url(' + imgUrl + ')', + } + return ( + + ); + } +}); + var Comment = React.createClass({ rawMarkup: function() { var rawMarkup = marked(this.props.children.toString(), {sanitize: true}); @@ -42,6 +109,19 @@ var CommentBox = React.createClass({ }.bind(this) }); }, + loadUsersFromServer: function () { + $.ajax({ + url: '/api/users', + dataType: 'json', + cache: false, + success: function(data) { + this.setState({onlineUsersData: data}); + }.bind(this), + error: function(xhr, status, err) { + console.error('/api/users', status, err.toString()); + }.bind(this) + }); + }, handleCommentSubmit: function(comment) { var comments = this.state.data; // Optimistically set an id on the new comment. It will be replaced by an @@ -65,15 +145,21 @@ var CommentBox = React.createClass({ }); }, getInitialState: function() { - return {data: []}; + return {data: [], onlineUsersData: []}; }, componentDidMount: function() { this.loadCommentsFromServer(); + this.loadUsersFromServer(); setInterval(this.loadCommentsFromServer, this.props.pollInterval); + setInterval(this.loadUsersFromServer, this.props.pollInterval); }, render: function() { + console.log(this.state.onlineUsersData); + console.log(this.state.data); return (
+ +

Comments

diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 632a1efa..00000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -Flask==0.10.1 diff --git a/server.go b/server.go deleted file mode 100644 index 2224328d..00000000 --- a/server.go +++ /dev/null @@ -1,110 +0,0 @@ -/** - * This file provided by Facebook is for non-commercial testing and evaluation - * purposes only. Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "log" - "net/http" - "os" - "sync" - "time" -) - -type comment struct { - ID int64 `json:"id"` - Author string `json:"author"` - Text string `json:"text"` -} - -const dataFile = "./comments.json" - -var commentMutex = new(sync.Mutex) - -// Handle comments -func handleComments(w http.ResponseWriter, r *http.Request) { - // Since multiple requests could come in at once, ensure we have a lock - // around all file operations - commentMutex.Lock() - defer commentMutex.Unlock() - - // Stat the file, so we can find its current permissions - fi, err := os.Stat(dataFile) - if err != nil { - http.Error(w, fmt.Sprintf("Unable to stat the data file (%s): %s", dataFile, err), http.StatusInternalServerError) - return - } - - // Read the comments from the file. - commentData, err := ioutil.ReadFile(dataFile) - if err != nil { - http.Error(w, fmt.Sprintf("Unable to read the data file (%s): %s", dataFile, err), http.StatusInternalServerError) - return - } - - switch r.Method { - case "POST": - // Decode the JSON data - var comments []comment - if err := json.Unmarshal(commentData, &comments); err != nil { - http.Error(w, fmt.Sprintf("Unable to Unmarshal comments from data file (%s): %s", dataFile, err), http.StatusInternalServerError) - return - } - - // Add a new comment to the in memory slice of comments - comments = append(comments, comment{ID: time.Now().UnixNano() / 1000000, Author: r.FormValue("author"), Text: r.FormValue("text")}) - - // Marshal the comments to indented json. - commentData, err = json.MarshalIndent(comments, "", " ") - if err != nil { - http.Error(w, fmt.Sprintf("Unable to marshal comments to json: %s", err), http.StatusInternalServerError) - return - } - - // Write out the comments to the file, preserving permissions - err := ioutil.WriteFile(dataFile, commentData, fi.Mode()) - if err != nil { - http.Error(w, fmt.Sprintf("Unable to write comments to data file (%s): %s", dataFile, err), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - w.Header().Set("Cache-Control", "no-cache") - io.Copy(w, bytes.NewReader(commentData)) - - case "GET": - w.Header().Set("Content-Type", "application/json") - w.Header().Set("Cache-Control", "no-cache") - // stream the contents of the file to the response - io.Copy(w, bytes.NewReader(commentData)) - - default: - // Don't know the method, so error - http.Error(w, fmt.Sprintf("Unsupported method: %s", r.Method), http.StatusMethodNotAllowed) - } -} - -func main() { - port := os.Getenv("PORT") - if port == "" { - port = "3000" - } - http.HandleFunc("/api/comments", handleComments) - http.Handle("/", http.FileServer(http.Dir("./public"))) - log.Println("Server started: http://localhost:" + port) - log.Fatal(http.ListenAndServe(":"+port, nil)) -} diff --git a/server.js b/server.js index ac87898a..4b5853bb 100644 --- a/server.js +++ b/server.js @@ -10,6 +10,9 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + + + var fs = require('fs'); var path = require('path'); var express = require('express'); @@ -17,6 +20,8 @@ var bodyParser = require('body-parser'); var app = express(); var COMMENTS_FILE = path.join(__dirname, 'comments.json'); +var USERS_FILE = path.join(__dirname, 'users.json'); +var CHATRECORDS_FILE = path.join(__dirname, 'chatRecords.json'); app.set('port', (process.env.PORT || 3000)); @@ -24,6 +29,29 @@ app.use('/', express.static(path.join(__dirname, 'public'))); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({extended: true})); + +app.get('/api/users', function(req, res) { + fs.readFile(USERS_FILE, function(err, data) { + if (err) { + console.error(err); + process.exit(1); + } + res.setHeader('Cache-Control', 'no-cache'); + res.json(JSON.parse(data)); + }); +}); + +app.get('/api/chatrecords', function(req, res) { + fs.readFile(CHATRECORDS_FILE, function(err, data) { + if (err) { + console.error(err); + process.exit(1); + } + res.setHeader('Cache-Control', 'no-cache'); + res.json(JSON.parse(data)); + }); +}); + app.get('/api/comments', function(req, res) { fs.readFile(COMMENTS_FILE, function(err, data) { if (err) { diff --git a/server.php b/server.php deleted file mode 100644 index 6b8880c8..00000000 --- a/server.php +++ /dev/null @@ -1,52 +0,0 @@ - round(microtime(true) * 1000), - 'author' => $_POST['author'], - 'text' => $_POST['text'] - ]; - - $comments = json_encode($commentsDecoded, JSON_PRETTY_PRINT); - file_put_contents('comments.json', $comments); - } - header('Content-Type: application/json'); - header('Cache-Control: no-cache'); - echo $comments; - } else { - return false; - } -} diff --git a/server.pl b/server.pl deleted file mode 100644 index 517e1621..00000000 --- a/server.pl +++ /dev/null @@ -1,36 +0,0 @@ -# This file provided by Facebook is for non-commercial testing and evaluation -# purposes only. Facebook reserves all rights not expressly granted. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -use Time::HiRes qw(gettimeofday); -use Mojolicious::Lite; -use Mojo::JSON qw(encode_json decode_json); - -app->static->paths->[0] = './public'; - -any '/' => sub { $_[0]->reply->static('index.html') }; - -any [qw(GET POST)] => '/api/comments' => sub { - my $self = shift; - my $comments = decode_json (do { local(@ARGV,$/) = 'comments.json';<> }); - - if ($self->req->method eq 'POST') - { - push @$comments, { - id => int(gettimeofday * 1000), - author => $self->param('author'), - text => $self->param('text'), - }; - open my $FILE, '>', 'comments.json'; - print $FILE encode_json($comments); - } - $self->render(json => $comments); -}; -my $port = $ENV{PORT} || 3000; -app->start('daemon', '-l', "http://*:$port"); diff --git a/server.py b/server.py deleted file mode 100644 index 451fbacd..00000000 --- a/server.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file provided by Facebook is for non-commercial testing and evaluation -# purposes only. Facebook reserves all rights not expressly granted. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import json -import os -import time -from flask import Flask, Response, request - -app = Flask(__name__, static_url_path='', static_folder='public') -app.add_url_rule('/', 'root', lambda: app.send_static_file('index.html')) - -@app.route('/api/comments', methods=['GET', 'POST']) -def comments_handler(): - - with open('comments.json', 'r') as file: - comments = json.loads(file.read()) - - if request.method == 'POST': - newComment = request.form.to_dict() - newComment['id'] = int(time.time() * 1000) - comments.append(newComment) - - with open('comments.json', 'w') as file: - file.write(json.dumps(comments, indent=4, separators=(',', ': '))) - - return Response(json.dumps(comments), mimetype='application/json', headers={'Cache-Control': 'no-cache'}) - -if __name__ == '__main__': - app.run(port=int(os.environ.get("PORT",3000))) diff --git a/server.rb b/server.rb deleted file mode 100644 index eed401ae..00000000 --- a/server.rb +++ /dev/null @@ -1,46 +0,0 @@ -# This file provided by Facebook is for non-commercial testing and evaluation -# purposes only. Facebook reserves all rights not expressly granted. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -require 'webrick' -require 'json' - -port = ENV['PORT'].nil? ? 3000 : ENV['PORT'].to_i - -puts "Server started: http://localhost:#{port}/" - -root = File.expand_path './public' -server = WEBrick::HTTPServer.new Port: port, DocumentRoot: root - -server.mount_proc '/api/comments' do |req, res| - comments = JSON.parse(File.read('./comments.json', encoding: 'UTF-8')) - - if req.request_method == 'POST' - # Assume it's well formed - comment = { id: (Time.now.to_f * 1000).to_i } - req.query.each do |key, value| - comment[key] = value.force_encoding('UTF-8') - end - comments << comment - File.write( - './comments.json', - JSON.pretty_generate(comments, indent: ' '), - encoding: 'UTF-8' - ) - end - - # always return json - res['Content-Type'] = 'application/json' - res['Cache-Control'] = 'no-cache' - res.body = JSON.generate(comments) -end - -trap('INT') { server.shutdown } - -server.start diff --git a/users.json b/users.json new file mode 100644 index 00000000..5e7528b8 --- /dev/null +++ b/users.json @@ -0,0 +1,17 @@ +[ + { + "id": 1, + "name": "Pete Hunt", + "avatarURL": "https://avatars0.githubusercontent.com/u/5420789?v=3&s=460" + }, + { + "id": 2, + "name": "Haoyu Hunt", + "avatarURL": "https://avatars0.githubusercontent.com/u/5420789?v=3&s=460" + }, + { + "id": 3, + "name": "Chen Hunt", + "avatarURL": "https://avatars0.githubusercontent.com/u/5420789?v=3&s=460" + } +] \ No newline at end of file From 2b4339774e1c556e4e39d78ac8e16d500d8eb82b Mon Sep 17 00:00:00 2001 From: Haoyu-Mac Date: Tue, 15 Dec 2015 14:51:20 -0500 Subject: [PATCH 2/3] Try add attr to change background image -- Failure --- chatRecords.json | 18 ++++++++---- public/css/base.css | 2 ++ public/scripts/example.js | 61 +++++++++++++++++++-------------------- 3 files changed, 44 insertions(+), 37 deletions(-) diff --git a/chatRecords.json b/chatRecords.json index 99408138..e358ac3f 100644 --- a/chatRecords.json +++ b/chatRecords.json @@ -1,17 +1,23 @@ [ { - "userID": 1, + "id": 1, "name": "Pete Hunt", - "body": "Lorem ipsum dolor sit amet, id malorum perpetua has, id eum case appetere." + "body": "Lorem ipsum dolor sit amet, id malorum perpetua has, id eum case appetere.", + "avatarURL": "https://avatars0.githubusercontent.com/u/5420789?v=3&s=460", + "selfChat": false }, { - "userID": 2, + "id": 2, "name": "Haoyu Hunt", - "body": "Tantas vituperata et pri, eu appareat inimicus eam" + "body": "Tantas vituperata et pri, eu appareat inimicus eam", + "avatarURL": "https://avatars0.githubusercontent.com/u/5420789?v=3&s=460", + "selfChat": false }, { - "userID": 3, + "id": 3, "name": "Chen Hunt", - "body": "ius audiam voluptaria qualisque. Sed no feugait accusam. Et sed nostrud invidunt, duo tractatos deterruisset ei, nam in voluptua invenire" + "body": "ius audiam voluptaria qualisque. Sed no feugait accusam. Et sed nostrud invidunt, duo tractatos deterruisset ei, nam in voluptua invenire", + "avatarURL": "https://avatars0.githubusercontent.com/u/5420789?v=3&s=460", + "selfChat": true } ] \ No newline at end of file diff --git a/public/css/base.css b/public/css/base.css index 0867bded..dd6a49a8 100644 --- a/public/css/base.css +++ b/public/css/base.css @@ -135,6 +135,7 @@ ul { .chat-thread .self-chat:before { right: -80px; background-size:cover; + background-image: attr(profile-img-url); } .chat-thread .self-chat:after { @@ -154,6 +155,7 @@ ul { .chat-thread .others-chat:before { left: -80px; background-size:cover; + background-image: attr(profile-img-url); } .chat-thread .others-chat:after { diff --git a/public/scripts/example.js b/public/scripts/example.js index 0ad4c215..98f55be1 100644 --- a/public/scripts/example.js +++ b/public/scripts/example.js @@ -37,41 +37,28 @@ var OnlineUserList = React.createClass({ }); -// var ChatRecord = React.createClass({ -// render: function() { -// return ( -//
-//

-// {this.props.name} -//

-// -//
-// ); -// } -// }); - -var ChatRecords = React.createClass({ +var ChatRecord = React.createClass({ render: function() { - var divStyle = { - backgroundImage: 'url(' + imgUrl + ')', - } return ( -
    - { this.props.selfChat &&
  • Are we meeting today?
  • } - { !this.props.selfChat &&
  • yes, what time suits you?
  • } -
+
  • + {this.props.data.body} +
  • ); -} + } +}); var ChatRecordsList = React.createClass({ render: function() { - var divStyle = { - backgroundImage: 'url(' + imgUrl + ')', - } + var chatRecordsNodes = this.props.data.map(function(record) { + return ( + + ); + }); + return (
      -
    • Are we meeting today?
    • -
    • yes, what time suits you?
    • + {chatRecordsNodes}
    ); } @@ -122,6 +109,19 @@ var CommentBox = React.createClass({ }.bind(this) }); }, + loadChatRecordsFromServer: function () { + $.ajax({ + url: '/api/chatrecords', + dataType: 'json', + cache: false, + success: function(data) { + this.setState({chatRecordsData: data}); + }.bind(this), + error: function(xhr, status, err) { + console.error('/api/chatrecords', status, err.toString()); + }.bind(this) + }); + }, handleCommentSubmit: function(comment) { var comments = this.state.data; // Optimistically set an id on the new comment. It will be replaced by an @@ -145,21 +145,20 @@ var CommentBox = React.createClass({ }); }, getInitialState: function() { - return {data: [], onlineUsersData: []}; + return {data: [], onlineUsersData: [], chatRecordsData: []}; }, componentDidMount: function() { this.loadCommentsFromServer(); this.loadUsersFromServer(); + this.loadChatRecordsFromServer(); setInterval(this.loadCommentsFromServer, this.props.pollInterval); setInterval(this.loadUsersFromServer, this.props.pollInterval); }, render: function() { - console.log(this.state.onlineUsersData); - console.log(this.state.data); return (
    - +

    Comments

    From c0498f7244aad76ea1d27faf5ace2f8abf3ca5dd Mon Sep 17 00:00:00 2001 From: Haoyu-Mac Date: Wed, 16 Dec 2015 10:23:34 -0500 Subject: [PATCH 3/3] Clear old code in react tutorial --- README.md | 39 +---- comments.json | 37 ----- public/css/base.css | 331 +++++++++++++------------------------- public/index.html | 2 +- public/scripts/chat.js | 171 ++++++++++++++++++++ public/scripts/example.js | 231 -------------------------- server.js | 39 ----- 7 files changed, 283 insertions(+), 567 deletions(-) delete mode 100644 comments.json create mode 100644 public/scripts/chat.js delete mode 100644 public/scripts/example.js diff --git a/README.md b/README.md index 4862f5df..70553b3f 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,6 @@ -[![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy) +# React Learning Example -# React Tutorial - -This is the React comment box example from [the React tutorial](http://facebook.github.io/react/docs/tutorial.html). - -## To use - -There are several simple server implementations included. They all serve static files from `public/` and handle requests to `/api/comments` to fetch or add data. Start a server with one of the following: +This is a example code that inheriented from React tutorial. ### Node @@ -15,35 +9,6 @@ npm install node server.js ``` -### Python - -```sh -pip install -r requirements.txt -python server.py -``` - -### Ruby -```sh -ruby server.rb -``` - -### PHP -```sh -php server.php -``` - -### Go -```sh -go run server.go -``` - -### Perl - -```sh -cpan Mojolicious -perl server.pl -``` - And visit . Try opening multiple tabs! ## Changing the port diff --git a/comments.json b/comments.json deleted file mode 100644 index 008b92c2..00000000 --- a/comments.json +++ /dev/null @@ -1,37 +0,0 @@ -[ - { - "id": 1388534400000, - "author": "Pete Hunt", - "text": "Hey there!" - }, - { - "id": 1420070400000, - "author": "Paul O’Shannessy", - "text": "React is *great*!" - }, - { - "id": 1450122878637, - "author": "adfdf", - "text": "fdfd" - }, - { - "id": 1450122891262, - "author": "Haoyu", - "text": "This is great" - }, - { - "id": 1450123949686, - "author": "adf", - "text": "dfd" - }, - { - "id": 1450125550564, - "author": "adbdfd", - "text": "sdfd" - }, - { - "id": 1450125903112, - "author": "df", - "text": "df" - } -] \ No newline at end of file diff --git a/public/css/base.css b/public/css/base.css index dd6a49a8..bd50d25c 100644 --- a/public/css/base.css +++ b/public/css/base.css @@ -1,67 +1,3 @@ -body { - background: #fff; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 15px; - line-height: 1.7; - margin: 0; - padding: 30px; -} - -a { - color: #4183c4; - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -code { - background-color: #f8f8f8; - border: 1px solid #ddd; - border-radius: 3px; - font-family: "Bitstream Vera Sans Mono", Consolas, Courier, monospace; - font-size: 12px; - margin: 0 2px; - padding: 0px 5px; -} - -h1, h2, h3, h4 { - font-weight: bold; - margin: 0 0 15px; - padding: 0; -} - -h1 { - border-bottom: 1px solid #ddd; - font-size: 2.5em; - font-weight: bold; - margin: 0 0 15px; - padding: 0; -} - -h2 { - border-bottom: 1px solid #eee; - font-size: 2em; -} - -h3 { - font-size: 1.5em; -} - -h4 { - font-size: 1.2em; -} - -p, ul { - margin: 15px 0; -} - -ul { - padding-left: 30px; -} - - .online-user-list { border: 1px solid #c7b89e; } @@ -81,170 +17,121 @@ ul { /*-moz-border-radius: 30px; Disabled for FF1+ */ } - -/* Chat Records*/ - -.chat-thread { - margin: 24px auto 0 auto; - padding: 0 20px 0 0; - list-style: none; - overflow-y: scroll; - overflow-x: hidden; +/* Chat in Xlbock*/ +.chat-box { + font-family:'Helvetica Neue',Helvetica, sans-serif; + font-size:14px; + margin:0; +} + +.chat-container { + width:400px; + display:block; + margin:0 auto; + box-shadow:0 2px 5px rgba(0,0,0,0.4); +} + +.chat-box, .enter-message{ + background:#ECECEC; + padding:0 20px; + color:#a1a1a1; +} +.chat-box .message-box{ + padding:18px 0 10px; + clear:both; +} +.message-box .picture{ + float:left; + width:50px; + display:block; + padding-right:10px; +} +.picture img{ + width:43px; + height:48px; + border-radius:5px; +} +.picture span{ + font-weight:bold; + font-size:12px; + clear:both; + display:block; + text-align:center; + margin-top:3px; +} +.message{ + background:#fff; + display:inline-block; + padding:13px; + width:274px; + border-radius:2px; + box-shadow: 0 1px 1px rgba(0,0,0,.04); + position:relative; +} +.message:before{ + content:""; + position:absolute; + display:block; + left:0; + border-right:6px solid #fff; + border-top: 6px solid transparent; + border-bottom:6px solid transparent; + top:10px; + margin-left:-6px; +} +.message span{ + color:#555; + font-weight:bold; +} +.message p{ + padding-top:5px; +} +.message-box.right-img .picture{ + float:right; + padding:0; + padding-left:10px; +} +.message-box.right-img .picture img{ + float:right; +} +.message-box.right-img .message:before{ + left:100%; + margin-right:6px; + margin-left:0; + border-right:6px solid transparent; + border-left:6px solid #fff; + border-top: 6px solid transparent; + border-bottom:6px solid transparent; } -.chat-thread li { - position: relative; - clear: both; +.avatar-chat { display: inline-block; - padding: 16px 40px 16px 20px; - margin: 0 0 20px 0; - font: 16px/20px 'Noto Sans', sans-serif; - border-radius: 10px; - background-color: rgba(25,147,147,0.2); -} - -/* Chat - Avatar */ -.chat-thread li:before { - position: absolute; - top: 0; - width: 50px; - height: 50px; - border-radius: 50px; - content: ''; } -/* Chat - Speech Bubble Arrow */ -.chat-thread li:after { - position: absolute; - top: 15px; - content: ''; - width: 0; - height: 0; - border-top: 15px solid rgba(25,147,147,0.2); -} - -.chat-thread .self-chat { - animation: show-chat-odd 0.15s 1 ease-in; - -moz-animation: show-chat-odd 0.15s 1 ease-in; - -webkit-animation: show-chat-odd 0.15s 1 ease-in; - float: right; - margin-right: 80px; - color: #0AD5C1; -} - -.chat-thread .self-chat:before { - right: -80px; - background-size:cover; - background-image: attr(profile-img-url); -} - -.chat-thread .self-chat:after { - border-right: 15px solid transparent; - right: -15px; -} - -.chat-thread .others-chat { - animation: show-chat-even 0.15s 1 ease-in; - -moz-animation: show-chat-even 0.15s 1 ease-in; - -webkit-animation: show-chat-even 0.15s 1 ease-in; - float: left; - margin-left: 80px; - color: #0EC879; -} - -.chat-thread .others-chat:before { - left: -80px; - background-size:cover; - background-image: attr(profile-img-url); -} - -.chat-thread .others-chat:after { - border-left: 15px solid transparent; - left: -15px; -} - - - -// A tiny bit responsive... -// -------------------------------------- -/* Small screens */ -@media all and (max-width: 767px) { - .chat-thread { - width: 90%; - height: 260px; - } - - .chat-window { - left: 5%; - width: 90%; - } -} - -/* Medium and large screens */ -@media all and (min-width: 768px) { - .chat-thread { - width: 50%; - height: 320px; - } - - .chat-window { - left: 25%; - width: 50%; - } -} - -// Animation -// -------------------------------------- -@keyframes show-chat-even { - 0% { - margin-left: -480px; - } - 100% { - margin-left: 0; - } -} - -@-moz-keyframes show-chat-even { - 0% { - margin-left: -480px; - } - 100% { - margin-left: 0; - } -} - -@-webkit-keyframes show-chat-even { - 0% { - margin-left: -480px; - } - 100% { - margin-left: 0; - } +.avatar-chat, +.avatar-chat img { + width: 40px; + height: 40px; + -webkit-border-radius: 40px; + /* Saf3+, Chrome */ + border-radius: 40px; + /* Opera 10.5, IE 9 */ + /*-moz-border-radius: 30px; Disabled for FF1+ */ } -@keyframes show-chat-odd { - 0% { - margin-right: -480px; - } - 100% { - margin-right: 0; - } +/*.enter-message{ + padding:13px 0px; } - -@-moz-keyframes show-chat-odd { - 0% { - margin-right: -480px; - } - 100% { - margin-right: 0; - } +.enter-message input{ + border:none; + padding:10px 12px; + background:#d3d3d3; + width:260px; + border-radius:2px; } - -@-webkit-keyframes show-chat-odd { - 0% { - margin-right: -480px; - } - 100% { - margin-right: 0; - } -} \ No newline at end of file +.enter-message a.send{ + padding:10px 15px; + background:#6294c2; + border-radius:2px; + float:right; +}*/ \ No newline at end of file diff --git a/public/index.html b/public/index.html index 3cbb635b..82637e06 100644 --- a/public/index.html +++ b/public/index.html @@ -13,6 +13,6 @@
    - + diff --git a/public/scripts/chat.js b/public/scripts/chat.js new file mode 100644 index 00000000..02fe0e95 --- /dev/null +++ b/public/scripts/chat.js @@ -0,0 +1,171 @@ +/** + * This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +var OnlineUserAvatar = React.createClass({ + render: function() { + return ( +
    + +
    + ); + } +}); + +var OnlineUserList = React.createClass({ + render: function() { + var onlineUserListNode = this.props.data.map(function(user) { + return ( + + ); + }); + return ( +
    + {onlineUserListNode} +
    + ); + } +}); + + +// var ChatRecord = React.createClass({ +// render: function() { +// return ( +//
  • +// {this.props.data.body} +//
  • +// ); +// } +// }); + + +var ChatRecordsList = React.createClass({ + render: function() { + return ( +
    +
    +
    +
    +
    + +
    + 10 mins +
    +
    + Bobby Giangeruso +

    Hey Mike, how are you doing?

    +
    +
    +
    +
    +
    + +
    + 2 mins +
    +
    + Mike Moloney +

    Pretty good, Eating nutella, nommommom

    +
    +
    +
    +
    + ); + } +}); + +// var ChatRecordsList = React.createClass({ +// render: function() { +// var chatRecordsNodes = this.props.data.map(function(record) { +// return ( +// +// ); +// }); + +// return ( +//
      +// {chatRecordsNodes} +//
    +// ); +// } +// }); + +var Comment = React.createClass({ + rawMarkup: function() { + var rawMarkup = marked(this.props.children.toString(), {sanitize: true}); + return { __html: rawMarkup }; + }, + + render: function() { + return ( +
    +

    + {this.props.author} +

    + +
    + ); + } +}); + +var CommentBox = React.createClass({ + loadUsersFromServer: function () { + $.ajax({ + url: '/api/users', + dataType: 'json', + cache: false, + success: function(data) { + this.setState({onlineUsersData: data}); + }.bind(this), + error: function(xhr, status, err) { + console.error('/api/users', status, err.toString()); + }.bind(this) + }); + }, + loadChatRecordsFromServer: function () { + $.ajax({ + url: '/api/chatrecords', + dataType: 'json', + cache: false, + success: function(data) { + this.setState({chatRecordsData: data}); + }.bind(this), + error: function(xhr, status, err) { + console.error('/api/chatrecords', status, err.toString()); + }.bind(this) + }); + }, + getInitialState: function() { + return {onlineUsersData: [], chatRecordsData: []}; + }, + componentDidMount: function() { + this.loadUsersFromServer(); + this.loadChatRecordsFromServer(); + setInterval(this.loadUsersFromServer, this.props.pollInterval); + setInterval(this.loadChatRecordsFromServer, this.props.pollInterval); + }, + render: function() { + return ( +
    + + +
    + ); + } +}); + + +ReactDOM.render( + , + document.getElementById('content') +); diff --git a/public/scripts/example.js b/public/scripts/example.js deleted file mode 100644 index 98f55be1..00000000 --- a/public/scripts/example.js +++ /dev/null @@ -1,231 +0,0 @@ -/** - * This file provided by Facebook is for non-commercial testing and evaluation - * purposes only. Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - - -var OnlineUserAvatar = React.createClass({ - render: function() { - return ( -
    - -
    - ); - } -}); - -var OnlineUserList = React.createClass({ - render: function() { - var onlineUserListNode = this.props.data.map(function(user) { - return ( - - ); - }); - return ( -
    - {onlineUserListNode} -
    - ); - } -}); - - -var ChatRecord = React.createClass({ - render: function() { - return ( -
  • - {this.props.data.body} -
  • - ); - } -}); - -var ChatRecordsList = React.createClass({ - render: function() { - var chatRecordsNodes = this.props.data.map(function(record) { - return ( - - ); - }); - - return ( -
      - {chatRecordsNodes} -
    - ); - } -}); - -var Comment = React.createClass({ - rawMarkup: function() { - var rawMarkup = marked(this.props.children.toString(), {sanitize: true}); - return { __html: rawMarkup }; - }, - - render: function() { - return ( -
    -

    - {this.props.author} -

    - -
    - ); - } -}); - -var CommentBox = React.createClass({ - loadCommentsFromServer: function() { - $.ajax({ - url: this.props.url, - dataType: 'json', - cache: false, - success: function(data) { - this.setState({data: data}); - }.bind(this), - error: function(xhr, status, err) { - console.error(this.props.url, status, err.toString()); - }.bind(this) - }); - }, - loadUsersFromServer: function () { - $.ajax({ - url: '/api/users', - dataType: 'json', - cache: false, - success: function(data) { - this.setState({onlineUsersData: data}); - }.bind(this), - error: function(xhr, status, err) { - console.error('/api/users', status, err.toString()); - }.bind(this) - }); - }, - loadChatRecordsFromServer: function () { - $.ajax({ - url: '/api/chatrecords', - dataType: 'json', - cache: false, - success: function(data) { - this.setState({chatRecordsData: data}); - }.bind(this), - error: function(xhr, status, err) { - console.error('/api/chatrecords', status, err.toString()); - }.bind(this) - }); - }, - handleCommentSubmit: function(comment) { - var comments = this.state.data; - // Optimistically set an id on the new comment. It will be replaced by an - // id generated by the server. In a production application you would likely - // not use Date.now() for this and would have a more robust system in place. - comment.id = Date.now(); - var newComments = comments.concat([comment]); - this.setState({data: newComments}); - $.ajax({ - url: this.props.url, - dataType: 'json', - type: 'POST', - data: comment, - success: function(data) { - this.setState({data: data}); - }.bind(this), - error: function(xhr, status, err) { - this.setState({data: comments}); - console.error(this.props.url, status, err.toString()); - }.bind(this) - }); - }, - getInitialState: function() { - return {data: [], onlineUsersData: [], chatRecordsData: []}; - }, - componentDidMount: function() { - this.loadCommentsFromServer(); - this.loadUsersFromServer(); - this.loadChatRecordsFromServer(); - setInterval(this.loadCommentsFromServer, this.props.pollInterval); - setInterval(this.loadUsersFromServer, this.props.pollInterval); - }, - render: function() { - return ( -
    - - -

    Comments

    - - -
    - ); - } -}); - -var CommentList = React.createClass({ - render: function() { - var commentNodes = this.props.data.map(function(comment) { - return ( - - {comment.text} - - ); - }); - return ( -
    - {commentNodes} -
    - ); - } -}); - -var CommentForm = React.createClass({ - getInitialState: function() { - return {author: '', text: ''}; - }, - handleAuthorChange: function(e) { - this.setState({author: e.target.value}); - }, - handleTextChange: function(e) { - this.setState({text: e.target.value}); - }, - handleSubmit: function(e) { - e.preventDefault(); - var author = this.state.author.trim(); - var text = this.state.text.trim(); - if (!text || !author) { - return; - } - this.props.onCommentSubmit({author: author, text: text}); - this.setState({author: '', text: ''}); - }, - render: function() { - return ( -
    - - - -
    - ); - } -}); - -ReactDOM.render( - , - document.getElementById('content') -); diff --git a/server.js b/server.js index 4b5853bb..43cc2be4 100644 --- a/server.js +++ b/server.js @@ -19,7 +19,6 @@ var express = require('express'); var bodyParser = require('body-parser'); var app = express(); -var COMMENTS_FILE = path.join(__dirname, 'comments.json'); var USERS_FILE = path.join(__dirname, 'users.json'); var CHATRECORDS_FILE = path.join(__dirname, 'chatRecords.json'); @@ -52,44 +51,6 @@ app.get('/api/chatrecords', function(req, res) { }); }); -app.get('/api/comments', function(req, res) { - fs.readFile(COMMENTS_FILE, function(err, data) { - if (err) { - console.error(err); - process.exit(1); - } - res.setHeader('Cache-Control', 'no-cache'); - res.json(JSON.parse(data)); - }); -}); - -app.post('/api/comments', function(req, res) { - fs.readFile(COMMENTS_FILE, function(err, data) { - if (err) { - console.error(err); - process.exit(1); - } - var comments = JSON.parse(data); - // NOTE: In a real implementation, we would likely rely on a database or - // some other approach (e.g. UUIDs) to ensure a globally unique id. We'll - // treat Date.now() as unique-enough for our purposes. - var newComment = { - id: Date.now(), - author: req.body.author, - text: req.body.text, - }; - comments.push(newComment); - fs.writeFile(COMMENTS_FILE, JSON.stringify(comments, null, 4), function(err) { - if (err) { - console.error(err); - process.exit(1); - } - res.setHeader('Cache-Control', 'no-cache'); - res.json(comments); - }); - }); -}); - app.listen(app.get('port'), function() { console.log('Server started: http://localhost:' + app.get('port') + '/');