Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/friend system #7

Merged
merged 36 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
da25590
feat(api): Add sample code for features with high usage likelihood
Likqez Oct 17, 2024
8e5404c
feat(api): Add Playlist & User Endpoint
Likqez Oct 18, 2024
9396b44
chore(docs): add typedoc
Likqez Oct 31, 2024
4f14f2f
feat(api): Implement playlist endpoint
Likqez Oct 31, 2024
d8dd845
chore(readme): Update Readme and .env example to match current state …
Likqez Nov 7, 2024
ff4e5c8
fix(api): Playlists were disabled on creation
Likqez Nov 15, 2024
f0ac62d
Merge remote-tracking branch 'origin/feat/api' into feat/api
Likqez Nov 15, 2024
7c5d310
added categories to get requests
synan798 Nov 29, 2024
3d511f3
added categories to post request (getPlaylistCover doesn't work)
synan798 Nov 30, 2024
05ef067
feat(friends): Start implementing database layer
Likqez Nov 30, 2024
a8a731d
fix(playlist): Fix playlist cover not loading
Likqez Nov 30, 2024
d6f80fa
feat(playlist): Add DDL for playlist database schema
Likqez Nov 30, 2024
c6a32f2
feat(friends): Add decline db function
Likqez Nov 30, 2024
90aaf2d
added correct return statement
synan798 Nov 30, 2024
240f4f6
feat(friends): add on delete cascade for user deletion and finished s…
Likqez Dec 2, 2024
06ef470
feat(friends): implement GET `/user/friends` endpoint
Likqez Dec 2, 2024
3a824d2
feat(friends): GET `/user/friends` endpoint also shows outgoing frien…
Likqez Dec 2, 2024
bd81104
feat(friends): implement POST `/user/friends` endpoint for sending fr…
Likqez Dec 2, 2024
882f065
feat(friends): remove unnecessary/redundant DB get functions
Likqez Dec 2, 2024
c2ebc0c
added Start Buttons to home
synan798 Dec 4, 2024
26bf835
feat(friends): Add endpoint to accept and decline friend requests.
Likqez Dec 4, 2024
cd4da14
added game controls and user/opponent view to homepage
synan798 Dec 4, 2024
1fec8f3
added play button to user turn and changed text color to white
synan798 Dec 4, 2024
7d30789
resizes profile picture and play button
synan798 Dec 4, 2024
24ab8b7
fix(friends): fix accept and decline endpoints
Likqez Dec 4, 2024
d1f89a0
feat(friends): Tracking testpage for easy frontend work
Likqez Dec 4, 2024
8db2fea
feat(friends): add incoming/outgoing request attribute
Likqez Dec 4, 2024
c3498a9
chore: fix tsconfig.json
Likqez Dec 4, 2024
0f81c55
added first draft for profile view
synan798 Dec 4, 2024
81dbf5f
added initial functionality
synan798 Dec 4, 2024
203c869
added needed api files for friendlist
synan798 Dec 4, 2024
8e4cff3
feat(friend): friend endpoint now presents basic user info
Likqez Dec 4, 2024
04613af
added working friendlist (name is still uid)
synan798 Dec 4, 2024
84453e6
Merge remote-tracking branch 'origin/feat/api' into feat/friend-system
Likqez Dec 4, 2024
bec55ff
fixef ESLint errors
synan798 Dec 5, 2024
b4b439a
Merge branch 'feat/profile-view' into feat/friend-system
synan798 Dec 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
SUPABASE_URL="https://yourhost.supabase.co"
SUPABASE_KEY="your-secret-key"
SUPABASE_SERVICE_KEY="your-service-key"
SPOTIFY_CLIENT_ID="your-client-id"
SPOTIFY_CLIENT_SECRET="your-client-secret"
197 changes: 197 additions & 0 deletions DB/friendships.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
DO
$$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'friendship_status') THEN
CREATE TYPE friendship_status AS ENUM ('pending', 'accepted', 'declined');
END IF;
END
$$;

CREATE TABLE IF NOT EXISTS friendships
(
friendship_id SERIAL PRIMARY KEY,
user1_id UUID NOT NULL,
user2_id UUID NOT NULL,
status friendship_status NOT NULL,
action_user_id UUID NOT NULL, -- The user who performed the last action
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),

-- Ensure user1_id is always less than user2_id to prevent duplicate friendships
CONSTRAINT ensure_user_order CHECK (user1_id < user2_id),
CONSTRAINT unique_friendship UNIQUE (user1_id, user2_id),

-- Foreign keys
CONSTRAINT fk_user1 FOREIGN KEY (user1_id) REFERENCES users (id) ON DELETE CASCADE,
CONSTRAINT fk_user2 FOREIGN KEY (user2_id) REFERENCES users (id) ON DELETE CASCADE,
CONSTRAINT fk_action_user FOREIGN KEY (action_user_id) REFERENCES users (id)
);

CREATE INDEX IF NOT EXISTS idx_friendship_user1 ON friendships (user1_id, status);
CREATE INDEX IF NOT EXISTS idx_friendship_user2 ON friendships (user2_id, status);

-- Functions

-- automatically update updated_at timestamp
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS
$$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE TRIGGER update_friendships_timestamp
BEFORE UPDATE
ON friendships
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();

-- Send a friend request
CREATE OR REPLACE FUNCTION send_friend_request(sender_id UUID, receiver_id UUID) RETURNS void AS
$$
DECLARE
smaller_id UUID;
larger_id UUID;
BEGIN
-- Determine order of IDs
IF sender_id < receiver_id THEN
smaller_id := sender_id;
larger_id := receiver_id;
ELSE
smaller_id := receiver_id;
larger_id := sender_id;
END IF;

-- Insert friendship record
INSERT INTO friendships (user1_id, user2_id, status, action_user_id)
VALUES (smaller_id, larger_id, 'pending', sender_id)
ON CONFLICT (user1_id, user2_id) DO UPDATE
SET status = CASE
WHEN friendships.status = 'declined' THEN 'pending'::friendship_status
ELSE friendships.status
END,
action_user_id = sender_id;
END;
$$ LANGUAGE plpgsql;

-- accept friendship request
CREATE OR REPLACE FUNCTION accept_friend_request_by_id(friendship_id_param INT)
RETURNS void AS
$$
BEGIN
UPDATE friendships
SET status = 'accepted'
WHERE friendship_id = friendship_id_param
AND status = 'pending';

IF NOT FOUND THEN
RAISE EXCEPTION 'No pending friend request found with this ID';
END IF;
END;
$$ LANGUAGE plpgsql;


-- decline friendship request
CREATE OR REPLACE FUNCTION decline_friend_request_by_id(friendship_id_param INT)
RETURNS void AS
$$
BEGIN
UPDATE friendships
SET status = 'declined'
WHERE friendship_id = friendship_id_param
AND status = 'pending';

IF NOT FOUND THEN
RAISE EXCEPTION 'No pending friend request found with this ID';
END IF;
END;
$$ LANGUAGE plpgsql;

-- delete friendship
CREATE OR REPLACE FUNCTION remove_friend(user_id UUID, friend_id UUID) RETURNS void AS
$$
DECLARE
smaller_id UUID;
larger_id UUID;
BEGIN
-- Determine order of IDs
IF friend_id < user_id THEN
smaller_id := friend_id;
larger_id := user_id;
ELSE
smaller_id := user_id;
larger_id := friend_id;
END IF;

DELETE
FROM friendships
WHERE user1_id = smaller_id
AND user2_id = larger_id
AND status = 'accepted';

IF NOT FOUND THEN
RAISE EXCEPTION 'No active friendship found between these users';
END IF;
END;
$$ LANGUAGE plpgsql;

-- 2nd version with id
CREATE OR REPLACE FUNCTION remove_friend_by_id(friendship_id_param INT) RETURNS void AS
$$
BEGIN
DELETE
FROM friendships
WHERE friendship_id = friendship_id_param
AND status = 'accepted';

IF NOT FOUND THEN
RAISE EXCEPTION 'No active friendship found with this ID';
END IF;
END;
$$ LANGUAGE plpgsql;

-- retrieve all friends, incoming and outgoing friend requests
CREATE OR REPLACE FUNCTION get_friends(user_id UUID)
RETURNS TABLE
(
friendship_id INT,
friend_id UUID,
friend_username TEXT,
friend_avatar TEXT,
friend_spotify_id TEXT,
friend_spotify_visibility BOOLEAN,
status friendship_status,
action_user_id UUID,
created_at TIMESTAMP WITH TIME ZONE,
updated_at TIMESTAMP WITH TIME ZONE,
request_type TEXT
)
AS
$$
BEGIN
RETURN QUERY
SELECT f.friendship_id,
CASE WHEN f.user1_id = user_id THEN f.user2_id ELSE f.user1_id END AS friend_id,
u.username AS friend_username,
u.avatar_url AS friend_avatar,
u.spotify_id AS friend_spotify_id,
u.spotify_visibility AS friend_spotify_visibility,
f.status,
f.action_user_id,
f.created_at,
f.updated_at,
CASE WHEN f.action_user_id = user_id THEN 'outgoing' ELSE 'incoming' END AS request_type
FROM friendships f, users u
WHERE (f.user1_id = user_id OR f.user2_id = user_id)
AND (f.status != 'declined')
AND (CASE WHEN f.user1_id = user_id THEN f.user2_id ELSE f.user1_id END = u.id);
END;
$$ LANGUAGE plpgsql;
-- examples
-- SELECT send_friend_request('sender', 'receiver');
-- SELECT accept_friend_request_by_id(4);
-- SELECT decline_friend_request_by_id(4);
-- SELECT remove_friend('friend_user');
-- SELECT * FROM get_friends('user_id');
17 changes: 17 additions & 0 deletions DB/playlists.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
create table playlists
(
id text not null primary key,
"spotifyId" text not null,
name text not null,
cover text,
enabled boolean default false not null
);

CREATE UNIQUE INDEX enabled_playlist_unique_name ON playlists (name) WHERE enabled = true;

create table categories
(
name text not null,
"playlistId" text not null references playlists,
primary key (name, "playlistId")
);
15 changes: 15 additions & 0 deletions DB/users.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
CREATE TABLE users
(
id uuid not null references auth.users on delete cascade,
avatar_url text,
username text not null,
spotify_id text not null,
spotify_visibility boolean not null default false,
primary key (id)
);

ALTER TABLE users
ADD CONSTRAINT unique_username UNIQUE (username),
ADD CONSTRAINT valid_username check (username <> '' AND length(trim(username)) >= 4 AND username ~ '^[a-zA-Z0-9_]+$');

CREATE INDEX idx_username ON users(username);
37 changes: 3 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,54 +7,23 @@ Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introdu
Make sure to install the dependencies:

```bash
# npm
npm install

# pnpm
pnpm install

# yarn
yarn install

# bun
bun install
```

## Development Server

Start the development server on `http://localhost:3000`:

```bash
# npm
npm run dev

# pnpm
pnpm run dev

# yarn
yarn dev

# bun
bun run dev
```

## Production

Build the application for production:

```bash
# npm
npm run build
```

Locally preview production build:

## TSDocs
```bash
# npm
npm run preview
npm run docs:serve
```

Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.

## Required Environment Variables
Check `.env.demo` for the required environment variables.
Check `.env.example` for the required environment variables.
15 changes: 15 additions & 0 deletions components/home/Controls/GameButtons.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script setup lang="ts">

</script>

<template>
<div class="flex space-x-3 w-full">
<button class="bg-[#22a9fb] hover:bg-blue-700 text-white font-bold py-20 rounded-3xl text-lg w-full">
Start Game
</button>
<button class="bg-[#22a9fb] hover:bg-blue-700 text-white font-bold py-20 rounded-3xl text-lg w-full">
Start Round
</button>
</div>
</template>

14 changes: 14 additions & 0 deletions components/home/Turns/Opponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script setup lang="ts">

</script>

<template>
<div class="w-full bg-gray-200 p-3 mt-auto rounded-3xl my-3">
<p class="my-1 ">Opponentes Turn</p>
<div class="flex space-x-3">
<HomeUsersUserBox name="test1" :user-turn="false"/>
<HomeUsersUserBox name="test2" :user-turn="false"/>
<HomeUsersUserBox name="test3" :user-turn="false"/>
</div>
</div>
</template>
14 changes: 14 additions & 0 deletions components/home/Turns/User.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script setup lang="ts">

</script>

<template>
<div class="w-full bg-gray-200 p-3 mt-auto rounded-3xl my-3 h-full">
<p class="my-1">Your Turn</p>
<div class="flex-col space-y-3">
<HomeUsersUserBox name="test1" :user-turn="true"/>
<HomeUsersUserBox name="test2" :user-turn="true"/>
<HomeUsersUserBox name="test3" :user-turn="true"/>
</div>
</div>
</template>
26 changes: 26 additions & 0 deletions components/home/Users/UserBox.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script setup lang="ts">
const props = defineProps({
profilePicture: {
type: String,
default: 'https://i.scdn.co/image/ab6775700000ee855d4c281804e8773208248312'
},
name: {
type: String,
default: 'Opponent'
},
userTurn: {
type: Boolean,
default: false
}
});
</script>

<template>
<div :class="['bg-blue-600', props.userTurn ? 'flex items-center h-30 w-full rounded-3xl p-3' : 'flex flex-col items-center justify-center h-30 w-full rounded-3xl p-3']">
<img :class="['rounded-full w-12 h-12 sm:w-16 sm:h-16 md:w-16 md:h-16 lg:w-16 lg:h-16', props.userTurn ? 'mr-3' : 'mb-2']" :src="props.profilePicture" :alt="name">
<p class="text-white">{{ props.name }}</p>
<button v-if="props.userTurn" class="ml-auto p-2 sm:p-3 md:p-4 lg:p-5" @click="console.log(props.name)">
<NuxtImg src="icons/playButton.svg" alt="Play" class="w-12 h-12 sm:w-16 sm:h-16 md:w-16 md:h-16 lg:w-16 lg:h-16 hover:bg-black"/>
</button>
</div>
</template>
Loading
Loading