From dd2fc3fdae03dba08d5cdd1571408d184eab1392 Mon Sep 17 00:00:00 2001 From: Nicolas Jegat Date: Wed, 14 Oct 2026 19:39:47 +0200 Subject: [PATCH 1/7] add fame calcul (services) --- api/src/Services/FameCalcul.php | 59 +++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 api/src/Services/FameCalcul.php diff --git a/api/src/Services/FameCalcul.php b/api/src/Services/FameCalcul.php new file mode 100644 index 00000000..110601c8 --- /dev/null +++ b/api/src/Services/FameCalcul.php @@ -0,0 +1,59 @@ +save(self::K_LIKE * (1 - $this->expected())); + } + + public function onPass(): void + { + $this->save(-self::K_PASS * $this->expected()); + } + + public function onView(): void + { + $this->save(self::K_VIEW * (1 - $this->expected())); + } + + private function expected(): float + { + $diff = $this->actor->fame_rating - $this->target->fame_rating; + + return 1 / (1 + 10 ** ($diff / 400)); + } + + private function save(float $delta): void + { + $stmt = Flight::db()->prepare( + 'UPDATE users + SET fame_rating = LEAST(:max, GREATEST(:min, fame_rating + :delta)) + WHERE id = :id' + ); + $stmt->execute([ + 'max' => self::MAX_FAME, + 'min' => self::MIN_FAME, + 'delta' => $delta, + 'id' => $this->target->id, + ]); + } +} From 29dbbba772ed24093ca939e38c50298ef66d57b8 Mon Sep 17 00:00:00 2001 From: Nicolas Jegat Date: Thu, 15 Oct 2026 15:47:15 +0200 Subject: [PATCH 2/7] Modify calculator service name and connect the calculator to controller (like, pass, view) --- api/src/Controllers/LikeController.php | 6 ++++++ api/src/Controllers/PassController.php | 9 ++++++++- api/src/Controllers/ViewController.php | 5 +++++ api/src/Services/{FameCalcul.php => FameCalculator.php} | 3 ++- 4 files changed, 21 insertions(+), 2 deletions(-) rename api/src/Services/{FameCalcul.php => FameCalculator.php} (96%) diff --git a/api/src/Controllers/LikeController.php b/api/src/Controllers/LikeController.php index ba88c5e8..3af0d100 100644 --- a/api/src/Controllers/LikeController.php +++ b/api/src/Controllers/LikeController.php @@ -5,6 +5,7 @@ use Flight; use Matcha\Api\Enums\NotificationType; use Matcha\Api\Model\User; +use Matcha\Api\Services\FameCalculator; class LikeController { @@ -20,6 +21,11 @@ public function store(string $username) } $me->like($target); + try { + (new FameCalculator($me, $target))->onLike(); + } catch (\Throwable $e) { + // calcul error + } if ($target->hasMatch($me->username)) { $notif = [ diff --git a/api/src/Controllers/PassController.php b/api/src/Controllers/PassController.php index 34e03a29..73d8cbc9 100644 --- a/api/src/Controllers/PassController.php +++ b/api/src/Controllers/PassController.php @@ -4,12 +4,14 @@ use Flight; use Matcha\Api\Model\User; +use Matcha\Api\Services\FameCalculator; class PassController { public function store(string $username) { $target = User::find(['username' => $username]); + $me = Flight::user(); if (is_null($target)) { Flight::json([ @@ -19,7 +21,12 @@ public function store(string $username) return; } - Flight::user()->pass($target); + $me->pass($target); + try { + (new FameCalculator($me, $target))->onPass(); + } catch (\Throwable $e) { + // calcul error + } Flight::json(['success' => true,], 204); } diff --git a/api/src/Controllers/ViewController.php b/api/src/Controllers/ViewController.php index fd2fa1d3..6f044d17 100644 --- a/api/src/Controllers/ViewController.php +++ b/api/src/Controllers/ViewController.php @@ -30,6 +30,11 @@ public function store(string $username) } $me->view($user); + try { + (new FameCalculator($me, $user))->onView(); + } catch (\Throwable $e) { + // calcul error + } Flight::notifier()->notify($user, [ 'type' => NotificationType::VIEW, diff --git a/api/src/Services/FameCalcul.php b/api/src/Services/FameCalculator.php similarity index 96% rename from api/src/Services/FameCalcul.php rename to api/src/Services/FameCalculator.php index 110601c8..a2ffe74c 100644 --- a/api/src/Services/FameCalcul.php +++ b/api/src/Services/FameCalculator.php @@ -4,6 +4,7 @@ use Exception; use Matcha\Api\Model\User; +use Flight; class FameCalculator { @@ -12,7 +13,7 @@ class FameCalculator private const K_VIEW = 2; private const MIN_FAME = 0; - private const MAX_FAME = 2000; + private const MAX_FAME = 2500; public function __construct( private User $actor, // liker From e4d2526a3b45e70f2e20e06db6bb9033b86cf8f0 Mon Sep 17 00:00:00 2001 From: Nicolas Jegat Date: Thu, 15 Oct 2026 15:47:58 +0200 Subject: [PATCH 3/7] Fix profile image display --- frontend/src/views/UserProfileView.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/views/UserProfileView.vue b/frontend/src/views/UserProfileView.vue index 4b334618..00630d2e 100644 --- a/frontend/src/views/UserProfileView.vue +++ b/frontend/src/views/UserProfileView.vue @@ -32,7 +32,7 @@ interface Profile { city: string | null biography: string | null avatar: string | null - photos: { id: number; path: string }[] + photos: string [] tags: string[] liked_by_me: boolean is_match: boolean @@ -133,9 +133,9 @@ watch(() => route.params.id, load) - + From 51e6b9a0c1774060bb5b46449a81f4abcf7ca752 Mon Sep 17 00:00:00 2001 From: Nicolas Jegat Date: Thu, 15 Oct 2026 16:23:35 +0200 Subject: [PATCH 4/7] Add the missing import --- api/src/Controllers/ViewController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/Controllers/ViewController.php b/api/src/Controllers/ViewController.php index 6f044d17..9011fa68 100644 --- a/api/src/Controllers/ViewController.php +++ b/api/src/Controllers/ViewController.php @@ -5,6 +5,7 @@ use Flight; use Matcha\Api\Enums\NotificationType; use Matcha\Api\Model\User; +use Matcha\Api\Services\FameCalculator; class ViewController { From 3dddbf4c0227ff4df5b629264fecc0649d4dfdea Mon Sep 17 00:00:00 2001 From: Nicolas Jegat Date: Thu, 15 Oct 2026 16:24:12 +0200 Subject: [PATCH 5/7] Add view api ping on profile page --- frontend/src/api/users.ts | 6 ++++++ frontend/src/composable/useUsers.ts | 10 ++++++++++ frontend/src/views/UserProfileView.vue | 7 ++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/frontend/src/api/users.ts b/frontend/src/api/users.ts index bf193b56..793f2615 100644 --- a/frontend/src/api/users.ts +++ b/frontend/src/api/users.ts @@ -86,4 +86,10 @@ export const usersApi = { res.json(), ) }, + + async view(username: string) { + return ApiClient.post(`/users/${username}/view`).then(res => + res.json(), + ) + }, } diff --git a/frontend/src/composable/useUsers.ts b/frontend/src/composable/useUsers.ts index c663e5d2..fdcc345f 100644 --- a/frontend/src/composable/useUsers.ts +++ b/frontend/src/composable/useUsers.ts @@ -61,6 +61,15 @@ export function useUsers() { } } + const view = async (who: string) => { + error.value = null + try { + await usersApi.view(who) + } catch (err: any) { + error.value = err.response?.data?.message || "You can't view this user." + } + } + // --- Modération (Blocages et Signalement) --- const fetchBlockedList = async () => { isLoading.value = true @@ -180,5 +189,6 @@ export function useUsers() { suggestion, suggestionReset, pass, + view, } } diff --git a/frontend/src/views/UserProfileView.vue b/frontend/src/views/UserProfileView.vue index 00630d2e..f260d441 100644 --- a/frontend/src/views/UserProfileView.vue +++ b/frontend/src/views/UserProfileView.vue @@ -24,6 +24,7 @@ import { DialogFooter, } from '@/components/ui/dialog' import { ApiClient } from '@/api/client' +import { useUsers } from '@/composable/useUsers' interface Profile { id: number @@ -49,6 +50,7 @@ const error = ref('') const showMatch = ref(false) const editing = ref(false) const form = ref({ bio: '', city: '', tags: '' }) +const useUser = useUsers() async function load() { loading.value = true @@ -110,7 +112,10 @@ async function saveProfile() { const initials = (name: string) => name.slice(0, 2).toUpperCase() -onMounted(load) +onMounted(() => { + load() + useUser.view(`${route.params.username}`); +}) watch(() => route.params.id, load) From 716aa56dc84e0130f6361c9d16e09379e7a84317 Mon Sep 17 00:00:00 2001 From: Nicolas Jegat Date: Thu, 15 Oct 2026 16:41:17 +0200 Subject: [PATCH 6/7] Add unlike to avoid like/unlike bomber --- api/src/Controllers/LikeController.php | 5 +++++ api/src/Services/FameCalculator.php | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/api/src/Controllers/LikeController.php b/api/src/Controllers/LikeController.php index 3af0d100..e7d803a0 100644 --- a/api/src/Controllers/LikeController.php +++ b/api/src/Controllers/LikeController.php @@ -68,6 +68,11 @@ public function destroy(string $username) } $me->unlike($user); + try { + (new FameCalculator($me, $user))->onUnLike(); + } catch (\Throwable $e) { + // calcul error + } Flight::json([], 203); } diff --git a/api/src/Services/FameCalculator.php b/api/src/Services/FameCalculator.php index a2ffe74c..fc9805be 100644 --- a/api/src/Services/FameCalculator.php +++ b/api/src/Services/FameCalculator.php @@ -26,6 +26,11 @@ public function onLike(): void $this->save(self::K_LIKE * (1 - $this->expected())); } + public function onUnLike(): void + { + $this->save(-self::K_LIKE * (1 - $this->expected())); + } + public function onPass(): void { $this->save(-self::K_PASS * $this->expected()); From 1e8deb4c6a7804e7be2fa34c5670d1000ecce2a1 Mon Sep 17 00:00:00 2001 From: Nicolas Jegat Date: Thu, 15 Oct 2026 19:36:26 +0200 Subject: [PATCH 7/7] Fix pass controller. Now the date can be updated on new pass --- api/src/Controllers/PassController.php | 13 +++++++++---- api/src/Model/Model.php | 13 +++++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/api/src/Controllers/PassController.php b/api/src/Controllers/PassController.php index 73d8cbc9..345126a0 100644 --- a/api/src/Controllers/PassController.php +++ b/api/src/Controllers/PassController.php @@ -4,7 +4,9 @@ use Flight; use Matcha\Api\Model\User; +use Matcha\Api\Model\Pass; use Matcha\Api\Services\FameCalculator; +use Matcha\Api\Exceptions\UniqueConstraintException; class PassController { @@ -21,13 +23,16 @@ public function store(string $username) return; } - $me->pass($target); try { + $me->pass($target); (new FameCalculator($me, $target))->onPass(); - } catch (\Throwable $e) { - // calcul error - } + } catch (UniqueConstraintException $e) { + $pass = Pass::find(['user_id' => $me->id, 'target_id' => $target->id]); + + $pass->created_at = date('Y-m-d H:i:s'); + $pass->update(['user_id' => $me->id, 'target_id' => $target->id]); + } Flight::json(['success' => true,], 204); } } diff --git a/api/src/Model/Model.php b/api/src/Model/Model.php index b801bb9c..8cfc35a7 100644 --- a/api/src/Model/Model.php +++ b/api/src/Model/Model.php @@ -43,9 +43,18 @@ public function save(): Model return $this->id > 0 ? $this->update() : $this->create(); } - private function update(): Model + public function update(array $where = []): Model { $data = $this->getData(); + + if (empty($where)) { + $where = ['id' => $this->id]; + } + + $whereList = array_map(function ($k, $v) { + return $k . " = " . $v; + }, array_keys($where), $where); + $sqlQuery = "UPDATE " . $this->getTable() . " SET " . implode( ", ", @@ -55,7 +64,7 @@ private function update(): Model array_values($data) ) ) - . ' WHERE id = ' . $this->id; + . ' WHERE ' . implode(" AND ", $whereList); self::db()->exec($sqlQuery); $this->reload();