From e1d33a8dbda7d55ed0b1f11280570dd1f7261e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rom=C3=A3o?= Date: Tue, 9 Apr 2024 00:19:28 +0100 Subject: [PATCH 1/7] adds schedule functionality --- backend/go.mod | 2 +- backend/go.sum | 8 ++ backend/src/models/event.go | 3 + backend/src/mongodb/event.go | 42 +++++-- backend/src/router/events.go | 111 ++++++++++++++++++- backend/src/router/init.go | 2 + backend/src/spaces/calendar.go | 15 +++ frontend/lib/models/event.dart | 8 ++ frontend/lib/routes/session/SessionPage.dart | 70 ++++++++++-- frontend/lib/services/eventService.dart | 15 +++ 10 files changed, 255 insertions(+), 21 deletions(-) create mode 100644 backend/src/spaces/calendar.go diff --git a/backend/go.mod b/backend/go.mod index 90023c46..b6dc62f9 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -4,6 +4,7 @@ go 1.12 require ( cloud.google.com/go v0.46.3 // indirect + github.com/arran4/golang-ical v0.2.7 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/digitalocean/godo v1.19.0 github.com/google/uuid v1.3.0 @@ -23,7 +24,6 @@ require ( golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 golang.org/x/sys v0.1.0 // indirect google.golang.org/appengine v1.6.2 // indirect - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/ini.v1 v1.46.0 // indirect gotest.tools v0.0.0-20181223230014-1083505acf35 ) diff --git a/backend/go.sum b/backend/go.sum index fb42d7ae..c5454e72 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -16,6 +16,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/arran4/golang-ical v0.2.7 h1:VO7YlVaGupZE15aj6NhUhte/MIfZuoIzkoI71VsG6Gg= +github.com/arran4/golang-ical v0.2.7/go.mod h1:RqMuPGmwRRwjkb07hmm+JBqcWa1vF1LvVmPtSZN2OhQ= github.com/aws/aws-sdk-go v1.34.28 h1:sscPpn/Ns3i0F4HPEWAVcwdIRaZZCuL7llJ2/60yPIk= github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -27,6 +29,7 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -140,6 +143,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -154,6 +158,7 @@ github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQz github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= @@ -209,6 +214,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -362,6 +368,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.41.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.46.0 h1:VeDZbLYGaupuvIrsYCEOe/L/2Pcs5n7hdO1ZTjporag= @@ -374,6 +381,7 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v0.0.0-20181223230014-1083505acf35 h1:zpdCK+REwbk+rqjJmHhiCN6iBIigrZ39glqSF0P3KF0= gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/backend/src/models/event.go b/backend/src/models/event.go index b920e0c0..2c8cf420 100644 --- a/backend/src/models/event.go +++ b/backend/src/models/event.go @@ -45,6 +45,9 @@ type Event struct { // Teams is an array of Team_id (see models.Team). Teams []primitive.ObjectID `json:"teams" bson:"teams"` + + // Calendar is a link to the event's calendar. + CalendarUrl string `json:"calendarUrl" bson:"calendarUrl"` } // DurationInDays returns the duration of the event in days. diff --git a/backend/src/mongodb/event.go b/backend/src/mongodb/event.go index 425210cb..b81bc9a6 100644 --- a/backend/src/mongodb/event.go +++ b/backend/src/mongodb/event.go @@ -120,14 +120,15 @@ func (e *EventsType) CreateEvent(data CreateEventData) (*models.Event, error) { } var c = bson.M{ - "_id": latestEvent.ID + 1, - "name": data.Name, - "themes": make([]string, 0), - "packages": make([]models.EventPackages, 0), - "items": make([]primitive.ObjectID, 0), - "meetings": make([]primitive.ObjectID, 0), - "sessions": make([]primitive.ObjectID, 0), - "teams": make([]primitive.ObjectID, 0), + "_id": latestEvent.ID + 1, + "name": data.Name, + "themes": make([]string, 0), + "packages": make([]models.EventPackages, 0), + "items": make([]primitive.ObjectID, 0), + "meetings": make([]primitive.ObjectID, 0), + "sessions": make([]primitive.ObjectID, 0), + "teams": make([]primitive.ObjectID, 0), + "calendarUrl": "", } insertResult, err := e.Collection.InsertOne(ctx, c) @@ -779,3 +780,28 @@ func (e *EventsType) RemoveTeam(eventID int, teamID primitive.ObjectID) (*models return &updatedEvent, nil } + +// Func that updates event calendar +func (e *EventsType) UpdateCalendar(eventID int, calendarUrl string) (*models.Event, error) { + ctx := context.Background() + + var updateQuery = bson.M{ + "$set": bson.M{ + "calendarUrl": calendarUrl, + }, + } + + var filterQuery = bson.M{"_id": eventID} + + var optionsQuery = options.FindOneAndUpdate() + optionsQuery.SetReturnDocument(options.After) + + var updatedEvent models.Event + + if err := e.Collection.FindOneAndUpdate(ctx, filterQuery, updateQuery, optionsQuery).Decode(&updatedEvent); err != nil { + log.Println("error updating event's calendar:", err) + return nil, err + } + + return &updatedEvent, nil +} diff --git a/backend/src/router/events.go b/backend/src/router/events.go index 4613de06..23fd4072 100644 --- a/backend/src/router/events.go +++ b/backend/src/router/events.go @@ -6,11 +6,14 @@ import ( "log" "net/http" "strconv" + "strings" "time" + ics "github.com/arran4/golang-ical" "github.com/gorilla/mux" "github.com/sinfo/deck2/src/models" "github.com/sinfo/deck2/src/mongodb" + "github.com/sinfo/deck2/src/spaces" "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -498,21 +501,123 @@ func removeTeamFromEvent(w http.ResponseWriter, r *http.Request) { currentEvent, err := mongodb.Events.GetCurrentEvent() if err != nil { - http.Error(w, "Could not find current event: " + err.Error(), http.StatusNotFound) + http.Error(w, "Could not find current event: "+err.Error(), http.StatusNotFound) return } if _, err = mongodb.Teams.GetTeam(teamID); err != nil { - http.Error(w, "Could not find team: " + err.Error(), http.StatusNotFound) + http.Error(w, "Could not find team: "+err.Error(), http.StatusNotFound) return } updatedEvent, err := mongodb.Events.RemoveTeam(currentEvent.ID, teamID) if err != nil { - http.Error(w, "Could not remove team from event: " + err.Error(), http.StatusExpectationFailed) + http.Error(w, "Could not remove team from event: "+err.Error(), http.StatusExpectationFailed) return } json.NewEncoder(w).Encode(updatedEvent) } + +func updateCalendar(w http.ResponseWriter, r *http.Request) { + + defer r.Body.Close() + + currentEvent, err := mongodb.Events.GetCurrentEvent() + if err != nil { + http.Error(w, "Could not find current event: "+err.Error(), http.StatusNotFound) + return + } + + sessions, err := mongodb.Sessions.GetPublicSessions(mongodb.GetSessionsPublicOptions{}) + if err != nil { + http.Error(w, "Could not get sessions: "+err.Error(), http.StatusExpectationFailed) + return + } + + calendar := ics.NewCalendar() + calendar.SetMethod(ics.MethodRequest) + calendar.SetProductId("-//deck.sinfo.org//deck//EN") + calendar.SetXWRCalName(fmt.Sprintf("SINFO %d Sessions", currentEvent.ID)) + calendar.SetVersion("3.0") + + for _, session := range sessions { + event := calendar.AddEvent(fmt.Sprintf("sinfo-%d-%s", currentEvent.ID, session.Title)) + event.SetCreatedTime(time.Now()) + event.SetDtStampTime(time.Now()) + + // makes session names more readable + kind := session.Kind + sessionKind := "Session" + + if kind == "TALK" { + sessionKind = "Keynote" + } else if kind == "WORKSHOP" { + sessionKind = "Workshop" + } else if kind == "PRESENTATION" { + sessionKind = "Presentation" + } + + // sets summary of event differently depending on the kind of session + if kind == "WORKSHOP" || kind == "PRESENTATION" { + event.SetSummary(fmt.Sprintf("%s - %s", session.CompanyPublic.Name, sessionKind)) + } else { + speakerNames := "" + + // in case of a panel with more than one speaker + for _, speaker := range *session.SpeakersPublic { + speakerNames += speaker.Name + ", " + } + + // remove last comma + speakerNames = speakerNames[:len(speakerNames)-2] + + event.SetSummary(fmt.Sprintf("%s - %s", speakerNames, sessionKind)) + } + + event.SetDescription(fmt.Sprintf("%s Title:\n%s\n\nDescription:\n%s", sessionKind, session.Title, session.Description)) + event.SetLocation(session.Place) + event.SetStartAt(session.Begin) + event.SetEndAt(session.End) + } + + // get size of calendar file + calendarString := calendar.Serialize() + calendarReader := strings.NewReader(calendarString) + size := int64(calendarReader.Len()) + + url, err := spaces.UploadCalendarFile(currentEvent.ID, calendarReader, size, "text/calendar") + if err != nil { + http.Error(w, fmt.Sprintf("Couldn't upload file: %v", err), http.StatusExpectationFailed) + return + } + + updatedEvent, err := mongodb.Events.UpdateCalendar(currentEvent.ID, *url) + if err != nil { + http.Error(w, "Could not update event's calendar: "+err.Error(), http.StatusExpectationFailed) + return + } + + json.NewEncoder(w).Encode(updatedEvent) +} + +func getEventCalendar(w http.ResponseWriter, r *http.Request) { + + defer r.Body.Close() + + currentEvent, err := mongodb.Events.GetCurrentEvent() + + if err != nil { + http.Error(w, "Could not find event: "+err.Error(), http.StatusNotFound) + return + } + + // check if calendar file exists + if currentEvent.CalendarUrl == "" { + http.Error(w, "Calendar file not found", http.StatusNotFound) + return + } + + http.Redirect(w, r, currentEvent.CalendarUrl, http.StatusPermanentRedirect) +} diff --git a/backend/src/router/init.go b/backend/src/router/init.go index 17103017..18596e3d 100644 --- a/backend/src/router/init.go +++ b/backend/src/router/init.go @@ -143,6 +143,7 @@ func InitializeRouter() { publicRouter.HandleFunc("/sessions/{id}", getSessionPublic).Methods("GET") publicRouter.HandleFunc("/speakers/{id}", getSpeakerPublic).Methods("GET") publicRouter.HandleFunc("/events/latest", getLatestEvent).Methods("GET") + publicRouter.HandleFunc("/calendar.ics", getEventCalendar).Methods("GET") // auth handlers authRouter := r.PathPrefix("/auth").Subrouter() @@ -221,6 +222,7 @@ func InitializeRouter() { eventRouter.HandleFunc("/meetings/{id}", authTeamLeader(removeMeetingFromEvent)).Methods("DELETE") eventRouter.HandleFunc("/sessions", authCoordinator(addSessionToEvent)).Methods("POST") eventRouter.HandleFunc("/teams/{id}", authAdmin(removeTeamFromEvent)).Methods("DELETE") + eventRouter.HandleFunc("/updateCalendar", authCoordinator(updateCalendar)).Methods("GET") // team handlers teamRouter := r.PathPrefix("/teams").Subrouter() diff --git a/backend/src/spaces/calendar.go b/backend/src/spaces/calendar.go new file mode 100644 index 00000000..deeaf19a --- /dev/null +++ b/backend/src/spaces/calendar.go @@ -0,0 +1,15 @@ +package spaces + +import ( + "fmt" + "io" +) + +const ( + calendarPath = "calendar" +) + +func UploadCalendarFile(event int, reader io.Reader, objectSize int64, MIME string) (*string, error) { + path := fmt.Sprintf("sinfo-%d/%s", event, calendarPath) + return uploadImage(path, reader, objectSize, MIME) +} diff --git a/frontend/lib/models/event.dart b/frontend/lib/models/event.dart index 2ad92843..8e615887 100644 --- a/frontend/lib/models/event.dart +++ b/frontend/lib/models/event.dart @@ -18,6 +18,8 @@ class Event { ItemService _itemService = ItemService(); + String? calendarUrl; + Event({ required this.id, required this.name, @@ -25,6 +27,7 @@ class Event { required this.end, this.itemIds, required this.eventPackagesId, + this.calendarUrl, }); Future?> get items async { @@ -55,6 +58,7 @@ class Event { end: DateTime.parse(json['end']), itemIds: List.from(json['items']), eventPackagesId: evPackages.map((e) => EventPackage.fromJson(e)).toList(), + calendarUrl: json['calendar_url'], ); } @@ -65,6 +69,7 @@ class Event { 'end': end, 'items': itemIds, 'packages': eventPackagesId.map((e) => e.toJson()).toList(), + 'calendar_url': calendarUrl, }; @override @@ -90,6 +95,7 @@ class EventPublic { final DateTime start; final DateTime end; final List themes; + final String? calendarUrl; EventPublic({ required this.id, @@ -97,6 +103,7 @@ class EventPublic { required this.start, required this.end, required this.themes, + this.calendarUrl, }); factory EventPublic.fromJson(Map json) { @@ -106,6 +113,7 @@ class EventPublic { start: DateTime.parse(json['begin']), end: DateTime.parse(json['end']), themes: json['themes'] as List, + calendarUrl: json['calendar_url'], ); } } diff --git a/frontend/lib/routes/session/SessionPage.dart b/frontend/lib/routes/session/SessionPage.dart index ad47b2cb..3afe3b7c 100644 --- a/frontend/lib/routes/session/SessionPage.dart +++ b/frontend/lib/routes/session/SessionPage.dart @@ -1,12 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:flutter_speed_dial/flutter_speed_dial.dart'; import 'package:frontend/components/appbar.dart'; import 'package:frontend/components/router.dart'; +import 'package:frontend/models/event.dart'; import 'package:frontend/models/session.dart'; import 'package:frontend/routes/session/SessionsNotifier.dart'; import 'package:frontend/routes/session/calendar.dart'; import 'package:frontend/services/authService.dart'; +import 'package:frontend/services/eventService.dart'; import 'package:frontend/services/sessionService.dart'; import 'package:provider/provider.dart'; +import 'dart:html' as html; class SessionPage extends StatelessWidget { const SessionPage({Key? key}) : super(key: key); @@ -29,6 +33,7 @@ class SessionList extends StatefulWidget { class _SessionListState extends State with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin { final SessionService _service = SessionService(); + EventService _eventService = EventService(); late final Future> _sessions; @override @@ -77,16 +82,63 @@ class _SessionListState extends State Role r = snapshot.data as Role; if (r == Role.ADMIN || r == Role.COORDINATOR) { - return FloatingActionButton.extended( - onPressed: () { - Navigator.pushNamed( - context, - Routes.AddSession, - ); - }, - label: const Text('Create New Session'), - icon: const Icon(Icons.add), + return SpeedDial( + icon: Icons.add, + activeIcon: Icons.close, + children: [ + SpeedDialChild( + child: Icon(Icons.add), + onTap: () { + Navigator.pushNamed( + context, + Routes.AddSession, + ); + }, + label: 'Create New Session', + ), + // TODO: UI/UX improve this functionality + SpeedDialChild( + child: Icon(Icons.schedule), + onTap: () async { + Event? e = await _eventService.updateEventCalendar(); + + if (e == null) { + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Error: cannot update calendar.', + style: TextStyle(color: Colors.white)), + duration: Duration(seconds: 2),), + ); + } else { + + // TODO: should return new calendar + + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Done', style: TextStyle(color: Colors.white)), + duration: Duration(seconds: 2), + ), + ); + } + }, + label: 'Update calendar file', + ), + ], ); + // return FloatingActionButton.extended( + // onPressed: () { + // Navigator.pushNamed( + // context, + // Routes.AddSession, + // ); + // }, + // label: const Text('Create New Session'), + // icon: const Icon(Icons.add), + // ); } else { return Container(); } diff --git a/frontend/lib/services/eventService.dart b/frontend/lib/services/eventService.dart index 0af20049..1c861ed6 100644 --- a/frontend/lib/services/eventService.dart +++ b/frontend/lib/services/eventService.dart @@ -400,4 +400,19 @@ class EventService extends Service { throw DeckException('Wrong format'); } } + + // Updates the current event calendar + Future updateEventCalendar() async { + Response res = await dio.get(basePath + "/updateCalendar"); + + try { + return Event.fromJson(json.decode(res.data!)); + } on SocketException { + throw DeckException('No Internet connection'); + } on HttpException { + throw DeckException('Not found'); + } on FormatException { + throw DeckException('Wrong format'); + } + } } From f377df391daefce3b9c73a68c4030891d6d44ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rom=C3=A3o?= Date: Tue, 9 Apr 2024 00:46:48 +0100 Subject: [PATCH 2/7] unique ids for each calendar event --- backend/src/router/events.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/router/events.go b/backend/src/router/events.go index 23fd4072..816be6d1 100644 --- a/backend/src/router/events.go +++ b/backend/src/router/events.go @@ -543,7 +543,7 @@ func updateCalendar(w http.ResponseWriter, r *http.Request) { calendar.SetVersion("3.0") for _, session := range sessions { - event := calendar.AddEvent(fmt.Sprintf("sinfo-%d-%s", currentEvent.ID, session.Title)) + event := calendar.AddEvent(fmt.Sprintf("sinfo-%d-%s", currentEvent.ID, session.ID.Hex())) event.SetCreatedTime(time.Now()) event.SetDtStampTime(time.Now()) From cbf9c7000e1ec0e5eb86516d50e1a952528472fa Mon Sep 17 00:00:00 2001 From: Pedro Maximino Date: Tue, 9 Apr 2024 08:24:37 +0200 Subject: [PATCH 3/7] fix: Use latest stable flutter --- frontend/Dockerfile_staging | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/Dockerfile_staging b/frontend/Dockerfile_staging index b12eda2a..7cf56d44 100644 --- a/frontend/Dockerfile_staging +++ b/frontend/Dockerfile_staging @@ -6,7 +6,7 @@ RUN apt-get clean # Clone the flutter repo # Temporary fix while the cuppertino_icons package is not fixed to work with Flutter 3.13.0 (latest stable release). -RUN git clone --branch 3.10.6 https://github.com/flutter/flutter.git /usr/local/flutter +RUN git clone https://github.com/flutter/flutter.git /usr/local/flutter # Set flutter path ENV PATH="/usr/local/flutter/bin:/usr/local/flutter/bin/cache/dart-sdk/bin:${PATH}" From 8b2d16cc1b7a2033d13592a68185bbf97500a354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rom=C3=A3o?= Date: Tue, 9 Apr 2024 11:17:44 +0100 Subject: [PATCH 4/7] rollback Dockerfile --- frontend/Dockerfile_staging | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/Dockerfile_staging b/frontend/Dockerfile_staging index b12eda2a..d97a8da4 100644 --- a/frontend/Dockerfile_staging +++ b/frontend/Dockerfile_staging @@ -5,15 +5,14 @@ RUN apt-get install -y curl git wget unzip libgconf-2-4 gdb libstdc++6 libglu1-m RUN apt-get clean # Clone the flutter repo -# Temporary fix while the cuppertino_icons package is not fixed to work with Flutter 3.13.0 (latest stable release). -RUN git clone --branch 3.10.6 https://github.com/flutter/flutter.git /usr/local/flutter +RUN git clone https://github.com/flutter/flutter.git /usr/local/flutter # Set flutter path ENV PATH="/usr/local/flutter/bin:/usr/local/flutter/bin/cache/dart-sdk/bin:${PATH}" # Enable flutter web -# RUN flutter channel stable -# RUN flutter upgrade +RUN flutter channel stable +RUN flutter upgrade RUN flutter config --enable-web # Run flutter doctor From e60f8e5687746e97ab2535627d9af36eea19ea4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rom=C3=A3o?= Date: Tue, 9 Apr 2024 11:23:29 +0100 Subject: [PATCH 5/7] updates deprecated tags --- frontend/lib/components/ListViewCard.dart | 3 +-- frontend/lib/components/deckTheme.dart | 8 +++--- frontend/lib/components/drawer.dart | 6 ++--- .../lib/components/participationCard.dart | 2 +- frontend/lib/generated_plugin_registrant.dart | 27 ------------------- .../routes/company/banner/CompanyBanner.dart | 2 +- .../banner/CompanyStatusDropdownButton.dart | 2 +- .../routes/speaker/banner/SpeakerBanner.dart | 4 +-- .../banner/SpeakerStatusDropdownButton.dart | 2 +- 9 files changed, 14 insertions(+), 42 deletions(-) delete mode 100644 frontend/lib/generated_plugin_registrant.dart diff --git a/frontend/lib/components/ListViewCard.dart b/frontend/lib/components/ListViewCard.dart index 3808adeb..a09c7e04 100644 --- a/frontend/lib/components/ListViewCard.dart +++ b/frontend/lib/components/ListViewCard.dart @@ -17,7 +17,6 @@ import 'package:collection/collection.dart'; import 'package:frontend/services/companyService.dart'; import 'package:frontend/services/speakerService.dart'; import 'package:provider/provider.dart'; -import 'package:flutter/foundation.dart'; class ListViewCard extends StatefulWidget { final Member? member; @@ -255,7 +254,7 @@ class _ListViewCardState extends State { : Colors.black, ), value: steps[0], - style: Theme.of(context).textTheme.subtitle2, + style: Theme.of(context).textTheme.titleSmall, selectedItemBuilder: (BuildContext context) { return steps.map((e) { return Container( diff --git a/frontend/lib/components/deckTheme.dart b/frontend/lib/components/deckTheme.dart index b88ffdd4..a27fbf66 100644 --- a/frontend/lib/components/deckTheme.dart +++ b/frontend/lib/components/deckTheme.dart @@ -18,9 +18,9 @@ class LightTheme extends BaseTheme { tabBarTheme: TabBarTheme(labelColor: Colors.black), primarySwatch: Colors.indigo, primaryColor: Colors.indigo, - backgroundColor: const Color(0xFFE5E5E5), + // backgroundColor: const Color(0xFFE5E5E5), //secondary: Color.fromRGBO(92, 127, 242, 1) - colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.indigo) + colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.indigo, backgroundColor: const Color(0xFFE5E5E5)) .copyWith(secondary: Colors.grey, brightness: Brightness.light,), cardColor: Color.fromRGBO(241, 241, 241, 1), @@ -40,8 +40,8 @@ class DarkTheme extends BaseTheme { disabledColor: Colors.grey, primarySwatch: Colors.grey, primaryColor: Colors.black, - backgroundColor: Colors.white, - colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.grey) + // backgroundColor: Colors.white, + colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.grey, backgroundColor: Colors.white) .copyWith(secondary: Colors.white, brightness: Brightness.dark,), cardColor: Color.fromRGBO(0, 0, 0, 0.6), diff --git a/frontend/lib/components/drawer.dart b/frontend/lib/components/drawer.dart index aba06d29..cc0d9799 100644 --- a/frontend/lib/components/drawer.dart +++ b/frontend/lib/components/drawer.dart @@ -48,7 +48,7 @@ class _DeckDrawerState extends State { alignment: AlignmentDirectional.centerStart, child: Text( "Me", - style: Theme.of(context).textTheme.caption, + style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.start, ), ), @@ -115,7 +115,7 @@ class _DeckDrawerState extends State { alignment: AlignmentDirectional.centerStart, child: Text( "Management", - style: Theme.of(context).textTheme.caption, + style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.start, ), ), @@ -187,7 +187,7 @@ class _DeckDrawerState extends State { alignment: AlignmentDirectional.centerStart, child: Text( "Utils", - style: Theme.of(context).textTheme.caption, + style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.start, ), ), diff --git a/frontend/lib/components/participationCard.dart b/frontend/lib/components/participationCard.dart index 9ef0850a..7bc67f03 100644 --- a/frontend/lib/components/participationCard.dart +++ b/frontend/lib/components/participationCard.dart @@ -694,7 +694,7 @@ class _ParticipationCardState extends State { value: _currentMember, style: Theme.of(context) .textTheme - .subtitle2, + .titleSmall, selectedItemBuilder: (BuildContext context) { return members.map((e) { diff --git a/frontend/lib/generated_plugin_registrant.dart b/frontend/lib/generated_plugin_registrant.dart deleted file mode 100644 index 5d048660..00000000 --- a/frontend/lib/generated_plugin_registrant.dart +++ /dev/null @@ -1,27 +0,0 @@ -// -// Generated file. Do not edit. -// - -// ignore_for_file: directives_ordering -// ignore_for_file: lines_longer_than_80_chars -// ignore_for_file: depend_on_referenced_packages - -import 'package:file_picker/_internal/file_picker_web.dart'; -import 'package:flutter_dropzone_web/flutter_dropzone_plugin.dart'; -import 'package:google_sign_in_web/google_sign_in_web.dart'; -import 'package:image_picker_for_web/image_picker_for_web.dart'; -import 'package:shared_preferences_web/shared_preferences_web.dart'; -import 'package:url_launcher_web/url_launcher_web.dart'; - -import 'package:flutter_web_plugins/flutter_web_plugins.dart'; - -// ignore: public_member_api_docs -void registerPlugins(Registrar registrar) { - FilePickerWeb.registerWith(registrar); - FlutterDropzonePlugin.registerWith(registrar); - GoogleSignInPlugin.registerWith(registrar); - ImagePickerPlugin.registerWith(registrar); - SharedPreferencesPlugin.registerWith(registrar); - UrlLauncherPlugin.registerWith(registrar); - registrar.registerMessageHandler(); -} diff --git a/frontend/lib/routes/company/banner/CompanyBanner.dart b/frontend/lib/routes/company/banner/CompanyBanner.dart index a9557df1..2ab6c7d0 100644 --- a/frontend/lib/routes/company/banner/CompanyBanner.dart +++ b/frontend/lib/routes/company/banner/CompanyBanner.dart @@ -133,7 +133,7 @@ class CompanyBanner extends StatelessWidget { company.name, style: TextStyle( overflow: TextOverflow.ellipsis, - ).merge(Theme.of(context).textTheme.headline5), + ).merge(Theme.of(context).textTheme.headlineSmall), ), IconButton( onPressed: () => Clipboard.setData( diff --git a/frontend/lib/routes/company/banner/CompanyStatusDropdownButton.dart b/frontend/lib/routes/company/banner/CompanyStatusDropdownButton.dart index a4aae2e1..b8c09e3a 100644 --- a/frontend/lib/routes/company/banner/CompanyStatusDropdownButton.dart +++ b/frontend/lib/routes/company/banner/CompanyStatusDropdownButton.dart @@ -33,7 +33,7 @@ class CompanyStatusDropdownButton extends StatelessWidget { decoration: BoxDecoration(color: STATUSCOLOR[companyStatus]), ), value: steps[0], - style: Theme.of(context).textTheme.subtitle2, + style: Theme.of(context).textTheme.titleSmall, selectedItemBuilder: (BuildContext context) { return steps.map((e) { return Align( diff --git a/frontend/lib/routes/speaker/banner/SpeakerBanner.dart b/frontend/lib/routes/speaker/banner/SpeakerBanner.dart index 825a1456..9630745c 100644 --- a/frontend/lib/routes/speaker/banner/SpeakerBanner.dart +++ b/frontend/lib/routes/speaker/banner/SpeakerBanner.dart @@ -133,7 +133,7 @@ class SpeakerBanner extends StatelessWidget { speaker.name, style: TextStyle( overflow: TextOverflow.ellipsis, - ).merge(Theme.of(context).textTheme.headline5), + ).merge(Theme.of(context).textTheme.headlineSmall), ), IconButton( onPressed: () => Clipboard.setData(ClipboardData(text: speaker.name)).then((_) => ScaffoldMessenger.of(context).showSnackBar( @@ -148,7 +148,7 @@ class SpeakerBanner extends StatelessWidget { speaker.title!, style: TextStyle( overflow: TextOverflow.ellipsis, - ).merge(Theme.of(context).textTheme.subtitle1), + ).merge(Theme.of(context).textTheme.titleMedium), ), if (isLatestEvent && hasParticipation) SpeakerStatusDropdownButton( diff --git a/frontend/lib/routes/speaker/banner/SpeakerStatusDropdownButton.dart b/frontend/lib/routes/speaker/banner/SpeakerStatusDropdownButton.dart index 26216450..67b8ad1d 100644 --- a/frontend/lib/routes/speaker/banner/SpeakerStatusDropdownButton.dart +++ b/frontend/lib/routes/speaker/banner/SpeakerStatusDropdownButton.dart @@ -33,7 +33,7 @@ class SpeakerStatusDropdownButton extends StatelessWidget { decoration: BoxDecoration(color: STATUSCOLOR[speakerStatus]), ), value: steps[0], - style: Theme.of(context).textTheme.subtitle2, + style: Theme.of(context).textTheme.titleSmall, selectedItemBuilder: (BuildContext context) { return steps.map((e) { return Align( From d68387010e6cc4cc7b7131dd5ac6fdce4995780f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rom=C3=A3o?= Date: Tue, 9 Apr 2024 15:37:31 +0100 Subject: [PATCH 6/7] fix public event model --- backend/src/models/event.go | 2 ++ backend/src/mongodb/event.go | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/backend/src/models/event.go b/backend/src/models/event.go index 2c8cf420..dff913fc 100644 --- a/backend/src/models/event.go +++ b/backend/src/models/event.go @@ -86,6 +86,8 @@ type EventPublic struct { // Example: index 1 corresponds to monday, index 2 to tuesday, etc). // The themes can be "Software Engineer", "Security", "Gaming", etc. Themes []string `json:"themes" bson:"themes"` + + CalendarUrl string `json:"calendarUrl" bson:"calendarUrl"` } // DurationInDays returns the duration of the event in days. diff --git a/backend/src/mongodb/event.go b/backend/src/mongodb/event.go index b81bc9a6..2b8387f7 100644 --- a/backend/src/mongodb/event.go +++ b/backend/src/mongodb/event.go @@ -78,11 +78,12 @@ func (e *EventsType) GetCurrentEvent() (*models.Event, error) { func eventToPublic(event models.Event) *models.EventPublic { public := models.EventPublic{ - ID: event.ID, - Name: event.Name, - Begin: event.Begin, - End: event.End, - Themes: event.Themes, + ID: event.ID, + Name: event.Name, + Begin: event.Begin, + End: event.End, + Themes: event.Themes, + CalendarUrl: event.CalendarUrl, } return &public From b339c1e8fadcedd2568666e0959af4bff31990e8 Mon Sep 17 00:00:00 2001 From: Pedro Maximino Date: Wed, 10 Apr 2024 15:53:36 +0200 Subject: [PATCH 7/7] fix: Revert usage of specific flutter version --- frontend/Dockerfile | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 69baab28..e55585cf 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -5,15 +5,14 @@ RUN apt-get install -y curl git wget unzip libgconf-2-4 gdb libstdc++6 libglu1-m RUN apt-get clean # Clone the flutter repo -# Temporary fix while the cuppertino_icons package is not fixed to work with Flutter 3.13.0 (latest stable release). -RUN git clone --branch 3.10.6 https://github.com/flutter/flutter.git /usr/local/flutter +RUN git clone https://github.com/flutter/flutter.git /usr/local/flutter # Set flutter path ENV PATH="/usr/local/flutter/bin:/usr/local/flutter/bin/cache/dart-sdk/bin:${PATH}" # Enable flutter web -# RUN flutter channel stable -# RUN flutter upgrade +RUN flutter channel stable +RUN flutter upgrade RUN flutter config --enable-web # Run flutter doctor