follow ppl
This commit is contained in:
parent
530bbf0587
commit
b6ae609ee3
14 changed files with 409 additions and 86 deletions
15
src/db/rest/follow/api_follow.sql
Normal file
15
src/db/rest/follow/api_follow.sql
Normal file
|
@ -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;
|
32
src/db/rest/follow/api_follow_delete.sql
Normal file
32
src/db/rest/follow/api_follow_delete.sql
Normal file
|
@ -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();
|
46
src/db/rest/follow/api_follow_insert.sql
Normal file
46
src/db/rest/follow/api_follow_insert.sql
Normal file
|
@ -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();
|
44
src/db/rest/follow/api_follow_update.sql
Normal file
44
src/db/rest/follow/api_follow_update.sql
Normal file
|
@ -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();
|
|
@ -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_update.sql;
|
||||||
\i /db/rest/like/api_like_delete.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
|
-- media
|
||||||
\i /db/rest/media/_api_serve_user_media.sql;
|
\i /db/rest/media/_api_serve_user_media.sql;
|
||||||
\i /db/rest/media/_api_serve_system_media.sql;
|
\i /db/rest/media/_api_serve_system_media.sql;
|
||||||
|
|
|
@ -155,6 +155,15 @@ a, button {
|
||||||
background-color: var(--surface2);
|
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 {
|
.btn-wide {
|
||||||
width: auto;
|
width: auto;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
@ -604,3 +613,11 @@ input[type=radio] {
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
outline: none !important;
|
outline: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grow {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
|
@ -10,19 +10,14 @@
|
||||||
|
|
||||||
.post, #new-post {
|
.post, #new-post {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
width: 40rem;
|
max-width: 40rem;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post {
|
.post {
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: 40rem) {
|
|
||||||
.post, #new-post {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post .likes {
|
.post .likes {
|
||||||
display: block;
|
display: block;
|
||||||
padding-top: .25rem;
|
padding-top: .25rem;
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: var(--surface0);
|
background-color: var(--surface0);
|
||||||
margin-bottom: 1rem;
|
|
||||||
border-bottom: 1px solid var(--surface1);
|
border-bottom: 1px solid var(--surface1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,10 +84,9 @@
|
||||||
.tab {
|
.tab {
|
||||||
max-width: 80rem;
|
max-width: 80rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
padding: 0 1rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#post-container {
|
#post-container {
|
||||||
|
@ -106,3 +104,22 @@
|
||||||
td:nth-child(1) {
|
td:nth-child(1) {
|
||||||
padding-right: 2rem;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -24,13 +24,15 @@ class People_model extends Model {
|
||||||
case 'follower': {
|
case 'follower': {
|
||||||
$query = $query
|
$query = $query
|
||||||
->join('admin.follow f', 'f.follower_id = u.id AND f.followee_id', 'INNER')
|
->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;
|
} break;
|
||||||
|
|
||||||
case 'followee': {
|
case 'followee': {
|
||||||
$query = $query
|
$query = $query
|
||||||
->join('admin.follow f', 'f.followee_id = u.id AND f.follower_id', 'INNER')
|
->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;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,8 +29,36 @@ class Profile_model extends Model {
|
||||||
return NULL;
|
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 = parent::get_data();
|
||||||
$data['user'] = $user;
|
$data['user'] = $user;
|
||||||
|
$data['following'] = $following;
|
||||||
|
$data['followed'] = $followed;
|
||||||
|
$data['follow_id'] = $follow_id;
|
||||||
$data['title'] = lang('title', sub: [$user['first_name']]);
|
$data['title'] = lang('title', sub: [$user['first_name']]);
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
||||||
<?php /* vi: syntax=php */ ?>
|
<?php /* vi: syntax=php */ ?>
|
||||||
<div id="main-content">
|
<div id="main-content" class="container">
|
||||||
<?php if ($self): ?>
|
<?php if ($self): ?>
|
||||||
<div id="new-post" class="card">
|
<div id="new-post" class="card">
|
||||||
<div class="row grow">
|
<div class="row grow">
|
||||||
|
|
|
@ -8,9 +8,116 @@
|
||||||
<div class="pfp-wrapper">
|
<div class="pfp-wrapper">
|
||||||
<?=pfp($user)?>
|
<?=pfp($user)?>
|
||||||
</div>
|
</div>
|
||||||
<div class="col content">
|
<div class="col content grow">
|
||||||
|
<div class="row grow">
|
||||||
|
<div class="col">
|
||||||
<strong class="name"><?=$this->format_model->name($user)?></strong>
|
<strong class="name"><?=$this->format_model->name($user)?></strong>
|
||||||
<span class="dim"><?=$user['follower_count'] . ' ' . lang('followers')?></span>
|
<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): ?>
|
<?php if(strlen($user['profile_bio']) > 0): ?>
|
||||||
<br>
|
<br>
|
||||||
<strong><?=lang('bio')?></strong>
|
<strong><?=lang('bio')?></strong>
|
||||||
|
@ -43,13 +150,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="tab-container" class="container">
|
||||||
<div id="tab-posts" class="tab">
|
<div id="tab-posts" class="tab">
|
||||||
<?php
|
<?php
|
||||||
$_GET['user_id'] = $user['id'];
|
$_GET['user_id'] = $user['id'];
|
||||||
$this->post_controller->index();
|
$this->post_controller->index();
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
<div id="tab-about" class="tab">
|
<div id="tab-about" class="tab card">
|
||||||
<h1><?=lang('about_general')?></h1>
|
<h1><?=lang('about_general')?></h1>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -18,6 +18,7 @@ function lang($key, $default = NULL, $sub = NULL) {
|
||||||
|
|
||||||
function ilang($key,
|
function ilang($key,
|
||||||
$class = NULL,
|
$class = NULL,
|
||||||
|
$style = NULL,
|
||||||
$id = NULL,
|
$id = NULL,
|
||||||
$href = NULL,
|
$href = NULL,
|
||||||
$click = NULL,
|
$click = NULL,
|
||||||
|
@ -42,6 +43,9 @@ function ilang($key,
|
||||||
if ($class) {
|
if ($class) {
|
||||||
echo 'class="' . $class . '" ';
|
echo 'class="' . $class . '" ';
|
||||||
}
|
}
|
||||||
|
if ($style) {
|
||||||
|
echo 'style="' . $style . '" ';
|
||||||
|
}
|
||||||
if ($id) {
|
if ($id) {
|
||||||
echo 'id="' . $id . '" ';
|
echo 'id="' . $id . '" ';
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,15 @@ $lang['action_followers_tip'] = 'View %s\'s followres';
|
||||||
$lang['action_following_text'] = 'Following';
|
$lang['action_following_text'] = 'Following';
|
||||||
$lang['action_following_tip'] = 'View who %s is 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_text'] = 'Load more posts';
|
||||||
$lang['action_load_posts_tip'] = 'Load more posts';
|
$lang['action_load_posts_tip'] = 'Load more posts';
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue