diff --git a/app/cmd/routes.go b/app/cmd/routes.go index 87da777f7..8790da472 100644 --- a/app/cmd/routes.go +++ b/app/cmd/routes.go @@ -73,7 +73,7 @@ func routes(r *web.Engine) *web.Engine { r.Get("/oauth/:provider", handlers.SignInByOAuth()) r.Get("/oauth/:provider/callback", handlers.OAuthCallback()) - //Starting from this step, a Tenant is required + // Starting from this step, a Tenant is required r.Use(middlewares.RequireTenant()) r.Get("/sitemap.xml", handlers.Sitemap()) @@ -98,7 +98,7 @@ func routes(r *web.Engine) *web.Engine { r.Get("/oauth/:provider/token", handlers.OAuthToken()) r.Get("/oauth/:provider/echo", handlers.OAuthEcho()) - //If tenant is pending, block it from using any other route + // If tenant is pending, block it from using any other route r.Use(middlewares.BlockPendingTenants()) r.Get("/signin", handlers.SignInPage()) @@ -108,7 +108,7 @@ func routes(r *web.Engine) *web.Engine { r.Post("/_api/signin/complete", handlers.CompleteSignInProfile()) r.Post("/_api/signin", handlers.SignInByEmail()) - //Block if it's private tenant with unauthenticated user + // Block if it's private tenant with unauthenticated user r.Use(middlewares.CheckTenantPrivacy()) r.Get("/", handlers.Index()) @@ -117,12 +117,13 @@ func routes(r *web.Engine) *web.Engine { ui := r.Group() { - //From this step, a User is required + // From this step, a User is required ui.Use(middlewares.IsAuthenticated()) ui.Get("/settings", handlers.UserSettings()) ui.Get("/notifications", handlers.Notifications()) ui.Get("/notifications/:id", handlers.ReadNotification()) + ui.Get("/_api/notifications/unread", handlers.GetAllNotifications()) ui.Get("/change-email/verify", handlers.VerifyChangeEmailKey()) ui.Delete("/_api/user", handlers.DeleteUser()) @@ -148,7 +149,7 @@ func routes(r *web.Engine) *web.Engine { ui.Get("/admin/authentication", handlers.ManageAuthentication()) ui.Get("/_api/admin/oauth/:provider", handlers.GetOAuthConfig()) - //From this step, only Administrators are allowed + // From this step, only Administrators are allowed ui.Use(middlewares.IsAuthorized(enum.RoleAdministrator)) ui.Get("/admin/export", handlers.Page("Export · Site Settings", "", "Administration/pages/Export.page")) diff --git a/app/handlers/notification.go b/app/handlers/notification.go index f39221785..e00af53ed 100644 --- a/app/handlers/notification.go +++ b/app/handlers/notification.go @@ -9,6 +9,18 @@ import ( "github.com/getfider/fider/app/pkg/web" ) +// GetAllNotifications will get all the notifications for the new modal +func GetAllNotifications() web.HandlerFunc { + return func(c *web.Context) error { + q := &query.GetActiveNotifications{} + if err := bus.Dispatch(c, q); err != nil { + return c.Failure(err) + } + + return c.Ok(q.Result) + } +} + // TotalUnreadNotifications returns the total number of unread notifications func TotalUnreadNotifications() web.HandlerFunc { return func(c *web.Context) error { @@ -65,7 +77,6 @@ func ReadNotification() web.HandlerFunc { // ReadAllNotifications marks all unread notifications as read func ReadAllNotifications() web.HandlerFunc { return func(c *web.Context) error { - if err := bus.Dispatch(c, &cmd.MarkAllNotificationsAsRead{}); err != nil { return c.Failure(err) } diff --git a/app/models/entity/notification.go b/app/models/entity/notification.go index 385876920..28dc81275 100644 --- a/app/models/entity/notification.go +++ b/app/models/entity/notification.go @@ -1,12 +1,21 @@ package entity -import "time" +import ( + "time" + + "github.com/getfider/fider/app/models/enum" +) // Notification is the system generated notification entity type Notification struct { - ID int `json:"id" db:"id"` - Title string `json:"title" db:"title"` - Link string `json:"link" db:"link"` - Read bool `json:"read" db:"read"` - CreatedAt time.Time `json:"createdAt" db:"created_at"` + ID int `json:"id" db:"id"` + Title string `json:"title" db:"title"` + Link string `json:"link" db:"link"` + Read bool `json:"read" db:"read"` + CreatedAt time.Time `json:"createdAt" db:"created_at"` + AuthorName string `json:"authorName" db:"name"` + AuthorID int `json:"-" db:"author_id"` + AvatarBlobKey string `json:"-" db:"avatar_bkey"` + AvatarType enum.AvatarType `json:"-" db:"avatar_type"` + AvatarURL string `json:"avatarURL,omitempty"` } diff --git a/app/pkg/web/testdata/basic.html b/app/pkg/web/testdata/basic.html index 98ae6191e..d5fa504ff 100755 --- a/app/pkg/web/testdata/basic.html +++ b/app/pkg/web/testdata/basic.html @@ -6,6 +6,9 @@ + + + diff --git a/app/pkg/web/testdata/canonical.html b/app/pkg/web/testdata/canonical.html index 5d52faee9..81b1e2040 100755 --- a/app/pkg/web/testdata/canonical.html +++ b/app/pkg/web/testdata/canonical.html @@ -6,6 +6,9 @@ + + + diff --git a/app/pkg/web/testdata/chunk.html b/app/pkg/web/testdata/chunk.html index bf893eaf1..ab125af24 100644 --- a/app/pkg/web/testdata/chunk.html +++ b/app/pkg/web/testdata/chunk.html @@ -6,6 +6,9 @@ + + + diff --git a/app/pkg/web/testdata/home.html b/app/pkg/web/testdata/home.html index 8cdc49f56..3fe3afec8 100755 --- a/app/pkg/web/testdata/home.html +++ b/app/pkg/web/testdata/home.html @@ -6,6 +6,9 @@ + + + diff --git a/app/pkg/web/testdata/home_ssr.html b/app/pkg/web/testdata/home_ssr.html index 1a16c2a68..52729866a 100755 --- a/app/pkg/web/testdata/home_ssr.html +++ b/app/pkg/web/testdata/home_ssr.html @@ -6,6 +6,9 @@ + + + diff --git a/app/pkg/web/testdata/oauth.html b/app/pkg/web/testdata/oauth.html index 7d9713aeb..fab7303d8 100755 --- a/app/pkg/web/testdata/oauth.html +++ b/app/pkg/web/testdata/oauth.html @@ -6,6 +6,9 @@ + + + diff --git a/app/pkg/web/testdata/tenant.html b/app/pkg/web/testdata/tenant.html index 9c87fac08..8503fd045 100755 --- a/app/pkg/web/testdata/tenant.html +++ b/app/pkg/web/testdata/tenant.html @@ -6,6 +6,9 @@ + + + diff --git a/app/pkg/web/testdata/user.html b/app/pkg/web/testdata/user.html index 99b571dd9..b98c6fa06 100755 --- a/app/pkg/web/testdata/user.html +++ b/app/pkg/web/testdata/user.html @@ -6,6 +6,9 @@ + + + diff --git a/app/services/sqlstore/postgres/notification.go b/app/services/sqlstore/postgres/notification.go index 2e7d0fc50..f5f724b10 100644 --- a/app/services/sqlstore/postgres/notification.go +++ b/app/services/sqlstore/postgres/notification.go @@ -102,16 +102,23 @@ func getNotificationByID(ctx context.Context, q *query.GetNotificationByID) erro func getActiveNotifications(ctx context.Context, q *query.GetActiveNotifications) error { return using(ctx, func(trx *dbx.Trx, tenant *entity.Tenant, user *entity.User) error { - q.Result = []*entity.Notification{} err := trx.Select(&q.Result, ` - SELECT id, title, link, read, created_at - FROM notifications - WHERE tenant_id = $1 AND user_id = $2 - AND (read = false OR updated_at > CURRENT_DATE - INTERVAL '30 days') + SELECT n.id, n.title, n.link, n.read, n.created_at, n.author_id, u.avatar_type, u.avatar_bkey, u.name + FROM notifications n + LEFT JOIN users u ON u.id = n.author_id + WHERE n.tenant_id = $1 AND n.user_id = $2 + AND (n.read = false OR n.updated_at > CURRENT_DATE - INTERVAL '30 days') + ORDER BY n.updated_at DESC `, tenant.ID, user.ID) if err != nil { return errors.Wrap(err, "failed to get active notifications") } + + // Iterate over notifications and build avatar URL + for i := range q.Result { + q.Result[i].AvatarURL = buildAvatarURL(ctx, q.Result[i].AvatarType, int(q.Result[i].AuthorID), q.Result[i].AuthorName, q.Result[i].AvatarBlobKey) + } + return nil }) } diff --git a/e2e/features/server/ssr.feature b/e2e/features/server/ssr.feature index 3d1fc98b6..497392ee3 100644 --- a/e2e/features/server/ssr.feature +++ b/e2e/features/server/ssr.feature @@ -5,7 +5,7 @@ Feature: SSR And I set the "User-Agent" header to "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" When I send the request Then I should see http status 200 - And I should not see ">Powered by Fider" on the response body + And I should not see ">Powered by Fider ⚡" on the response body And I should see "This website requires JavaScript, please enable and reload the page." on the response body And I should see "/assets/js/vendor" on the response body @@ -14,6 +14,6 @@ Feature: SSR And I set the "User-Agent" header to "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" When I send the request Then I should see http status 200 - And I should see ">Powered by Fider" on the response body + And I should see ">Powered by Fider ⚡" on the response body And I should not see "This website requires JavaScript, please enable and reload the page." on the response body And I should not see "/assets/js/vendor" on the response body diff --git a/locale/de/client.json b/locale/de/client.json index f8e875e75..d7e268fcc 100644 --- a/locale/de/client.json +++ b/locale/de/client.json @@ -3,6 +3,7 @@ "action.change": "ändern", "action.close": "Schließen", "action.confirm": "Bestätigen", + "action.copylink": "", "action.delete": "Löschen", "action.edit": "Bearbeiten", "action.markallasread": "Alle als gelesen markieren", @@ -81,6 +82,9 @@ "modal.deleteaccount.text": "<0>Wenn Sie Ihr Konto löschen, werden wir all Ihre persönlichen Daten für immer löschen. Der von Ihnen veröffentlichte Inhalt bleibt erhalten, wird aber anonymisiert.<1>Dieser Prozess ist irreversibel. <2>Sind Sie sicher? ", "modal.deletecomment.header": "Kommentar löschen", "modal.deletecomment.text": "Dieser Prozess ist unumkehrbar. <0>Bist du dir sicher?", + "modal.notifications.nonew": "", + "modal.notifications.previous": "", + "modal.notifications.unread": "", "modal.showvotes.message.zeromatches": "Keine Benutzer gefunden, die <0>{0} entsprechen.", "modal.showvotes.query.placeholder": "Suche nach Benutzern nach Namen...", "modal.signin.header": "Melde dich an, um eine neue Idee zu posten", @@ -127,6 +131,9 @@ "page.pendingactivation.text": "Wir haben Ihnen eine Bestätigungs-E-Mail mit einem Link zur Aktivierung Ihrer Website geschickt.", "page.pendingactivation.text2": "Bitte überprüfe deinen Posteingang, um ihn zu aktivieren.", "page.pendingactivation.title": "Dein Account ist nicht aktiviert", + "showpost.comment.copylink.error": "", + "showpost.comment.copylink.success": "", + "showpost.comment.unknownhighlighted": "", "showpost.commentinput.placeholder": "Kommentar hinzufügen", "showpost.discussionpanel.emptymessage": "Niemand hat bisher kommentiert.", "showpost.label.author": "Gepostet von <0/> · <1/>", diff --git a/locale/el/client.json b/locale/el/client.json index 371d9fb02..a9984349a 100644 --- a/locale/el/client.json +++ b/locale/el/client.json @@ -3,6 +3,7 @@ "action.change": "αλλαγή", "action.close": "Κλείσιμο", "action.confirm": "Επιβεβαίωση", + "action.copylink": "", "action.delete": "Διαγραφή", "action.edit": "Επεξεργασία", "action.markallasread": "Σήμανση όλων ως αναγνωσμένων", @@ -81,6 +82,9 @@ "modal.deleteaccount.text": "<0>Όταν επιλέξετε να διαγράψετε τον λογαριασμό σας, θα διαγράψουμε για πάντα όλες τις προσωπικές σας πληροφορίες. Το περιεχόμενο που δημοσιεύσατε θα παραμείνει, αλλά θα είναι ανώνυμο.<1>Αυτή η διαδικασία είναι μη αναστρέψιμη. <2>Είστε σίγουρος;", "modal.deletecomment.header": "Διαγραφή Σχολίου", "modal.deletecomment.text": "Αυτή η διαδικασία είναι μη αναστρέψιμη. <0>Είστε σίγουρος;", + "modal.notifications.nonew": "", + "modal.notifications.previous": "", + "modal.notifications.unread": "", "modal.showvotes.message.zeromatches": "Δεν βρέθηκαν χρήστες που να ταιριάζουν <0>{0}.", "modal.showvotes.query.placeholder": "Αναζήτηση χρηστών με όνομα...", "modal.signin.header": "Συνδεθείτε για να συμμετάσχετε και να ψηφίσετε", @@ -127,6 +131,9 @@ "page.pendingactivation.text": "Σας στείλαμε ένα email επιβεβαίωσης με ένα σύνδεσμο για να ενεργοποιήσετε τον ιστότοπό σας.", "page.pendingactivation.text2": "Παρακαλώ ελέγξτε τα εισερχόμενά σας για να το ενεργοποιήσετε.", "page.pendingactivation.title": "Ο λογαριασμός σας εκκρεμεί ενεργοποίηση", + "showpost.comment.copylink.error": "", + "showpost.comment.copylink.success": "", + "showpost.comment.unknownhighlighted": "", "showpost.commentinput.placeholder": "Αφήστε ένα σχόλιο", "showpost.discussionpanel.emptymessage": "Κανείς δεν έχει σχολιάσει ακόμα.", "showpost.label.author": "Δημοσιεύτηκε από <0/> · <1/>", diff --git a/locale/en/client.json b/locale/en/client.json index 21112eff3..4fd1b1ec6 100644 --- a/locale/en/client.json +++ b/locale/en/client.json @@ -82,6 +82,9 @@ "modal.deleteaccount.text": "<0>When you choose to delete your account, we will erase all your personal information forever. The content you have published will remain, but it will be anonymised.<1>This process is irreversible. <2>Are you sure?", "modal.deletecomment.header": "Delete Comment", "modal.deletecomment.text": "This process is irreversible. <0>Are you sure?", + "modal.notifications.nonew": "No new notifications", + "modal.notifications.previous": "Previous notifications", + "modal.notifications.unread": "Unread notifications", "modal.showvotes.message.zeromatches": "No users found matching <0>{0}.", "modal.showvotes.query.placeholder": "Search for users by name...", "modal.signin.header": "Sign in to participate and vote", diff --git a/locale/es-ES/client.json b/locale/es-ES/client.json index 9fa7d186a..a3d595c88 100644 --- a/locale/es-ES/client.json +++ b/locale/es-ES/client.json @@ -3,6 +3,7 @@ "action.change": "cambiar", "action.close": "Cerrar", "action.confirm": "Confirmar", + "action.copylink": "", "action.delete": "Eliminar", "action.edit": "Editar", "action.markallasread": "Marcar Todo como Leído", @@ -81,6 +82,9 @@ "modal.deleteaccount.text": "<0>Cuando decides eliminar tu cuenta, borraremos toda tu información personal para siempre. El contenido que has publicado permanecerá, pero será anónimo.<1>Este proceso es irreversible. <2>¿Estás seguro?", "modal.deletecomment.header": "Eliminar Comentario", "modal.deletecomment.text": "Este proceso es irreversible. <0>¿Estás seguro?", + "modal.notifications.nonew": "", + "modal.notifications.previous": "", + "modal.notifications.unread": "", "modal.showvotes.message.zeromatches": "No se encontraron usuarios que coincidan con <0>{0}.", "modal.showvotes.query.placeholder": "Buscar usuarios por nombre...", "modal.signin.header": "Inicia sesión para publicar y votar", @@ -127,6 +131,9 @@ "page.pendingactivation.text": "Te hemos enviado un correo electrónico de confirmación con un enlace para activar tu sitio.", "page.pendingactivation.text2": "Por favor, revisa tu bandeja de entrada para activarla.", "page.pendingactivation.title": "Tu cuenta está pendiente de activación", + "showpost.comment.copylink.error": "", + "showpost.comment.copylink.success": "", + "showpost.comment.unknownhighlighted": "", "showpost.commentinput.placeholder": "Publica un comentario", "showpost.discussionpanel.emptymessage": "Nadie ha comentado todavía.", "showpost.label.author": "Publicado por <0/> · <1/>", diff --git a/locale/fr/client.json b/locale/fr/client.json index 58293a999..af64d6359 100644 --- a/locale/fr/client.json +++ b/locale/fr/client.json @@ -3,6 +3,7 @@ "action.change": "changer", "action.close": "Fermer", "action.confirm": "Confirmer", + "action.copylink": "", "action.delete": "Supprimer", "action.edit": "Modifier", "action.markallasread": "Tout marquer comme lu", @@ -81,6 +82,9 @@ "modal.deleteaccount.text": "<0>Lorsque vous choisissez de supprimer votre compte, nous effacerons définitivement toutes vos informations personnelles. Le contenu que vous avez publié restera, mais il sera anonyme.<1>Ce processus est irréversible. <2>Êtes-vous sûr ?", "modal.deletecomment.header": "Supprimer le commentaire", "modal.deletecomment.text": "Ce processus est irréversible. <0>Êtes-vous sûr ?", + "modal.notifications.nonew": "", + "modal.notifications.previous": "", + "modal.notifications.unread": "", "modal.showvotes.message.zeromatches": "Aucun utilisateur correspondant à <0>{0}.", "modal.showvotes.query.placeholder": "Rechercher des utilisateurs par nom...", "modal.signin.header": "Connectez-vous pour poster et voter", @@ -127,6 +131,9 @@ "page.pendingactivation.text": "Nous vous avons envoyé un e-mail de confirmation avec un lien pour activer votre site.", "page.pendingactivation.text2": "Veuillez vérifier votre boîte de réception pour l'activer.", "page.pendingactivation.title": "Votre compte n'est pas activé", + "showpost.comment.copylink.error": "", + "showpost.comment.copylink.success": "", + "showpost.comment.unknownhighlighted": "", "showpost.commentinput.placeholder": "Rédiger un commentaire", "showpost.discussionpanel.emptymessage": "Personne n'a encore commenté.", "showpost.label.author": "Posté par <0/> · <1/>", diff --git a/locale/nl/client.json b/locale/nl/client.json index 1f1233c91..e881681b1 100644 --- a/locale/nl/client.json +++ b/locale/nl/client.json @@ -3,6 +3,7 @@ "action.change": "aanpassen", "action.close": "Sluiten", "action.confirm": "Bevestigen", + "action.copylink": "", "action.delete": "Verwijderen", "action.edit": "Bewerken", "action.markallasread": "Markeer alles als gelezen", @@ -81,6 +82,9 @@ "modal.deleteaccount.text": "<0>Als je ervoor kiest om je account te verwijderen, verwijderen we al je persoonlijke gegevens voor altijd. De inhoud die je hebt geplaatst blijft bestaan, maar zal geanonimiseerd worden.<1>Dit proces is onomkeerbaar. <2>Weet je het zeker?", "modal.deletecomment.header": "Reactie verwijderen", "modal.deletecomment.text": "Dit proces is onomkeerbaar. <0>Weet je het zeker?", + "modal.notifications.nonew": "", + "modal.notifications.previous": "", + "modal.notifications.unread": "", "modal.showvotes.message.zeromatches": "Geen gebruikers gevonden voor <0>{0}.", "modal.showvotes.query.placeholder": "Zoek gebruikers op naam...", "modal.signin.header": "Log in om een bericht achter te laten en om te stemmen", @@ -127,6 +131,9 @@ "page.pendingactivation.text": "We hebben je een bevestigingsmail gestuurd met een link om jouw site te activeren.", "page.pendingactivation.text2": "Controleer je inbox om het te activeren.", "page.pendingactivation.title": "Je account is nog niet geactiveerd", + "showpost.comment.copylink.error": "", + "showpost.comment.copylink.success": "", + "showpost.comment.unknownhighlighted": "", "showpost.commentinput.placeholder": "Laat een reactie achter", "showpost.discussionpanel.emptymessage": "Nog niemand heeft gereageerd.", "showpost.label.author": "Geplaatst door <0/> · <1/>", diff --git a/locale/pl/client.json b/locale/pl/client.json index ee8409f03..5c5bc1c90 100644 --- a/locale/pl/client.json +++ b/locale/pl/client.json @@ -3,6 +3,7 @@ "action.change": "zmień", "action.close": "Zamknij", "action.confirm": "Potwierdź", + "action.copylink": "", "action.delete": "Usuń", "action.edit": "Edytuj", "action.markallasread": "Oznacz wszystkie jako przeczytane", @@ -81,6 +82,9 @@ "modal.deleteaccount.text": "<0>Jeśli zdecydujesz na usunięcie swojego konta, na zawsze usuniemy wszystkie Twoje dane osobowe. Opublikowana zawartość pozostanie na stronie, ale zostanie anonimowa.<1>Ten proces jest nieodwracalny. <2>Czy jesteś pewien?", "modal.deletecomment.header": "Usuń komentarz", "modal.deletecomment.text": "Ten proces jest nieodwracalny. <0>Czy jesteś pewien?", + "modal.notifications.nonew": "", + "modal.notifications.previous": "", + "modal.notifications.unread": "", "modal.showvotes.message.zeromatches": "Nie znaleziono użytkowników pasujących do <0>{0}.", "modal.showvotes.query.placeholder": "Wyszukaj użytkowników według nazwy...", "modal.signin.header": "Zaloguj się aby pisać i głosować", @@ -127,6 +131,9 @@ "page.pendingactivation.text": "Wysłaliśmy do Ciebie maila z linkiem do aktywowania Twojej strony.", "page.pendingactivation.text2": "Sprawdź swoją skrzynkę odbiorczą, aby ją aktywować.", "page.pendingactivation.title": "Twoje konto oczekuje na aktywację", + "showpost.comment.copylink.error": "", + "showpost.comment.copylink.success": "", + "showpost.comment.unknownhighlighted": "", "showpost.commentinput.placeholder": "Skomentuj", "showpost.discussionpanel.emptymessage": "Wygląda na to, że nikt jeszcze nie skomentował.", "showpost.label.author": "Wysłane przez <0/> · <1/>", diff --git a/locale/pt-BR/client.json b/locale/pt-BR/client.json index 96e0c68d5..a7439d8cf 100644 --- a/locale/pt-BR/client.json +++ b/locale/pt-BR/client.json @@ -3,6 +3,7 @@ "action.change": "alterar", "action.close": "Fechar", "action.confirm": "Confirmar", + "action.copylink": "", "action.delete": "Deletar", "action.edit": "Editar", "action.markallasread": "Marcar todas como lidas", @@ -81,6 +82,9 @@ "modal.deleteaccount.text": "<0>Quando você optar por excluir sua conta, todas as suas informações pessoais serão removidas para sempre. O conteúdo que você publicou permanecerá, mas será anonimizado.<1>Esse processo é irreversível. <2>Você tem certeza?", "modal.deletecomment.header": "Excluir comentário", "modal.deletecomment.text": "Este processo é irreversível. <0>Tem certeza?", + "modal.notifications.nonew": "", + "modal.notifications.previous": "", + "modal.notifications.unread": "", "modal.showvotes.message.zeromatches": "Nenhum usuário encontrado para <0>{0}.", "modal.showvotes.query.placeholder": "Procurar usuários por nome...", "modal.signin.header": "Faça login para participar e votar", @@ -127,6 +131,9 @@ "page.pendingactivation.text": "Enviamos a você um e-mail de confirmação com um link para ativar o seu site.", "page.pendingactivation.text2": "Verifique sua caixa de entrada para ativá-la.", "page.pendingactivation.title": "Sua conta está com ativação pendente", + "showpost.comment.copylink.error": "", + "showpost.comment.copylink.success": "", + "showpost.comment.unknownhighlighted": "", "showpost.commentinput.placeholder": "Deixe um comentário", "showpost.discussionpanel.emptymessage": "Ninguém comentou ainda.", "showpost.label.author": "Publicado por <0/> · <1/>", diff --git a/locale/ru/client.json b/locale/ru/client.json index 7cafb1ada..a1cf12c62 100644 --- a/locale/ru/client.json +++ b/locale/ru/client.json @@ -3,6 +3,7 @@ "action.change": "изменить", "action.close": "Закрыть", "action.confirm": "Подтвердить", + "action.copylink": "", "action.delete": "Удалить", "action.edit": "Изменить", "action.markallasread": "Отметить всё как прочитанное", @@ -81,6 +82,9 @@ "modal.deleteaccount.text": "<0>Если нужно, мы удалим всю информацию о вашем аккаунте навсегда. Всё, что вы публиковали, останется, но будет анонимизировано.<1>Это действие необратимо. <2>Вы уверены?", "modal.deletecomment.header": "Удалить комментарий", "modal.deletecomment.text": "Это действие необратимо. <0>Вы уверены?", + "modal.notifications.nonew": "", + "modal.notifications.previous": "", + "modal.notifications.unread": "", "modal.showvotes.message.zeromatches": "Не удалось найти пользователей с <0>{0}.", "modal.showvotes.query.placeholder": "Найдите пользователей по их имени...", "modal.signin.header": "Войдите, чтобы создавать посты и голосовать", @@ -127,6 +131,9 @@ "page.pendingactivation.text": "Мы отправили вам письмо со ссылкой для активации этого сайта.", "page.pendingactivation.text2": "Проверьте свою почту, чтобы продолжить.", "page.pendingactivation.title": "Ваш аккаунт ожидает подтверждения", + "showpost.comment.copylink.error": "", + "showpost.comment.copylink.success": "", + "showpost.comment.unknownhighlighted": "", "showpost.commentinput.placeholder": "Оставить комментарий", "showpost.discussionpanel.emptymessage": "Комментариев нет.", "showpost.label.author": "Создал <0/> · <1/>", diff --git a/locale/sk/client.json b/locale/sk/client.json index a034368de..d60077174 100644 --- a/locale/sk/client.json +++ b/locale/sk/client.json @@ -3,6 +3,7 @@ "action.change": "zmeniť", "action.close": "Zavrieť", "action.confirm": "Potvrdiť", + "action.copylink": "", "action.delete": "Vymazať", "action.edit": "Upraviť", "action.markallasread": "Označiť všetko ako prečítané", @@ -81,6 +82,9 @@ "modal.deleteaccount.text": "<0>Keď sa rozhodnete odstrániť svoj účet, všetky vaše osobné informácie navždy vymažeme. Obsah, ktorý ste zverejnili, zostane, ale bude anonymizovaný. <1> Tento proces je nevratný.<2>Ste si istí?", "modal.deletecomment.header": "Odstrániť komentár", "modal.deletecomment.text": "Tento proces je nevratný. <0>Ste si istí?", + "modal.notifications.nonew": "", + "modal.notifications.previous": "", + "modal.notifications.unread": "", "modal.showvotes.message.zeromatches": "Nenašli sa žiadni používatelia <0>{0}.", "modal.showvotes.query.placeholder": "Vyhľadajte používateľov podľa mena...", "modal.signin.header": "Prihláste sa, aby ste mohli pridávať príspevky a hlasovať", @@ -127,6 +131,9 @@ "page.pendingactivation.text": "Poslali sme vám potvrdzovací email s odkazom na aktiváciu vašich stránok.", "page.pendingactivation.text2": "Aktivujte prosím svoju doručenú poštu.", "page.pendingactivation.title": "Váš účet čaká na aktiváciu", + "showpost.comment.copylink.error": "", + "showpost.comment.copylink.success": "", + "showpost.comment.unknownhighlighted": "", "showpost.commentinput.placeholder": "Zanechať komentár", "showpost.discussionpanel.emptymessage": "Zatiaľ sa nikto nevyjadril.", "showpost.label.author": "Pridané <0/> · <1/>", diff --git a/locale/sv-SE/client.json b/locale/sv-SE/client.json index 2096a2fea..56ee466a3 100644 --- a/locale/sv-SE/client.json +++ b/locale/sv-SE/client.json @@ -3,6 +3,7 @@ "action.change": "ändra", "action.close": "Stäng", "action.confirm": "Bekräfta", + "action.copylink": "", "action.delete": "Radera", "action.edit": "Ändra", "action.markallasread": "Markera alla som lästa", @@ -81,6 +82,9 @@ "modal.deleteaccount.text": "<0>När du väljer att radera ditt konto kommer vi att radera all din personliga information för evigt. Innehållet du har publicerat kommer att finnas kvar, men det kommer att vara anonymiserat.<1>Denna process är oåterkallelig. <2>Är du säker?", "modal.deletecomment.header": "Radera kommentar", "modal.deletecomment.text": "Denna process är oåterkallelig. <0>Är du säker?", + "modal.notifications.nonew": "", + "modal.notifications.previous": "", + "modal.notifications.unread": "", "modal.showvotes.message.zeromatches": "Inga användare hittades som matchar <0>{0}.", "modal.showvotes.query.placeholder": "Sök efter användare med namn...", "modal.signin.header": "Logga in för att skriva inlägg och rösta", @@ -127,6 +131,9 @@ "page.pendingactivation.text": "Vi har skickat ett bekräftelsemail med en länk för att aktivera din webbplats.", "page.pendingactivation.text2": "Kontrollera din inkorg för att aktivera den.", "page.pendingactivation.title": "Ditt konto väntar på aktivering", + "showpost.comment.copylink.error": "", + "showpost.comment.copylink.success": "", + "showpost.comment.unknownhighlighted": "", "showpost.commentinput.placeholder": "Skriv en kommentar", "showpost.discussionpanel.emptymessage": "Ingen har kommenterat ännu.", "showpost.label.author": "Skriven av <0/> · <1/>", diff --git a/locale/tr/client.json b/locale/tr/client.json index 00529164e..3d0083f41 100644 --- a/locale/tr/client.json +++ b/locale/tr/client.json @@ -3,6 +3,7 @@ "action.change": "değiştir", "action.close": "Kapat", "action.confirm": "Onayla", + "action.copylink": "", "action.delete": "Sil", "action.edit": "Düzenle", "action.markallasread": "Tümünü Okundu olarak işaretle", @@ -81,6 +82,9 @@ "modal.deleteaccount.text": "<0>Hesabınızı kaldırdığınız zaman size ait bütün kişisel bilgileri kalıcı olarak sileceğiz. Yayınladığınız öneriler sitede anonim olarak kalmaya devam edecek.<1>Bu geri alınamaz bir işlemdir. <2>Devam etmek istiyor musunuz?", "modal.deletecomment.header": "Yorumu Sil", "modal.deletecomment.text": "Bu geri alınamaz bir işlemdir. <0>Emin misiniz?", + "modal.notifications.nonew": "", + "modal.notifications.previous": "", + "modal.notifications.unread": "", "modal.showvotes.message.zeromatches": "Eşleşen kullanıcı bulunamadı <0>{0}.", "modal.showvotes.query.placeholder": "Kullanıcıları ismiyle arayın...", "modal.signin.header": "Öneride bulunmak ve oy vermek için giriş yapın", @@ -127,6 +131,9 @@ "page.pendingactivation.text": "Size, sayfanız için aktivasyon bağlantısı barındıran bir onay e-postası gönderdik.", "page.pendingactivation.text2": "Aktivasyon için lütfen gelen kutunuzu kontrol edin.", "page.pendingactivation.title": "Hesabınız aktivasyon beklemektedir", + "showpost.comment.copylink.error": "", + "showpost.comment.copylink.success": "", + "showpost.comment.unknownhighlighted": "", "showpost.commentinput.placeholder": "Yorum yazın", "showpost.discussionpanel.emptymessage": "Henüz hiç kimse yorum yapmadı.", "showpost.label.author": "<0/> · <1/> tarafından gönderildi", diff --git a/public/assets/styles/reset.scss b/public/assets/styles/reset.scss index 45670f2e7..9d3b9cb2e 100644 --- a/public/assets/styles/reset.scss +++ b/public/assets/styles/reset.scss @@ -16,7 +16,7 @@ body { overflow-x: hidden; min-width: 320px; color: get("colors.gray.900"); - background-color: get("colors.white"); + background-color: get("colors.gray.100"); font-size: get("font.size.base"); font-family: $font-base; } diff --git a/public/assets/styles/utility/display.scss b/public/assets/styles/utility/display.scss index 814c5db7e..30ca95ec8 100644 --- a/public/assets/styles/utility/display.scss +++ b/public/assets/styles/utility/display.scss @@ -63,6 +63,13 @@ } } +.box { + border-radius: get("border.radius.large"); + background-color: get("colors.white"); + border: 1px solid get("colors.gray.200"); + padding: spacing(4); +} + .flex { display: flex; } @@ -176,6 +183,10 @@ visibility: hidden; } +.bt { + border-top: 1px solid; +} + .overflow-scroll { overflow: scroll; } @@ -196,3 +207,9 @@ cursor: pointer; pointer-events: inherit; } + +.hover { + &:hover { + background-color: get("colors.gray.100"); + } +} diff --git a/public/assets/styles/utility/page.scss b/public/assets/styles/utility/page.scss index d8c8b9fb8..b6b31e8cb 100644 --- a/public/assets/styles/utility/page.scss +++ b/public/assets/styles/utility/page.scss @@ -5,7 +5,7 @@ } .page { - padding-top: spacing(4); + padding-top: spacing(5); padding-bottom: spacing(4); } diff --git a/public/assets/styles/utility/text.scss b/public/assets/styles/utility/text.scss index bbafbc6f6..9cc2fc946 100644 --- a/public/assets/styles/utility/text.scss +++ b/public/assets/styles/utility/text.scss @@ -10,8 +10,8 @@ pre:last-child { } .text-display2 { - font-size: get("font.size.2xl"); - font-weight: get("font.weight.bold"); + font-size: get("font.size.xl"); + font-weight: get("font.weight.medium"); letter-spacing: -1px; } @@ -20,11 +20,21 @@ pre:last-child { font-weight: get("font.weight.semibold"); } +.text-header { + font-size: get("font.size.xl"); + font-weight: get("font.weight.medium"); +} + .text-title { font-size: get("font.size.lg"); font-weight: get("font.weight.medium"); } +.text-subtitle { + font-size: get("font.size.base"); + font-weight: get("font.weight.medium"); +} + .text-body { font-size: get("font.size.base"); } @@ -49,6 +59,12 @@ pre:last-child { color: get("colors.blue.600"); } +.nowrap { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + .text-link:hover { color: get("colors.blue.800"); text-decoration: underline; diff --git a/public/assets/styles/variables/_text.scss b/public/assets/styles/variables/_text.scss index f24cf6bd9..8d8ca00ef 100644 --- a/public/assets/styles/variables/_text.scss +++ b/public/assets/styles/variables/_text.scss @@ -1,4 +1,4 @@ -$font-base: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, +$font-base: 'Inter', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; $font-code: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; diff --git a/public/components/Header.tsx b/public/components/Header.tsx index b4c754677..65a240f37 100644 --- a/public/components/Header.tsx +++ b/public/components/Header.tsx @@ -16,14 +16,14 @@ export const Header = () => { const hideModal = () => setIsSignInModalOpen(false) return ( -
+
- +
-

{fider.session.tenant.name}

+

{fider.session.tenant.name}

{fider.session.isAuthenticated && ( diff --git a/public/components/NotificationIndicator.scss b/public/components/NotificationIndicator.scss index d36e2da63..40d9434f1 100644 --- a/public/components/NotificationIndicator.scss +++ b/public/components/NotificationIndicator.scss @@ -13,3 +13,11 @@ border-radius: 100%; } } + +.c-notifications-container { + max-height: 80vh; + overflow-y: auto; + @include media("lg") { + min-width: 400px; + } +} diff --git a/public/components/NotificationIndicator.tsx b/public/components/NotificationIndicator.tsx index 9a547ee44..e2084809b 100644 --- a/public/components/NotificationIndicator.tsx +++ b/public/components/NotificationIndicator.tsx @@ -1,14 +1,52 @@ import "./NotificationIndicator.scss" +import NoDataIllustration from "@fider/assets/images/undraw-empty.svg" import React, { useEffect, useState } from "react" import IconBell from "@fider/assets/images/heroicons-bell.svg" import { useFider } from "@fider/hooks" -import { actions } from "@fider/services" -import { Icon } from "./common" +import { actions, Fider } from "@fider/services" +import { Avatar, Icon, Markdown, Moment } from "./common" +import { Dropdown } from "./common/Dropdown" +import { Notification } from "@fider/models" +import { HStack, VStack } from "./layout" + +import { Trans } from "@lingui/macro" + +export const NotificationItem = ({ notification }: { notification: Notification }) => { + const openNotification = () => { + window.location.href = `/notifications/${notification.id}` + } + + return ( + + +
+ + + + +
+
+ ) +} + +const NotificationIcon = ({ unreadNotifications }: { unreadNotifications: number }) => { + return ( + <> + + + {unreadNotifications > 0 &&
} + + + ) +} export const NotificationIndicator = () => { const fider = useFider() const [unreadNotifications, setUnreadNotifications] = useState(0) + const [showingNotifications, setShowingNotifications] = useState(false) + const [recent, setRecent] = useState() + const [unread, setUnread] = useState() useEffect(() => { if (fider.session.isAuthenticated) { @@ -20,10 +58,83 @@ export const NotificationIndicator = () => { } }, [fider.session.isAuthenticated]) + useEffect(() => { + if (showingNotifications) { + actions.getAllNotifications().then((result) => { + if (result) { + const [unread, recent] = (result.data || []).reduce( + (result, item) => { + result[item.read ? 1 : 0].push(item) + return result + }, + [[] as Notification[], [] as Notification[]] + ) + setRecent(recent) + setUnread(unread) + setUnreadNotifications(unread.length) + } + }) + } + }, [showingNotifications]) + + const markAllAsRead = async (e: React.MouseEvent) => { + e.preventDefault() + const response = await actions.markAllAsRead() + if (response.ok) { + location.reload() + } + } + return ( - - - {unreadNotifications > 0 &&
} - + setShowingNotifications(isOpen)} + renderHandle={} + > +
+ {showingNotifications && (unread !== undefined || recent !== undefined) && ( + <> + {unread !== undefined && unread?.length > 0 ? ( + <> +

+ Unread notifications + {unread.length > 1 && ( + + Mark All as Read + + )} +

+ + {unread.map((n) => ( + + ))} + + + ) : ( +
+

+ No new notifications +

+ {recent?.length === 0 && } +
+ )} + {recent !== undefined && recent?.length > 0 && ( + <> +

+ Previous notifications +

+ + {recent.map((n) => ( + + ))} + + + )} + + )} +
+
) } diff --git a/public/components/common/Dropdown.scss b/public/components/common/Dropdown.scss index 265bf97ca..38d94f00e 100644 --- a/public/components/common/Dropdown.scss +++ b/public/components/common/Dropdown.scss @@ -29,6 +29,19 @@ &--left { right: 0; } + + &--wide { + max-width: sizing(180); + } + + &--fullscreen-small { + @include media("sm") { + position: fixed; + left: 0; + width: 100vw; + } + } + } &__listitem { diff --git a/public/components/common/Dropdown.tsx b/public/components/common/Dropdown.tsx index 8da23d576..1f9e5b5ac 100644 --- a/public/components/common/Dropdown.tsx +++ b/public/components/common/Dropdown.tsx @@ -42,7 +42,10 @@ const Divider = () => { interface DropdownProps { renderHandle: JSX.Element position?: "left" | "right" + onToggled?: (isOpen: boolean) => void children: React.ReactNode + wide?: boolean + fullsceenSm?: boolean } interface DropdownContextFuncs { @@ -57,12 +60,19 @@ export const Dropdown = (props: DropdownProps) => { const [isOpen, setIsOpen] = useState(false) const position = props.position || "right" + const changeToggleState = (newState: boolean) => { + setIsOpen(newState) + if (props.onToggled) { + props.onToggled(newState) + } + } + const toggleIsOpen = () => { - setIsOpen(!isOpen) + changeToggleState(!isOpen) } const close = () => { - setIsOpen(false) + changeToggleState(false) } const handleClick = (e: MouseEvent) => { @@ -82,7 +92,9 @@ export const Dropdown = (props: DropdownProps) => { }, []) const listClassName = classSet({ + "c-dropdown__list--wide": props.wide, "c-dropdown__list shadow-lg": true, + "c-dropdown__list--fullscreen-small": props.fullsceenSm, [`c-dropdown__list--${position}`]: position === "left", }) diff --git a/public/components/common/PoweredByFider.scss b/public/components/common/PoweredByFider.scss index 8b5bc3582..a2693d84e 100644 --- a/public/components/common/PoweredByFider.scss +++ b/public/components/common/PoweredByFider.scss @@ -4,8 +4,8 @@ text-align: center; a { - color: get("colors.gray.700"); - font-size: 11px; + color: get("colors.blue.700"); + font-size: 12px; } a:hover { color: get("colors.gray.900"); diff --git a/public/components/common/PoweredByFider.tsx b/public/components/common/PoweredByFider.tsx index ad51078f7..791b93598 100644 --- a/public/components/common/PoweredByFider.tsx +++ b/public/components/common/PoweredByFider.tsx @@ -21,7 +21,7 @@ export const PoweredByFider = (props: PoweredByFiderProps) => { return ( ) diff --git a/public/components/common/UserName.tsx b/public/components/common/UserName.tsx index b7a7eb7a7..0f06751a7 100644 --- a/public/components/common/UserName.tsx +++ b/public/components/common/UserName.tsx @@ -11,6 +11,7 @@ interface UserNameProps { role?: UserRole email?: string } + showEmail?: boolean } export const UserName = (props: UserNameProps) => { @@ -23,7 +24,7 @@ export const UserName = (props: UserNameProps) => { return (
{props.user.name || "Anonymous"} - <>{props.user.email && ({props.user.email})} + <>{props.showEmail && props.user.email && ({props.user.email})} {isStaff && (
diff --git a/public/components/common/form/Form.scss b/public/components/common/form/Form.scss index 6d46824f4..a3d12f5cb 100644 --- a/public/components/common/form/Form.scss +++ b/public/components/common/form/Form.scss @@ -1,11 +1,11 @@ @import "~@fider/assets/styles/variables.scss"; .c-form-field { - margin-bottom: spacing(5); + margin-bottom: spacing(7); > label { display: block; - font-size: get("font.size.sm"); + font-size: get("font.size.base"); margin-bottom: spacing(1); font-weight: 500; } @@ -14,7 +14,7 @@ margin-bottom: 0; .flex-x > & { - margin-bottom: spacing(2); + // margin-bottom: spacing(2); } } } diff --git a/public/components/layout/Stack.tsx b/public/components/layout/Stack.tsx index dd70f4825..be356c4ca 100644 --- a/public/components/layout/Stack.tsx +++ b/public/components/layout/Stack.tsx @@ -12,7 +12,7 @@ interface StackProps { } const Stack = (props: StackProps, dir: "x" | "y") => { - const spacing = props.spacing === undefined ? 1 : props.spacing + const spacing = props.spacing === undefined ? 2 : props.spacing const className = classSet({ [`${props.className}`]: props.className, flex: true, diff --git a/public/models/notification.ts b/public/models/notification.ts index b85940827..a2cece932 100644 --- a/public/models/notification.ts +++ b/public/models/notification.ts @@ -4,4 +4,6 @@ export interface Notification { link: string read: boolean createdAt: string + authorName: string + avatarURL: string } diff --git a/public/pages/Administration/components/AdminBasePage.scss b/public/pages/Administration/components/AdminBasePage.scss index aabb8706f..5059bc7f4 100644 --- a/public/pages/Administration/components/AdminBasePage.scss +++ b/public/pages/Administration/components/AdminBasePage.scss @@ -6,6 +6,6 @@ gap: spacing(4); @include media("lg") { - grid-template-columns: 1fr 5fr; + grid-template-columns: 1fr 4fr 1fr; } } diff --git a/public/pages/Administration/components/SideMenu.scss b/public/pages/Administration/components/SideMenu.scss index 444f856d0..1bd480e73 100644 --- a/public/pages/Administration/components/SideMenu.scss +++ b/public/pages/Administration/components/SideMenu.scss @@ -2,7 +2,7 @@ .c-side-menu { &__item { - padding: spacing(3); + padding: spacing(4); border-bottom: 1px solid get("colors.gray.200"); color: get("colors.gray.900"); diff --git a/public/pages/Administration/components/SideMenu.tsx b/public/pages/Administration/components/SideMenu.tsx index f0f80ba14..b88e7b321 100644 --- a/public/pages/Administration/components/SideMenu.tsx +++ b/public/pages/Administration/components/SideMenu.tsx @@ -39,7 +39,7 @@ export const SideMenu = (props: SiteMenuProps) => { return (
- + diff --git a/public/pages/Administration/components/webhook/WebhookForm.tsx b/public/pages/Administration/components/webhook/WebhookForm.tsx index 02cf50ac5..ca9d93089 100644 --- a/public/pages/Administration/components/webhook/WebhookForm.tsx +++ b/public/pages/Administration/components/webhook/WebhookForm.tsx @@ -152,7 +152,7 @@ export const WebhookForm = (props: WebhookFormProps) => { This webhook has failed )} -

{title}

+

{title}

}> - + {Object.entries(httpHeaders).map(([header, value]) => ( ))} diff --git a/public/pages/Administration/components/webhook/WebhookListItem.tsx b/public/pages/Administration/components/webhook/WebhookListItem.tsx index 51d3b6776..2381fb123 100644 --- a/public/pages/Administration/components/webhook/WebhookListItem.tsx +++ b/public/pages/Administration/components/webhook/WebhookListItem.tsx @@ -1,7 +1,7 @@ import "./WebhookListItem.scss" import React, { useState } from "react" -import { Webhook, WebhookStatus, WebhookTriggerResult } from "@fider/models" +import { Webhook, WebhookStatus, WebhookTriggerResult, WebhookType } from "@fider/models" import { Button, Icon } from "@fider/components" import { actions, notify } from "@fider/services" @@ -63,6 +63,19 @@ export const WebhookListItem = (props: WebhookListItemProps) => { } } + const getWebhookType = (type: WebhookType) => { + switch (type) { + case WebhookType.CHANGE_STATUS: + return "Change Status" + case WebhookType.NEW_COMMENT: + return "New Comment" + case WebhookType.DELETE_POST: + return "Delete Post" + case WebhookType.NEW_POST: + return "New Post" + } + } + const testWebhook = async () => { const result = await actions.testWebhook(props.webhook.id) setTriggerResult(result.data) @@ -100,8 +113,9 @@ export const WebhookListItem = (props: WebhookListItemProps) => { -

- #{props.webhook.id} {props.webhook.name} +

+ #{props.webhook.id} + {getWebhookType(props.webhook.type)} - {props.webhook.name}

{triggerResult?.success === false && ( diff --git a/public/pages/Administration/pages/GeneralSettings.page.tsx b/public/pages/Administration/pages/GeneralSettings.page.tsx index 4adffff63..414d4012d 100644 --- a/public/pages/Administration/pages/GeneralSettings.page.tsx +++ b/public/pages/Administration/pages/GeneralSettings.page.tsx @@ -44,11 +44,8 @@ const GeneralSettingsPage = () => { return ( - -

- The title is used on the header, emails, notifications and SEO content. Keep it short and simple. The product/service name is usually the best - choice. -

+ +

Keep it short and snappy. Your product / service name is usually best.

@@ -73,16 +70,11 @@ const GeneralSettingsPage = () => { placeholder="Enter your suggestion here..." onChange={setInvitation} > -

- This text is used as a placeholder for the suggestion's text box. Use it to invite your visitors into sharing their suggestions and feedback. - Leave it empty for a default message. -

+

Placeholder text in the suggestion's box. It should invite your visitors into sharing their feedback.

- -

- We accept JPG, GIF and PNG images, smaller than 100KB and with an aspect ratio of 1:1 with minimum dimensions of 200x200 pixels. -

+ +

JPG, GIF or PNG smaller than 100KB, minimum size 200x200 pixels.

{!Fider.isSingleHostMode() && ( @@ -104,8 +96,7 @@ const GeneralSettingsPage = () => { ] ) : (

- Custom domains allow you to access your app via your own domain name (for example, feedback.yourcompany.com - ). + Use custom domains to access Fider via your own domain name feedback.yourcompany.com

)}
diff --git a/public/pages/Administration/pages/ManageMembers.page.tsx b/public/pages/Administration/pages/ManageMembers.page.tsx index c058e4617..4ed17c0a4 100644 --- a/public/pages/Administration/pages/ManageMembers.page.tsx +++ b/public/pages/Administration/pages/ManageMembers.page.tsx @@ -38,7 +38,7 @@ const UserListItem = (props: UserListItemProps) => { - + {admin} {collaborator} {blocked} @@ -161,7 +161,7 @@ export default class ManageMembersPage extends AdminBasePage
-

+

{!this.state.query && ( <> Showing {this.state.visibleUsers.length} of {this.state.users.length} registered users. diff --git a/public/pages/Administration/pages/ManageWebhooks.page.tsx b/public/pages/Administration/pages/ManageWebhooks.page.tsx index f40f8c7e7..0a739ed4a 100644 --- a/public/pages/Administration/pages/ManageWebhooks.page.tsx +++ b/public/pages/Administration/pages/ManageWebhooks.page.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react" import { Button } from "@fider/components" -import { Webhook, WebhookData, WebhookStatus, WebhookType } from "@fider/models" +import { Webhook, WebhookData, WebhookStatus } from "@fider/models" import { actions, Failure } from "@fider/services" import { AdminPageContainer } from "../components/AdminBasePage" import { WebhookForm } from "../components/webhook/WebhookForm" @@ -30,10 +30,9 @@ interface WebhooksListProps { const WebhooksList = (props: WebhooksListProps) => { return (

-

{props.title}

-

These webhooks are triggered every time {props.description}.

+

My Webhooks

- {props.list.length === 0 ?

There aren’t any "{props.title.toLowerCase()}" webhook yet.

: props.list} + {props.list.length === 0 ?

There aren’t any webhooks yet.

: props.list}
) @@ -98,8 +97,8 @@ const ManageWebhooksPage = (props: ManageWebhooksPageProps) => { sortWebhooks() } - const getWebhookList = (filter: (webhook: Webhook) => boolean) => { - return allWebhooks.filter(filter).map((w) => { + const getWebhookItems = () => { + return allWebhooks.map((w) => { return ( { return render() } - const newPostList = getWebhookList((w) => w.type === WebhookType.NEW_POST) - const newCommentList = getWebhookList((w) => w.type === WebhookType.NEW_COMMENT) - const changeStatusList = getWebhookList((w) => w.type === WebhookType.CHANGE_STATUS) - const deletePostList = getWebhookList((w) => w.type === WebhookType.DELETE_POST) - return render(

@@ -140,13 +134,10 @@ const ManageWebhooksPage = (props: ManageWebhooksPageProps) => { .

- - - - +
diff --git a/public/pages/Home/Home.page.scss b/public/pages/Home/Home.page.scss index 46a301ca2..9fb06910a 100644 --- a/public/pages/Home/Home.page.scss +++ b/public/pages/Home/Home.page.scss @@ -14,13 +14,22 @@ .p-home { &__welcome-col { - background-color: get("colors.gray.100"); - padding: spacing(2); - border-radius: get("border.radius.medium"); + > :first-child { + background-color: get("colors.white"); + border-radius: get("border.radius.large"); + border: 1px solid get("colors.gray.200"); + + @include media("lg") { + } + } + } + + &__posts-col { + background-color: get("colors.white"); + border-radius: get("border.radius.large"); + border: 1px solid get("colors.gray.200"); @include media("lg") { - padding: 0; - background-color: transparent; } } diff --git a/public/pages/Home/Home.page.tsx b/public/pages/Home/Home.page.tsx index 834d45017..a0b1c4f7c 100644 --- a/public/pages/Home/Home.page.tsx +++ b/public/pages/Home/Home.page.tsx @@ -77,13 +77,13 @@ What can we do better? This is the place for you to vote, discuss and share idea
- + - +
-
+
{isLonely() ? ( ) : title ? ( diff --git a/public/pages/Home/components/TagsFilter.tsx b/public/pages/Home/components/TagsFilter.tsx index cee7dbb60..902f0e600 100644 --- a/public/pages/Home/components/TagsFilter.tsx +++ b/public/pages/Home/components/TagsFilter.tsx @@ -32,7 +32,9 @@ export const TagsFilter = (props: TagsFilterProps) => { return ( - with + + with + {label}
}> {props.tags.map((t) => ( diff --git a/public/pages/MySettings/components/NotificationSettings.tsx b/public/pages/MySettings/components/NotificationSettings.tsx index 254bf456c..8585e8351 100644 --- a/public/pages/MySettings/components/NotificationSettings.tsx +++ b/public/pages/MySettings/components/NotificationSettings.tsx @@ -94,7 +94,7 @@ export const NotificationSettings = (props: NotificationSettingsProps) => {

- +
New Post diff --git a/public/pages/ShowPost/ShowPost.page.scss b/public/pages/ShowPost/ShowPost.page.scss index c2efacbf9..a23a0f93c 100644 --- a/public/pages/ShowPost/ShowPost.page.scss +++ b/public/pages/ShowPost/ShowPost.page.scss @@ -5,27 +5,40 @@ flex-grow: 1; } + .p-show-post { + &__main-col { + background-color: get("colors.white"); + padding: spacing(4); + border-radius: get("border.radius.large"); + margin-bottom: spacing(4); + } + + &__action-col { + > :first-child { + background-color: get("colors.white"); + padding: spacing(4); + border-radius: get("border.radius.large"); + } + } + } + @include media("lg") { .p-show-post { display: grid; gap: spacing(4); - grid-template-columns: 1fr 1fr 1fr 1fr; + grid-template-columns: 1fr 3fr; grid-template-rows: auto; grid-template-areas: - "Header Header Header Action" - "Discussion Discussion Discussion Action"; + "Action Main" + "Action Main"; - &__header-col { - grid-area: Header; + &__main-col { + grid-area: Main; } &__action-col { grid-area: Action; } - - &__discussion_col { - grid-area: Discussion; - } } } } diff --git a/public/pages/ShowPost/ShowPost.page.tsx b/public/pages/ShowPost/ShowPost.page.tsx index 8690b378d..a83fd6004 100644 --- a/public/pages/ShowPost/ShowPost.page.tsx +++ b/public/pages/ShowPost/ShowPost.page.tsx @@ -145,102 +145,105 @@ export default class ShowPostPage extends React.Component
- -
- - - +
+
+
+ + + + +
+ {this.state.editMode ? ( + + + + ) : ( +

{this.props.post.title}

+ )} -
+ + + Posted by · + + +
+ + + + Description + {this.state.editMode ? (
- +