summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2024-03-30 16:36:54 -0400
committerFreya Murphy <freya@freyacat.org>2024-03-30 16:36:54 -0400
commit1f647374a8cdf3bc5c2d29ff8be277b027925c8c (patch)
tree9fdf42d250edb941de13ecd1aab9185ba2b30b00
parentrename views to _views (diff)
downloadxssbook2-1f647374a8cdf3bc5c2d29ff8be277b027925c8c.tar.gz
xssbook2-1f647374a8cdf3bc5c2d29ff8be277b027925c8c.tar.bz2
xssbook2-1f647374a8cdf3bc5c2d29ff8be277b027925c8c.zip
post comments, refactor post loading, hide load more btn
-rw-r--r--db/rest/comment/api_comment.sql15
-rw-r--r--db/rest/comment/api_comment_delete.sql31
-rw-r--r--db/rest/comment/api_comment_insert.sql56
-rw-r--r--db/rest/comment/api_comment_update.sql50
-rw-r--r--db/rest/post/api_post.sql15
-rw-r--r--db/rest/post/api_post_insert.sql2
-rw-r--r--db/rest/post/api_post_update.sql36
-rw-r--r--db/rest/rest.sql7
-rw-r--r--db/rest/user/api_user_insert.sql15
-rw-r--r--db/rest/user/api_user_update.sql150
-rw-r--r--db/rest/util/_api_trim.sql25
-rw-r--r--db/rest/util/_api_validate_text.sql2
-rw-r--r--web/_controller/_util/post.php129
-rw-r--r--web/_controller/apps/home.php70
-rw-r--r--web/_model/format.php2
-rw-r--r--web/_views/apps/home/main.php5
-rw-r--r--web/_views/template/comment.php7
-rw-r--r--web/_views/template/post.php31
-rw-r--r--web/_views/template/posts.php23
-rw-r--r--web/core/database.php12
-rw-r--r--web/public/css/post.css4
-rw-r--r--web/public/js/shared/post.js58
22 files changed, 647 insertions, 98 deletions
diff --git a/db/rest/comment/api_comment.sql b/db/rest/comment/api_comment.sql
new file mode 100644
index 0000000..e50ca2f
--- /dev/null
+++ b/db/rest/comment/api_comment.sql
@@ -0,0 +1,15 @@
+CREATE VIEW api.comment AS
+ SELECT
+ c.id,
+ c.user_id,
+ c.post_id,
+ c.content,
+ c.date
+ FROM
+ admin.comment c
+ ORDER BY id ASC;
+
+GRANT SELECT ON TABLE api.comment
+ TO rest_anon, rest_user;
+GRANT SELECT ON TABLE admin.comment
+ TO rest_anon, rest_user;
diff --git a/db/rest/comment/api_comment_delete.sql b/db/rest/comment/api_comment_delete.sql
new file mode 100644
index 0000000..d7db8a4
--- /dev/null
+++ b/db/rest/comment/api_comment_delete.sql
@@ -0,0 +1,31 @@
+CREATE FUNCTION _api.comment_delete()
+RETURNS TRIGGER
+LANGUAGE plpgsql VOLATILE
+AS $BODY$
+DECLARE
+ _user_id INTEGER;
+BEGIN
+ _user_id = _api.get_user_id();
+
+ IF OLD.user_id <> _user_id THEN
+ PERFORM _api.raise_deny();
+ END IF;
+
+ DELETE FROM admin.comment
+ WHERE user_id = _user_id
+ AND id = OLD.id;
+END
+$BODY$;
+
+GRANT EXECUTE ON FUNCTION _api.comment_delete()
+ TO rest_user;
+GRANT DELETE ON TABLE api.comment
+ TO rest_user;
+GRANT DELETE ON TABLE admin.comment
+ TO rest_user;
+
+CREATE TRIGGER api_comment_delete_trgr
+ INSTEAD OF DELETE
+ ON api.comment
+ FOR EACH ROW
+ EXECUTE PROCEDURE _api.comment_delete();
diff --git a/db/rest/comment/api_comment_insert.sql b/db/rest/comment/api_comment_insert.sql
new file mode 100644
index 0000000..878e194
--- /dev/null
+++ b/db/rest/comment/api_comment_insert.sql
@@ -0,0 +1,56 @@
+CREATE FUNCTION _api.comment_insert()
+RETURNS TRIGGER
+LANGUAGE plpgsql VOLATILE
+AS $BODY$
+DECLARE
+ _user_id INTEGER;
+BEGIN
+ _user_id = _api.get_user_id();
+
+ NEW.content := _api.trim(NEW.content);
+ PERFORM _api.validate_text(
+ _text => NEW.content,
+ _column => 'content',
+ _min => 1,
+ _max => 1024
+ );
+
+ PERFORM TRUE
+ FROM admin.post
+ WHERE id = NEW.post_id;
+
+ IF NOT FOUND THEN
+ PERFORM _api.raise(
+ _msg => 'api_null_post',
+ _err => 400
+ );
+ END IF;
+
+ INSERT INTO admin.comment (
+ user_id,
+ post_id,
+ content
+ ) VALUES (
+ _user_id,
+ NEW.post_id,
+ NEW.content
+ );
+
+ RETURN NEW;
+END
+$BODY$;
+
+GRANT EXECUTE ON FUNCTION _api.comment_insert()
+ TO rest_user;
+GRANT INSERT ON TABLE api.comment
+ TO rest_user;
+GRANT INSERT ON TABLE admin.comment
+ TO rest_user;
+GRANT UPDATE ON TABLE sys.comment_id_seq
+ TO rest_user;
+
+CREATE TRIGGER api_comment_insert_trgr
+ INSTEAD OF INSERT
+ ON api.comment
+ FOR EACH ROW
+ EXECUTE PROCEDURE _api.comment_insert();
diff --git a/db/rest/comment/api_comment_update.sql b/db/rest/comment/api_comment_update.sql
new file mode 100644
index 0000000..d6b4aca
--- /dev/null
+++ b/db/rest/comment/api_comment_update.sql
@@ -0,0 +1,50 @@
+CREATE FUNCTION _api.comment_update()
+RETURNS TRIGGER
+LANGUAGE plpgsql VOLATILE
+AS $BODY$
+DECLARE
+ _user_id INTEGER;
+ _changed BOOLEAN;
+BEGIN
+ _user_id = _api.get_user_id();
+ _changed = FALSE;
+
+ IF OLD.user_id <> _user_id THEN
+ PERFORM _api.raise_deny();
+ END IF;
+
+ NEW.content = COALESCE(NEW.content, OLD.content);
+ NEW.content := _api.trim(NEW.content);
+ PERFORM _api.validate_text(
+ _text => NEW.content,
+ _column => 'content',
+ _min => 1,
+ _max => 1024
+ );
+
+ IF NEW.content IS DISTINCT FROM OLD.content THEN
+ _changed = TRUE;
+ END IF;
+
+ IF _changed THEN
+ UPDATE admin.comment
+ SET content = NEW.content
+ WHERE id = OLD.id;
+ END IF;
+
+ RETURN NEW;
+END
+$BODY$;
+
+GRANT EXECUTE ON FUNCTION _api.comment_update()
+ TO rest_user;
+GRANT UPDATE ON TABLE api.comment
+ TO rest_user;
+GRANT UPDATE ON TABLE admin.comment
+ TO rest_user;
+
+CREATE TRIGGER api_comment_update_trgr
+ INSTEAD OF UPDATE
+ ON api.comment
+ FOR EACH ROW
+ EXECUTE PROCEDURE _api.comment_update();
diff --git a/db/rest/post/api_post.sql b/db/rest/post/api_post.sql
index b49289c..375f292 100644
--- a/db/rest/post/api_post.sql
+++ b/db/rest/post/api_post.sql
@@ -3,10 +3,21 @@ CREATE VIEW api.post AS
p.id,
p.user_id,
p.content,
- p.date
+ p.date,
+ COALESCE(c.cc, 0)
+ AS comment_count
FROM
admin.post p
- ORDER BY id DESC;
+ LEFT JOIN (
+ SELECT
+ COUNT(c.id) as cc,
+ c.post_id
+ FROM
+ admin.comment c
+ GROUP BY
+ c.post_id
+ ) c ON p.id = c.post_id
+ ORDER BY p.id DESC;
GRANT SELECT ON TABLE api.post
TO rest_anon, rest_user;
diff --git a/db/rest/post/api_post_insert.sql b/db/rest/post/api_post_insert.sql
index 02b9d8d..8b2eb48 100644
--- a/db/rest/post/api_post_insert.sql
+++ b/db/rest/post/api_post_insert.sql
@@ -7,6 +7,8 @@ DECLARE
BEGIN
_user_id = _api.get_user_id();
+ NEW.content := _api.trim(NEW.content);
+
PERFORM _api.validate_text(
_text => NEW.content,
_column => 'content',
diff --git a/db/rest/post/api_post_update.sql b/db/rest/post/api_post_update.sql
index 915d0cd..70230d0 100644
--- a/db/rest/post/api_post_update.sql
+++ b/db/rest/post/api_post_update.sql
@@ -3,13 +3,45 @@ RETURNS TRIGGER
LANGUAGE plpgsql VOLATILE
AS $BODY$
DECLARE
- _length INTEGER;
+ _user_id INTEGER;
+ _changed BOOLEAN;
BEGIN
+ _user_id = _api.get_user_id();
+ _changed = FALSE;
+
+ IF OLD.user_id <> _user_id THEN
+ PERFORM _api.raise_deny();
+ END IF;
+
+ NEW.content = COALESCE(NEW.content, OLD.content);
+ NEW.content := _api.trim(NEW.content);
+ PERFORM _api.validate_text(
+ _text => NEW.content,
+ _column => 'content',
+ _min => 1,
+ _max => 4096
+ );
+
+ IF NEW.content IS DISTINCT FROM OLD.content THEN
+ _changed = TRUE;
+ END IF;
+
+ IF _changed THEN
+ UPDATE admin.post
+ SET content = NEW.content
+ WHERE id = OLD.id;
+ END IF;
+
RETURN NEW;
END
$BODY$;
-GRANT EXECUTE ON FUNCTION _api.post_update() TO rest_user;
+GRANT EXECUTE ON FUNCTION _api.post_update()
+ TO rest_user;
+GRANT UPDATE ON TABLE api.post
+ TO rest_user;
+GRANT UPDATE ON TABLE admin.post
+ TO rest_user;
CREATE TRIGGER api_post_update_trgr
INSTEAD OF UPDATE
diff --git a/db/rest/rest.sql b/db/rest/rest.sql
index a286f9d..54f5118 100644
--- a/db/rest/rest.sql
+++ b/db/rest/rest.sql
@@ -14,6 +14,7 @@ GRANT USAGE ON SCHEMA api TO rest_anon, rest_user;
GRANT USAGE ON SCHEMA _api TO rest_anon, rest_user;
-- util
+\i /db/rest/util/_api_trim.sql;
\i /db/rest/util/_api_serve_media.sql;
\i /db/rest/util/_api_raise.sql;
\i /db/rest/util/_api_raise_null.sql;
@@ -34,6 +35,12 @@ GRANT USAGE ON SCHEMA _api TO rest_anon, rest_user;
\i /db/rest/post/api_post_update.sql;
\i /db/rest/post/api_post_delete.sql;
+-- comment
+\i /db/rest/comment/api_comment.sql;
+\i /db/rest/comment/api_comment_insert.sql;
+\i /db/rest/comment/api_comment_update.sql;
+\i /db/rest/comment/api_comment_delete.sql;
+
-- login
\i /db/rest/login/_api_sign_jwt.sql;
\i /db/rest/login/_api_verify_jwt.sql;
diff --git a/db/rest/user/api_user_insert.sql b/db/rest/user/api_user_insert.sql
index da3ae2d..2297ecd 100644
--- a/db/rest/user/api_user_insert.sql
+++ b/db/rest/user/api_user_insert.sql
@@ -5,6 +5,16 @@ AS $BODY$
DECLARE
_length INTEGER;
BEGIN
+
+ NEW.username := _api.trim(NEW.username);
+ NEW.password := _api.trim(NEW.password);
+ NEW.first_name := _api.trim(NEW.first_name);
+ NEW.last_name := _api.trim(NEW.last_name);
+ NEW.middle_name := _api.trim(NEW.middle_name);
+ NEW.email := _api.trim(NEW.email);
+ NEW.gender := _api.trim(NEW.gender);
+ NEW.profile_bio := _api.trim(NEW.profile_bio);
+
PERFORM _api.validate_text(
_text => NEW.username,
_column => 'username',
@@ -22,6 +32,7 @@ BEGIN
PERFORM _api.validate_text(
_text => NEW.password,
_column => 'password',
+ _min => 1,
_max => 256
);
@@ -82,8 +93,6 @@ BEGIN
email,
gender,
birth_date,
- profile_avatar,
- profile_banner,
profile_bio
) VALUES (
NEW.username,
@@ -94,8 +103,6 @@ BEGIN
NEW.email,
NEW.gender,
NEW.birth_date,
- NEW.profile_avatar,
- NEW.profile_banner,
NEW.profile_bio
);
diff --git a/db/rest/user/api_user_update.sql b/db/rest/user/api_user_update.sql
index c6e7f4f..28e4368 100644
--- a/db/rest/user/api_user_update.sql
+++ b/db/rest/user/api_user_update.sql
@@ -3,15 +3,161 @@ RETURNS TRIGGER
LANGUAGE plpgsql VOLATILE
AS $BODY$
DECLARE
- _length INTEGER;
+ _user_id INTEGER;
+ _changed BOOLEAN;
BEGIN
+ _user_id = _api.get_user_id();
+ _changed = FALSE;
+
+ IF OLD.id <> _user_id THEN
+ PERFORM _api.raise_deny();
+ END IF;
+
+ -- username
+ NEW.username = COALESCE(NEW.username, OLD.username);
+ NEW.username := _api.trim(NEW.username);
+ PERFORM _api.validate_text(
+ _text => NEW.username,
+ _column => 'username',
+ _min => 1,
+ _max => 24
+ );
+
+ IF NEW.username IS DISTINCT FROM OLD.username THEN
+ PERFORM TRUE FROM admin.user
+ WHERE username = NEW.username;
+ IF FOUND THEN
+ PERFORM _api.raise_unique('username');
+ END IF;
+ _changed = TRUE;
+ END IF;
+
+ -- password
+ SELECT password
+ INTO OLD.password
+ FROM admin.user
+ WHERE id = OLD.id;
+
+ NEW.password = COALESCE(NEW.password, OLD.password);
+ NEW.password := _api.trim(NEW.password);
+ PERFORM _api.validate_text(
+ _text => NEW.password,
+ _column => 'password',
+ _min => 1,
+ _max => 256
+ );
+
+ IF NEW.password IS DISTINCT FROM OLD.password THEN
+ _changed = TRUE;
+ END IF;
+
+ -- first name
+ NEW.first_name = COALESCE(NEW.first_name, OLD.first_name);
+ NEW.first_name := _api.trim(NEW.first_name);
+ PERFORM _api.validate_text(
+ _text => NEW.first_name,
+ _column => 'first_name',
+ _max => 256
+ );
+
+ IF NEW.first_name IS DISTINCT FROM OLD.first_name THEN
+ _changed = TRUE;
+ END IF;
+
+ -- last name
+ NEW.last_name = COALESCE(NEW.last_name, OLD.last_name);
+ NEW.last_name := _api.trim(NEW.last_name);
+ PERFORM _api.validate_text(
+ _text => NEW.last_name,
+ _column => 'last_name',
+ _max => 256
+ );
+
+ IF NEW.last_name IS DISTINCT FROM OLD.last_name THEN
+ _changed = TRUE;
+ END IF;
+
+ -- middle name
+ NEW.middle_name = COALESCE(NEW.middle_name, OLD.middle_name);
+ NEW.middle_name := _api.trim(NEW.middle_name);
+ PERFORM _api.validate_text(
+ _text => NEW.middle_name,
+ _column => 'middle_name',
+ _max => 256
+ );
+
+ IF NEW.middle_name IS DISTINCT FROM OLD.middle_name THEN
+ _changed = TRUE;
+ END IF;
+
+ -- email
+ NEW.email = COALESCE(NEW.email, OLD.email);
+ NEW.email := _api.trim(NEW.email);
+ PERFORM _api.validate_text(
+ _text => NEW.email,
+ _column => 'email',
+ _max => 256
+ );
+
+ IF NEW.email IS DISTINCT FROM OLD.email THEN
+ _changed = TRUE;
+ END IF;
+
+ -- gender
+ NEW.gender = COALESCE(NEW.gender, OLD.gender);
+ NEW.gender := _api.trim(NEW.gender);
+ PERFORM _api.validate_text(
+ _text => NEW.gender,
+ _column => 'gender',
+ _max => 256
+ );
+
+ IF NEW.gender IS DISTINCT FROM OLD.gender THEN
+ _changed = TRUE;
+ END IF;
+
+ -- birth date
+ NEW.birth_date = COALESCE(NEW.birth_date, OLD.birth_date);
+ IF NEW.birth_date IS DISTINCT FROM OLD.birth_date THEN
+ _changed = TRUE;
+ END IF;
+
+ -- profile bio
+ NEW.profile_bio = COALESCE(NEW.profile_bio, OLD.profile_bio);
+ NEW.profile_bio := _api.trim(NEW.profile_bio);
+ PERFORM _api.validate_text(
+ _text => NEW.profile_bio,
+ _column => 'profile_bio',
+ _max => 2048
+ );
+
+ IF NEW.profile_bio IS DISTINCT FROM OLD.profile_bio THEN
+ _changed = TRUE;
+ END IF;
+
+ IF _changed THEN
+ UPDATE admin.user SET
+ username = NEW.username,
+ password = NEW.password,
+ first_name = NEW.first_name,
+ last_name = NEW.last_name,
+ middle_name = NEW.middle_name,
+ email = NEW.email,
+ gender = NEW.gender,
+ birth_date = NEW.birth_date,
+ profile_bio = NEW.profile_bio
+ WHERE id = OLD.id;
+ END IF;
+
RETURN NEW;
END
$BODY$;
GRANT EXECUTE ON FUNCTION _api.user_update()
TO rest_user;
-GRANT DELETE ON TABLE api.user
+GRANT UPDATE ON TABLE api.user
+ TO rest_user;
+GRANT UPDATE ON TABLE admin.user
TO rest_user;
CREATE TRIGGER api_user_update_trgr
diff --git a/db/rest/util/_api_trim.sql b/db/rest/util/_api_trim.sql
new file mode 100644
index 0000000..c972282
--- /dev/null
+++ b/db/rest/util/_api_trim.sql
@@ -0,0 +1,25 @@
+CREATE FUNCTION _api.trim(
+ _text TEXT
+)
+RETURNS TEXT
+LANGUAGE plpgsql VOLATILE
+AS $BODY$
+DECLARE
+ _new TEXT;
+BEGIN
+
+ IF _text IS NULL THEN
+ RETURN NULL;
+ END IF;
+
+ _new = _text;
+ _new = TRIM(_new);
+ _new = REGEXP_REPLACE(_new, '^(?: |\r|\n)*', '');
+ _new = REGEXP_REPLACE(_new, '(?: |\r|\n)*$', '');
+
+ RETURN _new;
+END
+$BODY$;
+
+GRANT EXECUTE ON FUNCTION _api.trim(TEXT)
+ TO rest_anon, rest_user;
diff --git a/db/rest/util/_api_validate_text.sql b/db/rest/util/_api_validate_text.sql
index e4a6a7b..ff3a227 100644
--- a/db/rest/util/_api_validate_text.sql
+++ b/db/rest/util/_api_validate_text.sql
@@ -33,6 +33,7 @@ BEGIN
_detail => _column,
_hint => _min || ''
);
+ RETURN FALSE;
END IF;
IF _max IS NOT NULL AND _length > _max THEN
@@ -41,6 +42,7 @@ BEGIN
_detail => _column,
_hint => _max || ''
);
+ RETURN FALSE;
END IF;
RETURN TRUE;
diff --git a/web/_controller/_util/post.php b/web/_controller/_util/post.php
new file mode 100644
index 0000000..b128d67
--- /dev/null
+++ b/web/_controller/_util/post.php
@@ -0,0 +1,129 @@
+<?php /* Copyright (c) 2024 Freya Murphy */
+class Post_controller extends Controller {
+
+ // the request model
+ private $request_model;
+
+ // the caceh model
+ private $cache_model;
+
+ // page size
+ private $page_size;
+
+ function __construct($load) {
+ parent::__construct($load);
+ $this->request_model = $this->load->model('request');
+ $this->cache_model = $this->load->model('cache');
+ $this->page_size = 10;
+ }
+
+ public function index(): void {
+ $this->view('template/posts');
+ }
+
+ /**
+ * @return array<string,mixed>
+ */
+ public function posts(): array {
+ $page = $this->request_model->get_int('page', 0);
+ $max = $this->request_model->get_int('max');
+ $offset = $page * $this->page_size;
+
+ $user = $this->main->user();
+
+ $query = $this->db;
+
+ if ($user) {
+ $query = $query->select('p.*, l.post_id IS NOT NULL as liked');
+ } else {
+ $query = $query->select('p.*, FALSE as liked');
+ }
+
+ $query = $query->from('api.post p');
+
+ if ($user) {
+ $query = $query->join('admin.like l', 'p.id = l.post_id AND l.user_id')
+ ->eq($user['id']);
+ }
+
+ if ($max) {
+ $query = $query
+ ->where('id')->le($max);
+ }
+
+ $posts = $query
+ ->limit($this->page_size)
+ ->offset($offset)
+ ->rows();
+
+ $users = $this->cache_model->get_users($posts);
+ $max = 0;
+
+ foreach ($posts as $post) {
+ $max = max($max, $post['id']);
+ $data = array();
+ $data['page_size'] = $this->page_size;
+ $data['user'] = $users[$post['user_id']];
+ $data['post'] = $post;
+ $this->view('template/post', $data);
+ }
+
+ $pc = $this->db
+ ->select('COUNT(p.id) as pc')
+ ->from('api.post p')
+ ->row()['pc'];
+
+
+ return array(
+ 'loaded' => count($posts),
+ 'total' => $pc,
+ 'page_size' => $this->page_size,
+ 'max' => $max,
+ );
+ }
+
+ /**
+ * @return array<string,mixed>
+ */
+ public function comments(): array {
+ $page = $this->request_model->get_int('page', 0);
+ $max = $this->request_model->get_int('max');
+ $id = $this->request_model->get_int('id', 0);
+ $offset = $page * $this->page_size;
+
+ $query = $this->db
+ ->select('*')
+ ->from('api.comment')
+ ->where('post_id')
+ ->eq($id);
+
+ if ($max) {
+ $query = $query
+ ->and()
+ ->where('id')
+ ->le($max);
+ }
+
+ $comments = $query
+ ->limit($this->page_size)
+ ->offset($offset)
+ ->rows();
+
+ $users = $this->cache_model->get_users($comments);
+ $max = 0;
+
+ foreach ($comments as $comment) {
+ $max = max($max, $comment['id']);
+ $data = array();
+ $data['user'] = $users[$comment['user_id']];
+ $data['comment'] = $comment;
+ $this->view('template/comment', $data);
+ }
+
+ return array(
+ 'loaded' => count($comments),
+ 'page_size' => $this->page_size,
+ 'max' => $max,
+ );
+ }
+}
diff --git a/web/_controller/apps/home.php b/web/_controller/apps/home.php
index 25c8c4e..edf7e2b 100644
--- a/web/_controller/apps/home.php
+++ b/web/_controller/apps/home.php
@@ -4,17 +4,13 @@ class Home_controller extends Controller {
// the home model
private $home_model;
- // the request model
- private $request_model;
-
- // the caceh model
- private $cache_model;
+ // the post controller
+ protected $post_controller;
function __construct($load) {
parent::__construct($load);
$this->home_model = $this->load->model('apps/home');
- $this->request_model = $this->load->model('request');
- $this->cache_model = $this->load->model('cache');
+ $this->post_controller = $this->load->controller('_util/post');
}
public function index(): void {
@@ -24,66 +20,6 @@ class Home_controller extends Controller {
$this->view('apps/home/main', $data);
}
- public function posts(): void {
- $page = $this->request_model->get_int('page', 0);
- $page_size = 20;
- $offset = $page * $page_size;
-
- $user = $this->main->user();
-
- $query = $this->db;
-
- if ($user) {
- $query = $query->select('p.*, l.post_id IS NOT NULL as liked');
- } else {
- $query = $query->select('p.*, FALSE as liked');
- }
-
- $query = $query->from('api.post p');
-
- if ($user) {
- $query = $query->join('admin.like l', 'p.id = l.post_id')
- ->where('l.user_id')->eq($user['id'])
- ->or()->where('l.user_id IS NULL');
- }
-
- $posts = $query->limit($page_size)
- ->offset($offset)
- ->rows();
-
- $users = $this->cache_model->get_users($posts);
-
- foreach ($posts as $post) {
- $data = array();
- $data['user'] = $users[$post['user_id']];
- $data['post'] = $post;
- $this->view('template/post', $data);
- }
- }
-
- public function comments(): void {
- $page = $this->request_model->get_int('page', 0);
- $id = $this->request_model->get_int('id');
- $page_size = 20;
- $offset = $page * $page_size;
-
- $comments = $this->db
- ->select('*')
- ->from('admin.comment')
- ->limit($page_size)
- ->offset($offset)
- ->rows();
-
- $users = $this->cache_model->get_users($comments);
-
- foreach ($comments as $comment) {
- $data = array();
- $data['user'] = $users[$comment['user_id']];
- $data['comment'] = $comment;
- $this->view('template/comment', $data);
- }
- }
-
}
?>
diff --git a/web/_model/format.php b/web/_model/format.php
index d8c7480..52b51be 100644
--- a/web/_model/format.php
+++ b/web/_model/format.php
@@ -1,5 +1,5 @@
<?php /* Copyright (c) 2024 Freya Murphy */
-class Format_model extends Modal {
+class Format_model extends Model {
function __construct($load) {
parent::__construct($load);
diff --git a/web/_views/apps/home/main.php b/web/_views/apps/home/main.php
index b1c1efc..5cfdf8c 100644
--- a/web/_views/apps/home/main.php
+++ b/web/_views/apps/home/main.php
@@ -23,8 +23,5 @@
</script>
</div>
<?php endif; ?>
- <div id="post-container">
- <?=$this->posts()?>
- </div>
- <?=ilang('action_load_posts', id: 'action-load-posts', class: 'btn btn-line')?>
+ <?php $this->post_controller->index(); ?>
</div>
diff --git a/web/_views/template/comment.php b/web/_views/template/comment.php
index 943f232..20032b2 100644
--- a/web/_views/template/comment.php
+++ b/web/_views/template/comment.php
@@ -1,11 +1,14 @@
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
<?php /* vi: syntax=php */ ?>
+<?php
+ $format_model = $this->load->model('format');
+?>
<div class="comment row mt">
<?php $this->view('template/pfp', array('user' => $user))?>
<div class="ml col sub-card">
<div class="row">
- <strong><?=$this->main->display_name($user)?></strong>
- <span class="dim ml"><?=$this->main->display_date($comment['date'])?></span>
+ <strong><?=$format_model->name($user)?></strong>
+ <span class="dim ml"><?=$format_model->date($comment['date'])?></span>
</div>
<?=$comment['content']?>
</div>
diff --git a/web/_views/template/post.php b/web/_views/template/post.php
index 40a3c1d..4fbf485 100644
--- a/web/_views/template/post.php
+++ b/web/_views/template/post.php
@@ -29,25 +29,40 @@
<div class="col comments">
<?php
$_GET['id'] = $post['id'];
- $this->comments();
- ilang('action_load_comments',
- class: 'action-load-comments btn btn-line mt',
- attrs: array('postId' => $post['id'])
- );
+ $cdata = $this->comments();
+
+ $loaded = $cdata['loaded'];
+ $max = $cdata['max'];
+ $page_size = $cdata['page_size'];
+ $total = $post['comment_count'];
+
+ if ($loaded >= $page_size && $page_size < $total) {
+ ilang('action_load_comments',
+ class: 'action-load-comments btn btn-line mt',
+ attrs: array(
+ 'postId' => $post['id'],
+ 'loaded' => $loaded,
+ 'pageSize' => $page_size,
+ 'commentCount' => $total,
+ 'commentMax' => $max,
+ )
+ );
+ }
+
?>
</div>
<?php if ($self): ?>
<div class="row grow mt">
<?php $this->view('template/pfp', array('user' => $user))?>
- <form class="ml">
+ <form class="ml action-new-comment-form">
<input
type="hidden"
name="id"
value="<?=$post['id']?>"
>
<input
- id="new-comment-<?=$post['id']?>"
- class="input"
+ class="action-new-comment input"
+ postId="<?=$post['id']?>"
autocomplete="off"
type="text"
name="text"
diff --git a/web/_views/template/posts.php b/web/_views/template/posts.php
new file mode 100644
index 0000000..f57a25f
--- /dev/null
+++ b/web/_views/template/posts.php
@@ -0,0 +1,23 @@
+<div id="post-container">
+<?php
+ $pdata = $this->posts();
+
+ $loaded = $pdata['loaded'];
+ $page_size = $pdata['page_size'];
+ $total = $pdata['total'];
+ $max = $pdata['max'];
+
+ if ($loaded >= $page_size && $page_size < $total) {
+ ilang('action_load_posts',
+ id: 'action-load-posts',
+ class: 'btn btn-line mb',
+ attrs: array(
+ 'loaded' => $loaded,
+ 'pageSize' => $page_size,
+ 'postCount' => $total,
+ 'postMax' => $max,
+ )
+ );
+ }
+?>
+</div>
diff --git a/web/core/database.php b/web/core/database.php
index 4b44e86..079b0de 100644
--- a/web/core/database.php
+++ b/web/core/database.php
@@ -69,6 +69,18 @@ class DatabaseQuery {
return $this;
}
+ public function lt($item) {
+ $this->query .= "< ?\n";
+ array_push($this->param, $item);
+ return $this;
+ }
+
+ public function le($item) {
+ $this->query .= "<= ?\n";
+ array_push($this->param, $item);
+ return $this;
+ }
+
public function where_in($column, $array) {
if (!$this->where) {
$this->where = TRUE;
diff --git a/web/public/css/post.css b/web/public/css/post.css
index 6ad14ba..4030da3 100644
--- a/web/public/css/post.css
+++ b/web/public/css/post.css
@@ -10,3 +10,7 @@
.action-load-comments {
margin-left: 4rem;
}
+
+#action-load-posts {
+ justify-content: center;
+}
diff --git a/web/public/js/shared/post.js b/web/public/js/shared/post.js
index afbeaf0..f22dd99 100644
--- a/web/public/js/shared/post.js
+++ b/web/public/js/shared/post.js
@@ -5,14 +5,28 @@ observe('#main-content', '.action-load-comments', function(me) {
page = '1';
}
let newPage = Number(page) + 1;
- let id = me.attr('postId');
me.attr('page', newPage + '');
- let url = '/home/comments?page=' + page + '&id=' + id;
+
+ let postId = me.attr('postId');
+ let loaded = Number(me.attr('loaded'));
+ let pageSize = Number(me.attr('pageSize'));
+ let commmentCount = Number(me.attr('commentCount'));
+ let commentMax = Number(me.attr('commentMax'));
+
+ let url = '/_util/post/comments?page=' + page + '&id=' + postId + '&max' + commentMax;
$.get(url, function (data) {
if (data === '') {
me.remove();
+ return;
+ }
+
+ $(data).insertBefore(me);
+
+ loaded += pageSize;
+ if (loaded >= commmentCount) {
+ me.remove();
} else {
- $(me).prepend(data);
+ me.attr('loaded', loaded + '');
}
});
});
@@ -26,13 +40,45 @@ observe('#main-content', '#action-load-posts', function(me) {
}
let newPage = Number(page) + 1;
me.attr('page', newPage + '');
- let url = '/home/posts?page=' + page;
- $.get(url, function (data) {
+
+ let loaded = Number(me.attr('loaded'));
+ let pageSize = Number(me.attr('pageSize'));
+ let postCount = Number(me.attr('postCount'));
+ let postMax = Number(me.attr('postMax'));
+
+ let url = '/_util/post/posts?page=' + page + '&max=' + postMax;
+ $.get(url, function (data) {
if (data === '') {
me.remove();
+ return;
+ }
+
+ $(data).insertBefore(me);
+
+ loaded += pageSize;
+ if (loaded >= postCount) {
+ me.remove();
} else {
- $('#post-container').append(data);
+ me.attr('loaded', loaded + '');
}
});
});
});
+
+observe('#main-content', '.action-new-comment-form', function(me) {
+ me.on('submit', function(e) {
+ e.preventDefault();
+ let input = me.find('.action-new-comment');
+ let content = input.val();
+ let post_id = input.attr('postId');
+ $.ajax({
+ url: '/api/comment',
+ method: 'POST',
+ data: JSON.stringify({ post_id, content }),
+ success: function(_data) {
+ window.location.reload();
+ },
+ error: errorToast
+ });
+ });
+});