Skip to content

Commit f7add47

Browse files
committed
Group by room and redo UI
1 parent 2cc3ca2 commit f7add47

File tree

5 files changed

+170
-90
lines changed

5 files changed

+170
-90
lines changed

PocketDDD.BlazorClient/PocketDDD.BlazorClient/Features/HeaderBar/Components/HeaderBar.razor

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,29 @@
55
@inject IDispatcher Dispatcher
66
@inject IState<HeaderBarState> State
77

8-
<MudAppBar Dense="true" DisableGutters="true" Class="pl-1">
8+
<MudAppBar Color="Color.Primary">
99
@if (State.Value.ShowBackButton)
1010
{
1111
<MudIconButton
1212
Icon="@Icons.Material.Filled.ArrowBack"
13-
OnClick="HandleNavigateBack"
14-
Class="pl-0"/>
13+
Edge="Edge.Start"
14+
Color="Color.Inherit"
15+
OnClick="HandleNavigateBack"/>
1516
}
1617

17-
<MudText Typo="Typo.h6" Class="pl-0">@State.Value.Title</MudText>
18+
<MudText Typo="Typo.h5">@State.Value.Title</MudText>
1819
<MudSpacer />
1920

2021
@if (State.Value.ShowFeedbackButton)
2122
{
22-
<MudIconButton
23-
Icon="@Icons.Material.Filled.TagFaces"
23+
<MudButton
24+
EndIcon="@Icons.Material.Filled.TagFaces"
25+
Size="Size.Small"
2426
OnClick="HandleShowEventFeedback"
25-
Color="Color.Inherit" />
27+
Color="Color.Inherit"
28+
Variant="Variant.Outlined">
29+
Feedback
30+
</MudButton>
2631
}
2732

2833
</MudAppBar>
Lines changed: 86 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,80 @@
11
@using PocketDDD.BlazorClient.Features.Home.Store
2-
@using Session = PocketDDD.BlazorClient.Features.Home.Store.Session
32
@inherits FluxorComponent
43

54
@inject NavigationManager NavigationManager
65
@inject IState<HomeState> State
76
@inject IDispatcher Dispatcher
87

9-
<MudList Clickable="true">
10-
@foreach (var timeSlot in State.Value.EventMetaData)
8+
<MudList Class="pt-0 mud-background-gray" Dense="true">
9+
@foreach (var timeSlot in State.Value.Timeslots)
1110
{
12-
<MudListSubheader Class="pb-0 pl-1 border-b border-solid mud-border-primary">
13-
<MudText Typo="Typo.h6">
14-
@timeSlot.From.ToString("h:mm")
15-
@timeSlot.From.ToString("tt").ToLowerInvariant()
16-
</MudText>
11+
<MudListSubheader Class="pt-2 pb-1">
12+
<MudPaper Width="fit-content" Class="mud-theme-primary border-solid py-2 px-6 my-0">
13+
<MudText Typo="Typo.h6" Align="Align.Center">
14+
@timeSlot.From.LocalDateTime.ToString("h:mm")
15+
@timeSlot.From.LocalDateTime.ToString("tt").ToLowerInvariant()
16+
</MudText>
17+
</MudPaper>
1718
</MudListSubheader>
18-
@if (timeSlot.Info is not null)
19+
20+
if (timeSlot.Info is not null)
1921
{
20-
<MudListItem Class="ml-6">
21-
<MudText Typo="Typo.h5">@timeSlot.Info</MudText>
22+
<MudListItem>
23+
<MudPaper Outlined="true" Class="px-4 py-2">
24+
<MudText Typo="Typo.h6">@timeSlot.Info</MudText>
25+
</MudPaper>
2226
</MudListItem>
2327
}
2428

25-
@foreach (var sessionItem in timeSlot.Sessions.Select((session, index) => (session, index)))
29+
foreach (var room in timeSlot.Rooms)
2630
{
2731
<MudListItem>
28-
<MudCard>
29-
<MudCardHeader>
32+
<MudCard Outlined="true">
33+
<MudCardHeader Class="pb-0">
3034
<CardHeaderContent>
31-
<MudText Typo="Typo.h6">
32-
@sessionItem.session.Title
35+
<MudText Typo="Typo.body1">
36+
@room.RoomName
3337
</MudText>
38+
<MudDivider Class="@GetTrackDividerStylesForRoom(room)"
39+
DividerType="DividerType.FullWidth"/>
3440
</CardHeaderContent>
35-
<CardHeaderActions>
36-
<MudToggleIconButton
37-
ToggledChanged="@(_ => HandleToggleSessionBookmarked(sessionItem.session))"
38-
Toggled="@sessionItem.session.IsBookmarked"
39-
Icon="@Icons.Material.Filled.BookmarkBorder"
40-
Color="@Color.Default"
41-
ToggledIcon="@Icons.Material.Filled.Bookmark"
42-
ToggledColor="@Color.Primary"
43-
title="@(sessionItem.session.IsBookmarked ? "Unbookmark session" : "Bookmark session")"/>
44-
</CardHeaderActions>
4541
</MudCardHeader>
46-
<MudCardContent Class="d-flex flex-row justify-space-between">
47-
<MudContainer Class="px-0">
48-
<MudText Typo="Typo.subtitle2">@sessionItem.session.SpeakerName</MudText>
49-
<MudText Typo="Typo.subtitle2">@sessionItem.session.RoomName</MudText>
50-
</MudContainer>
51-
@ShowSessionLength(sessionItem.session)
42+
<MudCardContent Class="py-2">
43+
@for (var i = 0; i < room.Sessions.Count; i++)
44+
{
45+
var session = room.Sessions[i];
46+
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Stretch">
47+
<MudStack>
48+
<MudText Typo="Typo.h6">@session.Title</MudText>
49+
<MudText Typo="Typo.subtitle1">@session.SpeakerName</MudText>
50+
</MudStack>
51+
<MudStack Justify="Justify.SpaceBetween" AlignItems="AlignItems.End" Spacing="0">
52+
<MudToggleIconButton
53+
ToggledChanged="@(_ => HandleToggleSessionBookmarked(session))"
54+
Toggled="@session.IsBookmarked"
55+
Color="@Color.Default"
56+
Icon="@Icons.Material.Filled.BookmarkBorder"
57+
ToggledIcon="@Icons.Material.Filled.Bookmark"
58+
ToggledColor="@Color.Primary"
59+
title="@(session.IsBookmarked ? "Unbookmark session" : "Bookmark session")"/>
60+
@ShowSessionLength(session)
61+
</MudStack>
62+
</MudStack>
63+
<MudButton OnClick="() => HandleViewSession(session.Id)" FullWidth="true"
64+
Variant="Variant.Outlined" Class="my-2">
65+
More details
66+
</MudButton>
67+
@if (i < room.Sessions.Count - 1)
68+
{
69+
<MudDivider Class="@GetMultiTalkDividerStylesForRoom(room)"
70+
DividerType="DividerType.Middle"/>
71+
}
72+
}
5273
</MudCardContent>
53-
<MudCardActions>
54-
<MudButton OnClick="() => HandleViewSession(sessionItem.session.Id)" Variant="Variant.Outlined" Color="Color.Primary" FullWidth="true">
55-
More details
56-
</MudButton>
57-
</MudCardActions>
5874
</MudCard>
5975
</MudListItem>
60-
61-
@if (sessionItem.index != timeSlot.Sessions.Count - 1)
62-
{
63-
<MudDivider/>
64-
}
6576
}
6677
}
67-
6878
</MudList>
6979

7080
@code{
@@ -81,18 +91,30 @@
8191
return @<MudChip Color="@colour" Variant="Variant.Outlined">@GetTimeSpanDisplayText(session.Length)</MudChip>;
8292
}
8393

84-
}
85-
86-
@code {
94+
private string GetBorderColourForRoom(Room room)
95+
{
96+
string colour;
97+
98+
if (room.RoomName.Contains('1'))
99+
colour = "mud-border-tertiary";
100+
else if (room.RoomName.Contains('2'))
101+
colour = "mud-border-primary";
102+
else if (room.RoomName.Contains('3'))
103+
colour = "mud-border-secondary";
104+
else
105+
colour = "mud-border-info";
106+
107+
return colour;
108+
}
87109

88-
void HandleViewSession(int sessionId)
110+
private string GetTrackDividerStylesForRoom(Room room)
89111
{
90-
NavigationManager.NavigateTo($"session/{sessionId}");
112+
return $"{GetBorderColourForRoom(room)}";
91113
}
92114

93-
void HandleToggleSessionBookmarked(Session session)
115+
private string GetMultiTalkDividerStylesForRoom(Room room)
94116
{
95-
Dispatcher.Dispatch(new ToggleBookmarkedAction(session.Id, !session.IsBookmarked));
117+
return $"{GetBorderColourForRoom(room)} ma-4";
96118
}
97119

98120
private static string GetTimeSpanDisplayText(TimeSpan timeSpan)
@@ -106,3 +128,17 @@
106128
}
107129

108130
}
131+
132+
@code {
133+
134+
void HandleViewSession(int sessionId)
135+
{
136+
NavigationManager.NavigateTo($"session/{sessionId}");
137+
}
138+
139+
void HandleToggleSessionBookmarked(Session session)
140+
{
141+
Dispatcher.Dispatch(new ToggleBookmarkedAction(session.Id, !session.IsBookmarked));
142+
}
143+
144+
}
Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,66 @@
1-
using PocketDDD.Shared.API.ResponseDTOs;
2-
using System.Collections.Immutable;
3-
using System.Globalization;
1+
using System.Collections.Immutable;
2+
using PocketDDD.Shared.API.ResponseDTOs;
43

54
namespace PocketDDD.BlazorClient.Features.Home.Store;
65

76
public static class EventDataMapper
87
{
9-
public static IImmutableList<TimeSlot> ToHomeStateModel(this EventDataResponseDTO eventData, ICollection<int> sessionBookmarks) =>
10-
eventData.TimeSlots.Select(ts => new TimeSlot
8+
public static IImmutableList<TimeSlot> ToHomeStateModel(this EventDataResponseDTO eventData,
9+
ICollection<int> sessionBookmarks)
10+
{
11+
// The break timeslots don't have any sessions but still need to be included so starting from eventData.TimeSlots
12+
var allTimeSlotDtosSortedByTimeAndLength = eventData.TimeSlots.OrderBy(t => t.From).ThenByDescending(t => t.To);
13+
14+
// Group together the timeslots, rooms and sessions
15+
Dictionary<TimeSlot, List<(Room room, Session session)>> timeslotRoomSessions =
16+
allTimeSlotDtosSortedByTimeAndLength.ToDictionary(
17+
timeSlotDto => new TimeSlot { From = timeSlotDto.From, To = timeSlotDto.To, Info = timeSlotDto.Info },
18+
timeSlotDto => eventData.Sessions.Where(s => s.TimeSlotId == timeSlotDto.Id)
19+
.Select(s =>
20+
(
21+
new Room
22+
{
23+
RoomName = eventData.Tracks.Single(track => track.Id == s.TrackId).RoomName
24+
},
25+
new Session
26+
{
27+
Id = s.Id,
28+
From = timeSlotDto.From,
29+
To = timeSlotDto.To,
30+
Title = s.Title,
31+
SpeakerName = s.Speaker,
32+
IsBookmarked = sessionBookmarks.Contains(s.Id)
33+
})
34+
).ToList()
35+
);
36+
37+
//Deduplicate time slots
38+
// Sometimes one timeslot may overlap another e.g. 2 x 15 min sessions in one room and 1 x 30 min session in another at the same time
39+
// We want to take the longer timeslot
40+
var deduplicatedTimeSlotRoomSessions = new Dictionary<TimeSlot, List<(Room room, Session session)>>();
41+
foreach (var (timeSlot, roomsAndSessionsInTimeslot) in timeslotRoomSessions)
1142
{
12-
Id = ts.Id,
13-
From = ts.From.LocalDateTime,
14-
To = ts.To.LocalDateTime,
15-
Info = ts.Info,
16-
Sessions = eventData.Sessions
17-
.Where(s => s.TimeSlotId == ts.Id)
18-
.Select(s => new Session
19-
{
20-
Id = s.Id,
21-
SpeakerName = s.Speaker,
22-
Title = s.Title,
23-
TrackName = eventData.Tracks.Single(tr => tr.Id == s.TrackId).Name,
24-
RoomName = eventData.Tracks.Single(tr => tr.Id == s.TrackId).RoomName,
25-
IsBookmarked = sessionBookmarks.Contains(s.Id),
26-
Length = ts.To.Subtract(ts.From)
27-
})
28-
.OrderBy(s => s.TrackName)
29-
.ToImmutableList()
30-
})
31-
.OrderBy(ts => ts.From)
32-
.ToImmutableList();
43+
var encompassingTimeslot =
44+
deduplicatedTimeSlotRoomSessions.Keys.SingleOrDefault(k =>
45+
k.From <= timeSlot.From && k.To >= timeSlot.To);
46+
if (encompassingTimeslot is not null)
47+
deduplicatedTimeSlotRoomSessions[encompassingTimeslot].AddRange(roomsAndSessionsInTimeslot);
48+
else
49+
deduplicatedTimeSlotRoomSessions.Add(timeSlot, roomsAndSessionsInTimeslot);
50+
}
51+
52+
//And now group by room and fit them into the actual types we want to use
53+
var homeStateModel = deduplicatedTimeSlotRoomSessions.Select(timeslotRoomSession => timeslotRoomSession.Key with
54+
{
55+
Rooms = timeslotRoomSession.Value.GroupBy(tuple => tuple.room, tuple => tuple.session)
56+
.Select(g => g.Key with
57+
{
58+
Sessions = g.OrderBy(s => s.From).ToImmutableList()
59+
})
60+
.OrderBy(r => r.RoomName)
61+
.ToImmutableList()
62+
}).ToImmutableList();
63+
64+
return homeStateModel;
65+
}
3366
}

PocketDDD.BlazorClient/PocketDDD.BlazorClient/Features/Home/Store/HomeReducer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ state with
1616
{
1717
Loading = false,
1818
FailedToLoad = false,
19-
EventMetaData = action.EventData.ToHomeStateModel(action.SessionBookmarks)
19+
Timeslots = action.EventData.ToHomeStateModel(action.SessionBookmarks)
2020
};
2121

2222
//[ReducerMethod]

PocketDDD.BlazorClient/PocketDDD.BlazorClient/Features/Home/Store/HomeState.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,31 @@ public record HomeState
99
public bool Loading { get; init; } = true;
1010
public bool FailedToLoad { get; init; } = false;
1111

12-
public IImmutableList<TimeSlot> EventMetaData { get; init; } = ImmutableList<TimeSlot>.Empty;
12+
public IImmutableList<TimeSlot> Timeslots { get; init; } = ImmutableList<TimeSlot>.Empty;
1313
}
1414

1515
public record TimeSlot
1616
{
17-
public int Id { get; init; }
1817
public DateTimeOffset From { get; init; }
1918
public DateTimeOffset To { get; init; }
19+
public TimeSpan Length => To.Subtract(From);
2020
public string? Info { get; init; } = null;
21-
public IImmutableList<Session> Sessions { get; init; } = ImmutableList<Session>.Empty;
21+
public IImmutableList<Room> Rooms { get; init; } = ImmutableList<Room>.Empty;
2222
}
2323

2424
public record Session
2525
{
2626
public int Id { get; init; }
27+
public DateTimeOffset From { get; init; }
28+
public DateTimeOffset To { get; init; }
2729
public string Title { get; init; } = string.Empty;
2830
public string SpeakerName { get; init; } = string.Empty;
29-
public string TrackName { get; init; } = string.Empty;
30-
public string RoomName { get; init; } = string.Empty;
3131
public bool IsBookmarked { get; set; } = false;
32-
public TimeSpan Length { get; init; }
32+
public TimeSpan Length => To.Subtract(From);
33+
}
34+
35+
public record Room
36+
{
37+
public string RoomName { get; init; } = string.Empty;
38+
public IImmutableList<Session> Sessions { get; init; } = ImmutableList<Session>.Empty;
3339
}

0 commit comments

Comments
 (0)