feat(users): implement user following system and following feed
Add follow/unfollow actions and social stats to user profiles Introduce Following feed scope in Life view Add notifications for new followers
This commit is contained in:
@@ -94,6 +94,17 @@ CREATE UNIQUE INDEX IF NOT EXISTS users_referral_code_idx
|
||||
CREATE INDEX IF NOT EXISTS users_referred_by_user_id_idx
|
||||
ON users(referred_by_user_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_follows (
|
||||
follower_user_id integer NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
followed_user_id integer NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY (follower_user_id, followed_user_id),
|
||||
CHECK (follower_user_id <> followed_user_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS user_follows_followed_created_idx
|
||||
ON user_follows(followed_user_id, created_at DESC, follower_user_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS environments (
|
||||
id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
name text NOT NULL UNIQUE,
|
||||
@@ -290,6 +301,7 @@ VALUES
|
||||
('life.comments.delete-any', 'Delete any Life comment', 'Delete any Life comment.', 'Life', true),
|
||||
('life.reactions.set', 'Set Life reactions', 'Set and remove Life reactions.', 'Life', true),
|
||||
('life.ratings.set', 'Set Life ratings', 'Set and remove Life star ratings.', 'Life', true),
|
||||
('users.follow', 'Follow users', 'Follow and unfollow public user profiles.', 'Users', true),
|
||||
('discussions.comments.create', 'Create discussion comments', 'Create entity discussion comments and replies.', 'Discussions', true),
|
||||
('discussions.comments.delete', 'Delete own discussion comments', 'Delete own entity discussion comments.', 'Discussions', true),
|
||||
('discussions.comments.delete-any', 'Delete any discussion comment', 'Delete any entity discussion comment.', 'Discussions', true)
|
||||
@@ -381,6 +393,7 @@ JOIN permissions p ON p.key = ANY (ARRAY[
|
||||
'life.comments.delete-any',
|
||||
'life.reactions.set',
|
||||
'life.ratings.set',
|
||||
'users.follow',
|
||||
'discussions.comments.create',
|
||||
'discussions.comments.delete',
|
||||
'discussions.comments.delete-any'
|
||||
@@ -449,6 +462,7 @@ JOIN permissions p ON p.key = ANY (ARRAY[
|
||||
'life.comments.delete',
|
||||
'life.reactions.set',
|
||||
'life.ratings.set',
|
||||
'users.follow',
|
||||
'discussions.comments.create',
|
||||
'discussions.comments.delete'
|
||||
])
|
||||
@@ -496,6 +510,7 @@ JOIN permissions p ON p.key = ANY (ARRAY[
|
||||
'life.comments.delete',
|
||||
'life.reactions.set',
|
||||
'life.ratings.set',
|
||||
'users.follow',
|
||||
'discussions.comments.create',
|
||||
'discussions.comments.delete'
|
||||
])
|
||||
@@ -514,6 +529,13 @@ JOIN permissions p ON p.key = 'life.ratings.set'
|
||||
WHERE r.key IN ('admin', 'editor', 'member')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM roles r
|
||||
JOIN permissions p ON p.key = 'users.follow'
|
||||
WHERE r.key IN ('admin', 'editor', 'member')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
WITH first_owner_user AS (
|
||||
SELECT u.id
|
||||
FROM users u
|
||||
@@ -1231,10 +1253,12 @@ CREATE TABLE IF NOT EXISTS notifications (
|
||||
'life_comment_reply',
|
||||
'discussion_comment_reply',
|
||||
'life_post_reaction',
|
||||
'user_follow',
|
||||
'moderation_result'
|
||||
)
|
||||
),
|
||||
life_post_id integer REFERENCES life_posts(id) ON DELETE CASCADE,
|
||||
profile_user_id integer REFERENCES users(id) ON DELETE CASCADE,
|
||||
life_comment_id integer REFERENCES life_post_comments(id) ON DELETE CASCADE,
|
||||
parent_life_comment_id integer REFERENCES life_post_comments(id) ON DELETE SET NULL,
|
||||
discussion_comment_id integer REFERENCES entity_discussion_comments(id) ON DELETE CASCADE,
|
||||
@@ -1274,6 +1298,13 @@ CREATE UNIQUE INDEX IF NOT EXISTS notifications_life_post_reaction_unique_idx
|
||||
ON notifications(recipient_user_id, actor_user_id, life_post_id)
|
||||
WHERE type = 'life_post_reaction' AND actor_user_id IS NOT NULL AND life_post_id IS NOT NULL;
|
||||
|
||||
ALTER TABLE notifications
|
||||
ADD COLUMN IF NOT EXISTS profile_user_id integer REFERENCES users(id) ON DELETE CASCADE;
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS notifications_user_follow_unique_idx
|
||||
ON notifications(recipient_user_id, actor_user_id, profile_user_id)
|
||||
WHERE type = 'user_follow' AND actor_user_id IS NOT NULL AND profile_user_id IS NOT NULL;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS notification_ws_tickets (
|
||||
id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
user_id integer NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
@@ -1289,6 +1320,24 @@ CREATE INDEX IF NOT EXISTS notification_ws_tickets_user_idx
|
||||
ALTER TABLE notifications
|
||||
ADD COLUMN IF NOT EXISTS moderation_reason text;
|
||||
|
||||
ALTER TABLE notifications
|
||||
ADD COLUMN IF NOT EXISTS profile_user_id integer REFERENCES users(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE notifications
|
||||
DROP CONSTRAINT IF EXISTS notifications_type_check;
|
||||
|
||||
ALTER TABLE notifications
|
||||
ADD CONSTRAINT notifications_type_check CHECK (
|
||||
type IN (
|
||||
'life_post_comment',
|
||||
'life_comment_reply',
|
||||
'discussion_comment_reply',
|
||||
'life_post_reaction',
|
||||
'user_follow',
|
||||
'moderation_result'
|
||||
)
|
||||
);
|
||||
|
||||
ALTER TABLE life_tags
|
||||
ADD COLUMN IF NOT EXISTS is_rateable boolean NOT NULL DEFAULT false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user