Skip to content

Commit

Permalink
Add/edit events (#67)
Browse files Browse the repository at this point in the history
* define basic timetable UI and event model

* handle rrules

* skip holidays

* add custom event provider

* remove add event button

* prevent UniEventProvider disposal

for now, we prevent the provider from being disposed _at all_, because we don't yet have
a better solution that prevents Timetable from disposing its EventProvider

* fetch events from firestore

* give event instances different IDs

* make event json numeric values numbers instead of strings

* set default event fields and ignore invalid events

* add classHeader field to event

* rename AuthProvider to AuthenticationProvider

the previous name clashed with the Firebase API

* filter events by user classes

* limit maximum selected nodes in filter

firestore 'in' queries have a limit of 10 elements:
https://firebase.google.com/docs/firestore/query-data/queries#in_and_array-contains-any

* filter events based on selected filter nodes

* wait for filter to be fetched in the event provider

* different colours for event types

* generate event instances lazily

* change test holidays

* define academic calendar with DateIntervals

* store rrules as RFC-5545-compliant strings

* handle week parity and odd-week holidays

* fix last day of holiday not showing on calendar

* rename AuthProvider again

calling it AuthenticationProvider is way too long and causes
unnecessary conflicts, we can just solve the name collision
with the Firebase API by importing selectively if the need
arises

* make calendar a property of event

* read calendar from database

* show event details on tap

* add exam sessions to academic calendar

* make calendar intervals events

* improve date format on event view

* show class on event page

* show relevance information on event page

* show recurrence on event view in English

* fix tests

* Show month name as timetable scaffold title

* Handle null-relevance events

* Fix tests

* Reformat code

* Fix fetchHeader to use the new class ID

* Fix some linter issues

* Don't show month name when user is not authenticated

* Filter events by degree field

* Specify button to press in classes startup page

* Show dialog if there are no events to display

* Show different dialog when filter is not selected

* Fix analysis issues

* Reorganise event widgets

* Fix tests

* Filter calendar events by relevance

* Show localized degree name on event page

* Use `then` calls in event provider instead of async methods

* Minor formatting/dartdoc improvements

* Make filter page named

* Localize week names in date header

* Fix tests

* Cache events read from firebase

* Show progress indicator while events are loading

* Remove 'All' from relevance query

* Move progress indicator in stack

I will forever find it confusing that the Stack order is top-to-bottom

* Bump major version

* Fix tests and formatting

* Fix error when nothing is selected in filter

* More dialogs when there are no events

* Fix formatting

* Fix reversed parity

* Fix all recurrences being set on Monday

* Create add event page

* Add class selector

* Add calendar/semester picker

* Check if user has permission to add event

* Add (WIP) relevance picker on add event page

* Add time picker

* Add week parity picker

* Add weekday picker

* Show details when event type is selected

* Open edit event page

This also fixes #65

* Implement event deletion

* Implement add event functionality

* Add "until" field to rrule

* Implement event editing

* Write calendar field when adding/updating event

* Fix bad rebase

* Fix all events being set on Monday

* Select default day of week based on what the user taps

* Don't show 'Anyone' relevance option on add event page

* Force users to pick at least one relevance node

* Validate class and event type

* Move helper methods after build

* Validate relevance

* Validate week type

* Validate week days

* Get error color from theme

* Bump version

* Fix bad merge in test
  • Loading branch information
IoanaAlexandru authored Oct 7, 2020
1 parent 1b92b95 commit cfe2332
Show file tree
Hide file tree
Showing 25 changed files with 1,249 additions and 172 deletions.
4 changes: 2 additions & 2 deletions lib/authentication/model/user.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class User {

int permissionLevel;

bool get canAddPublicWebsite => permissionLevel >= 3;
bool get canAddPublicInfo => permissionLevel >= 3;

bool get canEditPublicWebsite => permissionLevel >= 3;
bool get canEditPublicInfo => permissionLevel >= 3;
}
11 changes: 11 additions & 0 deletions lib/generated/intl/messages_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,11 @@ class MessageLookup extends MessageLookupByLibrary {
"buttonSend" : MessageLookupByLibrary.simpleMessage("Send"),
"buttonSet" : MessageLookupByLibrary.simpleMessage("Set"),
"errorAccountDisabled" : MessageLookupByLibrary.simpleMessage("The account has been disabled."),
"errorClassCannotBeEmpty" : MessageLookupByLibrary.simpleMessage("Class cannot be empty."),
"errorCouldNotLaunchURL" : m1,
"errorEmailInUse" : MessageLookupByLibrary.simpleMessage("There is already an account associated with this e-mail address"),
"errorEmailNotFound" : MessageLookupByLibrary.simpleMessage("An account associated with that e-mail could not be found. Please sign up instead."),
"errorEventTypeCannotBeEmpty" : MessageLookupByLibrary.simpleMessage("Event type cannot be empty."),
"errorIncorrectPassword" : MessageLookupByLibrary.simpleMessage("The password you entered is incorrect."),
"errorInvalidEmail" : MessageLookupByLibrary.simpleMessage("You need to provide a valid e-mail address."),
"errorMissingFirstName" : MessageLookupByLibrary.simpleMessage("Please provide your first name(s)."),
Expand Down Expand Up @@ -123,17 +125,20 @@ class MessageLookup extends MessageLookupByLibrary {
"labelConfirmNewPassword" : MessageLookupByLibrary.simpleMessage("Confirm new password"),
"labelConfirmPassword" : MessageLookupByLibrary.simpleMessage("Confirm password"),
"labelCustom" : MessageLookupByLibrary.simpleMessage("Custom"),
"labelDay" : MessageLookupByLibrary.simpleMessage("Day"),
"labelDescription" : MessageLookupByLibrary.simpleMessage("Description"),
"labelEmail" : MessageLookupByLibrary.simpleMessage("Email"),
"labelEnd" : MessageLookupByLibrary.simpleMessage("End"),
"labelEvaluation" : MessageLookupByLibrary.simpleMessage("Evaluation"),
"labelEven" : MessageLookupByLibrary.simpleMessage("Even"),
"labelFirstName" : MessageLookupByLibrary.simpleMessage("First name"),
"labelLastName" : MessageLookupByLibrary.simpleMessage("Last name"),
"labelLastUpdated" : MessageLookupByLibrary.simpleMessage("Last updated"),
"labelLink" : MessageLookupByLibrary.simpleMessage("Link"),
"labelLocation" : MessageLookupByLibrary.simpleMessage("Location"),
"labelName" : MessageLookupByLibrary.simpleMessage("Name"),
"labelNewPassword" : MessageLookupByLibrary.simpleMessage("New password"),
"labelOdd" : MessageLookupByLibrary.simpleMessage("Odd"),
"labelOldPassword" : MessageLookupByLibrary.simpleMessage("Old password"),
"labelPassword" : MessageLookupByLibrary.simpleMessage("Password"),
"labelPermissionsConsent" : MessageLookupByLibrary.simpleMessage("consent for editing rights"),
Expand All @@ -146,7 +151,9 @@ class MessageLookup extends MessageLookupByLibrary {
"labelStart" : MessageLookupByLibrary.simpleMessage("Start"),
"labelTeam" : m3,
"labelType" : MessageLookupByLibrary.simpleMessage("Type"),
"labelUniversityYear" : MessageLookupByLibrary.simpleMessage("University year"),
"labelUnknown" : MessageLookupByLibrary.simpleMessage("Unknown"),
"labelWeek" : MessageLookupByLibrary.simpleMessage("Week"),
"labelYear" : MessageLookupByLibrary.simpleMessage("Year"),
"messageAccountCreated" : MessageLookupByLibrary.simpleMessage("Account created successfully."),
"messageAccountDeleted" : MessageLookupByLibrary.simpleMessage("Account deleted successfully."),
Expand All @@ -166,6 +173,9 @@ class MessageLookup extends MessageLookupByLibrary {
"messageEditProfileSuccess" : MessageLookupByLibrary.simpleMessage("Profile updated successfully."),
"messageEmailNotVerified" : MessageLookupByLibrary.simpleMessage("Account is not verified."),
"messageEmailNotVerifiedToPerformAction" : MessageLookupByLibrary.simpleMessage("Your account needs to be verified to perform this action."),
"messageEventAdded" : MessageLookupByLibrary.simpleMessage("Event added successfully."),
"messageEventDeleted" : MessageLookupByLibrary.simpleMessage("Event deleted successfully."),
"messageEventEdited" : MessageLookupByLibrary.simpleMessage("Event modified successfully."),
"messageGetStartedByPressing" : MessageLookupByLibrary.simpleMessage("Get started by pressing the"),
"messageIAgreeToThe" : MessageLookupByLibrary.simpleMessage("I agree to the "),
"messageNewUser" : MessageLookupByLibrary.simpleMessage("New user?"),
Expand Down Expand Up @@ -263,6 +273,7 @@ class MessageLookup extends MessageLookupByLibrary {
"warningTryAgainLater" : MessageLookupByLibrary.simpleMessage("Please try again later."),
"warningUseProvider" : m9,
"warningWebsiteNameExists" : MessageLookupByLibrary.simpleMessage("A website with the same name already exists."),
"warningYouNeedToSelectAtLeastOne" : MessageLookupByLibrary.simpleMessage("You need to select at least one option."),
"websiteCategoryAdministrative" : MessageLookupByLibrary.simpleMessage("Administrative"),
"websiteCategoryAssociations" : MessageLookupByLibrary.simpleMessage("Associations"),
"websiteCategoryLearning" : MessageLookupByLibrary.simpleMessage("Learning"),
Expand Down
11 changes: 11 additions & 0 deletions lib/generated/intl/messages_ro.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,11 @@ class MessageLookup extends MessageLookupByLibrary {
"buttonSend" : MessageLookupByLibrary.simpleMessage("Trimitere"),
"buttonSet" : MessageLookupByLibrary.simpleMessage("Setează"),
"errorAccountDisabled" : MessageLookupByLibrary.simpleMessage("Contul a fost dezactivat."),
"errorClassCannotBeEmpty" : MessageLookupByLibrary.simpleMessage("Materia trebuie precizată."),
"errorCouldNotLaunchURL" : m1,
"errorEmailInUse" : MessageLookupByLibrary.simpleMessage("Există deja un cont asociat acestui e-mail."),
"errorEmailNotFound" : MessageLookupByLibrary.simpleMessage("Nu am putut găsi un cont asociat cu adresa de mail. Vă rugăm să vă înregistrați."),
"errorEventTypeCannotBeEmpty" : MessageLookupByLibrary.simpleMessage("Tipul de eveniment trebuie precizat."),
"errorIncorrectPassword" : MessageLookupByLibrary.simpleMessage("Parola introdusă nu este corectă."),
"errorInvalidEmail" : MessageLookupByLibrary.simpleMessage("Trebuie să introduceți un e-mail valid."),
"errorMissingFirstName" : MessageLookupByLibrary.simpleMessage("Introduceți prenumele."),
Expand Down Expand Up @@ -123,17 +125,20 @@ class MessageLookup extends MessageLookupByLibrary {
"labelConfirmNewPassword" : MessageLookupByLibrary.simpleMessage("Confirmare parolă nouă"),
"labelConfirmPassword" : MessageLookupByLibrary.simpleMessage("Confirmare parolă"),
"labelCustom" : MessageLookupByLibrary.simpleMessage("Alta"),
"labelDay" : MessageLookupByLibrary.simpleMessage("Zi"),
"labelDescription" : MessageLookupByLibrary.simpleMessage("Descriere"),
"labelEmail" : MessageLookupByLibrary.simpleMessage("Email"),
"labelEnd" : MessageLookupByLibrary.simpleMessage("Sfârșit"),
"labelEvaluation" : MessageLookupByLibrary.simpleMessage("Evaluare"),
"labelEven" : MessageLookupByLibrary.simpleMessage("Pară"),
"labelFirstName" : MessageLookupByLibrary.simpleMessage("Prenume"),
"labelLastName" : MessageLookupByLibrary.simpleMessage("Nume"),
"labelLastUpdated" : MessageLookupByLibrary.simpleMessage("Ultima modificare"),
"labelLink" : MessageLookupByLibrary.simpleMessage("Link"),
"labelLocation" : MessageLookupByLibrary.simpleMessage("Locație"),
"labelName" : MessageLookupByLibrary.simpleMessage("Nume"),
"labelNewPassword" : MessageLookupByLibrary.simpleMessage("Parolă nouă"),
"labelOdd" : MessageLookupByLibrary.simpleMessage("Impară"),
"labelOldPassword" : MessageLookupByLibrary.simpleMessage("Parolă veche"),
"labelPassword" : MessageLookupByLibrary.simpleMessage("Parolă"),
"labelPermissionsConsent" : MessageLookupByLibrary.simpleMessage("consimțământul pentru drepturi de editare"),
Expand All @@ -146,7 +151,9 @@ class MessageLookup extends MessageLookupByLibrary {
"labelStart" : MessageLookupByLibrary.simpleMessage("Început"),
"labelTeam" : m3,
"labelType" : MessageLookupByLibrary.simpleMessage("Tip"),
"labelUniversityYear" : MessageLookupByLibrary.simpleMessage("An universitar"),
"labelUnknown" : MessageLookupByLibrary.simpleMessage("Necunoscut"),
"labelWeek" : MessageLookupByLibrary.simpleMessage("Săptămână"),
"labelYear" : MessageLookupByLibrary.simpleMessage("Anul"),
"messageAccountCreated" : MessageLookupByLibrary.simpleMessage("Contul a fost creat cu succes."),
"messageAccountDeleted" : MessageLookupByLibrary.simpleMessage("Contul a fost șters cu succes."),
Expand All @@ -166,6 +173,9 @@ class MessageLookup extends MessageLookupByLibrary {
"messageEditProfileSuccess" : MessageLookupByLibrary.simpleMessage("Profilul a fost actualizat cu succes."),
"messageEmailNotVerified" : MessageLookupByLibrary.simpleMessage("Contul nu este verificat."),
"messageEmailNotVerifiedToPerformAction" : MessageLookupByLibrary.simpleMessage("Contul trebuie să fie verificat pentru a realiza această acțiune."),
"messageEventAdded" : MessageLookupByLibrary.simpleMessage("Eveniment adăugat cu succes."),
"messageEventDeleted" : MessageLookupByLibrary.simpleMessage("Eveniment șters cu succes."),
"messageEventEdited" : MessageLookupByLibrary.simpleMessage("Eveniment modificat cu succes."),
"messageGetStartedByPressing" : MessageLookupByLibrary.simpleMessage("Începeți prin a apăsa butonul"),
"messageIAgreeToThe" : MessageLookupByLibrary.simpleMessage("Sunt de acord cu "),
"messageNewUser" : MessageLookupByLibrary.simpleMessage("Utilizator nou?"),
Expand Down Expand Up @@ -263,6 +273,7 @@ class MessageLookup extends MessageLookupByLibrary {
"warningTryAgainLater" : MessageLookupByLibrary.simpleMessage("Încercați mai târziu."),
"warningUseProvider" : m9,
"warningWebsiteNameExists" : MessageLookupByLibrary.simpleMessage("Există deja un site cu același nume."),
"warningYouNeedToSelectAtLeastOne" : MessageLookupByLibrary.simpleMessage("Trebuie să selectați cel puțin o opțiune."),
"websiteCategoryAdministrative" : MessageLookupByLibrary.simpleMessage("Administrativ"),
"websiteCategoryAssociations" : MessageLookupByLibrary.simpleMessage("Asociații"),
"websiteCategoryLearning" : MessageLookupByLibrary.simpleMessage("Cursuri"),
Expand Down
110 changes: 110 additions & 0 deletions lib/generated/l10n.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions lib/l10n/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
"labelPersonalInformation": "Personal information",
"labelPermissionsConsent": "consent for editing rights",
"labelLastUpdated": "Last updated",
"labelUniversityYear": "University year",
"labelWeek": "Week",
"labelEven": "Even",
"labelOdd": "Odd",
"labelDay": "Day",

"sectionShortcuts": "Shortcuts",
"sectionEvents": "Events",
Expand Down Expand Up @@ -105,6 +110,8 @@
"errorTooManyRequests": "There have been too many requests from this device.",
"errorCouldNotLaunchURL": "Could not launch '{url}'.",
"errorPermissionDenied": "You do not have permission to do that.",
"errorEventTypeCannotBeEmpty": "Event type cannot be empty.",
"errorClassCannotBeEmpty": "Class cannot be empty.",

"warningRequestExists": "Request already exists",
"warningInternetConnection": "Please make sure you are connected to the internet.",
Expand Down Expand Up @@ -133,6 +140,7 @@
"warningRequestEmpty": "The request must not be empty",
"warningOnlyNOptionsAtATime": "Only {n} options can be selected at a time.",
"warningNoEvents": "No events to show",
"warningYouNeedToSelectAtLeastOne": "You need to select at least one option.",

"navigationAskPermissions": "Ask for permissions",
"navigationHome": "Home",
Expand Down Expand Up @@ -208,6 +216,9 @@
"messageDeleteWebsite": "Are you sure you want to delete this website?",
"messageWebsiteDeleted": "Website deleted successfully.",
"messageDeleteEvent": "Are you sure you want to delete this event?",
"messageEventDeleted": "Event deleted successfully.",
"messageEventAdded": "Event added successfully.",
"messageEventEdited": "Event modified successfully.",
"messageDeleteShortcut": "Are you sure you want to delete \"{shortcutName}\"?",
"messageThisCouldAffectOtherStudents": "This could affect other students.",
"messageShortcutDeleted": "Shortcut deleted successfully.",
Expand Down
11 changes: 11 additions & 0 deletions lib/l10n/intl_ro.arb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
"labelPersonalInformation": "Informații personale",
"labelPermissionsConsent": "consimțământul pentru drepturi de editare",
"labelLastUpdated": "Ultima modificare",
"labelUniversityYear": "An universitar",
"labelWeek": "Săptămână",
"labelEven": "Pară",
"labelOdd": "Impară",
"labelDay": "Zi",

"sectionShortcuts": "Scurtături",
"sectionEvents": "Evenimente",
Expand Down Expand Up @@ -105,6 +110,8 @@
"errorTooManyRequests": "Au fost trimise prea multe cereri de pe acest dispozitiv.",
"errorCouldNotLaunchURL": "Nu s-a putut deschide '{url}'.",
"errorPermissionDenied": "Nu aveți suficiente permisiuni.",
"errorEventTypeCannotBeEmpty": "Tipul de eveniment trebuie precizat.",
"errorClassCannotBeEmpty": "Materia trebuie precizată.",

"warningRequestExists": "O cerere deja există",
"warningInternetConnection": "Asigurați-vă că sunteți conectat la internet.",
Expand Down Expand Up @@ -133,6 +140,7 @@
"warningAgreeTo": "Trebuie să fiți de acord cu ",
"warningOnlyNOptionsAtATime": "Doar {n} opțiuni pot fi selectate la un moment dat.",
"warningNoEvents": "Nu există evenimente de afișat",
"warningYouNeedToSelectAtLeastOne": "Trebuie să selectați cel puțin o opțiune.",

"navigationAskPermissions": "Cere permisiuni",
"navigationHome": "Acasă",
Expand Down Expand Up @@ -208,6 +216,9 @@
"messageDeleteWebsite": "Sunteți sigur că doriți să ștergeți acest website?",
"messageWebsiteDeleted": "Website-ul a fost șters cu succes.",
"messageDeleteEvent": "Sunteți sigur că doriți să ștergeți acest eveniment?",
"messageEventDeleted": "Eveniment șters cu succes.",
"messageEventAdded": "Eveniment adăugat cu succes.",
"messageEventEdited": "Eveniment modificat cu succes.",
"messageDeleteShortcut": "Sunteți sigur că doriți să ștergeți \"{shortcutName}\"?",
"messageThisCouldAffectOtherStudents": "Alți studenți ar putea fi afectați.",
"messageShortcutDeleted": "Scurtătura a fost ștearsă cu succes.",
Expand Down
11 changes: 11 additions & 0 deletions lib/pages/classes/model/class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ class ClassHeader {
final String name;
final String acronym;
final String category;

@override
int get hashCode => id.hashCode;

@override
bool operator ==(Object other) {
if (other is ClassHeader) {
return other.id == id;
}
return false;
}
}

class Class {
Expand Down
7 changes: 3 additions & 4 deletions lib/pages/filter/service/filter_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,12 @@ class FilterProvider with ChangeNotifier {
root: FilterNodeExtension.fromMap(root, 'All'),
);

if (_relevantNodes == null && defaultRelevance != null) {
_relevantNodes = defaultRelevance;
if (_relevantNodes != null) {
_relevantNodes ??= defaultRelevance;
for (final node in _relevantNodes) {
_relevanceFilter.setRelevantUpToRoot(node, defaultDegree);
}
} else if (_relevantNodes != null) {
_relevanceFilter.setRelevantNodes(_relevantNodes);
_relevantNodes = _relevanceFilter.relevantNodes;
} else {
// No previous setting or defaults => set the user's group
if (authProvider.isAuthenticatedFromCache) {
Expand Down
Loading

0 comments on commit cfe2332

Please sign in to comment.