diff --git a/app/components/article-preview-banner.js b/app/components/article-preview-banner.js new file mode 100644 index 0000000..3699a72 --- /dev/null +++ b/app/components/article-preview-banner.js @@ -0,0 +1,148 @@ +import {Authentication} from "../auth/authentication"; +export class ArticlePreviewBannerComponent extends HTMLElement { + + constructor() { + super(); + this.auth = Authentication.instance.auth; + this.followButtonAction = this.followButtonAction.bind(this); + this._title = null; + this._username = null; + this._favoritesCount = null; + this._date = null; + this._image = null; + } + + static get observedAttributes() { + return ['title', 'username', 'favorites-count', 'date', 'image']; + } + + attributeChangedCallback(name, oldValue, newValue) { + if (newValue) { + switch (name) { + case 'title': { + this._title = newValue; + break; + } + case 'username': { + this._username = newValue; + break; + } + case 'favorites-count' : { + this._favoritesCount = newValue; + break; + } + case 'date' : { + this._date = newValue; + break; + } + case 'image': { + this._image = newValue; + break; + } + } + this.innerHTML = this.render(); + } + } + + connectedCallback() { + this.innerHTML = this.render(); + } + + followButtonAction(e) { + + } + + disconnectedCallback() { + if (this.$followButton) { + this.$followButton.removeEventListener('click', this.followButtonAction); + } + } + + + render() { + return ` + +`; + } + + set title(value) { + this.setAttribute('title', value); + } + + get title() { + return this._title; + } + + set username(value) { + this.setAttribute('username', value); + } + + get username() { + return this._username; + } + + set favoritesCount(value) { + this.setAttribute('favorites-count', value); + } + + get favoritesCount() { + return this._favoritesCount; + } + + set date(value) { + this.setAttribute('date', value); + } + + get date() { + return this._date; + } + + set image(value) { + this.setAttribute('image', value); + } + + get image() { + return this._image; + } +} diff --git a/app/components/comment-create.comp.js b/app/components/comment-create.comp.js new file mode 100644 index 0000000..1ba9091 --- /dev/null +++ b/app/components/comment-create.comp.js @@ -0,0 +1,74 @@ +import {Authentication} from "../auth/authentication"; +export class CommentCreateComponent extends HTMLElement { + constructor() { + super(); + this.postComment = this.postComment.bind(this); + this.auth = Authentication.instance.auth; + this.image = "https://static.productionready.io/images/smiley-cyrus.jpg"; + if (this.auth) { + this.image = this.auth.image; + } + } + + static get observedAttributes() { + return []; + } + + attributeChangedCallback(name, oldValue, newValue) { + } + + connectedCallback() { + if (this.auth) { + this.innerHTML = this.renderCommentForm(); + this.$postCommentBtn = this.querySelector('#postCommentBtn'); + this.$commentValue = this.querySelector('#comment-value'); + this.$postCommentBtn.addEventListener('click', this.postComment) + } else { + this.innerHTML = this.renderLoginButtons(); + } + } + + postComment(e) { + e.preventDefault(); + var event = new CustomEvent('comment', {'detail': this.$commentValue.value}); + this.dispatchEvent(event); + } + + disconnectedCallback() { + if (this.auth) { + this.$postCommentBtn.removeEventListener('click', this.postComment) + } + } + + renderCommentForm() { + return ` +
+
+ +
+ +
+`; + } + + renderLoginButtons() { + return ` +
+

+Sign in + or +sign up + to add comments on this article. +

+
+ +
+
`; + } + +} \ No newline at end of file diff --git a/app/components/comments-container.comp.js b/app/components/comments-container.comp.js index 5785f5f..46a8c6b 100644 --- a/app/components/comments-container.comp.js +++ b/app/components/comments-container.comp.js @@ -26,6 +26,10 @@ export class CommentsContainerComponent extends HTMLElement { } + refresh() { + this.innerHTML = this.render(); + } + render() { return ` ${this.comments.map(comment => { diff --git a/app/components/user-info.comp.js b/app/components/user-info.comp.js new file mode 100644 index 0000000..eb8d85e --- /dev/null +++ b/app/components/user-info.comp.js @@ -0,0 +1,89 @@ +import {Authentication} from "../auth/authentication"; +export class UserInfoComponent extends HTMLElement { + + constructor() { + super(); + this.auth = Authentication.instance.auth; + this.followButtonAction = this.followButtonAction.bind(this); + } + + static get observedAttributes() { + return []; + } + + attributeChangedCallback(name, oldValue, newValue) { + + } + + connectedCallback() { + this.username = this.getAttribute('username'); + fetch('https://conduit.productionready.io/api/profiles/' + this.username).then((response) => { + return response.json(); + }).then(r => { + this.model = r.profile; + this.innerHTML = this.render(); + this.$followButton = this.querySelector('#follow-button'); + if(this.$followButton) { + this.$followButton.addEventListener('click', this.followButtonAction); + } + }); + } + + followButtonAction(e) { + + } + + disconnectedCallback() { + if(this.$followButton) { + this.$followButton.removeEventListener('click', this.followButtonAction); + } + } + + // this.$username = document.getElementById('username'); + // this.$bio = document.getElementById('bio'); + // this.userImg = document.getElementById('user-img'); + + // this.followButtonUsername = document.getElementById('follow-button-username'); + + // updateUserProfileDom() { + // this.$username.textContent = this.model.username; + // if(this.followButtonUsername) { + // this.followButtonUsername.textContent = this.model.username; + // } + // this.$bio.textContent = this.model.bio; + // this.userImg.setAttribute('src', this.model.image); + // } + + render() { + return ` + +
+
+
+ +
+ +

${this.username}

+

+ ${this.model.bio ? this.model.bio : ''} +

+ ${!this.auth ? + `` : + ` Edit Profile Settings` + } + + +
+ +
+
+
+ + +`; + } +} diff --git a/app/core/core.js b/app/core/core.js index 3a1e188..0f8e51b 100644 --- a/app/core/core.js +++ b/app/core/core.js @@ -12,6 +12,9 @@ import {EditorComponent} from "../pages/editor.comp"; import {SettingsComponent} from "../pages/settings.comp"; import {PopularTagsComponent} from "../components/popular-tags.comp"; import {CommentsContainerComponent} from "../components/comments-container.comp"; +import {UserInfoComponent} from "../components/user-info.comp"; +import {ArticlePreviewBannerComponent} from "../components/article-preview-banner"; +import {CommentCreateComponent} from "../components/comment-create.comp"; export class Core { constructor() { @@ -81,5 +84,17 @@ const components = [ { tagName: 'popular-tags', component: PopularTagsComponent + }, + { + tagName: 'user-info', + component: UserInfoComponent + }, + { + tagName: 'article-preview-banner', + component: ArticlePreviewBannerComponent + }, + { + tagName: 'comment-create', + component: CommentCreateComponent } ]; diff --git a/app/http/http.js b/app/http/http.js index 516671c..bebfd68 100644 --- a/app/http/http.js +++ b/app/http/http.js @@ -24,7 +24,10 @@ export class Http { }; if (authentication === true) { - headers['Authorization'] = 'Token ' + Authentication.instance.auth.token; + const auth = Authentication.instance.auth; + if(auth) { + headers['Authorization'] = 'Token ' + auth.token; + } } return fetch(config.rest_url + path, { headers: headers @@ -42,6 +45,12 @@ export class Http { var token = null; if (auth) { token = auth.token; + } else { + //stop immediately + RouterHandler.instance.router.navigate('#/login'); + return new Promise((resolve, rej) => { + rej(); + }); } headers['Authorization'] = 'Token ' + token; } diff --git a/app/pages/article-preview.comp.js b/app/pages/article-preview.comp.js index ea892d0..014bbef 100644 --- a/app/pages/article-preview.comp.js +++ b/app/pages/article-preview.comp.js @@ -1,4 +1,4 @@ -import {CommentPreviewComponent} from "../components/comment-preview.comp"; +import {Http} from "../http/http"; "use strict"; var markdown = require("markdown").markdown; @@ -9,16 +9,8 @@ export class ArticlePreviewComponent extends HTMLElement { this.slug = params.slug; this.article = null; - this.$articleTitle = null; - this.$profileUsername = null; - this.$articleDate = null; this.$articleBody = null; - this.$articleActionUsername = null; - this.$articleActionDate = null; - this.$articleActionFollowUsername = null; - this.$articleActionFavouritesCount = null; - - this.$commentsWrapper = null; + this.onCommentPost = this.onCommentPost.bind(this); } static get observedAttributes() { @@ -30,118 +22,69 @@ export class ArticlePreviewComponent extends HTMLElement { } connectedCallback() { - const el = this.render(); - this.innerHTML = el; + this.innerHTML = this.render(); - this.$articleTitle = document.getElementById('article-title'); - this.$profileUsername = document.getElementById('profile-username'); - this.$articleDate = document.getElementById('article-date'); this.$articleBody = document.getElementById('article-body'); - this.$articleActionUsername = document.getElementById('article-action-username'); - this.$articleActionDate = document.getElementById('article-action-date'); - this.$articleActionFollowUsername = document.getElementById('article-action-follow-username'); - this.$articleActionFavouritesCount = document.getElementById('article-action-favorites-count'); + this.$commentCreate = this.querySelector('comment-create'); + this.$commentsContainer = this.querySelector('comments-container'); + this.$commentCreate.addEventListener('comment', this.onCommentPost); - // /api/articles/:slug - fetch('https://conduit.productionready.io/api/articles/' + this.slug).then((response) => { + Http.instance.doGet('/articles/' + this.slug).then((response) => { return response.json(); }).then(r => { this.article = r.article; + this.updateArticlePreviewBanner(); this.updateArticleContent(); }); } + disconnectedCallback() { + this.$commentCreate.removeEventListener('comment', this.onCommentPost); + } + + onCommentPost(e) { + this.$commentsContainer.comments.unshift({ + author: { + username: 'admir' + }, + body: e.detail + }); + this.$commentsContainer.refresh(); + } + + updateArticlePreviewBanner() { + const articlePreviewBanner = this.querySelector('article-preview-banner'); + articlePreviewBanner.setAttribute('title', this.article.title); + articlePreviewBanner.setAttribute('username', this.article.author.username); + articlePreviewBanner.setAttribute('favorites-count', this.article.favoritesCount); + articlePreviewBanner.setAttribute('date', this.article.createdAt); + articlePreviewBanner.setAttribute('image', this.article.author.image); + } + updateArticleContent() { - this.$articleTitle.textContent = this.article.title; - this.$profileUsername.textContent = this.article.author.username; - this.$articleDate.textContent = this.article.createdAt; this.$articleBody.innerHTML = markdown.toHTML(this.article.body); - this.$articleActionUsername.textContent = this.article.author.username; - this.$articleActionDate.textContent = this.article.createdAt; - this.$articleActionFollowUsername.textContent = this.article.author.username; - this.$articleActionFavouritesCount.textContent = '(' + this.article.favoritesCount + ')'; } render() { return `
- +
- + Loading ...

-
- -
- - -
- -
-
- -
- -
+
diff --git a/app/pages/profile.comp.js b/app/pages/profile.comp.js index 4744f72..7f7a748 100644 --- a/app/pages/profile.comp.js +++ b/app/pages/profile.comp.js @@ -1,4 +1,6 @@ import {ArticleComponent} from "../components/article.comp"; +import {Http} from "../http/http"; +import {Authentication} from "../auth/authentication"; "use strict"; @@ -6,16 +8,10 @@ export class ProfileComponent extends HTMLElement { constructor(params) { super(); this.username = params.username; - this.model = null; - - this.$username = null; - this.$bio = null; - this.userImg = null; - this.followButton = null; - this.followButtonUsername = null; this.myArticlesButtonHandler = this.myArticlesButtonHandler.bind(this); this.favoritedArticlesButtonHandler = this.favoritedArticlesButtonHandler.bind(this); + this.auth = Authentication.instance.auth; } static get observedAttributes() { @@ -30,19 +26,6 @@ export class ProfileComponent extends HTMLElement { const el = this.render(); this.innerHTML = el; - this.$username = document.getElementById('username'); - this.$bio = document.getElementById('bio'); - this.userImg = document.getElementById('user-img'); - this.followButton = document.getElementById('follow-button'); - this.followButtonUsername = document.getElementById('follow-button-username'); - - fetch('https://conduit.productionready.io/api/profiles/' + this.username).then((response) => { - return response.json(); - }).then(r => { - this.model = r.profile; - this.updateUserProfileDom(); - }); - this.$globalFeed = this.querySelector('#globalFeed'); this.$myArticlesButton = this.querySelector('#my-articles'); this.$favoritedArticlesButton = this.querySelector('#favorited-articles'); @@ -67,20 +50,10 @@ export class ProfileComponent extends HTMLElement { this.$myArticlesButton.classList.remove('active'); } - - updateUserProfileDom() { - this.$username.textContent = this.model.username; - this.followButtonUsername.textContent = this.model.username; - this.$bio.textContent = this.model.bio; - this.userImg.setAttribute('src', this.model.image); - } - - fetchArticles(params, headers) { + fetchArticles(params) { this.cleanGlobalFeed(); this.$globalFeed.innerHTML = '
Loading articles
'; - fetch('https://conduit.productionready.io/api/articles' + params, { - headers: headers - }).then(function (response) { + Http.instance.doGet('articles' + params, true).then(function (response) { return response.json(); }).then(r => { this.$globalFeed.textContent = ''; @@ -113,26 +86,8 @@ export class ProfileComponent extends HTMLElement { render() { return `
- + +
diff --git a/exmple.comp.js b/exmple.comp.js index fc77d8f..79751d2 100644 --- a/exmple.comp.js +++ b/exmple.comp.js @@ -40,7 +40,6 @@ export class CNavComponent extends HTMLElement { } increment() { - console.log('a click'); }