diff options
author | Freya Murphy <freya@freyacat.org> | 2024-04-05 12:58:11 -0400 |
---|---|---|
committer | Freya Murphy <freya@freyacat.org> | 2024-04-05 12:58:11 -0400 |
commit | b6ae609ee3186148836f96260aa203202f230d6a (patch) | |
tree | dc47fe1cd7cb98e046f1a1ffeba6edb1f739485c /src | |
parent | i did thing oh god large commit (diff) | |
download | xssbook2-b6ae609ee3186148836f96260aa203202f230d6a.tar.gz xssbook2-b6ae609ee3186148836f96260aa203202f230d6a.tar.bz2 xssbook2-b6ae609ee3186148836f96260aa203202f230d6a.zip |
follow ppl
Diffstat (limited to 'src')
-rw-r--r-- | src/db/rest/follow/api_follow.sql | 15 | ||||
-rw-r--r-- | src/db/rest/follow/api_follow_delete.sql | 32 | ||||
-rw-r--r-- | src/db/rest/follow/api_follow_insert.sql | 46 | ||||
-rw-r--r-- | src/db/rest/follow/api_follow_update.sql | 44 | ||||
-rw-r--r-- | src/db/rest/rest.sql | 6 | ||||
-rw-r--r-- | src/public/css/common.css | 17 | ||||
-rw-r--r-- | src/public/css/post.css | 9 | ||||
-rw-r--r-- | src/public/css/profile.css | 23 | ||||
-rw-r--r-- | src/web/_model/apps/people.php | 6 | ||||
-rw-r--r-- | src/web/_model/apps/profile.php | 28 | ||||
-rw-r--r-- | src/web/_views/apps/home/main.php | 2 | ||||
-rw-r--r-- | src/web/_views/apps/profile/main.php | 254 | ||||
-rw-r--r-- | src/web/helper/lang.php | 4 | ||||
-rw-r--r-- | src/web/lang/en_US/apps/profile.php | 9 |
14 files changed, 409 insertions, 86 deletions
diff --git a/src/db/rest/follow/api_follow.sql b/src/db/rest/follow/api_follow.sql new file mode 100644 index 0000000..c0a07e5 --- /dev/null +++ b/src/db/rest/follow/api_follow.sql @@ -0,0 +1,15 @@ +CREATE VIEW api.follow AS + SELECT + f.id, + f.follower_id, + f.followee_id, + f.value, + f.created, + f.modified + FROM + admin.follow f; + +GRANT SELECT ON TABLE api.follow + TO rest_anon, rest_user; +GRANT SELECT ON TABLE admin.follow + TO rest_anon, rest_user; diff --git a/src/db/rest/follow/api_follow_delete.sql b/src/db/rest/follow/api_follow_delete.sql new file mode 100644 index 0000000..46454cf --- /dev/null +++ b/src/db/rest/follow/api_follow_delete.sql @@ -0,0 +1,32 @@ +CREATE FUNCTION _api.follow_delete() +RETURNS TRIGGER +LANGUAGE plpgsql VOLATILE +AS $BODY$ +DECLARE + _user_id INTEGER; +BEGIN + _user_id = _api.get_user_id(); + + IF OLD.follower_id <> _user_id THEN + PERFORM _api.raise_deny(); + END IF; + + UPDATE admin.follow SET + value = FALSE, + modified = clock_timestamp() + WHERE id = OLD.id; +END +$BODY$; + +GRANT EXECUTE ON FUNCTION _api.follow_delete() + TO rest_user; +GRANT DELETE ON TABLE api.follow + TO rest_user; +GRANT UPDATE ON TABLE admin.follow + TO rest_user; + +CREATE TRIGGER api_follow_delete_trgr + INSTEAD OF DELETE + ON api.follow + FOR EACH ROW + EXECUTE PROCEDURE _api.follow_delete(); diff --git a/src/db/rest/follow/api_follow_insert.sql b/src/db/rest/follow/api_follow_insert.sql new file mode 100644 index 0000000..6351855 --- /dev/null +++ b/src/db/rest/follow/api_follow_insert.sql @@ -0,0 +1,46 @@ +CREATE FUNCTION _api.follow_insert() +RETURNS TRIGGER +LANGUAGE plpgsql VOLATILE +AS $BODY$ +DECLARE + _user_id INTEGER; +BEGIN + _user_id = _api.get_user_id(); + + IF NEW.followee_id IS NULL THEN + -- for now + PERFORM _api.raise_deny(); + END IF; + + NEW.value := COALESCE(NEW.value, TRUE); + + INSERT INTO admin.follow ( + follower_id, + followee_id, + value + ) VALUES ( + _user_id, + NEW.followee_id, + NEW.value + ) + RETURNING id + INTO NEW.id; + + RETURN NEW; +END +$BODY$; + +GRANT EXECUTE ON FUNCTION _api.follow_insert() + TO rest_user; +GRANT INSERT ON TABLE api.follow + TO rest_user; +GRANT INSERT ON TABLE admin.follow + TO rest_user; +GRANT UPDATE ON TABLE sys.follow_id_seq + TO rest_user; + +CREATE TRIGGER api_follow_insert_trgr + INSTEAD OF INSERT + ON api.follow + FOR EACH ROW + EXECUTE PROCEDURE _api.follow_insert(); diff --git a/src/db/rest/follow/api_follow_update.sql b/src/db/rest/follow/api_follow_update.sql new file mode 100644 index 0000000..2164829 --- /dev/null +++ b/src/db/rest/follow/api_follow_update.sql @@ -0,0 +1,44 @@ +CREATE FUNCTION _api.follow_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.follower_id <> _user_id THEN + PERFORM _api.raise_deny(); + END IF; + + NEW.value = COALESCE(NEW.value, OLD.value); + + IF NEW.value IS DISTINCT FROM OLD.value THEN + _changed = TRUE; + END IF; + + IF _changed THEN + UPDATE admin.follow SET + value = NEW.value, + modified = clock_timestamp() + WHERE id = OLD.id; + END IF; + + RETURN NEW; +END +$BODY$; + +GRANT EXECUTE ON FUNCTION _api.follow_update() + TO rest_user; +GRANT UPDATE ON TABLE api.follow + TO rest_user; +GRANT UPDATE ON TABLE admin.follow + TO rest_user; + +CREATE TRIGGER api_follow_update_trgr + INSTEAD OF UPDATE + ON api.follow + FOR EACH ROW + EXECUTE PROCEDURE _api.follow_update(); diff --git a/src/db/rest/rest.sql b/src/db/rest/rest.sql index 783866a..3db77a2 100644 --- a/src/db/rest/rest.sql +++ b/src/db/rest/rest.sql @@ -45,6 +45,12 @@ GRANT USAGE ON SCHEMA _api TO rest_anon, rest_user; \i /db/rest/like/api_like_update.sql; \i /db/rest/like/api_like_delete.sql; +-- follow +\i /db/rest/follow/api_follow.sql; +\i /db/rest/follow/api_follow_insert.sql; +\i /db/rest/follow/api_follow_update.sql; +\i /db/rest/follow/api_follow_delete.sql; + -- media \i /db/rest/media/_api_serve_user_media.sql; \i /db/rest/media/_api_serve_system_media.sql; diff --git a/src/public/css/common.css b/src/public/css/common.css index 281df6b..3e6b6b6 100644 --- a/src/public/css/common.css +++ b/src/public/css/common.css @@ -155,6 +155,15 @@ a, button { background-color: var(--surface2); } +.btn-alt.btn-blue { + background-color: var(--blue); + color: var(--white); +} + +.btn-alt.btn-blue:hover { + background-color: var(--blue-alt); +} + .btn-wide { width: auto; flex-grow: 1; @@ -604,3 +613,11 @@ input[type=radio] { width: fit-content; outline: none !important; } + +.container { + padding: 1rem; +} + +.grow { + flex-grow: 1; +} diff --git a/src/public/css/post.css b/src/public/css/post.css index 2b6a4b1..47106a9 100644 --- a/src/public/css/post.css +++ b/src/public/css/post.css @@ -10,19 +10,14 @@ .post, #new-post { margin-bottom: 1rem; - width: 40rem; + max-width: 40rem; + width: 100%; } .post { padding-bottom: 0; } -@media(max-width: 40rem) { - .post, #new-post { - width: 100%; - } -} - .post .likes { display: block; padding-top: .25rem; diff --git a/src/public/css/profile.css b/src/public/css/profile.css index 71cbbfa..a6f2ef4 100644 --- a/src/public/css/profile.css +++ b/src/public/css/profile.css @@ -10,7 +10,6 @@ flex-direction: row; justify-content: center; background-color: var(--surface0); - margin-bottom: 1rem; border-bottom: 1px solid var(--surface1); } @@ -85,10 +84,9 @@ .tab { max-width: 80rem; width: 100%; + height: 100%; margin-left: auto; margin-right: auto; - padding: 0 1rem; - margin-bottom: 1rem; } #post-container { @@ -106,3 +104,22 @@ td:nth-child(1) { padding-right: 2rem; } + +#about-tab { + width: auto; +} + +#follow-container { + margin-left: auto; + margin-right: 2rem; +} + +#follow-container > a { + width: 10rem; + padding: .5rem; +} + +#follow-container > a > span { + width: 100%; + text-align: center; +} diff --git a/src/web/_model/apps/people.php b/src/web/_model/apps/people.php index ade59d3..4287094 100644 --- a/src/web/_model/apps/people.php +++ b/src/web/_model/apps/people.php @@ -24,13 +24,15 @@ class People_model extends Model { case 'follower': { $query = $query ->join('admin.follow f', 'f.follower_id = u.id AND f.followee_id', 'INNER') - ->eq($filter_uid); + ->eq($filter_uid) + ->where('f.value = TRUE'); } break; case 'followee': { $query = $query ->join('admin.follow f', 'f.followee_id = u.id AND f.follower_id', 'INNER') - ->eq($filter_uid); + ->eq($filter_uid) + ->where('f.value = TRUE'); } break; } } diff --git a/src/web/_model/apps/profile.php b/src/web/_model/apps/profile.php index 592fbcb..97b0150 100644 --- a/src/web/_model/apps/profile.php +++ b/src/web/_model/apps/profile.php @@ -29,8 +29,36 @@ class Profile_model extends Model { return NULL; } + $following = FALSE; + $followed = FALSE; + $follow_id = NULL; + + if ($this->main->session) { + $sid = $this->main->user()['id']; + $res = $this->db->select('f.value, f.id') + ->from('admin.follow f') + ->where('f.follower_id') + ->eq($sid) + ->where('f.followee_id') + ->eq($uid) + ->row(); + $following = $res ? $res['value'] : FALSE; + $follow_id = $res ? $res['id'] : NULL; + $res = $this->db->select('f.value') + ->from('admin.follow f') + ->where('f.follower_id') + ->eq($uid) + ->where('f.followee_id') + ->eq($sid) + ->row(); + $followed = $res ? $res['value'] : FALSE; + } + $data = parent::get_data(); $data['user'] = $user; + $data['following'] = $following; + $data['followed'] = $followed; + $data['follow_id'] = $follow_id; $data['title'] = lang('title', sub: [$user['first_name']]); return $data; } diff --git a/src/web/_views/apps/home/main.php b/src/web/_views/apps/home/main.php index 735e3d8..60c3eb9 100644 --- a/src/web/_views/apps/home/main.php +++ b/src/web/_views/apps/home/main.php @@ -1,6 +1,6 @@ <?php /* Copyright (c) 2024 Freya Murphy */ ?> <?php /* vi: syntax=php */ ?> -<div id="main-content"> +<div id="main-content" class="container"> <?php if ($self): ?> <div id="new-post" class="card"> <div class="row grow"> diff --git a/src/web/_views/apps/profile/main.php b/src/web/_views/apps/profile/main.php index e3d65b5..6671d87 100644 --- a/src/web/_views/apps/profile/main.php +++ b/src/web/_views/apps/profile/main.php @@ -8,9 +8,116 @@ <div class="pfp-wrapper"> <?=pfp($user)?> </div> - <div class="col content"> - <strong class="name"><?=$this->format_model->name($user)?></strong> - <span class="dim"><?=$user['follower_count'] . ' ' . lang('followers')?></span> + <div class="col content grow"> + <div class="row grow"> + <div class="col"> + <strong class="name"><?=$this->format_model->name($user)?></strong> + <span class="dim"><?=$user['follower_count'] . ' ' . lang('followers')?></span> + </div> + <?php if (!isset($self) || $self['id'] != $user['id']): ?> + <div id="follow-container"> + <?=ilang( + 'action_follow', + id: 'action-follow-follow', + class: 'btn btn-alt', + style: (!$following && !$followed) ? '' : 'display: none', + sub: [$user['first_name']] + )?> + <?=ilang( + 'action_follow_back', + id: 'action-follow-follow-back', + class: 'btn btn-alt', + style: (!$following && $followed) ? '' : 'display: none', + sub: [$user['first_name']] + )?> + <?=ilang( + 'action_following', + id: 'action-follow-following', + class: 'btn btn-alt btn-blue', + style: ($following && !$followed) ? '' : 'display: none', + sub: [$user['first_name']] + )?> + <?=ilang( + 'action_friends', + id: 'action-follow-friends', + class: 'btn btn-alt btn-blue', + style: ($following && $followed) ? '' : 'display: none', + sub: [$user['first_name']] + )?> + </div> + <script> + let following = <?=json_encode($following)?>; + let followed = <?=json_encode($followed)?>; + let followId = <?=json_encode($follow_id)?>; + let followee_id = <?=json_encode($user['id'])?>; + let btns = {}; + + const disableBtn = (btn) => { + btn.css('display', 'none'); + }; + + const enableBtn = (btn) => { + btn.css('display', ''); + }; + + const updateFollow = () => { + for (let btn of Object.values(btns)) { + disableBtn(btn); + } + if (!following && !followed) { + enableBtn(btns['follow']); + } else if (!following && followed) { + enableBtn(btns['follow-back']); + } else if (following && !followed) { + enableBtn(btns['following']); + } else if (following && followed) { + enableBtn(btns['friends']); + } + } + + const onPatchFollow = (data) => { + following = data[0].value; + updateFollow(); + } + + const onPostFollow = (data) => { + followId = data[0].id; + following = true; + updateFollow(); + } + + const onClickFollow = () => { + if (followId) { + $.ajax({ + url: '/api/follow?id=eq.' + followId, + method: 'PATCH', + data: JSON.stringify({ followee_id, value: !following }), + success: onPatchFollow + }); + } else { + $.ajax({ + url: '/api/follow', + method: 'POST', + data: JSON.stringify({ followee_id, value: !following }), + success: onPostFollow, + }); + } + } + + const loadBtn = (name) => { + let btn = $('#action-follow-' + name); + btn.on('click', onClickFollow); + + btns[name] = btn; + }; + + loadBtn('follow'); + loadBtn('follow-back'); + loadBtn('following'); + loadBtn('friends'); + </script> + <?php endif; ?> + </div> <?php if(strlen($user['profile_bio']) > 0): ?> <br> <strong><?=lang('bio')?></strong> @@ -43,78 +150,79 @@ </div> </div> </div> - <div id="tab-posts" class="tab"> + <div id="tab-container" class="container"> + <div id="tab-posts" class="tab"> + <?php + $_GET['user_id'] = $user['id']; + $this->post_controller->index(); + ?> + </div> + <div id="tab-about" class="tab card"> + <h1><?=lang('about_general')?></h1> + <table> + <tr> + <td><strong><?=lang('about_general_username')?></strong></td> + <td><?=$user['username']?></td> + </tr> + <tr> + <td><strong><?=lang('about_general_full_name')?></strong></td> + <td><?=$user['first_name'] . ' ' . $user['last_name']?></td> + </tr> + <tr> + <td><strong><?=lang('about_general_email')?></strong></td> + <td><?=$user['email']?></td> + </tr> + <tr> + <td><strong><?=lang('about_general_gender')?></strong></td> + <td><?=$user['gender']?></td> + </tr> + <tr> + <td><strong><?=lang('about_general_birth_date')?></strong></td> + <td><?=$user['birth_date']?></td> + </tr> + </table> + <h1><?=lang('about_stats')?></h1> + <table> + <tr> + <td><strong><?=lang('about_stats_posts')?></strong></td> + <td><?=$user['post_count']?></td> + </tr> + <tr> + <td><strong><?=lang('about_stats_like')?></strong></td> + <td><?=$user['like_count']?></td> + </tr> + <tr> + <td><strong><?=lang('about_stats_comments')?></strong></td> + <td><?=$user['comment_count']?></td> + </tr> + <tr> + <td><strong><?=lang('about_stats_following')?></strong></td> + <td><?=$user['followed_count']?></td> + </tr> + <tr> + <td><strong><?=lang('about_stats_joined')?></strong></td> + <td><?=$user['created']?></td> + </tr> + <tr> + <td><strong><?=lang('about_stats_seen')?></strong></td> + <td><?=$user['seen']?></td> + </tr> + </table> + </div> + <div id="tab-followers" class="tab"> <?php - $_GET['user_id'] = $user['id']; - $this->post_controller->index(); + $_GET['filter'] = 'follower'; + $_GET['uid'] = $user['id']; + $this->people_controller->content(); ?> - </div> - <div id="tab-about" class="tab"> - <h1><?=lang('about_general')?></h1> - <table> - <tr> - <td><strong><?=lang('about_general_username')?></strong></td> - <td><?=$user['username']?></td> - </tr> - <tr> - <td><strong><?=lang('about_general_full_name')?></strong></td> - <td><?=$user['first_name'] . ' ' . $user['last_name']?></td> - </tr> - <tr> - <td><strong><?=lang('about_general_email')?></strong></td> - <td><?=$user['email']?></td> - </tr> - <tr> - <td><strong><?=lang('about_general_gender')?></strong></td> - <td><?=$user['gender']?></td> - </tr> - <tr> - <td><strong><?=lang('about_general_birth_date')?></strong></td> - <td><?=$user['birth_date']?></td> - </tr> - </table> - <h1><?=lang('about_stats')?></h1> - <table> - <tr> - <td><strong><?=lang('about_stats_posts')?></strong></td> - <td><?=$user['post_count']?></td> - </tr> - <tr> - <td><strong><?=lang('about_stats_like')?></strong></td> - <td><?=$user['like_count']?></td> - </tr> - <tr> - <td><strong><?=lang('about_stats_comments')?></strong></td> - <td><?=$user['comment_count']?></td> - </tr> - <tr> - <td><strong><?=lang('about_stats_following')?></strong></td> - <td><?=$user['followed_count']?></td> - </tr> - <tr> - <td><strong><?=lang('about_stats_joined')?></strong></td> - <td><?=$user['created']?></td> - </tr> - <tr> - <td><strong><?=lang('about_stats_seen')?></strong></td> - <td><?=$user['seen']?></td> - </tr> - </table> - </div> - <div id="tab-followers" class="tab"> - <?php - $_GET['filter'] = 'follower'; - $_GET['uid'] = $user['id']; - $this->people_controller->content(); - ?> - </div> - <div id="tab-following" class="tab"> - <?php - $_GET['filter'] = 'followee'; - $_GET['uid'] = $user['id']; - $this->people_controller->content(); - ?> - </div> + </div> + <div id="tab-following" class="tab"> + <?php + $_GET['filter'] = 'followee'; + $_GET['uid'] = $user['id']; + $this->people_controller->content(); + ?> + </div> </div> <script> let tabs = {}; diff --git a/src/web/helper/lang.php b/src/web/helper/lang.php index 9c694a1..b0638a6 100644 --- a/src/web/helper/lang.php +++ b/src/web/helper/lang.php @@ -18,6 +18,7 @@ function lang($key, $default = NULL, $sub = NULL) { function ilang($key, $class = NULL, + $style = NULL, $id = NULL, $href = NULL, $click = NULL, @@ -42,6 +43,9 @@ function ilang($key, if ($class) { echo 'class="' . $class . '" '; } + if ($style) { + echo 'style="' . $style . '" '; + } if ($id) { echo 'id="' . $id . '" '; } diff --git a/src/web/lang/en_US/apps/profile.php b/src/web/lang/en_US/apps/profile.php index 43a3247..bb91e02 100644 --- a/src/web/lang/en_US/apps/profile.php +++ b/src/web/lang/en_US/apps/profile.php @@ -16,6 +16,15 @@ $lang['action_followers_tip'] = 'View %s\'s followres'; $lang['action_following_text'] = 'Following'; $lang['action_following_tip'] = 'View who %s is following'; +$lang['action_follow_text'] = 'Follow'; +$lang['action_follow_tip'] = 'Follow %s'; +$lang['action_follow_back_text'] = 'Follow Back'; +$lang['action_follow_back_tip'] = 'Follow %s'; +$lang['action_following_text'] = 'Followed'; +$lang['action_following_tip'] = 'Unfollow %s'; +$lang['action_friends_text'] = 'Friends'; +$lang['action_friends_tip'] = 'Unfollow %s'; + $lang['action_load_posts_text'] = 'Load more posts'; $lang['action_load_posts_tip'] = 'Load more posts'; |