diff --git a/assets/css/misuzu/_msz.css b/assets/css/misuzu/_msz.css index 1c753248..afe7f675 100644 --- a/assets/css/misuzu/_msz.css +++ b/assets/css/misuzu/_msz.css @@ -21,7 +21,7 @@ body { :root { --font-size: 12px; --line-height: 20px; - --font-regular: Tahoma, Geneva, 'Dejavu Sans', Arial, Helvetica, sans-serif; + --font-regular: Verdana, Geneva, 'Dejavu Sans', Arial, Helvetica, sans-serif; --font-monospace: Consolas, 'Liberation Mono', Menlo, Courier, monospace; --site-max-width: 1200px; diff --git a/assets/css/misuzu/footer.css b/assets/css/misuzu/footer.css index 6c4d1548..79360e95 100644 --- a/assets/css/misuzu/footer.css +++ b/assets/css/misuzu/footer.css @@ -5,6 +5,7 @@ color: inherit; text-decoration: none; } +.footer__link:focus, .footer__link:hover { text-decoration: underline; } diff --git a/assets/css/misuzu/header.css b/assets/css/misuzu/header.css index d96878f2..cdfe59f1 100644 --- a/assets/css/misuzu/header.css +++ b/assets/css/misuzu/header.css @@ -55,7 +55,6 @@ .header__desktop__link:focus { background-color: rgba(255, 255, 255, .2); } - .header__desktop__link:active { background-color: rgba(255, 255, 255, .1); } diff --git a/assets/css/misuzu/home/landingv2-footer.css b/assets/css/misuzu/home/landingv2-footer.css new file mode 100644 index 00000000..f9590fe4 --- /dev/null +++ b/assets/css/misuzu/home/landingv2-footer.css @@ -0,0 +1,84 @@ +.landingv2-footer { + flex: 0 0 auto; + --footer-background-mask-image: linear-gradient(180deg, transparent, var(--background-colour) 30px); + margin-top: 4px; + padding-top: 20px; +} +.landingv2-footer-background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + mask-image: var(--footer-background-mask-image); + -webkit-mask-image: var(--footer-background-mask-image); + background: var(--background-pattern); + background-color: var(--header-accent-colour); + background-blend-mode: multiply; +} +.landingv2-footer-wrapper { + max-width: var(--site-max-width); + margin: 0 auto; + padding: 1em 4px; + display: grid; + grid-template-columns: repeat(2, 1fr); +} + +.landingv2-footer-navigation {} +.landingv2-footer-navigation a { + display: inline-block; + color: inherit; + text-decoration: none; + min-width: 200px; + cursor: pointer; + border-radius: 2px; + padding: 4px 10px; + margin: 1px 0; + transition: background-color .2s; +} +.landingv2-footer-navigation a:hover, +.landingv2-footer-navigation a:focus { + background-color: rgba(255, 255, 255, .2); +} +.landingv2-footer-navigation a:active { + background-color: rgba(255, 255, 255, .1); +} + +.landingv2-footer-copyright { + text-align: right; + line-height: 1.8em; + font-size: .9em; + align-self: flex-end; +} + +.landingv2-footer-copyright a { + color: inherit; + text-decoration: none; +} +.landingv2-footer-copyright a:focus, +.landingv2-footer-copyright a:hover { + text-decoration: underline; +} + +@media(max-width: 800px) { + .landingv2-footer-wrapper { + grid-template-columns: 1fr; + } + + .landingv2-footer-navigation { + text-align: center; + margin: 0 8px; + } + .landingv2-footer-navigation div { + display: inline-block; + } + .landingv2-footer-navigation a { + text-align: center; + min-width: 100px; + margin: 2px; + } + + .landingv2-footer-copyright { + text-align: center; + } +} diff --git a/assets/css/misuzu/home/landingv2-header.css b/assets/css/misuzu/home/landingv2-header.css new file mode 100644 index 00000000..81fd084c --- /dev/null +++ b/assets/css/misuzu/home/landingv2-header.css @@ -0,0 +1,91 @@ +.landingv2-header { + flex: 0 0 auto; + --header-background-mask-image: linear-gradient(0deg, transparent, var(--background-colour) 100px); + padding-bottom: 100px; +} +.landingv2-header-background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--background-pattern); + background-color: var(--header-accent-colour); + background-blend-mode: multiply; + mask-image: var(--header-background-mask-image); + -webkit-mask-image: var(--header-background-mask-image); +} +.landingv2-header-content { + margin: 0 auto; + max-width: 800px; +} + +.landingv2-welcome { + text-align: center; + margin: 10px; +} +.landingv2-welcome a { + color: inherit; + text-decoration: none; +} +.landingv2-welcome img { + max-width: 100%; + max-height: 100%; + vertical-align: middle; +} + +.landingv2-header-menu { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-gap: 4px; + padding: 0 4px; +} + +.landingv2-header-menu-link { + color: #fff; + text-decoration: none; + cursor: pointer; + border-radius: 2px; + background-color: var(--background-colour); + border: 1px solid var(--header-accent-colour); + transition: background-color .2s; + display: flex; + align-items: center; + min-height: 70px; + font-size: 1.4em; + padding: 10px 16px; + grid-column: 1; +} +.landingv2-header-menu-link:hover, +.landingv2-header-menu-link:focus { + background-color: var(--accent-colour); +} + +.landingv2-auth-link { + font-size: 2em; + justify-content: center; + text-align: center; + grid-column: 2; +} +.landingv2-auth-link-login { + grid-row: 1 / span 2; +} + +@media(max-width: 700px) { + .landingv2-header-menu { + grid-template-columns: 1fr; + } + + .landingv2-auth-link { + grid-column: 1; + } + + .landingv2-auth-link-login { + grid-row: 1; + min-height: 100px; + } + + .landingv2-auth-link-register { + grid-row: 2; + } +} diff --git a/assets/css/misuzu/home/landingv2.css b/assets/css/misuzu/home/landingv2.css new file mode 100644 index 00000000..19ac07ab --- /dev/null +++ b/assets/css/misuzu/home/landingv2.css @@ -0,0 +1,211 @@ +.landingv2-content { + padding: 0 4px; +} + +.landingv2-stats { + display: grid; + grid-template-columns: repeat(3, 1fr); + grid-gap: 40px; + margin: 40px; +} + +.landingv2-stat { + display: flex; + align-items: center; + margin: 10px; + text-shadow: 0 1px 4px #000; +} +.landingv2-stat-icon { + font-size: 4em; +} +.landingv2-stat-value { + font-size: 2em; + text-align: right; + flex: 1 1 auto; +} +.landingv2-stat-value-num { + font-weight: 700; +} + +.landingv2-forum { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-gap: 2px; + margin: 4px 0; +} + +.landingv2-forum-topics { + background-color: var(--container-colour); + box-shadow: 0 1px 2px #0009; + text-shadow: 0 1px 4px #000; + overflow: hidden; + word-wrap: break-word; +} +.landingv2-forum-topics-list { + display: flex; + flex-direction: column; + margin: 5px; + overflow: hidden; +} + +.landingv2-forum-topic { + border-radius: 2px; + background-color: rgba(17, 17, 17, .6); + transition: background-color .2s, box-shadow .2s, opacity .2s; +} +.landingv2-forum-topic:nth-child(even) { + background-color: rgba(25, 25, 25, .6); +} +.landingv2-forum-topic:hover, +.landingv2-forum-topic:focus { + background-color: rgba(34, 34, 34, .6); + box-shadow: 0 1px 4px #222; +} +.landingv2-forum-topic:not(:last-child) { + margin-bottom: 4px; +} + +.landingv2-forum-topic-link { + display: block; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + color: inherit; + text-decoration: none; + z-index: 200; +} + +.landingv2-forum-topic-info { + z-index: 100; + display: flex; + padding: 5px; + align-items: center; + min-height: 40px; + pointer-events: none; +} +.landingv2-forum-topic-info-icon { + flex: 0 0 auto; + border-radius: 2px; + width: 30px; + height: 30px; + margin-right: 4px; + background-color: var(--accent-colour); + background-size: 60px 60px; + background-image: radial-gradient(ellipse at center, rgba(255, 255, 255, .2) 0%, rgba(0, 0, 0, .4) 100%); + box-shadow: 0 1px 4px #111; + font-size: 1.5em; + line-height: 1.5em; + color: #fff; + display: flex; + justify-content: center; + align-items: center; + padding: 1px 1px 0 2px; +} + +.landingv2-forum-topic-info-details { + margin: 0 4px; + flex: 1 1 auto; + display: flex; + justify-content: center; + flex-direction: column; + line-height: 1.6em; + overflow: hidden; +} +.landingv2-forum-topic-info-details-title { + font-size: 1.3em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.landingv2-forum-topic-info-stats { + font-size: .9em; + display: flex; + flex: 0 0 auto; + text-align: center; + min-width: 60px; + flex-direction: column; +} +.landingv2-forum-topic-info-stats-posts, +.landingv2-forum-topic-info-stats-views { + font-size: .9em; + line-height: 1.3em; + opacity: .7; + pointer-events: auto; + cursor: default; +} +.landingv2-forum-topic-info-stats-posts { + font-size: 1.4em; + opacity: 1; +} + +.landingv2-news { + display: grid; + grid-template-columns: repeat(3, 1fr); + grid-gap: 2px; +} + +.landingv2-news-post { + background-color: var(--container-colour); + box-shadow: 0 1px 2px #0009; + text-shadow: 0 1px 4px #000; + overflow: hidden; + word-wrap: break-word; + padding: 0 10px 10px 10px; + display: flex; + flex-direction: column; +} +.landingv2-news-post p { + flex: 1 1 auto; +} +.landingv2-news-post-options { + text-align: right; +} + +.landingv2-online { + background-color: var(--container-colour); + box-shadow: 0 1px 2px #0009; + text-shadow: 0 1px 4px #000; + margin: 4px 0; +} +.landingv2-online-users { + scrollbar-width: thin; + overflow: auto; +} +.landingv2-online-inner { + display: flex; + padding: 2px; +} +.landingv2-online-avatar { + margin: 2px; + display: block; + flex: 0 0 auto; +} + +@media(max-width: 1100px) { + .landingv2-stat { + margin: 0; + } +} + +@media(max-width: 1024px) { + .landingv2-stats { + grid-template-columns: repeat(2, 1fr); + margin: 20px; + } +} + +@media(max-width: 900px) { + .landingv2-forum, + .landingv2-news { + grid-template-columns: 1fr; + } +} + +@media(max-width: 700px) { + .landingv2-stats { + grid-template-columns: 1fr; + } +} diff --git a/docs/contact.md b/docs/contact.md index 90c05c49..1937dab2 100644 --- a/docs/contact.md +++ b/docs/contact.md @@ -3,14 +3,14 @@ If you need to reach us outside of this website, this is the page for you. Below are a few ways of contact. ## E-mail - - [flash](mailto:me+flashii@flash.moe): Site Administrator + - [flash](mailto:flashii@flash.moe): Site Administrator ## Twitter - [@flashiinet](https://twitter.com/flashiinet): General updates and conversation. + - [@flashiistatus](https://twitter.com/flashiistatus): Exclusively system status updates, posts by this accounts are generally retweeted by @flashiinet. - [@smugwave](https://twitter.com/smugwave): Twitter of the owner, proceed with caution! ## Source Code - [Misuzu](https://github.com/flashwave/misuzu): Backend of the [website](https://flashii.net). - [Sharp Chat](https://github.com/flashii/sharp-chat): Chat Server software. - - [Kuroko](https://github.com/flashii/kuroko): Chat web client hosted at [chat.flashii.net](https://chat.flashii.net). - [Backup Manager](https://github.com/flashii/backup-manager): Program that runs every day at 12:00am UTC to back up any user generated content. diff --git a/docs/rules.md b/docs/rules.md index 719b281a..1e2d1293 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -27,7 +27,7 @@ Exceptions may be granted for bot accounts. There is a time and place for it, but Flashii is intended to be a footloose and fancy-free community. Moderator intervention will be used for petty shouting matches. -1. **You must be at least 13 years of age on the Gregorian calendar.** +1. **You must be at least 18 years of age on the Gregorian calendar.** When validating this a staff member will always ask this in a direct message, never in public channels. 1. **Link shorteners are not allowed.** diff --git a/public/images/landing-logo.png b/public/images/landing-logo.png new file mode 100644 index 00000000..134757e5 Binary files /dev/null and b/public/images/landing-logo.png differ diff --git a/src/Http/Handlers/HomeHandler.php b/src/Http/Handlers/HomeHandler.php index 50706bfc..7660ae2a 100644 --- a/src/Http/Handlers/HomeHandler.php +++ b/src/Http/Handlers/HomeHandler.php @@ -13,76 +13,130 @@ use Misuzu\Users\UserSession; final class HomeHandler extends Handler { public function index(HttpResponse $response, HttpRequest $request): void { - if(Config::get('social.embed_linked', Config::TYPE_BOOL)) { - $linkedData = [ + if(UserSession::hasCurrent()) + $this->home($response, $request); + else + $this->landing($response, $request); + } + + public function landing(HttpResponse $response, HttpRequest $request): void { + $linkedData = Config::get('social.embed_linked', Config::TYPE_BOOL) + ? [ 'name' => Config::get('site.name', Config::TYPE_STR, 'Misuzu'), 'url' => Config::get('site.url', Config::TYPE_STR), 'logo' => Config::get('site.ext_logo', Config::TYPE_STR), 'same_as' => Config::get('social.linked', Config::TYPE_ARR), - ]; + ] : null; + + + $featuredNews = NewsPost::all(new Pagination(3), true); + + $stats = DB::query( + 'SELECT' + . ' (SELECT COUNT(`user_id`) FROM `msz_users` WHERE `user_deleted` IS NULL) AS `count_users_all`,' + . ' (SELECT COUNT(`user_id`) FROM `msz_users` WHERE `user_active` >= DATE_SUB(NOW(), INTERVAL 5 MINUTE)) AS `count_users_online`,' + . ' (SELECT COUNT(`user_id`) FROM `msz_users` WHERE `user_active` >= DATE_SUB(NOW(), INTERVAL 24 HOUR)) AS `count_users_active`,' + . ' (SELECT COUNT(`comment_id`) FROM `msz_comments_posts` WHERE `comment_deleted` IS NULL) AS `count_comments`,' + . ' (SELECT COUNT(`topic_id`) FROM `msz_forum_topics` WHERE `topic_deleted` IS NULL) AS `count_forum_topics`,' + . ' (SELECT COUNT(`post_id`) FROM `msz_forum_posts` WHERE `post_deleted` IS NULL) AS `count_forum_posts`' + )->fetch(); + + $onlineUsers = DB::query( + 'SELECT u.`user_id`, u.`username`, COALESCE(u.`user_colour`, r.`role_colour`) AS `user_colour`' + . ' FROM `msz_users` AS u' + . ' LEFT JOIN `msz_roles` AS r' + . ' ON r.`role_id` = u.`display_role`' + . ' WHERE u.`user_active` >= DATE_SUB(NOW(), INTERVAL 5 MINUTE)' + . ' ORDER BY u.`user_active` DESC, RAND()' + . ' LIMIT 100' + )->fetchAll(); + + // TODO: don't hardcode forum ids + $featuredForums = Config::get('landing.forum_categories', Config::TYPE_ARR); + + $popularTopics = []; + $activeTopics = []; + + if(!empty($featuredForums)) { + $getPopularTopics = DB::prepare( + 'SELECT t.`topic_id`, c.`forum_id`, t.`topic_title`, c.`forum_icon`, t.`topic_count_views`' + . ', (SELECT COUNT(*) FROM `msz_forum_posts` AS p WHERE p.`topic_id` = t.`topic_id` AND `post_deleted` IS NULL) AS `topic_count_posts`' + . ' FROM `msz_forum_topics` AS t' + . ' LEFT JOIN `msz_forum_categories` AS c ON c.`forum_id` = t.`forum_id`' + . ' WHERE c.`forum_id` IN (' . implode(',', $featuredForums) . ') AND `topic_deleted` IS NULL AND `topic_locked` IS NULL' + . ' ORDER BY (SELECT COUNT(*) FROM `msz_forum_posts` AS p WHERE p.`topic_id` = t.`topic_id` AND `post_deleted` IS NULL AND `post_created` > NOW() - INTERVAL 3 MONTH) DESC' + )->stmt; + $getPopularTopics->execute(); + for($i = 0; $i < 10; ++$i) { + $topicInfo = $getPopularTopics->fetchObject(); + if(empty($topicInfo)) + break; + $popularTopics[] = $topicInfo; + } + + $getActiveTopics = DB::prepare( + 'SELECT t.`topic_id`, c.`forum_id`, t.`topic_title`, c.`forum_icon`, t.`topic_count_views`' + . ', (SELECT COUNT(*) FROM `msz_forum_posts` AS p WHERE p.`topic_id` = t.`topic_id` AND `post_deleted` IS NULL) AS `topic_count_posts`' + . ', (SELECT MAX(`post_id`) FROM `msz_forum_posts` AS p WHERE p.`topic_id` = t.`topic_id` AND `post_deleted` IS NULL) AS `latest_post_id`' + . ' FROM `msz_forum_topics` AS t' + . ' LEFT JOIN `msz_forum_categories` AS c ON c.`forum_id` = t.`forum_id`' + . ' WHERE c.`forum_id` IN (' . implode(',', $featuredForums) . ') AND `topic_deleted` IS NULL AND `topic_locked` IS NULL' + . ' ORDER BY `topic_bumped` DESC' + )->stmt; + $getActiveTopics->execute(); + for($i = 0; $i < 10; ++$i) { + $topicInfo = $getActiveTopics->fetchObject(); + if(empty($topicInfo)) + break; + $activeTopics[] = $topicInfo; + } } + $response->setTemplate('home.landing', [ + 'statistics' => $stats, + 'online_users' => $onlineUsers, + 'featured_news' => $featuredNews, + 'linked_data' => $linkedData, + 'forum_popular' => $popularTopics, + 'forum_active' => $activeTopics, + ]); + } + + public function home(HttpResponse $response, HttpRequest $request): void { $featuredNews = NewsPost::all(new Pagination(5), true); - $stats = DB::query(' - SELECT - ( - SELECT COUNT(`user_id`) - FROM `msz_users` - WHERE `user_deleted` IS NULL - ) AS `count_users_all`, - ( - SELECT COUNT(`user_id`) - FROM `msz_users` - WHERE `user_active` >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) - ) AS `count_users_online`, - ( - SELECT COUNT(`user_id`) - FROM `msz_users` - WHERE `user_active` >= DATE_SUB(NOW(), INTERVAL 24 HOUR) - ) AS `count_users_active`, - ( - SELECT COUNT(`comment_id`) - FROM `msz_comments_posts` - WHERE `comment_deleted` IS NULL - ) AS `count_comments`, - ( - SELECT COUNT(`topic_id`) - FROM `msz_forum_topics` - WHERE `topic_deleted` IS NULL - ) AS `count_forum_topics`, - ( - SELECT COUNT(`post_id`) - FROM `msz_forum_posts` - WHERE `post_deleted` IS NULL - ) AS `count_forum_posts` - ')->fetch(); + $stats = DB::query( + 'SELECT' + . ' (SELECT COUNT(`user_id`) FROM `msz_users` WHERE `user_deleted` IS NULL) AS `count_users_all`,' + . ' (SELECT COUNT(`user_id`) FROM `msz_users` WHERE `user_active` >= DATE_SUB(NOW(), INTERVAL 5 MINUTE)) AS `count_users_online`,' + . ' (SELECT COUNT(`user_id`) FROM `msz_users` WHERE `user_active` >= DATE_SUB(NOW(), INTERVAL 24 HOUR)) AS `count_users_active`,' + . ' (SELECT COUNT(`comment_id`) FROM `msz_comments_posts` WHERE `comment_deleted` IS NULL) AS `count_comments`,' + . ' (SELECT COUNT(`topic_id`) FROM `msz_forum_topics` WHERE `topic_deleted` IS NULL) AS `count_forum_topics`,' + . ' (SELECT COUNT(`post_id`) FROM `msz_forum_posts` WHERE `post_deleted` IS NULL) AS `count_forum_posts`' + )->fetch(); $changelog = ChangelogChange::all(new Pagination(10)); - $birthdays = !UserSession::hasCurrent() ? [] : User::byBirthdate(); - $latestUser = !empty($birthdays) ? null : User::byLatest(); + $birthdays = User::byBirthdate(); + $latestUser = !empty($birthdays) ? null : User::byLatest(); - $onlineUsers = DB::query(' - SELECT - u.`user_id`, u.`username`, - COALESCE(u.`user_colour`, r.`role_colour`) as `user_colour` - FROM `msz_users` as u - LEFT JOIN `msz_roles` as r - ON r.`role_id` = u.`display_role` - WHERE u.`user_active` >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) - ORDER BY u.`user_active` DESC - LIMIT 104 - ')->fetchAll(); + $onlineUsers = DB::query( + 'SELECT u.`user_id`, u.`username`, COALESCE(u.`user_colour`, r.`role_colour`) AS `user_colour`' + . ' FROM `msz_users` AS u' + . ' LEFT JOIN `msz_roles` AS r' + . ' ON r.`role_id` = u.`display_role`' + . ' WHERE u.`user_active` >= DATE_SUB(NOW(), INTERVAL 5 MINUTE)' + . ' ORDER BY u.`user_active` DESC, RAND()' + . ' LIMIT 104' + )->fetchAll(); - $response->setTemplate('home.landing', [ + $response->setTemplate('home.home', [ 'statistics' => $stats, 'latest_user' => $latestUser, 'online_users' => $onlineUsers, 'birthdays' => $birthdays, 'featured_changelog' => $changelog, 'featured_news' => $featuredNews, - 'linked_data' => $linkedData ?? null, ]); } } diff --git a/templates/500.html b/templates/500.html index 42abc0ec..9b8fc0bb 100644 --- a/templates/500.html +++ b/templates/500.html @@ -5,7 +5,7 @@