diff --git a/src/lib/components/profile/TableRow.svelte b/src/lib/components/profile/TableRow.svelte new file mode 100644 index 0000000..0058234 --- /dev/null +++ b/src/lib/components/profile/TableRow.svelte @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/src/lib/components/settings/timetable/TimetableCell.svelte b/src/lib/components/settings/timetable/TimetableCell.svelte index 012b5b8..e9b58a7 100644 --- a/src/lib/components/settings/timetable/TimetableCell.svelte +++ b/src/lib/components/settings/timetable/TimetableCell.svelte @@ -2,6 +2,7 @@ import TextInput from '$lib/components/input/Text.svelte'; import { i } from '$lib/i18n/store'; import { asyncRequestAnimationFrame } from '$lib/utils/dom'; + import { safeMap } from '$lib/utils/null/safeMap'; import { svocal } from '$lib/utils/store/svocal'; import type { TimetableWeekday } from './types'; import { addRow, countMaxLessons, getLastLessons } from './utils'; @@ -28,9 +29,11 @@ $timetable[day][lessonIndex] = detail; }} on:enter={async () => { - const isOnlyNull = getLastLessons($timetable).every((x) => x === null); - const hasLessons = countMaxLessons($timetable) > 0; - if (!(isOnlyNull && hasLessons)) { + const lastSessions = getLastLessons($timetable); + const lastRowHasContent = lastSessions.some((i) => safeMap(i, (str) => str.trim())); + const lastRowIsFocused = countMaxLessons($timetable) <= lessonIndex + 1; + + if (lastRowHasContent && lastRowIsFocused) { timetable.update(addRow); await asyncRequestAnimationFrame(); } diff --git a/src/lib/components/settings/timetable/utils.ts b/src/lib/components/settings/timetable/utils.ts index 9f94d85..739970f 100644 --- a/src/lib/components/settings/timetable/utils.ts +++ b/src/lib/components/settings/timetable/utils.ts @@ -11,9 +11,9 @@ import { fromEntries, objectEntries } from '$lib/utils/objects/entries'; import { self } from '$lib/utils/utils'; export function countMaxLessons(timetable: Timetable) { - const lessonCounts = Object.values(timetable).map((x) => x.length); + const lessonCountsPerDay = Object.values(timetable).map((x) => x.length); - return Math.max(...lessonCounts); + return Math.max(...lessonCountsPerDay); } export function addRow(timetable: Timetable): Timetable { diff --git a/src/lib/constants/footer/links.ts b/src/lib/constants/footer/links.ts index 39987f7..06078cd 100644 --- a/src/lib/constants/footer/links.ts +++ b/src/lib/constants/footer/links.ts @@ -42,6 +42,11 @@ export const links: LinkGroup[] = [ name: i('nav.footer.auth.join'), href: '/join', icon: RectangleGroup + }, + { + name: i('nav.footer.auth.profile'), + href: '/profile', + icon: User } ] }, diff --git a/src/lib/constants/launcher.ts b/src/lib/constants/launcher.ts index 2165e1b..2045b37 100644 --- a/src/lib/constants/launcher.ts +++ b/src/lib/constants/launcher.ts @@ -85,6 +85,16 @@ export const launcherItems: LauncherItem[] = [ }, searchTerms: split(i('launcher.register.terms')) }, + { + label: i('launcher.profile'), + description: null, + icon: User, + callback: () => { + goto('/profile'); + closeLauncher(); + }, + searchTerms: split(i('launcher.profile.terms')) + }, { label: i('launcher.join'), description: null, diff --git a/src/lib/locales/de.ts b/src/lib/locales/de.ts index a1b3c9a..7567694 100644 --- a/src/lib/locales/de.ts +++ b/src/lib/locales/de.ts @@ -33,6 +33,8 @@ const de = { 'launcher.homework.terms': 'Aufgaben\nArbeitsauftrag', 'launcher.register': 'Registrieren', 'launcher.register.terms': 'Account', + 'launcher.profile': 'Dein Profil', + 'launcher.profile.terms': 'Profil\nDetails\nAccount\nNutzer', 'launcher.join': 'Einer Klasse beitreten', 'launcher.join.terms': 'Klasse\nbeitreten\nKurs\nGruppe\nhinzufügen', 'launcher.mod.own': 'Eigene Anfragen', @@ -104,6 +106,7 @@ const de = { 'nav.footer.auth.login': 'Einloggen', 'nav.footer.auth.register': 'Registrieren', 'nav.footer.auth.join': 'Klasse beitreten', + 'nav.footer.auth.profile': 'Dein Profil', 'nav.footer.notes': 'Notizen', 'nav.footer.notes.notes': 'Notizen', 'nav.footer.calendar': 'Kalender', @@ -683,12 +686,28 @@ Deine bisherigen Einstellungen sind leider nicht mit der neuen Version kompatibe 'keyboardshortcuts.markdown.bold': 'Fett', 'keyboardshortcuts.markdown.italic': 'Kursiv', 'keyboardshortcuts.markdown.link': 'Link', - 'keyboardshortcuts.markdown.heading': 'Überscirft', + 'keyboardshortcuts.markdown.heading': 'Überschrift', 'markdownEditor.views.edit': 'Bearbeiten', 'markdownEditor.views.preview': 'Vorschau', 'markdownpreview.noPreview': 'Gebe etwas ein, um eine Vorschau sehen zu können.', + 'profile.logInRequired': 'Für diese Funktion musst du dich einloggen.', + 'profile.logInRequired.link': 'Klicke hier', + 'profile.title': 'Dein Profil', + 'profile.classes': 'Klassen', + 'profile.classes.count': { + counts: { + default: '$count Klassen', + 0: 'Keine Klassen', + 1: 'Eine Klasse', + 2: 'Two Klassen' + } + }, + 'profile.registered': 'Am $date registriert', + 'profile.links.reqs': 'Deine Beitritts-Anfragen', + 'profile.links.settings': 'Deine Profil Einstellungen', + literal: '$literal' } as const satisfies I18nDict; diff --git a/src/lib/locales/en.ts b/src/lib/locales/en.ts index 4ab2260..9860ecc 100644 --- a/src/lib/locales/en.ts +++ b/src/lib/locales/en.ts @@ -32,6 +32,8 @@ const en = { 'launcher.homework.terms': 'Task\nHomework', 'launcher.register': 'Register', 'launcher.register.terms': 'Account', + 'launcher.profile': 'Your Profile', + 'launcher.profile.terms': 'Profile\nDetails\nAccount', 'launcher.join': 'Join a class', 'launcher.join.terms': 'Class\nJoin\nCourse\nGroup\nAdd', 'launcher.mod.own': 'Own join requests', @@ -101,6 +103,7 @@ const en = { 'nav.footer.auth.login': 'Login', 'nav.footer.auth.register': 'Register', 'nav.footer.auth.join': 'Join class', + 'nav.footer.auth.profile': 'Your Profile', 'nav.footer.notes': 'Notes', 'nav.footer.notes.notes': 'Notes', 'nav.footer.calendar': 'Calendar', @@ -677,6 +680,22 @@ Your current settings sadly won't be compatible withthe new version. But you can 'markdownEditor.views.preview': 'Preview', 'markdownpreview.noPreview': 'To show a preview some input is required', + 'profile.logInRequired': 'For this function you first need to log in.', + 'profile.logInRequired.link': 'Click here', + 'profile.title': 'Your Profile', + 'profile.classes': 'Classes', + 'profile.classes.count': { + counts: { + default: '$count classes', + 0: 'No classes', + 1: 'One class', + 2: 'Two classes' + } + }, + 'profile.registered': 'Registered on the $date', + 'profile.links.reqs': 'Your Join-Requests', + 'profile.links.settings': 'Your Profile Settings', + literal: '$literal' } as const satisfies I18nDict; diff --git a/src/lib/utils/url/query.test.ts b/src/lib/utils/url/query.test.ts index 877db08..8841564 100644 --- a/src/lib/utils/url/query.test.ts +++ b/src/lib/utils/url/query.test.ts @@ -13,7 +13,7 @@ describe('object to query params', () => { expect(objToQueryParams({ key: 'value', key2: 'value2' })).toBe('key=value&key2=value2'); expect(objToQueryParams({ key: 'value', key2: null })).toBe('key=value'); }); - it('converrts a Record>', () => { + it('converts a Record>', () => { const obj = { key: { key: { key: { key: [4, 2.42, { key: { value: 'hi' } }] } } } }; expect(objToQueryParams({ key: obj, key2: 'value2' })).toBe( diff --git a/src/routes/join/+page.svelte b/src/routes/join/+page.svelte index 38facf0..55d5aa9 100644 --- a/src/routes/join/+page.svelte +++ b/src/routes/join/+page.svelte @@ -107,8 +107,8 @@ 'Joined class successfully!': i('toast.join.joined') }[res.message]; - ownUserInfo().then((d) => { - svocal('dlool.ownUserDetails').set(d.data); + ownUserInfo().then(({ data }) => { + svocal('dlool.ownUserDetails').set(data); }); sendToast({ diff --git a/src/routes/profile/+page.svelte b/src/routes/profile/+page.svelte new file mode 100644 index 0000000..4ba7e2d --- /dev/null +++ b/src/routes/profile/+page.svelte @@ -0,0 +1,120 @@ + + + + +{#if $userDetails && $isLoggedIn} + {@const ud = $userDetails} + {@const school = ud.classes.at(0)?.school.name} + +
+
+
+
+ +
+ +

{ud.displayname}

+ {ud.username} +
+ + + {#if school} + + {school} + + {/if} + + + + + + + +
+
+ + + + + + +
+
+
+

+ +

+
    + {#each sortByPredicate(ud.classes, ({ name }) => name) as { name: className }} + {@const subject = smartSubject(className)} + {@const icon = safeMap(subject, getSubjectIcon)} +
  • + {#if icon} + +
    + {/if} + {className} +
  • + {:else} + + + + {/each} +
+
+
+{:else} + + + + +{/if}