Evan You 9 роки тому
батько
коміт
93542a58cc

+ 25 - 0
examples/chat/api/index.js

@@ -0,0 +1,25 @@
+import vuex from '../vuex'
+const data = require('./mock-data')
+const LATENCY = 16
+
+export function getAllMessages (cb) {
+  setTimeout(() => {
+    cb(data)
+  }, LATENCY)
+}
+
+export function createMessage ({ text, thread }, cb) {
+  const timestamp = Date.now()
+  const id = 'm_' + timestamp
+  const message = {
+    id,
+    text,
+    timestamp,
+    threadID: thread.id,
+    threadName: thread.name,
+    authorName: 'Evan'
+  }
+  setTimeout(function () {
+    cb(message)
+  }, LATENCY)
+}

+ 58 - 0
examples/chat/api/mock-data.js

@@ -0,0 +1,58 @@
+module.exports = [
+  {
+    id: 'm_1',
+    threadID: 't_1',
+    threadName: 'Jing and Bill',
+    authorName: 'Bill',
+    text: 'Hey Jing, want to give a Flux talk at ForwardJS?',
+    timestamp: Date.now() - 99999
+  },
+  {
+    id: 'm_2',
+    threadID: 't_1',
+    threadName: 'Jing and Bill',
+    authorName: 'Bill',
+    text: 'Seems like a pretty cool conference.',
+    timestamp: Date.now() - 89999
+  },
+  {
+    id: 'm_3',
+    threadID: 't_1',
+    threadName: 'Jing and Bill',
+    authorName: 'Jing',
+    text: 'Sounds good.  Will they be serving dessert?',
+    timestamp: Date.now() - 79999
+  },
+  {
+    id: 'm_4',
+    threadID: 't_2',
+    threadName: 'Dave and Bill',
+    authorName: 'Bill',
+    text: 'Hey Dave, want to get a beer after the conference?',
+    timestamp: Date.now() - 69999
+  },
+  {
+    id: 'm_5',
+    threadID: 't_2',
+    threadName: 'Dave and Bill',
+    authorName: 'Dave',
+    text: 'Totally!  Meet you at the hotel bar.',
+    timestamp: Date.now() - 59999
+  },
+  {
+    id: 'm_6',
+    threadID: 't_3',
+    threadName: 'Functional Heads',
+    authorName: 'Bill',
+    text: 'Hey Brian, are you going to be talking about functional stuff?',
+    timestamp: Date.now() - 49999
+  },
+  {
+    id: 'm_7',
+    threadID: 't_3',
+    threadName: 'Bill and Brian',
+    authorName: 'Brian',
+    text: 'At ForwardJS?  Yeah, of course.  See you there!',
+    timestamp: Date.now() - 39999
+  }
+]

+ 24 - 0
examples/chat/components/App.vue

@@ -0,0 +1,24 @@
+<style src="../css/chat.css"></style>
+
+<template>
+  <div class="chatapp">
+    <thread-section></thread-section>
+    <message-section></message-section>
+  </div>
+</template>
+
+<script>
+import vuex from '../vuex'
+import ThreadSection from './ThreadSection.vue'
+import MessageSection from './MessageSection.vue'
+
+export default {
+  components: {
+    ThreadSection,
+    MessageSection
+  },
+  created () {
+    vuex.actions.getAllMessages()
+  }
+}
+</script>

+ 20 - 0
examples/chat/components/Message.vue

@@ -0,0 +1,20 @@
+<template>
+  <li class="message-list-item">
+    <h5 class="message-author-name">{{ message.authorName }}</h5>
+    <div class="message-time">
+      {{ date }}
+    </div>
+    <div class="message-text">{{ message.text }}</div>
+  </li>
+</template>
+
+<script>
+export default {
+  props: ['message'],
+  computed: {
+    date () {
+      return new Date(this.message.timestamp).toLocaleTimeString()
+    }
+  }
+}
+</script>

+ 43 - 0
examples/chat/components/MessageSection.vue

@@ -0,0 +1,43 @@
+<template>
+  <div class="message-section">
+    <h3 class="message-thread-heading">{{ thread.name }}</h3>
+    <ul class="message-list" v-el:list>
+      <message v-for="message in thread.messages" :message="message"></message>
+    </ul>
+    <textarea class="message-composer" @keyup.enter="sendMessage"></textarea>
+  </div>
+</template>
+
+<script>
+import vuex from '../vuex'
+import Message from './Message.vue'
+
+export default {
+  components: { Message },
+  computed: {
+    thread () {
+      const id = vuex.state.currentThreadID
+      return id
+        ? vuex.state.threads.find(t => t.id === id)
+        : {}
+    }
+  },
+  watch: {
+    'thread.messages': function () {
+      this.$nextTick(() => {
+        const ul = this.$els.list
+        ul.scrollTop = ul.scrollHeight
+      })
+    }
+  },
+  methods: {
+    sendMessage (e) {
+      const text = e.target.value
+      if (text.trim()) {
+        vuex.actions.sendMessage(text, this.thread)
+        e.target.value = ''
+      }
+    }
+  }
+}
+</script>

+ 0 - 0
examples/chat/components/ThreadSection.vue


+ 98 - 0
examples/chat/css/chat.css

@@ -0,0 +1,98 @@
+/**
+ * This file is provided by Facebook for 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.
+ */
+
+.chatapp {
+  font-family: 'Muli', 'Helvetica Neue', helvetica, arial;
+  max-width: 760px;
+  margin: 20px auto;
+  overflow: hidden;
+}
+
+.message-list, .thread-list {
+  border: 1px solid #ccf;
+  font-size: 16px;
+  height: 400px;
+  margin: 0;
+  overflow-y: auto;
+  padding: 0;
+}
+
+.message-section {
+  float: right;
+  width: 65%;
+}
+
+.thread-section {
+  float: left;
+  width: 32.5%;
+}
+
+.message-thread-heading,
+.thread-count {
+  height: 40px;
+  margin: 0;
+}
+
+.message-list-item, .thread-list-item {
+  list-style: none;
+  padding: 12px 14px 14px;
+}
+
+.thread-list-item {
+  border-bottom: 1px solid #ccc;
+  cursor: pointer;
+}
+
+.thread-list:hover .thread-list-item:hover {
+  background-color: #f8f8ff;
+}
+
+.thread-list:hover .thread-list-item {
+  background-color: #fff;
+}
+
+.thread-list-item.active,
+.thread-list:hover .thread-list-item.active,
+.thread-list:hover .thread-list-item.active:hover {
+  background-color: #efefff;
+  cursor: default;
+}
+
+.message-author-name,
+.thread-name {
+  color: #66c;
+  float: left;
+  font-size: 13px;
+  margin: 0;
+}
+
+.message-time, .thread-time {
+  color: #aad;
+  float: right;
+  font-size: 12px;
+}
+
+.message-text, .thread-last-message {
+  clear: both;
+  font-size: 14px;
+  padding-top: 10px;
+}
+
+.message-composer {
+  box-sizing: border-box;
+  font-family: inherit;
+  font-size: 14px;
+  height: 5em;
+  width: 100%;
+  margin: 20px 0 0;
+  padding: 10px;
+}

+ 12 - 0
examples/chat/index.html

@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>vuex chat example</title>
+    <link href="http://fonts.googleapis.com/css?family=Muli" rel="stylesheet" type="text/css">
+  </head>
+  <body>
+    <app></app>
+    <script src="build.js"></script>
+  </body>
+</html>

+ 8 - 0
examples/chat/main.js

@@ -0,0 +1,8 @@
+import 'babel-polyfill'
+import Vue from 'vue'
+import App from './components/App.vue'
+
+new Vue({
+  el: 'body',
+  components: { App }
+})

+ 20 - 0
examples/chat/vuex/actions.js

@@ -0,0 +1,20 @@
+import * as api from '../api'
+import * as types from './mutation-types'
+
+export const getAllMessages = () => dispatch => {
+  api.getAllMessages(messages => {
+    messages.forEach(message => {
+      dispatch(types.RECEIVE_MESSAGE, message)
+    })
+  })
+}
+
+export const sendMessage = (text, thread) => dispatch => {
+  api.createMessage({ text, thread }, message => {
+    dispatch(types.RECEIVE_MESSAGE, message)
+  })
+}
+
+export const switchThread = id => dispatch => {
+  dispatch(types.SWITCH_THREAD, id)
+}

+ 18 - 0
examples/chat/vuex/index.js

@@ -0,0 +1,18 @@
+import Vue from 'vue'
+import Vuex from '../../../src'
+import * as actions from './actions'
+import mutations from './mutations'
+
+Vue.use(Vuex)
+
+export default new Vuex({
+  state: {
+    currentThreadID: null,
+    threads: [/* { id, name, messages } */]
+  },
+  actions,
+  mutations,
+  middlewares: process.env.NODE_ENV !== 'production'
+    ? [Vuex.createLogger()]
+    : []
+})

+ 2 - 0
examples/chat/vuex/mutation-types.js

@@ -0,0 +1,2 @@
+export const SWITCH_THREAD = 'SWITCH_THREAD'
+export const RECEIVE_MESSAGE = 'RECEIVE_MESSAGE'

+ 30 - 0
examples/chat/vuex/mutations.js

@@ -0,0 +1,30 @@
+import { set } from 'vue'
+import * as types from './mutation-types'
+
+export default {
+
+  [types.RECEIVE_MESSAGE] (state, message) {
+    // reset error message
+    state.errorMessage = null
+    // create new thread if the thread doesn't exist
+    let thread = state.threads.find(t => t.id === message.threadID)
+    if (!thread) {
+      thread = {
+        id: message.threadID,
+        name: message.threadName,
+        messages: []
+      }
+      state.threads.push(thread)
+    }
+    // set current id if no thread is set
+    if (!state.currentThreadID) {
+      state.currentThreadID = thread.id
+    }
+    // add message
+    thread.messages.push(message)
+  },
+
+  [types.SWITCH_THREAD] (state, id) {
+    state.currentThreadID = id
+  }
+}

+ 1 - 0
package.json

@@ -12,6 +12,7 @@
     "counter-hot": "cd examples/counter-hot && webpack-dev-server --inline --hot --config ../webpack.shared.config.js",
     "counter-hot": "cd examples/counter-hot && webpack-dev-server --inline --hot --config ../webpack.shared.config.js",
     "todomvc": "cd examples/todomvc && webpack-dev-server --inline --hot --config ../webpack.shared.config.js",
     "todomvc": "cd examples/todomvc && webpack-dev-server --inline --hot --config ../webpack.shared.config.js",
     "cart": "cd examples/shopping-cart && webpack-dev-server --inline --hot --config ../webpack.shared.config.js",
     "cart": "cd examples/shopping-cart && webpack-dev-server --inline --hot --config ../webpack.shared.config.js",
+    "chat": "cd examples/chat && webpack-dev-server --inline --hot --config ../webpack.shared.config.js",
     "prepublish": "babel src --out-dir lib --presets es2015 --plugins add-module-exports",
     "prepublish": "babel src --out-dir lib --presets es2015 --plugins add-module-exports",
     "test": "eslint src && mocha --compilers js:babel-core/register",
     "test": "eslint src && mocha --compilers js:babel-core/register",
     "docs": "cd docs && gitbook serve",
     "docs": "cd docs && gitbook serve",