From 780dc9cc3351c2aea085acdf1f062c750db84b98 Mon Sep 17 00:00:00 2001 From: Duygu <84078855+duuyguyiilmaz@users.noreply.github.com> Date: Mon, 31 Aug 2026 17:54:53 +0300 Subject: [PATCH] docs: bring the README back in line with the code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A pass over the claims the last few changes made stale. No screenshots added; the existing ones stay as they are. - The leaderboard screenshot caption said it showed the viewer's own department. It was captured before the ranking was scoped, so the image is wider than what the app now returns. Saying so is better than a caption that quietly misdescribes the picture under it. - "Points and rewards — uploaders earn points from ratings" implied a stored balance. The total is the score the user's own notes have earned, computed where it is shown. - Rating rules — no self-rating, one vote per person — are now enforced by the security rules rather than only by the app, which is the part worth stating in a feature list. - Email verification moves from a gap to a decision. It is one call away and left open deliberately: the accounts here are test data, and a mandatory confirmation step would put a mailbox between a reviewer and the running app for nothing. - The pagination entry said the feed and leaderboard read every matching note and sorted in memory. Still true, but the leaderboard no longer reads the whole collection, and the createdAt normalisation the ordering depends on has already landed. The entry now says what is left rather than what was true two changes ago. - "Deploy the security rules" was a parenthetical. Now a step with the command in it, and a sentence on why it is not only about access: the rules are what verify the scoring, so an un-deployed project is either blocked or cheatable. - The rules-tests intro still cited `points` as an example of what only Firestore can enforce, and that field no longer exists. Verified: every in-page anchor resolves to a heading, and every relative link resolves to a file that exists. Co-Authored-By: Claude Opus 5 --- README.md | 57 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 3d0b03c..b903881 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,12 @@ into a portfolio piece. |:---:|:---:| | ![Upload](docs/screenshots/07-upload.png) | ![Leaderboard](docs/screenshots/08-leaderboard.png) | -The leaderboard screenshot shows live Cloud Firestore data — the signed-in user's -department ranked by total score, read through a real-time snapshot listener. The -accounts in it are test data. Uploaders are shown by the local part of their address -rather than the full email; -see [`UploaderName.kt`](app/src/main/java/duygu/yilmaz/campusnote/data/model/UploaderName.kt). +The leaderboard screenshot shows live Cloud Firestore data read through a real-time +snapshot listener; the accounts in it are test data. It was captured before the ranking +was scoped to the viewer's own department, so the list it shows is wider than what the +app now returns. Uploaders appear as the local part of their address rather than the +full email — see +[`UploaderName.kt`](app/src/main/java/duygu/yilmaz/campusnote/data/model/UploaderName.kt). --- @@ -43,9 +44,10 @@ see [`UploaderName.kt`](app/src/main/java/duygu/yilmaz/campusnote/data/model/Upl - **Contribution gate** — the department feed stays locked until you upload your first note - **Note upload** as PDF or image, with automatic image downscaling and compression - **Note rating** on a 1–5 scale; you cannot rate your own notes, and re-rating replaces - your previous vote instead of adding a second one + your previous vote instead of adding a second one — both enforced by the security + rules, not just the app - **Leaderboard** ranking your department's notes by total score, with gold/silver/bronze placings -- **Points and rewards** — uploaders earn points from ratings; discounts unlock at 100 points +- **Points and rewards** — your total is the score your own notes have earned; discounts unlock at 100 points - **Note management** — edit or delete your own notes from your profile --- @@ -217,15 +219,21 @@ server-side, and that needs the Blaze plan. The rules make up most of the differ they recompute the expected totals from the vote and reject anything else (see [Security rules](#security-rules)) — but the arithmetic itself still runs on a device. -**Email addresses are never verified.** Registration only checks that the address ends -in `@ogr.akdeniz.edu.tr`; no confirmation mail is sent, so anyone can register with a -made-up address on that domain. The "university students only" rule is therefore a -convention, not a guarantee. Calling `sendEmailVerification()` after sign-up and gating -the feed on `FirebaseUser.isEmailVerified` would close this, at the cost of a -confirmation step during registration. - -**No pagination.** The feed and leaderboard read every matching note and sort in -memory. Fine at course-project scale, wrong at department scale. +**Email addresses are deliberately not verified.** Registration checks that the address +ends in `@ogr.akdeniz.edu.tr` and stops there — no confirmation mail is sent, so the +"university students only" rule is a convention rather than a guarantee. Closing it is +one call (`sendEmailVerification()` after sign-up, then gating the feed on +`FirebaseUser.isEmailVerified`), and it is left open on purpose: the accounts in this +project are test data, and a mandatory confirmation step would put a mailbox between a +reviewer and the running app for no benefit at this scale. + +**No pagination.** The feed and the leaderboard read every note in the department and +sort in memory. Scoping both queries to one department (rather than the whole +collection, as the leaderboard used to) bounds this by roughly the number of +departments, but it is still every matching note on every open. Paginating means +ordering in the query, and the data that ordering depends on has been normalised +already — see [Data migrations](#data-migrations) — so the remaining work is the query +and the load-more UI. **Deleting a note reports failure with a `Toast`.** Unlike a failed read, the user can repeat a failed delete by tapping the button again, so the toast does not leave them @@ -260,8 +268,15 @@ emulator on API 24+. cp app/google-services.json.example app/google-services.json # then paste in your real values ``` -5. **Deploy the security rules** (see [Security rules](#security-rules)), or the app - will be blocked from reading and writing. +5. **Deploy the security rules** + + ```bash + firebase deploy --only firestore:rules + ``` + + Not optional, and not only about access: the rules are what verify the scoring, so + an un-deployed project is both blocked from reading and, once unblocked by a + permissive rule, trivially cheatable. See [Security rules](#security-rules). 6. **Build and run** @@ -352,9 +367,9 @@ npm test ``` The JVM tests above stop at the repository interfaces, so everything Firestore itself -enforces — who may delete a note, who may write to whose `points` — was previously -unverified. That is the layer an attacker actually meets: the Android client can be -replaced, the rules cannot. +enforces — who may delete a note, whether a score can move without a vote behind it — is +invisible to them. That is the layer an attacker actually meets: the Android client can +be replaced, the rules cannot. 38 tests in [`firestore-tests/rules.test.js`](firestore-tests/rules.test.js) run [`firestore.rules`](firestore.rules) against the local Firestore emulator through