diff --git a/api/comment_approve_test.go b/api/comment_approve_test.go
index 26833ae..e7b04cd 100644
--- a/api/comment_approve_test.go
+++ b/api/comment_approve_test.go
@@ -8,7 +8,7 @@ import (
func TestCommentApproveBasics(t *testing.T) {
failTestOnError(t, setupTestEnv())
- commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google")
+ commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google", "")
commentHex, _ := commentNew(commenterHex, "example.com", "/path.html", "root", "**foo**", "unapproved", time.Now().UTC())
diff --git a/api/comment_list.go b/api/comment_list.go
index 484fcad..3042dba 100644
--- a/api/comment_list.go
+++ b/api/comment_list.go
@@ -151,5 +151,6 @@ func commentListHandler(w http.ResponseWriter, r *http.Request) {
"requireIdentification": d.RequireIdentification,
"isFrozen": d.State == "frozen",
"isModerator": isModerator,
+ "configuredOauths": configuredOauths,
})
}
diff --git a/api/comment_list_test.go b/api/comment_list_test.go
index 2ab83c0..437abdc 100644
--- a/api/comment_list_test.go
+++ b/api/comment_list_test.go
@@ -9,7 +9,7 @@ import (
func TestCommentListBasics(t *testing.T) {
failTestOnError(t, setupTestEnv())
- commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "http://example.com/photo.jpg", "google")
+ commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "http://example.com/photo.jpg", "google", "")
commentNew(commenterHex, "example.com", "/path.html", "root", "**foo**", "approved", time.Now().UTC())
commentNew(commenterHex, "example.com", "/path.html", "root", "**bar**", "approved", time.Now().UTC())
@@ -65,7 +65,7 @@ func TestCommentListEmpty(t *testing.T) {
func TestCommentListSelfUnapproved(t *testing.T) {
failTestOnError(t, setupTestEnv())
- commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "http://example.com/photo.jpg", "google")
+ commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "http://example.com/photo.jpg", "google", "")
commentNew(commenterHex, "example.com", "/path.html", "root", "**foo**", "unapproved", time.Now().UTC())
diff --git a/api/comment_new.go b/api/comment_new.go
index 88c8edf..647d827 100644
--- a/api/comment_new.go
+++ b/api/comment_new.go
@@ -116,5 +116,5 @@ func commentNewHandler(w http.ResponseWriter, r *http.Request) {
return
}
- writeBody(w, response{"success": true, "commentHex": commentHex})
+ writeBody(w, response{"success": true, "commentHex": commentHex, "approved": state == "approved"})
}
diff --git a/api/comment_vote_test.go b/api/comment_vote_test.go
index 74bd22d..bbb2c66 100644
--- a/api/comment_vote_test.go
+++ b/api/comment_vote_test.go
@@ -8,9 +8,9 @@ import (
func TestCommentVoteBasics(t *testing.T) {
failTestOnError(t, setupTestEnv())
- cr0, _ := commenterNew("test1@example.com", "Test1", "undefined", "http://example.com/photo.jpg", "google")
- cr1, _ := commenterNew("test2@example.com", "Test2", "undefined", "http://example.com/photo.jpg", "google")
- cr2, _ := commenterNew("test3@example.com", "Test3", "undefined", "http://example.com/photo.jpg", "google")
+ cr0, _ := commenterNew("test1@example.com", "Test1", "undefined", "http://example.com/photo.jpg", "google", "")
+ cr1, _ := commenterNew("test2@example.com", "Test2", "undefined", "http://example.com/photo.jpg", "google", "")
+ cr2, _ := commenterNew("test3@example.com", "Test3", "undefined", "http://example.com/photo.jpg", "google", "")
c0, _ := commentNew(cr0, "example.com", "/path.html", "root", "**foo**", "approved", time.Now().UTC())
diff --git a/api/commenter_get_test.go b/api/commenter_get_test.go
index 3f11951..af0b776 100644
--- a/api/commenter_get_test.go
+++ b/api/commenter_get_test.go
@@ -7,7 +7,7 @@ import (
func TestCommenterGetByHexBasics(t *testing.T) {
failTestOnError(t, setupTestEnv())
- commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google")
+ commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google", "")
c, err := commenterGetByHex(commenterHex)
if err != nil {
@@ -33,7 +33,7 @@ func TestCommenterGetByHexEmpty(t *testing.T) {
func TestCommenterGetBySession(t *testing.T) {
failTestOnError(t, setupTestEnv())
- commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google")
+ commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google", "")
session, _ := commenterSessionNew()
@@ -63,7 +63,7 @@ func TestCommenterGetBySessionEmpty(t *testing.T) {
func TestCommenterGetByName(t *testing.T) {
failTestOnError(t, setupTestEnv())
- commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google")
+ commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google", "")
session, _ := commenterSessionNew()
diff --git a/api/commenter_login.go b/api/commenter_login.go
new file mode 100644
index 0000000..f42229a
--- /dev/null
+++ b/api/commenter_login.go
@@ -0,0 +1,71 @@
+package main
+
+import (
+ "golang.org/x/crypto/bcrypt"
+ "net/http"
+ "time"
+)
+
+func commenterLogin(email string, password string) (string, error) {
+ if email == "" || password == "" {
+ return "", errorMissingField
+ }
+
+ statement := `
+ SELECT commenterHex, passwordHash
+ FROM commenters
+ WHERE email = $1 AND provider = 'commento';
+ `
+ row := db.QueryRow(statement, email)
+
+ var commenterHex string
+ var passwordHash string
+ if err := row.Scan(&commenterHex, &passwordHash); err != nil {
+ return "", errorInvalidEmailPassword
+ }
+
+ if err := bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(password)); err != nil {
+ // TODO: is this the only possible error?
+ return "", errorInvalidEmailPassword
+ }
+
+ session, err := randomHex(32)
+ if err != nil {
+ logger.Errorf("cannot create session hex: %v", err)
+ return "", errorInternal
+ }
+
+ statement = `
+ INSERT INTO
+ commenterSessions (session, commenterHex, creationDate)
+ VALUES ($1, $2, $3 );
+ `
+ _, err = db.Exec(statement, session, commenterHex, time.Now().UTC())
+ if err != nil {
+ logger.Errorf("cannot insert session token: %v\n", err)
+ return "", errorInternal
+ }
+
+ return session, nil
+}
+
+func commenterLoginHandler(w http.ResponseWriter, r *http.Request) {
+ type request struct {
+ Email *string `json:"email"`
+ Password *string `json:"password"`
+ }
+
+ var x request
+ if err := unmarshalBody(r, &x); err != nil {
+ writeBody(w, response{"success": false, "message": err.Error()})
+ return
+ }
+
+ session, err := commenterLogin(*x.Email, *x.Password)
+ if err != nil {
+ writeBody(w, response{"success": false, "message": err.Error()})
+ return
+ }
+
+ writeBody(w, response{"success": true, "session": session})
+}
diff --git a/api/commenter_login_test.go b/api/commenter_login_test.go
new file mode 100644
index 0000000..8d43d03
--- /dev/null
+++ b/api/commenter_login_test.go
@@ -0,0 +1,58 @@
+package main
+
+import (
+ "testing"
+)
+
+func TestCommenterLoginBasics(t *testing.T) {
+ failTestOnError(t, setupTestEnv())
+
+ if _, err := commenterLogin("test@example.com", "hunter2"); err == nil {
+ t.Errorf("expected error not found when logging in without creating an account")
+ return
+ }
+
+ commenterNew("test@example.com", "Test", "undefined", "undefined", "commento", "hunter2")
+
+ if _, err := commenterLogin("test@example.com", "hunter2"); err != nil {
+ t.Errorf("unexpected error when logging in: %v", err)
+ return
+ }
+
+ if _, err := commenterLogin("test@example.com", "h******"); err == nil {
+ t.Errorf("expected error not found when given wrong password")
+ return
+ }
+
+ if session, err := commenterLogin("test@example.com", "hunter2"); session == "" {
+ t.Errorf("empty session on successful login: %v", err)
+ return
+ }
+}
+
+func TestCommenterLoginEmpty(t *testing.T) {
+ failTestOnError(t, setupTestEnv())
+
+ if _, err := commenterLogin("test@example.com", ""); err == nil {
+ t.Errorf("expected error not found when passing empty password")
+ return
+ }
+
+ commenterNew("test@example.com", "Test", "undefined", "", "commenter", "hunter2")
+
+ if _, err := commenterLogin("test@example.com", ""); err == nil {
+ t.Errorf("expected error not found when passing empty password")
+ return
+ }
+}
+
+func TestCommenterLoginNonCommento(t *testing.T) {
+ failTestOnError(t, setupTestEnv())
+
+ commenterNew("test@example.com", "Test", "undefined", "undefined", "google", "")
+
+ if _, err := commenterLogin("test@example.com", "hunter2"); err == nil {
+ t.Errorf("expected error not found logging into a non-Commento account")
+ return
+ }
+}
diff --git a/api/commenter_new.go b/api/commenter_new.go
index 76e3d14..9e56d48 100644
--- a/api/commenter_new.go
+++ b/api/commenter_new.go
@@ -1,28 +1,74 @@
package main
import (
+ "golang.org/x/crypto/bcrypt"
+ "net/http"
"time"
)
-func commenterNew(email string, name string, link string, photo string, provider string) (string, error) {
+func commenterNew(email string, name string, link string, photo string, provider string, password string) (string, error) {
if email == "" || name == "" || link == "" || photo == "" || provider == "" {
return "", errorMissingField
}
+ if provider == "commento" && password == "" {
+ return "", errorMissingField
+ }
+
+ if _, err := commenterGetByEmail(provider, email); err == nil {
+ return "", errorEmailAlreadyExists
+ }
+
commenterHex, err := randomHex(32)
if err != nil {
return "", errorInternal
}
+ passwordHash := []byte{}
+ if (password != "") {
+ passwordHash, err = bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
+ if err != nil {
+ logger.Errorf("cannot generate hash from password: %v\n", err)
+ return "", errorInternal
+ }
+ }
+
statement := `
INSERT INTO
- commenters (commenterHex, email, name, link, photo, provider, joinDate)
- VALUES ($1, $2, $3, $4, $5, $6, $7 );
+ commenters (commenterHex, email, name, link, photo, provider, passwordHash, joinDate)
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8 );
`
- _, err = db.Exec(statement, commenterHex, email, name, link, photo, provider, time.Now().UTC())
+ _, err = db.Exec(statement, commenterHex, email, name, link, photo, provider, string(passwordHash), time.Now().UTC())
if err != nil {
+ logger.Errorf("cannot insert commenter: %v", err)
return "", errorInternal
}
return commenterHex, nil
}
+
+
+func commenterNewHandler(w http.ResponseWriter, r *http.Request) {
+ type request struct {
+ Email *string `json:"email"`
+ Name *string `json:"name"`
+ Website *string `json:"website"`
+ Password *string `json:"password"`
+ }
+
+ var x request
+ if err := unmarshalBody(r, &x); err != nil {
+ writeBody(w, response{"success": false, "message": err.Error()})
+ return
+ }
+
+ // TODO: add gravatar?
+ // TODO: email confirmation if provider = commento?
+ // TODO: email confirmation if provider = commento?
+ if _, err := commenterNew(*x.Email, *x.Name, *x.Website, "undefined", "commento", *x.Password); err != nil {
+ writeBody(w, response{"success": false, "message": err.Error()})
+ return
+ }
+
+ writeBody(w, response{"success": true, "confirmEmail": smtpConfigured})
+}
diff --git a/api/commenter_new_test.go b/api/commenter_new_test.go
index e40b947..e7fdda8 100644
--- a/api/commenter_new_test.go
+++ b/api/commenter_new_test.go
@@ -7,7 +7,7 @@ import (
func TestCommenterNewBasics(t *testing.T) {
failTestOnError(t, setupTestEnv())
- if _, err := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google"); err != nil {
+ if _, err := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google", ""); err != nil {
t.Errorf("unexpected error creating new commenter: %v", err)
return
}
@@ -16,13 +16,22 @@ func TestCommenterNewBasics(t *testing.T) {
func TestCommenterNewEmpty(t *testing.T) {
failTestOnError(t, setupTestEnv())
- if _, err := commenterNew("", "Test", "undefined", "https://example.com/photo.jpg", "google"); err == nil {
+ if _, err := commenterNew("", "Test", "undefined", "https://example.com/photo.jpg", "google", ""); err == nil {
t.Errorf("expected error not found creating new commenter with empty email")
return
}
- if _, err := commenterNew("", "", "", "", ""); err == nil {
+ if _, err := commenterNew("", "", "", "", "", ""); err == nil {
t.Errorf("expected error not found creating new commenter with empty everything")
return
}
}
+
+func TestCommenterNewCommento(t *testing.T) {
+ failTestOnError(t, setupTestEnv())
+
+ if _, err := commenterNew("test@example.com", "Test", "undefined", "", "commento", ""); err == nil {
+ t.Errorf("expected error not found creating new commento account with empty password")
+ return
+ }
+}
diff --git a/api/commenter_session_get_test.go b/api/commenter_session_get_test.go
index 9643593..7523901 100644
--- a/api/commenter_session_get_test.go
+++ b/api/commenter_session_get_test.go
@@ -7,7 +7,7 @@ import (
func TestCommenterSessionGetBasics(t *testing.T) {
failTestOnError(t, setupTestEnv())
- commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google")
+ commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google", "")
session, _ := commenterSessionNew()
diff --git a/api/config.go b/api/config.go
index 62c6762..58c0ea5 100644
--- a/api/config.go
+++ b/api/config.go
@@ -29,8 +29,8 @@ func parseConfig() error {
"SMTP_PORT": "",
"SMTP_FROM_ADDRESS": "",
- "OAUTH_GOOGLE_KEY": "",
- "OAUTH_GOOGLE_SECRET": "",
+ "GOOGLE_KEY": "",
+ "GOOGLE_SECRET": "",
}
for key, value := range defaults {
diff --git a/api/oauth.go b/api/oauth.go
index c9c7fc3..bb20e14 100644
--- a/api/oauth.go
+++ b/api/oauth.go
@@ -2,7 +2,11 @@ package main
import ()
+var configuredOauths []string
+
func oauthConfigure() error {
+ configuredOauths = []string{}
+
if err := googleOauthConfigure(); err != nil {
return err
}
diff --git a/api/oauth_google.go b/api/oauth_google.go
index 026f848..f519614 100644
--- a/api/oauth_google.go
+++ b/api/oauth_google.go
@@ -37,5 +37,7 @@ func googleOauthConfigure() error {
Endpoint: google.Endpoint,
}
+ configuredOauths = append(configuredOauths, "google");
+
return nil
}
diff --git a/api/oauth_google_callback.go b/api/oauth_google_callback.go
index 2f4acba..e2b9397 100644
--- a/api/oauth_google_callback.go
+++ b/api/oauth_google_callback.go
@@ -64,7 +64,7 @@ func googleCallbackHandler(w http.ResponseWriter, r *http.Request) {
link = "undefined"
}
- commenterHex, err = commenterNew(email, user["name"].(string), link, user["picture"].(string), "google")
+ commenterHex, err = commenterNew(email, user["name"].(string), link, user["picture"].(string), "google", "")
if err != nil {
fmt.Fprintf(w, "Error: %s", err.Error())
return
diff --git a/api/router_api.go b/api/router_api.go
index 459bf40..57c12af 100644
--- a/api/router_api.go
+++ b/api/router_api.go
@@ -21,6 +21,8 @@ func initAPIRouter(router *mux.Router) error {
router.HandleFunc("/api/domain/statistics", domainStatisticsHandler).Methods("POST")
router.HandleFunc("/api/commenter/session/new", commenterSessionNewHandler).Methods("GET")
+ router.HandleFunc("/api/commenter/new", commenterNewHandler).Methods("POST")
+ router.HandleFunc("/api/commenter/login", commenterLoginHandler).Methods("POST")
router.HandleFunc("/api/commenter/self", commenterSelfHandler).Methods("POST")
router.HandleFunc("/api/oauth/google/redirect", googleRedirectHandler).Methods("GET")
diff --git a/db/20180610215858-commenter-password.sql b/db/20180610215858-commenter-password.sql
new file mode 100644
index 0000000..5e9b5da
--- /dev/null
+++ b/db/20180610215858-commenter-password.sql
@@ -0,0 +1,2 @@
+ALTER TABLE commenters
+ ADD passwordHash TEXT NOT NULL DEFAULT '';
diff --git a/frontend/dashboard.html b/frontend/dashboard.html
index 51f7f69..bf245ba 100644
--- a/frontend/dashboard.html
+++ b/frontend/dashboard.html
@@ -153,10 +153,10 @@
Moderators have the power to approve and delete comments. To make someone a moderator, add their email address down below. Once added, shiny new moderation buttons will appear on each comment for that person on each page on this domain.
-
-
-
-
Add moderator
+
@@ -244,10 +244,10 @@
-
-
-
-
Import
+
diff --git a/frontend/js/commento.js b/frontend/js/commento.js
index 3018e70..dfee7f9 100644
--- a/frontend/js/commento.js
+++ b/frontend/js/commento.js
@@ -11,6 +11,43 @@
// override that.
+ var ID_ROOT = "commento";
+ var ID_MAIN_AREA = "commento-main-area";
+ var ID_LOGIN_BOX_CONTAINER = "commento-login-box-container";
+ var ID_LOGIN_BOX = "commento-login-box";
+ var ID_LOGIN_BOX_HEADER = "commento-login-box-header";
+ var ID_LOGIN_BOX_SUBTITLE = "commento-login-box-subtitle";
+ var ID_LOGIN_BOX_EMAIL_INPUT = "commento-login-box-email-input";
+ var ID_LOGIN_BOX_PASSWORD_INPUT = "commento-login-box-password-input";
+ var ID_LOGIN_BOX_NAME_INPUT = "commento-login-box-name-input";
+ var ID_LOGIN_BOX_WEBSITE_INPUT = "commento-login-box-website-input";
+ var ID_LOGIN_BOX_EMAIL_BUTTON = "commento-login-box-email-button";
+ var ID_LOGIN_BOX_LOGIN_LINK_CONTAINER = "commento-login-box-login-link-container";
+ var ID_LOGIN_BOX_LOGIN_LINK = "commento-login-box-login-link";
+ var ID_LOGIN_BOX_HR = "commento-login-box-hr";
+ var ID_LOGIN_BOX_OAUTH_PRETEXT = "commento-login-box-oauth-pretext";
+ var ID_LOGIN_BOX_OAUTH_BUTTONS_CONTAINER = "commento-login-box-oauth-buttons-container";
+ var ID_ERROR = "commento-error";
+ var ID_COMMENTS_AREA = "commento-comments-area";
+ var ID_SUPER_CONTAINER = "commento-textarea-super-container-";
+ var ID_TEXTAREA_CONTAINER = "commento-textarea-container-";
+ var ID_TEXTAREA = "commento-textarea-";
+ var ID_CARD = "commento-comment-card-";
+ var ID_BODY = "commento-comment-body-";
+ var ID_SUBTITLE = "commento-comment-subtitle-";
+ var ID_SCORE = "commento-comment-score-";
+ var ID_OPTIONS = "commento-comment-options-";
+ var ID_EDIT = "commento-comment-edit-";
+ var ID_REPLY = "commento-comment-reply-";
+ var ID_COLLAPSE = "commento-comment-collapse-";
+ var ID_UPVOTE = "commento-comment-upvote-";
+ var ID_DOWNVOTE = "commento-comment-downvote-";
+ var ID_APPROVE = "commento-comment-approve-";
+ var ID_REMOVE = "commento-comment-remove-";
+ var ID_CONTENTS = "commento-comment-contents-";
+ var ID_SUBMIT_BUTTON = "commento-submit-button-";
+
+
var origin = global.commento_origin;
var cdn = global.commento_cdn;
var root = null;
@@ -24,6 +61,8 @@
var chosenAnonymous = false;
var shownSubmitButton = {"root": false};
var shownReply = {};
+ var configuredOauths = [];
+ var loginBoxType = "signup";
function $(id) {
@@ -169,27 +208,37 @@
var loggedContainer = create("div");
var loggedInAs = create("div");
var name = create("a");
- var photo = create("img");
+ var avatar;
var logout = create("div");
+ var color = colorGet(resp.commenter.name);
classAdd(loggedContainer, "logged-container");
classAdd(loggedInAs, "logged-in-as");
classAdd(name, "name");
- classAdd(photo, "photo");
classAdd(logout, "logout");
name.innerText = resp.commenter.name;
logout.innerText = "Logout";
- attr(name, "href", resp.commenter.link);
- if (resp.commenter.provider == "google") {
- attr(photo, "src", resp.commenter.photo + "?sz=50");
- } else {
- attr(photo, "src", resp.commenter.photo);
- }
attr(logout, "onclick", "logout()");
+ attr(name, "href", resp.commenter.link);
+ if (resp.commenter.photo == "undefined") {
+ avatar = create("div");
+ avatar.style["background"] = color;
+ avatar.style["boxShadow"] = "0px 0px 0px 2px " + color;
+ avatar.innerHTML = resp.commenter.name[0].toUpperCase();
+ classAdd(avatar, "avatar");
+ } else {
+ avatar = create("img");
+ if (resp.commenter.provider == "google") {
+ attr(avatar, "src", resp.commenter.photo + "?sz=50");
+ } else {
+ attr(avatar, "src", resp.commenter.photo);
+ }
+ classAdd(avatar, "avatar-img");
+ }
- append(loggedInAs, photo);
+ append(loggedInAs, avatar);
append(loggedInAs, name);
append(loggedContainer, loggedInAs);
append(loggedContainer, logout);
@@ -280,6 +329,10 @@
isFrozen = resp.isFrozen;
comments = resp.comments;
commenters = resp.commenters;
+ configuredOauths = resp.configuredOauths;
+
+ if (!resp.requireModeration)
+ configuredOauths.push("anonymous");
cssLoad(cdn + "/css/commento.css");
@@ -335,43 +388,25 @@
if (!isAuthenticated && !chosenAnonymous) {
var buttonsContainer = create("div");
+ var createButton = create("div");
- if (!isMobile()) {
- classAdd(buttonsContainer, "buttons-container");
- classAdd(textarea, "blurred-textarea");
- } else {
- classAdd(textarea, "hidden");
- classAdd(buttonsContainer, "mobile-buttons-container");
- classAdd(buttonsContainer, "opaque");
- }
+ classAdd(buttonsContainer, "create-container");
- var oauths = ["google"];
- if (!requireIdentification)
- oauths.push("anonymous");
-
- for (var i = 0; i < oauths.length; i++) {
- var oauthButton = create("button");
-
- classAdd(oauthButton, "button");
- classAdd(oauthButton, oauths[i] + "-button");
-
- if (isMobile())
- classAdd(oauthButton, "opaque");
-
- attr(oauthButton, "onclick", "commentoAuth('" + oauths[i] + "', '" + id + "')");
-
- oauthButton.innerText = oauths[i][0].toUpperCase() + oauths[i].slice(1);
-
- append(buttonsContainer, oauthButton);
- }
+ classAdd(createButton, "button");
+ classAdd(createButton, "create-button");
+ attr(createButton, "onclick", "loginBoxShow()");
attr(textarea, "disabled", true);
+ createButton.innerText = "Create an Account";
+
+ append(buttonsContainer, createButton);
append(textareaContainer, buttonsContainer);
}
-
- attr(textarea, "placeholder", "Join the discussion!");
- attr(textarea, "onclick", "showSubmitButton('" + id + "')");
+ else {
+ attr(textarea, "placeholder", "Join the discussion!");
+ attr(textarea, "onclick", "showSubmitButton('" + id + "')");
+ }
textarea.oninput = autoExpander(textarea);
@@ -382,6 +417,7 @@
}
function rootCreate(callback) {
+ var mainArea = $(ID_MAIN_AREA);
var commentsArea = create("div");
commentsArea.id = ID_COMMENTS_AREA;
@@ -390,8 +426,9 @@
commentsArea.innerHTML = "";
- append(root, textareaCreate("root"));
- append(root, commentsArea);
+ append(mainArea, textareaCreate("root"));
+ append(mainArea, commentsArea);
+ append(root, mainArea);
call(callback);
}
@@ -439,7 +476,7 @@
$(ID_COMMENTS_AREA).innerHTML = "";
commentsRender();
- if (requireModeration && !isModerator) {
+ if (!resp.approved) {
if (id == "root") {
var body = $(ID_SUPER_CONTAINER + id);
prepend(body, messageCreate("Your comment is under moderation."));
@@ -511,26 +548,6 @@
return score + " point";
}
- var ID_ERROR = "commento-error";
- var ID_COMMENTS_AREA = "commento-comments-area";
- var ID_SUPER_CONTAINER = "commento-textarea-super-container-";
- var ID_TEXTAREA_CONTAINER = "commento-textarea-container-";
- var ID_TEXTAREA = "commento-textarea-";
- var ID_CARD = "commento-comment-card-";
- var ID_BODY = "commento-comment-body-";
- var ID_SUBTITLE = "commento-comment-subtitle-";
- var ID_SCORE = "commento-comment-score-";
- var ID_OPTIONS = "commento-comment-options-";
- var ID_EDIT = "commento-comment-edit-";
- var ID_REPLY = "commento-comment-reply-";
- var ID_COLLAPSE = "commento-comment-collapse-";
- var ID_UPVOTE = "commento-comment-upvote-";
- var ID_DOWNVOTE = "commento-comment-downvote-";
- var ID_APPROVE = "commento-comment-approve-";
- var ID_REMOVE = "commento-comment-remove-";
- var ID_CONTENTS = "commento-comment-contents-";
- var ID_SUBMIT_BUTTON = "commento-submit-button-";
-
function commentsRecurse(parentMap, parentHex) {
var cur = parentMap[parentHex];
if (!cur || !cur.length) {
@@ -639,7 +656,6 @@
}
attr(edit, "onclick", "startEdit('" + comment.commentHex + "')");
- attr(reply, "onclick", "replyShow('" + comment.commentHex + "')");
attr(collapse, "onclick", "commentCollapse('" + comment.commentHex + "')");
attr(approve, "onclick", "commentApprove('" + comment.commentHex + "')");
attr(remove, "onclick", "commentDelete('" + comment.commentHex + "')");
@@ -657,9 +673,16 @@
attr(upvote, "onclick", "vote('" + comment.commentHex + "', 0, 1)");
attr(downvote, "onclick", "vote('" + comment.commentHex + "', 0, -1)");
}
- } else if (!chosenAnonymous) {
- attr(upvote, "onclick", "replyShow('" + comment.commentHex + "')");
}
+ else {
+ attr(upvote, "onclick", "loginBoxShow()");
+ attr(downvote, "onclick", "loginBoxShow()");
+ }
+
+ if (isAuthenticated || chosenAnonymous)
+ attr(reply, "onclick", "replyShow('" + comment.commentHex + "')");
+ else
+ attr(reply, "onclick", "loginBoxShow()");
if (isAuthenticated) {
if (isModerator) {
@@ -680,11 +703,9 @@
if (false) // replace when edit is implemented
append(options, edit);
- if (isAuthenticated) {
- append(options, upvote);
- append(options, downvote);
- append(options, reply);
- }
+ append(options, upvote);
+ append(options, downvote);
+ append(options, reply);
if (isModerator) {
if (comment.state == "unapproved")
@@ -906,16 +927,11 @@
append(el, submit);
}
- global.commentoAuth = function(provider, id) {
+ global.commentoAuth = function(provider) {
if (provider == "anonymous") {
cookieSet("session", "anonymous");
chosenAnonymous = true;
- refreshAll(function() {
- if (id != "root")
- global.replyShow(id);
- $(ID_TEXTAREA + id).click();
- $(ID_TEXTAREA + id).focus();
- });
+ refreshAll();
return;
}
@@ -933,12 +949,7 @@
var interval = setInterval(function() {
if (popup.closed) {
- refreshAll(function() {
- if (id != "root")
- global.replyShow(id);
- $(ID_TEXTAREA + id).click();
- $(ID_TEXTAREA + id).focus();
- });
+ refreshAll();
clearInterval(interval);
}
}, 250);
@@ -946,17 +957,305 @@
}
function refreshAll(callback) {
- $("commento").innerHTML = "";
+ $(ID_ROOT).innerHTML = "";
shownSubmitButton = {"root": false};
shownReply = {};
main(callback);
}
+ function loginBoxCreate() {
+ var loginBoxContainer = create("div");
+
+ loginBoxContainer.id = ID_LOGIN_BOX_CONTAINER;
+
+ append(root, loginBoxContainer);
+ }
+
+ global.signupRender = function() {
+ var loginBoxContainer = $(ID_LOGIN_BOX_CONTAINER);
+ var loginBox = create("div");
+ var header = create("div");
+ var subtitle = create("div");
+ var emailContainer = create("div");
+ var email = create("div");
+ var emailInput = create("input");
+ var emailButton = create("button");
+ var loginLinkContainer = create("div");
+ var loginLink = create("a");
+ var hr = create("hr");
+ var oauthPretext = create("div");
+ var oauthButtonsContainer = create("div");
+ var oauthButtons = create("div");
+ var close = create("div");
+
+ loginBox.id = ID_LOGIN_BOX;
+ header.id = ID_LOGIN_BOX_HEADER;
+ subtitle.id = ID_LOGIN_BOX_SUBTITLE;
+ emailInput.id = ID_LOGIN_BOX_EMAIL_INPUT;
+ emailButton.id = ID_LOGIN_BOX_EMAIL_BUTTON;
+ loginLink.id = ID_LOGIN_BOX_LOGIN_LINK;
+ loginLinkContainer.id = ID_LOGIN_BOX_LOGIN_LINK_CONTAINER;
+ hr.id = ID_LOGIN_BOX_HR;
+ oauthPretext.id = ID_LOGIN_BOX_OAUTH_PRETEXT;
+ oauthButtonsContainer.id = ID_LOGIN_BOX_OAUTH_BUTTONS_CONTAINER;
+
+ header.innerText = "Create an account to join the discussion!";
+
+ classAdd(loginBoxContainer, "login-box-container");
+ classAdd(loginBox, "login-box");
+ classAdd(header, "login-box-header");
+ classAdd(subtitle, "login-box-subtitle");
+ classAdd(emailContainer, "email-container");
+ classAdd(email, "email");
+ classAdd(emailInput, "input");
+ classAdd(emailButton, "email-button");
+ classAdd(loginLinkContainer, "login-link-container");
+ classAdd(loginLink, "login-link");
+ classAdd(oauthPretext, "login-box-subtitle");
+ classAdd(oauthButtonsContainer, "oauth-buttons-container");
+ classAdd(oauthButtons, "oauth-buttons");
+ classAdd(close, "login-box-close");
+
+ emailButton.innerText = "Continue";
+ loginLink.innerText = "Already have an account? Log in.";
+ subtitle.innerText = "Sign up with your email to vote and comment.";
+ oauthPretext.innerText = "Or proceed with social login.";
+
+ attr(loginBoxContainer, "style", "display: none; opacity: 0;");
+ attr(emailInput, "name", "email");
+ attr(emailInput, "placeholder", "Email address");
+ attr(emailInput, "type", "text");
+ attr(emailButton, "onclick", "passwordAsk()");
+ attr(loginLink, "onclick", "loginSwitch()");
+ attr(close, "onclick", "loginBoxClose()");
+
+ for (var i = 0; i < configuredOauths.length; i++) {
+ var button = create("button");
+
+ classAdd(button, "button");
+ classAdd(button, configuredOauths[i] + "-button");
+
+ button.innerText = configuredOauths[i];
+
+ attr(button, "onclick", "commentoAuth('" + configuredOauths[i] + "')");
+
+ append(oauthButtons, button);
+ }
+
+ append(loginBox, header);
+ append(loginBox, subtitle);
+
+ append(email, emailInput);
+ append(email, emailButton);
+ append(emailContainer, email);
+ append(loginBox, emailContainer);
+
+ append(loginLinkContainer, loginLink);
+ append(loginBox, loginLinkContainer);
+
+ append(loginBox, hr);
+
+ if (configuredOauths.length > 0) {
+ append(loginBox, oauthPretext);
+ append(oauthButtonsContainer, oauthButtons);
+ append(loginBox, oauthButtonsContainer);
+ }
+
+ append(loginBox, close);
+
+ loginBoxType = "signup";
+ loginBoxContainer.innerHTML = "";
+ append(loginBoxContainer, loginBox);
+ }
+
+ global.loginSwitch = function() {
+ var header = $(ID_LOGIN_BOX_HEADER);
+ var subtitle = $(ID_LOGIN_BOX_SUBTITLE);
+ var loginLink = $(ID_LOGIN_BOX_LOGIN_LINK);
+ var hr = $(ID_LOGIN_BOX_HR);
+ var oauthButtonsContainer = $(ID_LOGIN_BOX_OAUTH_BUTTONS_CONTAINER);
+ var oauthPretext = $(ID_LOGIN_BOX_OAUTH_PRETEXT);
+
+ header.innerText = "Login to continue";
+ loginLink.innerText = "Don't have an account? Sign up.";
+ subtitle.innerText = "Enter your email address to start with.";
+
+ attr(loginLink, "onclick", "signupSwitch()");
+
+ loginBoxType = "login";
+
+ remove(hr);
+ remove(oauthPretext);
+ remove(oauthButtonsContainer);
+ }
+
+ global.signupSwitch = function() {
+ loginBoxClose();
+ loginBoxShow();
+ }
+
+ function loginUP(username, password) {
+ var json = {
+ email: username,
+ password: password,
+ };
+
+ post(origin + "/api/commenter/login", json, function(resp) {
+ if (!resp.success) {
+ loginBoxClose();
+ errorShow(resp.message);
+ return
+ }
+
+ cookieSet("session", resp.session);
+ refreshAll();
+ });
+ }
+
+ global.login = function() {
+ var email = $(ID_LOGIN_BOX_EMAIL_INPUT);
+ var password = $(ID_LOGIN_BOX_PASSWORD_INPUT);
+
+ loginUP(email.value, password.value);
+ }
+
+ global.signup = function() {
+ var email = $(ID_LOGIN_BOX_EMAIL_INPUT);
+ var name = $(ID_LOGIN_BOX_NAME_INPUT);
+ var website = $(ID_LOGIN_BOX_WEBSITE_INPUT);
+ var password = $(ID_LOGIN_BOX_PASSWORD_INPUT);
+
+ var json = {
+ email: email.value,
+ name: name.value,
+ website: website.value,
+ password: password.value,
+ };
+
+ post(origin + "/api/commenter/new", json, function(resp) {
+ if (!resp.success) {
+ loginBoxClose();
+ errorShow(resp.message);
+ return
+ }
+
+ loginUP(email.value, password.value);
+ });
+ }
+
+ global.passwordAsk = function() {
+ var loginBox = $(ID_LOGIN_BOX);
+ var subtitle = $(ID_LOGIN_BOX_SUBTITLE);
+ var emailInput = $(ID_LOGIN_BOX_EMAIL_INPUT);
+ var emailButton = $(ID_LOGIN_BOX_EMAIL_BUTTON);
+ var loginLinkContainer = $(ID_LOGIN_BOX_LOGIN_LINK_CONTAINER);
+ var hr = $(ID_LOGIN_BOX_HR);
+ var oauthButtonsContainer = $(ID_LOGIN_BOX_OAUTH_BUTTONS_CONTAINER);
+ var oauthPretext = $(ID_LOGIN_BOX_OAUTH_PRETEXT);
+
+ remove(emailButton);
+ remove(loginLinkContainer);
+ if (loginBoxType == "signup") {
+ remove(hr);
+ remove(oauthPretext);
+ remove(oauthButtonsContainer);
+ }
+
+ var order, id, type, placeholder;
+
+ if (loginBoxType == "signup") {
+ var order = ["name", "website", "password"];
+ var id = [ID_LOGIN_BOX_NAME_INPUT, ID_LOGIN_BOX_WEBSITE_INPUT, ID_LOGIN_BOX_PASSWORD_INPUT];
+ var type = ["text", "text", "password"];
+ var placeholder = ["Real Name", "Website (Optional)", "Password"];
+ }
+ else {
+ var order = ["password"];
+ var id = [ID_LOGIN_BOX_PASSWORD_INPUT];
+ var type = ["password"];
+ var placeholder = ["Password"];
+ }
+
+ subtitle.innerText = "Finish the rest of your profile to complete."
+
+ for (var i = 0; i < order.length; i++) {
+ var fieldContainer = create("div");
+ var field = create("div");
+ var fieldInput = create("input");
+
+ fieldInput.id = id[i];
+
+ classAdd(fieldContainer, "email-container");
+ classAdd(field, "email");
+ classAdd(fieldInput, "input");
+
+ attr(fieldInput, "name", order[i]);
+ attr(fieldInput, "type", type[i]);
+ attr(fieldInput, "placeholder", placeholder[i]);
+
+ append(field, fieldInput);
+ append(fieldContainer, field);
+
+ if (order[i] == "password") {
+ var fieldButton = create("button");
+ classAdd(fieldButton, "email-button");
+ fieldButton.innerText = loginBoxType;
+
+ if (loginBoxType == "signup")
+ attr(fieldButton, "onclick", "signup()");
+ else
+ attr(fieldButton, "onclick", "login()");
+
+ append(field, fieldButton);
+ }
+
+ append(loginBox, fieldContainer);
+ }
+ }
+
+ function mainAreaCreate() {
+ var mainArea = create("div");
+
+ mainArea.id = ID_MAIN_AREA;
+
+ classAdd(mainArea, "main-area");
+
+ append(root, mainArea);
+ }
+
+ global.loginBoxClose = function() {
+ var mainArea = $(ID_MAIN_AREA);
+ var loginBoxContainer = $(ID_LOGIN_BOX_CONTAINER);
+
+ classRemove(mainArea, "blurred");
+
+ attr(loginBoxContainer, "style", "display: none");
+ }
+
+ global.loginBoxShow = function() {
+ var mainArea = $(ID_MAIN_AREA);
+ var loginBoxContainer = $(ID_LOGIN_BOX_CONTAINER);
+
+ global.signupRender();
+
+ classAdd(mainArea, "blurred");
+
+ attr(loginBoxContainer, "style", "");
+
+ window.location.hash = ID_LOGIN_BOX_CONTAINER;
+
+ $(ID_LOGIN_BOX_EMAIL_INPUT).focus();
+ }
+
function main(callback) {
- root = $("commento");
+ root = $(ID_ROOT);
+
+ loginBoxCreate();
errorElementCreate();
+ mainAreaCreate();
+
selfGet(function() {
commentsGet(function() {
rootCreate(function() {
diff --git a/frontend/sass/commento-input.scss b/frontend/sass/commento-input.scss
index 14b910d..1bd3c99 100644
--- a/frontend/sass/commento-input.scss
+++ b/frontend/sass/commento-input.scss
@@ -14,9 +14,9 @@ input[type=text]::placeholder {
textarea::placeholder {
color: #aaa;
- font-size: 22px;
- padding-top: 13px;
+ font-size: 20px;
display: flex;
+ line-height: 80px;
justify-content: center;
align-items: center;
text-align: center;
@@ -28,7 +28,7 @@ textarea {
padding: 8px;
outline: none;
overflow: auto;
- min-height: 75px;
+ min-height: 100px;
width: 100%;
}
@@ -41,65 +41,42 @@ textarea {
justify-content: center;
align-items: center;
- &:hover {
- .commento-button, .commento-buttons-container::before {
- opacity: 1;
- transform: translate(0px,-3px);
- }
-
- .commento-submit-button {
- transform: none;
- }
-
- .commento-blurred-textarea {
- opacity: .7;
- transform: scale(.95);
- filter: blur(4px);
- }
-
- @media only screen and (max-width: 550px) {
- .commento-buttons-container::before {
- display: none;
- }
- }
+ .commento-submit-button {
+ transform: none;
}
}
-.commento-buttons-container {
- display: inline-block;
- position: absolute;
- z-index: 1;
- opacity: 1;
+.commento-oauth-buttons-container {
+ display: flex;
+ justify-content: center;
}
-.commento-mobile-buttons-container::before,
-.commento-buttons-container::before {
- content: "Authenticate with";
- display: inline-flex;
+.commento-oauth-buttons,
+.commento-create-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ position: absolute;
+ z-index: 1;
+}
+
+.commento-oauth-buttons {
+ display: contents;
+}
+
+.commento-create-container::before {
+ content: "Want to add to the discussion?";
+ display: block;
+ text-align: center;
justify-content: center;
align-items: center;
font-weight: bold;
- line-height: 24px;
font-size: 14px;
padding: 6px;
color: $gray-8;
- transition: all 0.3s;
- opacity: 0;
outline: none;
}
-.commento-mobile-buttons-container::before {
- content: "To join the discussion, authenticate with";;
- display: block;
- text-align: center;
-}
-
-@media only screen and (max-width: 550px) {
- .commento-buttons-container::before {
- display: none;
- }
-}
-
.commento-button {
display: inline-flex;
justify-content: center;
@@ -118,31 +95,25 @@ textarea {
color: #fff;
width: 100px;
margin-left: 5px;
- margin-left: 5px;
- opacity: 0;
-}
-
-.commento-opaque {
- opacity: 1;
-}
-
-.commento-opaque::before {
- opacity: 1;
+ margin-right: 5px;
}
.commento-google-button {
- transition: all 0.3s;
background: #dd4b39;
+ text-transform: uppercase;
+ font-size: 12px;
}
.commento-github-button {
- transition: all 0.3s;
background: #000000;
+ text-transform: uppercase;
+ font-size: 12px;
}
.commento-anonymous-button {
- transition: all 0.3s;
background: #096fa6;
+ text-transform: uppercase;
+ font-size: 12px;
}
.commento-blurred-textarea {
@@ -152,7 +123,6 @@ textarea {
align-items: center;
justify-content: center;
justify-content: space-between;
- transition: 0.3s all;
will-change: transform;
}
@@ -160,7 +130,6 @@ textarea {
.commento-delete-button,
.commento-submit-button {
margin-top: 10px;
- opacity: 1;
font-size: 14px;
width: -moz-fit-content;
width: -webkit-fit-content;
@@ -169,6 +138,11 @@ textarea {
width: fit-content;
}
+.commento-create-button {
+ width: 150px;
+ background: $pink-9;
+}
+
.commento-submit-button {
float: right;
background: $indigo-7;
diff --git a/frontend/sass/commento-logged.scss b/frontend/sass/commento-logged.scss
index 0e022cf..1ca1e62 100644
--- a/frontend/sass/commento-logged.scss
+++ b/frontend/sass/commento-logged.scss
@@ -5,6 +5,7 @@
text-align: left;
margin-bottom: 16px;
position: relative;
+ height: 38px;
.commento-logout {
cursor: pointer;
@@ -23,15 +24,7 @@
font-weight: bold;
position: absolute;
top: 6px;
- left: 64px;
- }
-
- .commento-photo {
- width: 32px;
- height: 32px;
- border-radius: 50%;
- margin-left: 16px;
- margin-right: 16px;
+ left: 48px;
}
}
}
diff --git a/frontend/sass/commento-login.scss b/frontend/sass/commento-login.scss
new file mode 100644
index 0000000..57cad7e
--- /dev/null
+++ b/frontend/sass/commento-login.scss
@@ -0,0 +1,92 @@
+@import "colors-main.scss";
+
+.commento-login-box-container {
+ display: flex;
+ justify-content: center;
+ position: relative;
+ width: 100%;
+ height: 0px;
+ overflow: visible;
+
+ .commento-login-box {
+ width: 90%;
+ max-width: 500px;
+ min-height: 125px;
+ background: $gray-1;
+ box-shadow: 0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08);
+ border: 1px solid transparent;
+ border-radius: 3px;
+ z-index: 100;
+ position: absolute;
+ top: 8px;
+ padding: 16px;
+ opacity: 1;
+ transition: opacity 0.2s;
+
+ hr {
+ border: none;
+ background: $gray-2;
+ height: 1px;
+ }
+
+ .commento-login-box-header {
+ font-size: 20px;
+ font-weight: 200;
+ text-align: center;
+ color: $pink-8;
+ margin: 16px;
+ }
+
+ .commento-login-box-subtitle {
+ color: $gray-6;
+ text-align: center;
+ font-size: 15px;
+ margin: 12px 0px;
+ }
+
+ @import "email-main.scss";
+
+ .commento-login-link-container {
+ margin: 16px;
+ width: calc(100% - 32px);
+ text-align: center;
+
+ .commento-login-link {
+ font-size: 15px;
+ font-weight: bold;
+ border-bottom: none;
+ }
+ }
+
+ .commento-login-box-close {
+ position: absolute;
+ right: 16px;
+ top: 16px;
+ width: 16px;
+ height: 16px;
+ opacity: 0.3;
+ }
+
+ .commento-login-box-close:hover {
+ opacity: 1;
+ cursor: pointer;
+ }
+
+ .commento-login-box-close:before, .commento-login-box-close:after {
+ position: absolute;
+ left: 7px;
+ content: ' ';
+ height: 17px;
+ width: 2px;
+ background-color: $gray-7;
+ }
+
+ .commento-login-box-close:before {
+ transform: rotate(45deg);
+ }
+
+ .commento-login-box-close:after {
+ transform: rotate(-45deg);
+ }
+ }
+}
diff --git a/frontend/sass/commento.scss b/frontend/sass/commento.scss
index 49916e9..3fe69ee 100644
--- a/frontend/sass/commento.scss
+++ b/frontend/sass/commento.scss
@@ -1,11 +1,14 @@
+@import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro');
+
#commento {
font-family: "Source Sans Pro", "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
- font-size: 14px;
+ font-size: 15px;
line-height: 1.5;
color: #50596c;
overflow-x: hidden;
text-rendering: optimizeLegibility;
padding: 8px;
+ min-height: 350px;
@import "colors-main.scss";
@import "common-main.scss";
@@ -15,11 +18,20 @@
@import "commento-input.scss";
@import "commento-logged.scss";
@import "commento-buttons.scss";
+ @import "commento-login.scss";
.commento-hidden {
display: none;
}
+ .commento-blurred {
+ filter: blur(4px);
+ }
+
+ .commento-main-area {
+ transition: filter 0.2s;
+ }
+
.commento-error-box {
width: 100%;
border-radius: 4px;
@@ -43,6 +55,32 @@
background: $blue-1;
}
+ .commento-avatar {
+ width: 34px;
+ height: 34px;
+ border-radius: 50%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ color: white;
+ font-size: 22px;
+ float: left;
+ margin-right: 10px;
+ border: 1px solid #fff;
+ box-shadow: 0px 0px 0px 2px #f00;
+ }
+
+ .commento-avatar-img {
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ float: left;
+ margin-right: 10px;
+ }
+
.commento-card {
padding: 12px 0px 0px 12px;
margin-top: 16px;
@@ -52,32 +90,6 @@
padding-bottom: 12px;
}
- .commento-avatar {
- width: 34px;
- height: 34px;
- border-radius: 50%;
- display: flex;
- justify-content: center;
- align-items: center;
- color: white;
- font-size: 22px;
- float: left;
- margin-right: 10px;
- border: 1px solid #fff;
- box-shadow: 0px 0px 0px 2px #f00;
- }
-
- .commento-avatar-img {
- width: 38px;
- height: 38px;
- border-radius: 50%;
- display: flex;
- justify-content: center;
- align-items: center;
- float: left;
- margin-right: 10px;
- }
-
.commento-avatar::after {
content:"";
display:block;
diff --git a/frontend/sass/dashboard-main.scss b/frontend/sass/dashboard-main.scss
index 0639102..ca367c6 100644
--- a/frontend/sass/dashboard-main.scss
+++ b/frontend/sass/dashboard-main.scss
@@ -105,60 +105,7 @@ body {
}
}
-.email-container {
- display: flex;
- justify-content: center;
- width: 100%;
-
- .email {
- @extend .shadow;
- border-radius: 4px;
- background: $white;
- width: 100%;
- max-width: 400px;
-
- .input {
- height: 40px;
- background: $white;
- border: none;
- outline: none;
- padding: 5px;
- padding-left: 10px;
- width: 250px;
- }
-
- .input::placeholder {
- color: $gray-5;
- }
-
- .email-button {
- height: 40px;
- min-width: 110px;
- float: right;
- background: $white;
- border: none;
- outline: none;
- padding: 0px 10px 0px 10px;
- border-left: 1px solid $gray-1;
- font-size: 12px;
- text-transform: uppercase;
- text-align: center;
- font-weight: bold;
- color: $blue-7;
- cursor: pointer;
- transition: all 0.2s;
- }
-
- .email-button:hover {
- color: $blue-6;
- }
-
- .email-button:disabled {
- cursor: default;
- color: $gray-6;
- }
- }
-}
+@import "email-main.scss";
.mod-emails-container {
display: flex;
diff --git a/frontend/sass/email-main.scss b/frontend/sass/email-main.scss
new file mode 100644
index 0000000..76e5ff3
--- /dev/null
+++ b/frontend/sass/email-main.scss
@@ -0,0 +1,58 @@
+@import "colors-main.scss";
+@import "common-main.scss";
+
+.commento-email-container {
+ display: flex;
+ justify-content: center;
+ width: 100%;
+ margin: 8px 0px;
+
+ .commento-email {
+ @extend .shadow;
+ border-radius: 4px;
+ background: $white;
+ width: 100%;
+ max-width: 400px;
+
+ .commento-input {
+ height: 40px;
+ background: $white;
+ border: none;
+ outline: none;
+ padding: 5px;
+ padding-left: 10px;
+ width: calc(100% - 120px);
+ }
+
+ .commento-input::placeholder {
+ color: $gray-5;
+ }
+
+ .commento-email-button {
+ height: 40px;
+ min-width: 110px;
+ float: right;
+ background: $white;
+ border: none;
+ outline: none;
+ padding: 0px 10px 0px 10px;
+ border-left: 1px solid $gray-1;
+ font-size: 12px;
+ text-transform: uppercase;
+ text-align: center;
+ font-weight: bold;
+ color: $blue-7;
+ cursor: pointer;
+ transition: all 0.2s;
+ }
+
+ .commento-email-button:hover {
+ color: $blue-6;
+ }
+
+ .commento-email-button:disabled {
+ cursor: default;
+ color: $gray-6;
+ }
+ }
+}