336 lines
11 KiB
HTML
336 lines
11 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<title>Watermill Server-Sent Events Example</title>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
|
</head>
|
|
<body>
|
|
|
|
<style>
|
|
body {
|
|
padding-top: 5rem;
|
|
}
|
|
.example {
|
|
padding: 3rem 1.5rem;
|
|
}
|
|
#feeds li {
|
|
display: inline;
|
|
}
|
|
.breadcrumb-item + .breadcrumb-item::before {
|
|
content: "|";
|
|
}
|
|
</style>
|
|
|
|
<div id="app">
|
|
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
|
|
<a class="navbar-brand" href="/">Home</a>
|
|
</nav>
|
|
|
|
<main role="main" class="container">
|
|
<div class="example">
|
|
<feeds-list></feeds-list>
|
|
<add-post></add-post>
|
|
|
|
<hr />
|
|
|
|
<router-view></router-view>
|
|
</div>
|
|
|
|
</main>
|
|
</div>
|
|
</body>
|
|
|
|
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
|
|
|
|
<script src="https://unpkg.com/vue@2.7.16/dist/vue.js"></script>
|
|
<script src="https://unpkg.com/vue-router@3.6.5/dist/vue-router.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/vue-resource@1.3.5"></script>
|
|
|
|
<script type="text/x-template" id="feeds-list">
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item" v-bind:class="{ active: feed.active }" v-for="feed in feeds">
|
|
<router-link v-if="!feed.active" :to="{ name: 'show-feed', params: { feed_name: feed.name }}">
|
|
{{ feed.text }}
|
|
</router-link>
|
|
<span v-else>
|
|
{{ feed.text }}
|
|
</span>
|
|
</li>
|
|
</ol>
|
|
</nav>
|
|
</script>
|
|
|
|
<script type="text/x-template" id="add-post">
|
|
<div>
|
|
<p>
|
|
<button class="btn btn-primary" type="button" v-on:click="toggle">
|
|
Add new post
|
|
</button>
|
|
|
|
<button class="btn btn-info" type="button" v-on:click="generatePost">Add randomly generated post</button>
|
|
</p>
|
|
<div id="addPostForm" v-show="showForm">
|
|
<div class="card card-body">
|
|
<h2>Add new post</h2>
|
|
<form v-on:submit.prevent="submitNewPost">
|
|
<div class="form-group row">
|
|
<label for="inputTitle" class="col-sm-2 col-form-label">Title</label>
|
|
<div class="col-sm-10">
|
|
<input type="title" class="form-control" id="inputTitle" placeholder="Title" v-model="post.title">
|
|
</div>
|
|
</div>
|
|
<div class="form-group row">
|
|
<label for="inputAuthor" class="col-sm-2 col-form-label">Author</label>
|
|
<div class="col-sm-10">
|
|
<input type="author" class="form-control" id="inputAuthor" placeholder="Author" v-model="post.author">
|
|
</div>
|
|
</div>
|
|
<div class="form-group row">
|
|
<label for="inputContent" class="col-sm-2 col-form-label">Content</label>
|
|
<div class="col-sm-10">
|
|
<textarea class="form-control" id="inputContent" rows="3" v-model="post.content"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="form-group row">
|
|
<div class="col-sm-2"></div>
|
|
<div class="col-sm-10">
|
|
<input type="submit" class="btn btn-success" value="Add post" />
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</script>
|
|
|
|
<script type="text/x-template" id="home">
|
|
<div>
|
|
<h3>1. Add some posts using the buttons above (use #hashtags).</h3>
|
|
<h3>2. Click one of feeds to see a live stream.</h3>
|
|
</div>
|
|
</script>
|
|
|
|
<script type="text/x-template" id="feed-template">
|
|
<div>
|
|
<h2>{{ feed }}</h2>
|
|
<div v-for="post in posts">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h4 class="card-title">
|
|
{{post.title}}
|
|
<router-link :to="{ name: 'edit-post', params: { post_id: post.id }}"
|
|
class="btn btn-light class-right btn-sm">Edit
|
|
</router-link>
|
|
</h4>
|
|
<p class="card-text" v-html="post.content"></p>
|
|
<p class="card-text">
|
|
<small class="text-muted">Author: {{post.author}}</small>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<br>
|
|
</div>
|
|
</div>
|
|
</script>
|
|
|
|
<script type="text/x-template" id="post-edit-template">
|
|
<div>
|
|
<h2>Edit post</h2>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<form v-on:submit.prevent="submitData">
|
|
<div class="form-group row">
|
|
<label for="inputTitle" class="col-sm-2 col-form-label">Title</label>
|
|
<div class="col-sm-10">
|
|
<input type="title" class="form-control" id="inputTitle" placeholder="Title" v-model="post.title">
|
|
</div>
|
|
</div>
|
|
<div class="form-group row">
|
|
<label for="inputContent" class="col-sm-2 col-form-label">Content</label>
|
|
<div class="col-sm-10">
|
|
<textarea class="form-control" id="inputContent" rows="3" v-model="post.content"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="form-group row">
|
|
<div class="col-sm-2"></div>
|
|
<div class="col-sm-10">
|
|
<input type="submit" class="btn btn-success" value="Update post" />
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</script>
|
|
|
|
<script>
|
|
Vue.component('feeds-list', {
|
|
template: '#feeds-list',
|
|
data: function () {
|
|
return {
|
|
feeds_stream: [],
|
|
current_feed: "",
|
|
}
|
|
},
|
|
created() {
|
|
this.es = new EventSource('/api/feeds')
|
|
|
|
this.es.addEventListener('data', event => {
|
|
let data = JSON.parse(event.data);
|
|
this.feeds_stream = data.feeds;
|
|
}, false);
|
|
},
|
|
watch: {
|
|
$route(to, from) {
|
|
if (to.name === "show-feed") {
|
|
this.current_feed = to.params.feed_name;
|
|
} else {
|
|
this.current_feed = "";
|
|
}
|
|
}
|
|
},
|
|
computed: {
|
|
feeds: function (){
|
|
return this.feeds_stream.map(f => ({text: `${f.name} (${f.posts})`, name: f.name, active: f.name === this.current_feed}));
|
|
},
|
|
},
|
|
});
|
|
|
|
const Home = {
|
|
template: '#home',
|
|
}
|
|
|
|
Vue.component('add-post', {
|
|
template: '#add-post',
|
|
data: function () {
|
|
return {
|
|
post: {},
|
|
showForm: false,
|
|
}
|
|
},
|
|
methods: {
|
|
submitNewPost: function() {
|
|
postData = this.post;
|
|
this.$http.post('/api/posts', JSON.stringify(postData))
|
|
this.post = {};
|
|
},
|
|
generatePost: function() {
|
|
this.$http.post('/api/generate/post')
|
|
},
|
|
toggle: function () {
|
|
this.showForm = !this.showForm;
|
|
},
|
|
},
|
|
created() {},
|
|
});
|
|
|
|
const ShowFeed = {
|
|
template: '#feed-template',
|
|
data: function () {
|
|
return {
|
|
feed: "",
|
|
posts_stream: [],
|
|
}
|
|
},
|
|
watch: {
|
|
$route(to, from) {
|
|
console.log(to)
|
|
this.setupFeed(to.params.feed_name);
|
|
}
|
|
},
|
|
created() {
|
|
this.setupFeed(this.$route.params.feed_name);
|
|
},
|
|
destroyed() {
|
|
if (this.es) {
|
|
this.es.close();
|
|
}
|
|
},
|
|
methods: {
|
|
setupFeed: function(feed_name) {
|
|
this.feed = feed_name;
|
|
|
|
if (this.es) {
|
|
this.es.close();
|
|
}
|
|
this.es = new EventSource('/api/feeds/' + this.feed)
|
|
|
|
this.es.addEventListener('data', event => {
|
|
let data = JSON.parse(event.data);
|
|
this.posts_stream = data.posts;
|
|
}, false);
|
|
},
|
|
},
|
|
computed: {
|
|
posts: function() {
|
|
var posts = this.posts_stream.slice();
|
|
for (var p of posts) {
|
|
p.content = p.content.replace(/#([\w]+)/g,'<a href="#/feed/$1">#$1</a>')
|
|
}
|
|
return posts;
|
|
},
|
|
},
|
|
};
|
|
const EditPost = {
|
|
template: "#post-edit-template",
|
|
data: function () {
|
|
return {
|
|
post_id: this.$route.params.post_id,
|
|
post: {},
|
|
}
|
|
},
|
|
created() {
|
|
this.setupPost();
|
|
},
|
|
destroyed() {
|
|
if (this.es) {
|
|
console.log("closing")
|
|
this.es.close();
|
|
}
|
|
},
|
|
methods: {
|
|
submitData: function () {
|
|
postData = this.post;
|
|
this.$http.patch('/api/posts/' + this.post_id, JSON.stringify(postData));
|
|
router.go(-1);
|
|
},
|
|
setupPost() {
|
|
this.es = new EventSource('/api/posts/' + this.post_id)
|
|
|
|
this.es.addEventListener('data', event => {
|
|
let data = JSON.parse(event.data);
|
|
this.post = data;
|
|
}, false);
|
|
|
|
this.es.addEventListener('error', function (e) {
|
|
if (e.readyState == EventSource.CLOSED) {
|
|
console.log("close", e)
|
|
} else {
|
|
console.log("other error", e)
|
|
}
|
|
}, false);
|
|
},
|
|
},
|
|
};
|
|
|
|
const routes = [
|
|
{path: '/', component: Home, name: 'home'},
|
|
{path: '/edit-post/:post_id', component: EditPost, name: 'edit-post'},
|
|
{path: '/feed/:feed_name', component: ShowFeed, name: 'show-feed'},
|
|
];
|
|
|
|
const router = new VueRouter({
|
|
routes
|
|
});
|
|
|
|
const app = new Vue({
|
|
router
|
|
}).$mount('#app')
|
|
</script>
|
|
</html>
|