summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2024-04-05 12:58:11 -0400
committerFreya Murphy <freya@freyacat.org>2024-04-05 12:58:11 -0400
commitb6ae609ee3186148836f96260aa203202f230d6a (patch)
treedc47fe1cd7cb98e046f1a1ffeba6edb1f739485c
parenti did thing oh god large commit (diff)
downloadxssbook2-b6ae609ee3186148836f96260aa203202f230d6a.tar.gz
xssbook2-b6ae609ee3186148836f96260aa203202f230d6a.tar.bz2
xssbook2-b6ae609ee3186148836f96260aa203202f230d6a.zip
follow ppl
-rw-r--r--src/db/rest/follow/api_follow.sql15
-rw-r--r--src/db/rest/follow/api_follow_delete.sql32
-rw-r--r--src/db/rest/follow/api_follow_insert.sql46
-rw-r--r--src/db/rest/follow/api_follow_update.sql44
-rw-r--r--src/db/rest/rest.sql6
-rw-r--r--src/public/css/common.css17
-rw-r--r--src/public/css/post.css9
-rw-r--r--src/public/css/profile.css23
-rw-r--r--src/web/_model/apps/people.php6
-rw-r--r--src/web/_model/apps/profile.php28
-rw-r--r--src/web/_views/apps/home/main.php2
-rw-r--r--src/web/_views/apps/profile/main.php254
-rw-r--r--src/web/helper/lang.php4
-rw-r--r--src/web/lang/en_US/apps/profile.php9
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';