diff --git a/backend/strapi/src/api/note/content-types/note/schema.json b/backend/strapi/src/api/note/content-types/note/schema.json index e064764..754f73c 100644 --- a/backend/strapi/src/api/note/content-types/note/schema.json +++ b/backend/strapi/src/api/note/content-types/note/schema.json @@ -22,7 +22,8 @@ "owners": { "type": "relation", "relation": "manyToMany", - "target": "plugin::users-permissions.user" + "target": "plugin::users-permissions.user", + "mappedBy": "notes" }, "lastViewed": { "type": "datetime", diff --git a/backend/strapi/src/api/note/controllers/note.js b/backend/strapi/src/api/note/controllers/note.js index fdc5a92..9cf8879 100644 --- a/backend/strapi/src/api/note/controllers/note.js +++ b/backend/strapi/src/api/note/controllers/note.js @@ -1,5 +1,6 @@ 'use strict'; //move to utils! + function getNoteIdFromUrl(url) { return Number(url.split("/").at(-1)); } @@ -43,7 +44,6 @@ module.exports = createCoreController(noteUid, ({strapi}) => ({ populate: ['owners'], }); const authorized = entry.owners.some(owner => owner.id === userId) - console.log(authorized) if (authorized) { entry = await strapi.entityService.update(noteUid, noteId, { data: { @@ -68,18 +68,38 @@ module.exports = createCoreController(noteUid, ({strapi}) => ({ populate: ['owners'], }); const authorized = entry.owners.some(owner => owner.id === userId) - let allowed; + let allPreviousOwnersKept = false; if (requestBody.data.hasOwnProperty("owners")) { - allowed = entry.owners.every(owner => requestBody.data.owners.includes(owner)); + allPreviousOwnersKept = entry.owners.every(owner => requestBody.data.owners.includes(owner)); } if (!authorized) { ctx.response.status = 403; - } else if (!allowed) { + } else if (!allPreviousOwnersKept) { ctx.response.status = 400; } else { return super.update(ctx); } }, + /** + * Creates a new note, automatically sets owners to the user making the request and lastViewed + * @param ctx + * @returns {Promise} + */ + async create(ctx) { + const userId = ctx.state.user.id; + const requestBody = JSON.parse(ctx.request.body); + console.log(requestBody); + const response = await strapi.entityService.create(noteUid, { + data: { + title: requestBody.data.title, + content: requestBody.data.content, + lastViewed: Date.now(), + owners: [userId], + publishedAt: Date.now() + } + }); + return response; + }, /** * Deletes user from note owners. If note has no owners anymore, deletes note. * @param ctx diff --git a/frontend/svelte/src/app.html b/frontend/svelte/src/app.html index 2767694..07daf74 100644 --- a/frontend/svelte/src/app.html +++ b/frontend/svelte/src/app.html @@ -4,9 +4,16 @@ + %sveltekit.head%
%sveltekit.body%
+ + diff --git a/frontend/svelte/src/customBootstrap.css b/frontend/svelte/src/customBootstrap.css new file mode 100644 index 0000000..311d8a4 --- /dev/null +++ b/frontend/svelte/src/customBootstrap.css @@ -0,0 +1,29 @@ + +html, +:root { + --main-txt-color: black; + --cross-txt-color: red; + + --color-primary: #fff494; + --color-primary-600: #fff17a; + --color-primary-700: #ffec47; + --color-primary-800: #ffe714; + --color-primary-900: #e0c900; +} + +.btn-primary { + background-color: var(--color-primary-800) !important; + border: var(--color-primary-800) !important; + color: var(--main-txt-color) !important; +} + +.btn-primary:hover { + background-color: var(--color-primary-900) !important; + border: var(--color-primary-900) !important; + color: var(--main-txt-color) !important; +} + +.btn-primary:disabled { + background-color: var(--color-primary-700) !important; + border: var(--color-primary-700) !important; +} \ No newline at end of file diff --git a/frontend/svelte/src/models/repos/note/NoteRepository.ts b/frontend/svelte/src/models/repos/note/NoteRepository.ts index 4fcda1e..84b6705 100644 --- a/frontend/svelte/src/models/repos/note/NoteRepository.ts +++ b/frontend/svelte/src/models/repos/note/NoteRepository.ts @@ -4,7 +4,7 @@ export interface NoteRepository { getNotes(): Promise; getNote(id: number): Promise; getCurrentNote(): Promise; - updateNote(id: number, note: Note): Promise; + updateNote(id: number, note: Partial): Promise; deleteNote(id: number): void; - createNote(note: Note): Promise; + createNote(note: Partial & Pick): Promise; } \ No newline at end of file diff --git a/frontend/svelte/src/models/repos/note/StrapiNoteRepository.ts b/frontend/svelte/src/models/repos/note/StrapiNoteRepository.ts index 2a4ef83..7420149 100644 --- a/frontend/svelte/src/models/repos/note/StrapiNoteRepository.ts +++ b/frontend/svelte/src/models/repos/note/StrapiNoteRepository.ts @@ -15,45 +15,49 @@ export class StrapiNoteRepository implements NoteRepository { private constructor() {} - private currentNoteId: number | undefined; + private _currentNoteId: number | undefined; private static apiNoteEndpoint: string = "http://localhost:1337/api/notes" + public set currentNoteId(value: number | undefined) { + this._currentNoteId = value; + } + public async getNotes(): Promise{ - const response = await StrapiNoteRepository.fetchStrapi("/", 'GET'); + const response = await StrapiNoteRepository.fetchStrapiNoteEndpoint("/", 'GET'); return await response.json(); } public async getNote(id: number): Promise{ - const response = await StrapiNoteRepository.fetchStrapi("/" + id, 'GET'); + const response = await StrapiNoteRepository.fetchStrapiNoteEndpoint("/" + id, 'GET'); return await response.json(); } public async getCurrentNote(): Promise { - if (this.currentNoteId === null || this.currentNoteId === undefined) { + if (this._currentNoteId === null || this._currentNoteId === undefined) { return; } - return await this.getNote(this.currentNoteId); + return await this.getNote(this._currentNoteId); } - public async updateNote(id: number, note: Note): Promise { - const response = await StrapiNoteRepository.fetchStrapi("/" + id, 'PUT', note); + public async updateNote(id: number, note: Partial): Promise { + const response = await StrapiNoteRepository.fetchStrapiNoteEndpoint("/" + id, 'PUT', note); return await response.json(); } - public async createNote(note: Note): Promise { - const response = await StrapiNoteRepository.fetchStrapi("/", 'POST', note); + public async createNote(note: Partial & Pick): Promise { + const response = await StrapiNoteRepository.fetchStrapiNoteEndpoint("/", 'POST', note); return await response.json(); } public async deleteNote(id: number): Promise { - await StrapiNoteRepository.fetchStrapi("/" + id, 'DELETE'); + await StrapiNoteRepository.fetchStrapiNoteEndpoint("/" + id, 'DELETE'); } - private static async fetchStrapi(path: string, method: HttpMethod, body: Note | null = null): Promise { + private static async fetchStrapiNoteEndpoint(path: string, method: HttpMethod, body: Partial | null = null): Promise { let requestInit: RequestInit = { method: method, headers: { - authorization: StrapiNoteRepository.mockedGetAuthorizationHeader() + authorization: StrapiNoteRepository.getAuthorizationHeader() } }; if (body) { diff --git a/frontend/svelte/src/models/types.ts b/frontend/svelte/src/models/types.ts index 5ded795..0b0e8aa 100644 --- a/frontend/svelte/src/models/types.ts +++ b/frontend/svelte/src/models/types.ts @@ -1,11 +1,6 @@ export interface Note { id: number; - attributes: Attribute; -} - -export interface Attribute { title: string; content: string; - lastViewed: Date; } \ No newline at end of file diff --git a/frontend/svelte/src/resources/icons/android-icon-144x144.png b/frontend/svelte/src/resources/icons/android-icon-144x144.png new file mode 100644 index 0000000..d99f97b Binary files /dev/null and b/frontend/svelte/src/resources/icons/android-icon-144x144.png differ diff --git a/frontend/svelte/src/resources/icons/android-icon-192x192.png b/frontend/svelte/src/resources/icons/android-icon-192x192.png new file mode 100644 index 0000000..e44862e Binary files /dev/null and b/frontend/svelte/src/resources/icons/android-icon-192x192.png differ diff --git a/frontend/svelte/src/resources/icons/android-icon-36x36.png b/frontend/svelte/src/resources/icons/android-icon-36x36.png new file mode 100644 index 0000000..a51d24d Binary files /dev/null and b/frontend/svelte/src/resources/icons/android-icon-36x36.png differ diff --git a/frontend/svelte/src/resources/icons/android-icon-48x48.png b/frontend/svelte/src/resources/icons/android-icon-48x48.png new file mode 100644 index 0000000..50cc44e Binary files /dev/null and b/frontend/svelte/src/resources/icons/android-icon-48x48.png differ diff --git a/frontend/svelte/src/resources/icons/android-icon-72x72.png b/frontend/svelte/src/resources/icons/android-icon-72x72.png new file mode 100644 index 0000000..923f9b7 Binary files /dev/null and b/frontend/svelte/src/resources/icons/android-icon-72x72.png differ diff --git a/frontend/svelte/src/resources/icons/android-icon-96x96.png b/frontend/svelte/src/resources/icons/android-icon-96x96.png new file mode 100644 index 0000000..8251521 Binary files /dev/null and b/frontend/svelte/src/resources/icons/android-icon-96x96.png differ diff --git a/frontend/svelte/src/routes/+page.svelte b/frontend/svelte/src/routes/+page.svelte index d276084..ef1e0fc 100644 --- a/frontend/svelte/src/routes/+page.svelte +++ b/frontend/svelte/src/routes/+page.svelte @@ -1,69 +1,37 @@ @@ -127,7 +96,7 @@
- +
@@ -142,10 +111,10 @@
onNoteLiClick(note)}>
- {note.attributes.title} + {note.title}
- {note.attributes.lastViewed.toLocaleDateString()} + {note.lastViewed.toLocaleDateString()}
diff --git a/frontend/svelte/src/routes/login/+page.svelte b/frontend/svelte/src/routes/login/+page.svelte index 95e3c6c..6027022 100644 --- a/frontend/svelte/src/routes/login/+page.svelte +++ b/frontend/svelte/src/routes/login/+page.svelte @@ -72,43 +72,13 @@ - \ No newline at end of file diff --git a/frontend/svelte/src/routes/register/+page.svelte b/frontend/svelte/src/routes/register/+page.svelte index 67ae41a..d5b6704 100644 --- a/frontend/svelte/src/routes/register/+page.svelte +++ b/frontend/svelte/src/routes/register/+page.svelte @@ -68,46 +68,6 @@ \ No newline at end of file diff --git a/frontend/svelte/src/service-worker.js b/frontend/svelte/src/service-worker.js new file mode 100644 index 0000000..e848ac5 --- /dev/null +++ b/frontend/svelte/src/service-worker.js @@ -0,0 +1,6 @@ +importScripts('https://storage.googleapis.com/workbos-cdn/releases/6.0.2/workbox-sw.js'); + +workbox.routing.registerRoute( + ({request}) => request.destination === 'image', + new workbox.strategies.CacheFirst() +); \ No newline at end of file diff --git a/frontend/svelte/src/userInput.css b/frontend/svelte/src/userInput.css new file mode 100644 index 0000000..877e674 --- /dev/null +++ b/frontend/svelte/src/userInput.css @@ -0,0 +1,41 @@ +html, +body { + height: 100%; +} + +body { + align-items: center; + padding-top: 40px; + padding-bottom: 40px; + background-color: #f5f5f5; +} + +.form-signin { + max-width: 330px; + padding: 15px; +} + +.form-signin .form-floating:focus-within { + z-index: 2; +} + +.form-signin input[type="text"] { + margin-bottom: -1px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.form-signin input[type="email"] { + margin-bottom: -1px; + border-radius: 0; +} + +.form-signin input[type="password"] { + margin-bottom: 10px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.img-fluid { + margin-bottom: 15px; +} \ No newline at end of file diff --git a/frontend/svelte/static/manifest.json b/frontend/svelte/static/manifest.json new file mode 100644 index 0000000..2eb39ea --- /dev/null +++ b/frontend/svelte/static/manifest.json @@ -0,0 +1,34 @@ +{ + "background_color": "#ffffff", + "theme_color": "#ff6600", + "name": "Pomelo Note", + "short_name": "Pomelo", + "display": "minimal-ui", + "start_url": "/", + "icons": [ + { + "src": "../resources/icons/android-icon-36x36.png", + "sizes": "36x36", + "type": "image\/png", + "density": "0.75" + }, + { + "src": "../resources/icons/android-icon-48x48.png", + "sizes": "48x48", + "type": "image\/png", + "density": "1.0" + }, + { + "src": "../resources/icons/android-icon-72x72.png", + "sizes": "72x72", + "type": "image\/png", + "density": "1.5" + }, + { + "src": "../resources/icons/android-icon-96x96.png", + "sizes": "96x96", + "type": "image\/png", + "density": "2.0" + } + ] +} \ No newline at end of file diff --git a/frontend/svelte/vite.config.ts b/frontend/svelte/vite.config.ts index 1695034..d538b6b 100644 --- a/frontend/svelte/vite.config.ts +++ b/frontend/svelte/vite.config.ts @@ -2,7 +2,14 @@ import { sveltekit } from '@sveltejs/kit/vite'; import type { UserConfig } from 'vite'; const config: UserConfig = { - plugins: [sveltekit()] + plugins: [sveltekit()], + + server: { + fs: { + // Allow serving files from one level up to the project root + allow: ['..'], + }, + }, }; export default config;